From 453b8e6a4d6b129d20e9906bee7f746a42b16a6c Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 12 Nov 2019 15:18:53 +1100 Subject: [PATCH] [porthole] added connection state support --- VERSION | 2 +- porthole/include/porthole/device.h | 53 ++++++++++++---- porthole/src/windows/device.c | 98 +++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 13 deletions(-) diff --git a/VERSION b/VERSION index fd879717..c9e5550a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -B1-31-gf4ad730cc4+1 \ No newline at end of file +B1-40-g968b313993+1 \ No newline at end of file diff --git a/porthole/include/porthole/device.h b/porthole/include/porthole/device.h index b4979907..7bfa2a9c 100644 --- a/porthole/include/porthole/device.h +++ b/porthole/include/porthole/device.h @@ -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 diff --git a/porthole/src/windows/device.c b/porthole/src/windows/device.c index 8b1a0d50..7684cf60 100644 --- a/porthole/src/windows/device.c +++ b/porthole/src/windows/device.c @@ -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);