[porthole] added connection state support

This commit is contained in:
Geoffrey McRae 2019-11-12 15:18:53 +11:00
parent 968b313993
commit 453b8e6a4d
3 changed files with 140 additions and 13 deletions

View File

@ -1 +1 @@
B1-31-gf4ad730cc4+1
B1-40-g968b313993+1

View File

@ -24,13 +24,20 @@ Place, Suite 330, Boston, MA 02111-1307 USA
typedef struct PortholeDev *PortholeDev;
typedef int PortholeID;
typedef enum PortholeState
{
PH_STATE_NEW_SESSION,
PH_STATE_CONNECTED,
PH_STATE_DISCONNECTED
}
PortholeState;
/**
* Open the porthole device
*
* @param handle The returned handle if successful, otherwise undefined
* @param vendor_id The subsystem vendor and device id to match
* @returns true on success
* @param handle The returned handle if successful, otherwise undefined
* @param vendor_id The subsystem vendor and device id to match
* @return true on success
*
* If successful the handle must be closed to free resources when finished.
*/
@ -45,14 +52,38 @@ bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id);
*/
void porthole_dev_close(PortholeDev *handle);
/**
* Get the state of the porthole device
*
* @param handle The porthole device handle obtained form porthole_dev_open
* @return The current state of the connection
*
* This method will return the current state of the porthole device
*
* * PH_STATE_NEW_SESSION = The client has connected
* * PH_STATE_CONNECTED = The client is still connected
* * PH_STATE_DISCONNECTED = There is no client connection
*/
PortholeState porthole_dev_get_state(PortholeDev handle);
/**
* Wait for the specified state
*
* @param handle The porthole device handle obtained from porthole_dev_open
* @param state The state to wait for
* @param timeout The maximum amount of time to wait in milliseconds for the state (0 = infinite)
* @return true on success, false on timeout
*/
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout);
/**
* Share the provided buffer over the porthole device
*
* @param handle The porthole device
* @param type The type
* @param buffer The buffer to share
* @param size The size of the buffer
* @returns the porthole mapping ID, or -1 on failure
* @param handle The porthole device
* @param type The type
* @param buffer The buffer to share
* @param size The size of the buffer
* @return the porthole mapping ID, or -1 on failure
*
* This function locks the supplied buffer in RAM via the porthole device
* driver and is then shared with the device for use outside the guest.
@ -73,9 +104,9 @@ PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffe
/**
* Unmap a previously shared buffer
*
* @param handle The porthole device
* @param id The porthole map id returned by porthole_dev_share
* @returns true on success
* @param handle The porthole device
* @param id The porthole map id returned by porthole_dev_share
* @return true on success
*
* Unmaps a previously shared buffer. Once this has been done the buffer can
* be freed or re-used. The client application should no longer attempt to

View File

@ -28,7 +28,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
struct PortholeDev
{
HANDLE dev;
HANDLE dev;
bool connected;
PortholeEvents events;
};
bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id)
@ -113,6 +115,22 @@ bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id)
(*handle)->dev = dev;
/* create the events and register them */
(*handle)->events.connect = CreateEvent(NULL, FALSE, FALSE, NULL);
(*handle)->events.disconnect = CreateEvent(NULL, FALSE, FALSE, NULL);
DWORD returned;
if (!DeviceIoControl(dev, IOCTL_PORTHOLE_REGISTER_EVENTS, &(*handle)->events, sizeof(PortholeEvents), NULL, 0, &returned, NULL))
{
DEBUG_ERROR("Failed to register the events");
CloseHandle((*handle)->events.connect );
CloseHandle((*handle)->events.disconnect);
CloseHandle(dev);
free(*handle);
*handle = NULL;
return false;
}
return true;
}
@ -120,11 +138,89 @@ void porthole_dev_close(PortholeDev *handle)
{
assert(handle && *handle);
CloseHandle((*handle)->events.connect );
CloseHandle((*handle)->events.disconnect);
CloseHandle((*handle)->dev);
free(*handle);
*handle = NULL;
}
static PortholeState get_state(PortholeDev handle, unsigned int timeout)
{
if (handle->connected)
{
switch(WaitForSingleObject(handle->events.disconnect, timeout))
{
case WAIT_OBJECT_0:
handle->connected = false;
return PH_STATE_DISCONNECTED;
case WAIT_TIMEOUT:
return PH_STATE_CONNECTED;
default:
DEBUG_FATAL("Error waiting on disconnect event");
break;
}
}
switch(WaitForSingleObject(handle->events.connect, timeout))
{
case WAIT_OBJECT_0:
handle->connected = true;
return PH_STATE_NEW_SESSION;
case WAIT_TIMEOUT:
return PH_STATE_DISCONNECTED;
default:
DEBUG_FATAL("Error waiting on connection event");
break;
}
}
PortholeState porthole_dev_get_state(PortholeDev handle)
{
return get_state(handle, 0);
}
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout)
{
const DWORD to = (timeout == 0) ? INFINITE : timeout;
PortholeState lastState = get_state(handle, 0);
if (state == lastState)
return true;
while(true)
{
PortholeState nextState;
switch(lastState)
{
case PH_STATE_DISCONNECTED:
nextState = PH_STATE_NEW_SESSION;
break;
case PH_STATE_NEW_SESSION:
nextState = PH_STATE_CONNECTED;
break;
case PH_STATE_CONNECTED:
nextState = PH_STATE_DISCONNECTED;
break;
}
PortholeState newState = get_state(handle, to);
if (newState == lastState || newState != nextState)
return false;
if (newState == state)
return true;
lastState = newState;
}
}
PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffer, size_t size)
{
assert(handle);