diff options
Diffstat (limited to 'src/VBox/Main/src-client/RemoteUSBBackend.cpp')
-rw-r--r-- | src/VBox/Main/src-client/RemoteUSBBackend.cpp | 1414 |
1 files changed, 1414 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/RemoteUSBBackend.cpp b/src/VBox/Main/src-client/RemoteUSBBackend.cpp new file mode 100644 index 00000000..8a8077f8 --- /dev/null +++ b/src/VBox/Main/src-client/RemoteUSBBackend.cpp @@ -0,0 +1,1414 @@ +/* $Id: RemoteUSBBackend.cpp $ */ +/** @file + * VirtualBox Remote USB backend + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_USB_REMOTE +#include "LoggingNew.h" + +#include "ConsoleImpl.h" +#include "ConsoleVRDPServer.h" +#include "RemoteUSBBackend.h" +#include "RemoteUSBDeviceImpl.h" + +#include <VBox/RemoteDesktop/VRDE.h> +#include <VBox/vrdpusb.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include <VBox/vusb.h> + +#include <iprt/time.h> + +/** @page pg_vrdb_usb Async Remote USB + * + * + * USB backend functions are called in EMT so care must be taken to prevent + * delays in the functions execution. + * + * Among 11 backend functions 10 just return a success indicator. + * + * Such a function usually will check pending error code and if everything is ok, + * submit asynchronous RDP request and return success immediately. + * + * On actual completion of each request, the status will be saved as + * pending, so in case of an error all further functions will fail with + * device disconnected condition. + * @todo May be a device disconnect notification for console is required? + * + * The only remaining function that needs special processing is + * the reap_urb. It has a timeout parameter. + * Normally, the timeout is 0, as result of polling from VUSB frame timer. + * It is ok for async processing, the backend will periodically reap urbs from client. + * And already reaped URBs from client will be returned for the call. + * Exceptions: + * 1) during device initialization, when obtaining device descriptions + * the timeout is -1, and the request is expected to be processed synchronously. + * It looks like only 3 URBs with some information are retrieved that way. + * Probably, one can return this information in DEVICE_LIST together with the + * device description and when such request are submitted, just return + * the prefetched data. + * 2) during suspend timeout is non zero (10 or less milliseconds), + * and URB's are reaped for about 1 second. But here network delays + * will not affect the timeout, so it is ok. + * + * + * @section sub_vrdb_usb_dad Device attaching/detaching + * + * Devices are attached when client is connected or when a new device is connected to client. + * Devices are detached when client is disconnected (all devices) or a device is disconnected + * the client side. + * + * The backend polls the client for list of attached USB devices from RemoteUSBThread. + * + */ + +/* Queued URB submitted to VRDP client. */ +typedef struct _REMOTEUSBQURB +{ + struct _REMOTEUSBQURB *next; + struct _REMOTEUSBQURB *prev; + + PREMOTEUSBDEVICE pDevice; /* Device, the URB is queued for. */ + + uint32_t u32Handle; /* The handle of the URB. Generated by the Remote USB backend. */ + + void *pvData; /* Pointer to URB data allocated by VUSB. */ + void *pvURB; /* Pointer to URB known to VUSB. */ + + uint32_t u32Len; /* Data length returned by the VRDP client. */ + uint32_t u32Err; /* URB error code returned by the VRDP client. */ + + bool fCompleted; /* The URB has been returned back by VRDP client. */ + bool fInput; /* This URB receives data from the client. */ + + uint32_t u32TransferredLen; /* For VRDE_USB_DIRECTION_OUT URBs = bytes written. + * For VRDE_USB_DIRECTION_IN URBs = bytes received. + */ +} REMOTEUSBQURB; + +/* Remote USB device instance data. */ +typedef struct _REMOTEUSBDEVICE +{ + struct _REMOTEUSBDEVICE *prev; + struct _REMOTEUSBDEVICE *next; + + RemoteUSBBackend *pOwner; + + VRDEUSBDEVID id; /* The remote identifier, assigned by client. */ + + uint32_t u32ClientId; /* The identifier of the remote client. */ + + REMOTEUSBQURB *pHeadQURBs; /* List of URBs queued for the device. */ + REMOTEUSBQURB *pTailQURBs; + + volatile uint32_t hURB; /* Source for URB's handles. */ + bool fFailed; /* True if an operation has failed for the device. */ + RTCRITSECT critsect; /* Protects the queued urb list. */ + volatile bool fWokenUp; /* Flag whther the reaper was woken up. */ +} REMOTEUSBDEVICE; + + + +static void requestDevice(REMOTEUSBDEVICE *pDevice) +{ + int vrc = RTCritSectEnter(&pDevice->critsect); + AssertRC(vrc); +} + +static void releaseDevice(REMOTEUSBDEVICE *pDevice) +{ + RTCritSectLeave(&pDevice->critsect); +} + +static REMOTEUSBQURB *qurbAlloc(PREMOTEUSBDEVICE pDevice) +{ + /** @todo reuse URBs. */ + REMOTEUSBQURB *pQURB = (REMOTEUSBQURB *)RTMemAllocZ (sizeof (REMOTEUSBQURB)); + + if (pQURB) + { + pQURB->pDevice = pDevice; + } + + return pQURB; +} + +static void qurbFree (REMOTEUSBQURB *pQURB) +{ + RTMemFree (pQURB); + return; +} + + +/* Called by VRDP server when the client responds to a request on USB channel. */ +DECLCALLBACK(int) USBClientResponseCallback(void *pv, uint32_t u32ClientId, uint8_t code, const void *pvRet, uint32_t cbRet) +{ + RT_NOREF(u32ClientId); + int vrc = VINF_SUCCESS; + + LogFlow(("USBClientResponseCallback: id = %d, pv = %p, code = %d, pvRet = %p, cbRet = %d\n", + u32ClientId, pv, code, pvRet, cbRet)); + + RemoteUSBBackend *pThis = (RemoteUSBBackend *)pv; + + switch (code) + { + case VRDE_USB_REQ_DEVICE_LIST: + { + vrc = pThis->saveDeviceList(pvRet, cbRet); + } break; + + case VRDE_USB_REQ_NEGOTIATE: + { + if (pvRet && cbRet >= sizeof(VRDEUSBREQNEGOTIATERET)) + { + VRDEUSBREQNEGOTIATERET *pret = (VRDEUSBREQNEGOTIATERET *)pvRet; + + vrc = pThis->negotiateResponse(pret, cbRet); + } + else + { + Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n", + pvRet, cbRet, sizeof(VRDEUSBREQNEGOTIATERET))); + + vrc = VERR_INVALID_PARAMETER; + } + } break; + + case VRDE_USB_REQ_REAP_URB: + { + vrc = pThis->reapURB(pvRet, cbRet); + + LogFlow(("USBClientResponseCallback: reap URB, rc = %Rrc.\n", vrc)); + } break; + + case VRDE_USB_REQ_QUEUE_URB: + case VRDE_USB_REQ_CLOSE: + case VRDE_USB_REQ_CANCEL_URB: + { + /* Do nothing, actually this should not happen. */ + Log(("USBClientResponseCallback: WARNING: response to a request %d is not expected!!!\n", code)); + } break; + + case VRDE_USB_REQ_OPEN: + case VRDE_USB_REQ_RESET: + case VRDE_USB_REQ_SET_CONFIG: + case VRDE_USB_REQ_CLAIM_INTERFACE: + case VRDE_USB_REQ_RELEASE_INTERFACE: + case VRDE_USB_REQ_INTERFACE_SETTING: + case VRDE_USB_REQ_CLEAR_HALTED_EP: + { + /* + * Device specific responses with status codes. + */ + if (pvRet && cbRet >= sizeof(VRDEUSBREQRETHDR)) + { + VRDEUSBREQRETHDR *pret = (VRDEUSBREQRETHDR *)pvRet; + + if (pret->status != VRDE_USB_STATUS_SUCCESS) + { + REMOTEUSBDEVICE *pDevice = pThis->deviceFromId(pret->id); + + if (!pDevice) + { + Log(("USBClientResponseCallback: WARNING: invalid device id %08X.\n", pret->id)); + vrc = VERR_INVALID_PARAMETER; + } + else + { + Log(("USBClientResponseCallback: WARNING: the operation failed, status %d\n", pret->status)); + pDevice->fFailed = true; + } + } + } + else + { + Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n", + pvRet, cbRet, sizeof(VRDEUSBREQRETHDR))); + } + } break; + + default: + { + Log(("USBClientResponseCallback: WARNING: invalid code %d\n", code)); + } break; + } + + return vrc; +} + +/* + * Backend entry points. + */ +static DECLCALLBACK(int) iface_Open(PREMOTEUSBBACKEND pInstance, const char *pszAddress, + size_t cbAddress, PREMOTEUSBDEVICE *ppDevice) +{ + RT_NOREF(cbAddress); + int vrc = VINF_SUCCESS; + + RemoteUSBBackend *pThis = (RemoteUSBBackend *)pInstance; + + REMOTEUSBDEVICE *pDevice = (REMOTEUSBDEVICE *)RTMemAllocZ(sizeof(REMOTEUSBDEVICE)); + + if (!pDevice) + { + vrc = VERR_NO_MEMORY; + } + else + { + /* Parse given address string to find the device identifier. + * The format is "REMOTEUSB0xAAAABBBB&0xCCCCDDDD", where AAAABBBB is hex device identifier + * and CCCCDDDD is hex client id. + */ + if (strncmp(pszAddress, REMOTE_USB_BACKEND_PREFIX_S, REMOTE_USB_BACKEND_PREFIX_LEN) != 0) + { + AssertFailed(); + vrc = VERR_INVALID_PARAMETER; + } + else + { + /* Initialize the device structure. */ + pDevice->pOwner = pThis; + pDevice->fWokenUp = false; + + vrc = RTCritSectInit(&pDevice->critsect); + AssertRC(vrc); + + if (RT_SUCCESS(vrc)) + { + pDevice->id = RTStrToUInt32(&pszAddress[REMOTE_USB_BACKEND_PREFIX_LEN]); + + size_t l = strlen(pszAddress); + + if (l >= REMOTE_USB_BACKEND_PREFIX_LEN + strlen("0x12345678&0x87654321")) + { + const char *p = &pszAddress[REMOTE_USB_BACKEND_PREFIX_LEN + strlen("0x12345678")]; + if (*p == '&') + { + pDevice->u32ClientId = RTStrToUInt32(p + 1); + } + else + { + AssertFailed(); + vrc = VERR_INVALID_PARAMETER; + } + } + else + { + AssertFailed(); + vrc = VERR_INVALID_PARAMETER; + } + + if (RT_SUCCESS(vrc)) + { + VRDE_USB_REQ_OPEN_PARM parm; + + parm.code = VRDE_USB_REQ_OPEN; + parm.id = pDevice->id; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + } + } + } + } + + if (RT_SUCCESS(vrc)) + { + *ppDevice = pDevice; + + pThis->addDevice(pDevice); + } + else + { + RTMemFree(pDevice); + } + + return vrc; +} + +static DECLCALLBACK(void) iface_Close(PREMOTEUSBDEVICE pDevice) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + VRDE_USB_REQ_CLOSE_PARM parm; + + parm.code = VRDE_USB_REQ_CLOSE; + parm.id = pDevice->id; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + pThis->removeDevice(pDevice); + + if (RTCritSectIsInitialized(&pDevice->critsect)) + { + RTCritSectDelete(&pDevice->critsect); + } + + RTMemFree(pDevice); + + return; +} + +static DECLCALLBACK(int) iface_Reset(PREMOTEUSBDEVICE pDevice) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_RESET_PARM parm; + + parm.code = VRDE_USB_REQ_RESET; + parm.id = pDevice->id; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) iface_SetConfig(PREMOTEUSBDEVICE pDevice, uint8_t u8Cfg) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_SET_CONFIG_PARM parm; + + parm.code = VRDE_USB_REQ_SET_CONFIG; + parm.id = pDevice->id; + parm.configuration = u8Cfg; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) iface_ClaimInterface(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_CLAIM_INTERFACE_PARM parm; + + parm.code = VRDE_USB_REQ_CLAIM_INTERFACE; + parm.id = pDevice->id; + parm.iface = u8Ifnum; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) iface_ReleaseInterface(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_RELEASE_INTERFACE_PARM parm; + + parm.code = VRDE_USB_REQ_RELEASE_INTERFACE; + parm.id = pDevice->id; + parm.iface = u8Ifnum; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) iface_InterfaceSetting(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum, uint8_t u8Setting) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_INTERFACE_SETTING_PARM parm; + + parm.code = VRDE_USB_REQ_INTERFACE_SETTING; + parm.id = pDevice->id; + parm.iface = u8Ifnum; + parm.setting = u8Setting; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) iface_ClearHaltedEP(PREMOTEUSBDEVICE pDevice, uint8_t u8Ep) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VRDE_USB_REQ_CLEAR_HALTED_EP_PARM parm; + + parm.code = VRDE_USB_REQ_CLEAR_HALTED_EP; + parm.id = pDevice->id; + parm.ep = u8Ep; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(void) iface_CancelURB(PREMOTEUSBDEVICE pDevice, PREMOTEUSBQURB pRemoteURB) +{ + RemoteUSBBackend *pThis = pDevice->pOwner; + + VRDE_USB_REQ_CANCEL_URB_PARM parm; + + parm.code = VRDE_USB_REQ_CANCEL_URB; + parm.id = pDevice->id; + parm.handle = pRemoteURB->u32Handle; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + + requestDevice(pDevice); + + /* Remove this urb from the queue. It is safe because if + * client will return the URB, it will be just ignored + * in reapURB. + */ + if (pRemoteURB->prev) + { + pRemoteURB->prev->next = pRemoteURB->next; + } + else + { + pDevice->pHeadQURBs = pRemoteURB->next; + } + + if (pRemoteURB->next) + { + pRemoteURB->next->prev = pRemoteURB->prev; + } + else + { + pDevice->pTailQURBs = pRemoteURB->prev; + } + + qurbFree(pRemoteURB); + + releaseDevice(pDevice); + + return; +} + +static DECLCALLBACK(int) iface_QueueURB(PREMOTEUSBDEVICE pDevice, uint8_t u8Type, uint8_t u8Ep, uint8_t u8Direction, + uint32_t u32Len, void *pvData, void *pvURB, PREMOTEUSBQURB *ppRemoteURB) +{ + int vrc = VINF_SUCCESS; + +#ifdef DEBUG_sunlover + LogFlow(("RemoteUSBBackend::iface_QueueURB: u8Type = %d, u8Ep = %d, u8Direction = %d, data\n%.*Rhxd\n", + u8Type, u8Ep, u8Direction, u32Len, pvData)); +#endif /* DEBUG_sunlover */ + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + RemoteUSBBackend *pThis = pDevice->pOwner; + + VRDE_USB_REQ_QUEUE_URB_PARM parm; + uint32_t u32Handle = 0; + uint32_t u32DataLen = 0; + + REMOTEUSBQURB *qurb = qurbAlloc(pDevice); + + if (qurb == NULL) + { + vrc = VERR_NO_MEMORY; + goto l_leave; + } + + /* + * Compute length of data which need to be transferred to the client. + */ + switch(u8Direction) + { + case VUSB_DIRECTION_IN: + { + if (u8Type == VUSBXFERTYPE_MSG) + { + u32DataLen = 8; /* 8 byte header. */ + // u32DataLen = u32Len; /// @todo do messages need all information? + } + } break; + + case VUSB_DIRECTION_OUT: + { + u32DataLen = u32Len; + } break; + + default: + { + AssertFailed(); + vrc = VERR_INVALID_PARAMETER; + goto l_leave; + } + } + + parm.code = VRDE_USB_REQ_QUEUE_URB; + parm.id = pDevice->id; + + u32Handle = pDevice->hURB++; + if (u32Handle == 0) + { + u32Handle = pDevice->hURB++; + } + + LogFlow(("RemoteUSBBackend::iface_QueueURB: handle = %d\n", u32Handle)); + + parm.handle = u32Handle; + + switch(u8Type) + { + case VUSBXFERTYPE_CTRL: parm.type = VRDE_USB_TRANSFER_TYPE_CTRL; break; + case VUSBXFERTYPE_ISOC: parm.type = VRDE_USB_TRANSFER_TYPE_ISOC; break; + case VUSBXFERTYPE_BULK: parm.type = VRDE_USB_TRANSFER_TYPE_BULK; break; + case VUSBXFERTYPE_INTR: parm.type = VRDE_USB_TRANSFER_TYPE_INTR; break; + case VUSBXFERTYPE_MSG: parm.type = VRDE_USB_TRANSFER_TYPE_MSG; break; + default: AssertFailed(); vrc = VERR_INVALID_PARAMETER; goto l_leave; + } + + parm.ep = u8Ep; + + switch(u8Direction) + { + case VUSB_DIRECTION_SETUP: AssertFailed(); parm.direction = VRDE_USB_DIRECTION_SETUP; break; + case VUSB_DIRECTION_IN: parm.direction = VRDE_USB_DIRECTION_IN; break; + case VUSB_DIRECTION_OUT: parm.direction = VRDE_USB_DIRECTION_OUT; break; + default: AssertFailed(); vrc = VERR_INVALID_PARAMETER; goto l_leave; + } + + parm.urblen = u32Len; + parm.datalen = u32DataLen; + + if (u32DataLen) + { + parm.data = pvData; + } + + requestDevice (pDevice); + + /* Add at tail of queued urb list. */ + qurb->next = NULL; + qurb->prev = pDevice->pTailQURBs; + qurb->u32Err = VRDE_USB_XFER_OK; + qurb->u32Len = u32Len; + qurb->pvData = pvData; + qurb->pvURB = pvURB; + qurb->u32Handle = u32Handle; + qurb->fCompleted = false; + qurb->fInput = (u8Direction == VUSB_DIRECTION_IN); + qurb->u32TransferredLen = 0; + + if (pDevice->pTailQURBs) + { + Assert(pDevice->pTailQURBs->next == NULL); + pDevice->pTailQURBs->next = qurb; + } + else + { + /* This is the first URB to be added. */ + Assert(pDevice->pHeadQURBs == NULL); + pDevice->pHeadQURBs = qurb; + } + + pDevice->pTailQURBs = qurb; + + releaseDevice(pDevice); + + *ppRemoteURB = qurb; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + +l_leave: + if (RT_FAILURE(vrc)) + { + qurbFree(qurb); + } + + return vrc; +} + +/* The function checks the URB queue for completed URBs. Also if the client + * has requested URB polling, the function will send URB poll requests. + */ +static DECLCALLBACK(int) iface_ReapURB(PREMOTEUSBDEVICE pDevice, uint32_t u32Millies, void **ppvURB, + uint32_t *pu32Len, uint32_t *pu32Err) +{ + int vrc = VINF_SUCCESS; + + LogFlow(("RemoteUSBBackend::iface_ReapURB %d ms\n", u32Millies)); + + if (pDevice->fFailed) + { + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + RemoteUSBBackend *pThis = pDevice->pOwner; + + /* Wait for transaction completion. */ + uint64_t u64StartTime = RTTimeMilliTS(); + + if (pThis->pollingEnabledURB()) + { + VRDE_USB_REQ_REAP_URB_PARM parm; + + parm.code = VRDE_USB_REQ_REAP_URB; + + pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm)); + } + + REMOTEUSBQURB *qurb = NULL; + + for (;;) + { + uint32_t u32ClientId; + + if (ASMAtomicXchgBool(&pDevice->fWokenUp, false)) + break; + + /* Scan queued URBs, look for completed. */ + requestDevice(pDevice); + + u32ClientId = pDevice->u32ClientId; + + qurb = pDevice->pHeadQURBs; + + while (qurb) + { + if (qurb->fCompleted) + { + /* Remove this completed urb from the queue. */ + if (qurb->prev) + { + qurb->prev->next = qurb->next; + } + else + { + pDevice->pHeadQURBs = qurb->next; + } + + if (qurb->next) + { + qurb->next->prev = qurb->prev; + } + else + { + pDevice->pTailQURBs = qurb->prev; + } + + qurb->next = NULL; + qurb->prev = NULL; + + break; + } + + qurb = qurb->next; + } + + releaseDevice(pDevice); + + if ( qurb + || !pDevice->pHeadQURBs + || u32Millies == 0 + || pDevice->fFailed + || (RTTimeMilliTS() - u64StartTime >= (uint64_t)u32Millies)) + { + /* Got an URB or do not have to wait for an URB. */ + break; + } + + LogFlow(("RemoteUSBBackend::iface_ReapURB iteration.\n")); + + RTThreadSleep(10); + + if (pThis->pollingEnabledURB()) + { + VRDE_USB_REQ_REAP_URB_PARM parm; + + parm.code = VRDE_USB_REQ_REAP_URB; + + pThis->VRDPServer()->SendUSBRequest(u32ClientId, &parm, sizeof(parm)); + } + } + + LogFlow(("RemoteUSBBackend::iface_ReapURB completed in %lld ms, qurb = %p\n", RTTimeMilliTS () - u64StartTime, qurb)); + + if (!qurb) + { + *ppvURB = NULL; + *pu32Len = 0; + *pu32Err = VUSBSTATUS_OK; + } + else + { + *ppvURB = qurb->pvURB; + *pu32Len = qurb->u32Len; + *pu32Err = qurb->u32Err; + +#ifdef LOG_ENABLED + Log(("URB len = %d, data = %p\n", qurb->u32Len, qurb->pvURB)); + if (qurb->u32Len) + { + Log(("Received URB content:\n%.*Rhxd\n", qurb->u32Len, qurb->pvData)); + } +#endif + + qurbFree(qurb); + } + + return vrc; +} + +static DECLCALLBACK(int) iface_Wakeup(PREMOTEUSBDEVICE pDevice) +{ + ASMAtomicXchgBool(&pDevice->fWokenUp, true); + return VINF_SUCCESS; +} + +void RemoteUSBBackend::AddRef(void) +{ + cRefs++; +} + +void RemoteUSBBackend::Release(void) +{ + cRefs--; + + if (cRefs <= 0) + { + delete this; + } +} + +void RemoteUSBBackend::PollRemoteDevices(void) +{ + if ( mfWillBeDeleted + && menmPollRemoteDevicesStatus != PollRemoteDevicesStatus_Dereferenced) + { + /* Unmount all remote USB devices. */ + mConsole->i_processRemoteUSBDevices(mu32ClientId, NULL, 0, false); + + menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_Dereferenced; + + Release(); + + return; + } + + switch(menmPollRemoteDevicesStatus) + { + case PollRemoteDevicesStatus_Negotiate: + { + VRDEUSBREQNEGOTIATEPARM parm; + + parm.code = VRDE_USB_REQ_NEGOTIATE; + parm.version = VRDE_USB_VERSION; + /* VRDE_USB_VERSION_3: support VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */ + parm.flags = VRDE_USB_SERVER_CAPS_PORT_VERSION; + + mServer->SendUSBRequest(mu32ClientId, &parm, sizeof(parm)); + + /* Reference the object. When the client disconnects and + * the backend is about to be deleted, the method must be called + * to disconnect the USB devices (as stated above). + */ + AddRef(); + + /* Goto the disabled state. When a response will be received + * the state will be changed to the SendRequest. + */ + menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_WaitNegotiateResponse; + } break; + + case PollRemoteDevicesStatus_WaitNegotiateResponse: + { + LogFlow(("USB::PollRemoteDevices: WaitNegotiateResponse\n")); + /* Do nothing. */ + } break; + + case PollRemoteDevicesStatus_SendRequest: + { + LogFlow(("USB::PollRemoteDevices: SendRequest\n")); + + /* Send a request for device list. */ + VRDE_USB_REQ_DEVICE_LIST_PARM parm; + + parm.code = VRDE_USB_REQ_DEVICE_LIST; + + mServer->SendUSBRequest(mu32ClientId, &parm, sizeof(parm)); + + menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_WaitResponse; + } break; + + case PollRemoteDevicesStatus_WaitResponse: + { + LogFlow(("USB::PollRemoteDevices: WaitResponse\n")); + + if (mfHasDeviceList) + { + mConsole->i_processRemoteUSBDevices(mu32ClientId, (VRDEUSBDEVICEDESC *)mpvDeviceList, mcbDeviceList, mfDescExt); + LogFlow(("USB::PollRemoteDevices: WaitResponse after process\n")); + + menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_SendRequest; + + mfHasDeviceList = false; + } + } break; + + case PollRemoteDevicesStatus_Dereferenced: + { + LogFlow(("USB::PollRemoteDevices: Dereferenced\n")); + /* Do nothing. */ + } break; + + default: + { + AssertFailed(); + } break; + } +} + +void RemoteUSBBackend::NotifyDelete(void) +{ + mfWillBeDeleted = true; +} + +/* + * The backend maintains a list of UUIDs of devices + * which are managed by the backend. + */ +bool RemoteUSBBackend::addUUID(const Guid *pUuid) +{ + unsigned i; + for (i = 0; i < RT_ELEMENTS(aGuids); i++) + { + if (aGuids[i].isZero()) + { + aGuids[i] = *pUuid; + return true; + } + } + + return false; +} + +bool RemoteUSBBackend::findUUID(const Guid *pUuid) +{ + unsigned i; + for (i = 0; i < RT_ELEMENTS(aGuids); i++) + { + if (aGuids[i] == *pUuid) + { + return true; + } + } + + return false; +} + +void RemoteUSBBackend::removeUUID(const Guid *pUuid) +{ + unsigned i; + for (i = 0; i < RT_ELEMENTS(aGuids); i++) + { + if (aGuids[i] == *pUuid) + { + aGuids[i].clear(); + break; + } + } +} + +RemoteUSBBackend::RemoteUSBBackend(Console *console, ConsoleVRDPServer *server, uint32_t u32ClientId) + : + mConsole(console), + mServer(server), + cRefs(0), + mu32ClientId(u32ClientId), + mfHasDeviceList(false), + mpvDeviceList(NULL), + mcbDeviceList(0), + menmPollRemoteDevicesStatus(PollRemoteDevicesStatus_Negotiate), + mfPollURB(true), + mpDevices(NULL), + mfWillBeDeleted(false), + mClientVersion(0), /* VRDE_USB_VERSION_2: the client version. */ + mfDescExt(false) /* VRDE_USB_VERSION_3: VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */ +{ + Assert(console); + Assert(server); + + int vrc = RTCritSectInit(&mCritsect); + + if (RT_FAILURE(vrc)) + { + AssertFailed(); + RT_ZERO(mCritsect); + } + + mCallback.pInstance = (PREMOTEUSBBACKEND)this; + mCallback.pfnOpen = iface_Open; + mCallback.pfnClose = iface_Close; + mCallback.pfnReset = iface_Reset; + mCallback.pfnSetConfig = iface_SetConfig; + mCallback.pfnClaimInterface = iface_ClaimInterface; + mCallback.pfnReleaseInterface = iface_ReleaseInterface; + mCallback.pfnInterfaceSetting = iface_InterfaceSetting; + mCallback.pfnQueueURB = iface_QueueURB; + mCallback.pfnReapURB = iface_ReapURB; + mCallback.pfnClearHaltedEP = iface_ClearHaltedEP; + mCallback.pfnCancelURB = iface_CancelURB; + mCallback.pfnWakeup = iface_Wakeup; +} + +RemoteUSBBackend::~RemoteUSBBackend() +{ + Assert(cRefs == 0); + + if (RTCritSectIsInitialized(&mCritsect)) + { + RTCritSectDelete(&mCritsect); + } + + RTMemFree(mpvDeviceList); + + mServer->usbBackendRemoveFromList(this); +} + +int RemoteUSBBackend::negotiateResponse(const VRDEUSBREQNEGOTIATERET *pret, uint32_t cbRet) +{ + int vrc = VINF_SUCCESS; + + Log(("RemoteUSBBackend::negotiateResponse: flags = %02X.\n", pret->flags)); + + LogRel(("Remote USB: Received negotiate response. Flags 0x%02X.\n", + pret->flags)); + + if (pret->flags & VRDE_USB_CAPS_FLAG_POLL) + { + Log(("RemoteUSBBackend::negotiateResponse: client requested URB polling.\n")); + mfPollURB = true; + } + else + { + mfPollURB = false; + } + + /* VRDE_USB_VERSION_2: check the client version. */ + if (pret->flags & VRDE_USB_CAPS2_FLAG_VERSION) + { + /* This could be a client version > 1. */ + if (cbRet >= sizeof(VRDEUSBREQNEGOTIATERET_2)) + { + VRDEUSBREQNEGOTIATERET_2 *pret2 = (VRDEUSBREQNEGOTIATERET_2 *)pret; + + if (pret2->u32Version <= VRDE_USB_VERSION) + { + /* This is OK. The client wants a version supported by the server. */ + mClientVersion = pret2->u32Version; + } + else + { + LogRel(("VRDP: ERROR: unsupported remote USB protocol client version %d.\n", pret2->u32Version)); + vrc = VERR_NOT_SUPPORTED; + } + } + else + { + LogRel(("VRDP: ERROR: invalid remote USB negotiate request packet size %d.\n", cbRet)); + vrc = VERR_NOT_SUPPORTED; + } + } + else + { + /* This is a client version 1. */ + mClientVersion = VRDE_USB_VERSION_1; + } + + if (RT_SUCCESS(vrc)) + { + LogRel(("VRDP: remote USB protocol version %d.\n", mClientVersion)); + + /* VRDE_USB_VERSION_3: check the client capabilities: VRDE_USB_CLIENT_CAPS_*. */ + if (mClientVersion == VRDE_USB_VERSION_3) + { + if (cbRet >= sizeof(VRDEUSBREQNEGOTIATERET_3)) + { + VRDEUSBREQNEGOTIATERET_3 *pret3 = (VRDEUSBREQNEGOTIATERET_3 *)pret; + + mfDescExt = (pret3->u32Flags & VRDE_USB_CLIENT_CAPS_PORT_VERSION) != 0; + } + else + { + LogRel(("VRDP: ERROR: invalid remote USB negotiate request packet size %d.\n", cbRet)); + vrc = VERR_NOT_SUPPORTED; + } + } + + menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_SendRequest; + } + + return vrc; +} + +int RemoteUSBBackend::saveDeviceList(const void *pvList, uint32_t cbList) +{ + Log(("RemoteUSBBackend::saveDeviceList: pvList = %p, cbList = %d\n", pvList, cbList)); + + if (!mfHasDeviceList) + { + RTMemFree(mpvDeviceList); + mpvDeviceList = NULL; + + mcbDeviceList = cbList; + + if (cbList > 0) + { + mpvDeviceList = RTMemAlloc(cbList); + memcpy(mpvDeviceList, pvList, cbList); + } + + mfHasDeviceList = true; + } + + return VINF_SUCCESS; +} + +void RemoteUSBBackend::request(void) +{ + int vrc = RTCritSectEnter(&mCritsect); + AssertRC(vrc); +} + +void RemoteUSBBackend::release(void) +{ + RTCritSectLeave(&mCritsect); +} + +PREMOTEUSBDEVICE RemoteUSBBackend::deviceFromId(VRDEUSBDEVID id) +{ + request(); + + REMOTEUSBDEVICE *pDevice = mpDevices; + + while (pDevice && pDevice->id != id) + { + pDevice = pDevice->next; + } + + release(); + + return pDevice; +} + +void RemoteUSBBackend::addDevice(PREMOTEUSBDEVICE pDevice) +{ + request(); + + pDevice->next = mpDevices; + + if (mpDevices) + { + mpDevices->prev = pDevice; + } + + mpDevices = pDevice; + + release(); +} + +void RemoteUSBBackend::removeDevice(PREMOTEUSBDEVICE pDevice) +{ + request(); + + if (pDevice->prev) + { + pDevice->prev->next = pDevice->next; + } + else + { + mpDevices = pDevice->next; + } + + if (pDevice->next) + { + pDevice->next->prev = pDevice->prev; + } + + release(); +} + +int RemoteUSBBackend::reapURB(const void *pvBody, uint32_t cbBody) +{ + int vrc = VINF_SUCCESS; + + LogFlow(("RemoteUSBBackend::reapURB: pvBody = %p, cbBody = %d\n", pvBody, cbBody)); + + VRDEUSBREQREAPURBBODY *pBody = (VRDEUSBREQREAPURBBODY *)pvBody; + + /* 'pvBody' memory buffer can contain multiple URBs. */ + while (cbBody >= sizeof(VRDEUSBREQREAPURBBODY)) + { + Log(("RemoteUSBBackend::reapURB: id = %d, flags = %02X, error = %d, handle %d, len = %d.\n", + pBody->id, pBody->flags, pBody->error, pBody->handle, pBody->len)); + + uint8_t fu8ReapValidFlags; + + if (mClientVersion == VRDE_USB_VERSION_1 || mClientVersion == VRDE_USB_VERSION_2) + { + fu8ReapValidFlags = VRDE_USB_REAP_VALID_FLAGS; + } + else + { + fu8ReapValidFlags = VRDE_USB_REAP_VALID_FLAGS_3; + } + + /* Verify client's data. */ + if ( (pBody->flags & ~fu8ReapValidFlags) != 0 + || pBody->handle == 0) + { + LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid reply data. Skipping the reply.\n")); + vrc = VERR_INVALID_PARAMETER; + break; + } + + PREMOTEUSBDEVICE pDevice = deviceFromId(pBody->id); + + if (!pDevice) + { + LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid device id. Skipping the reply.\n")); + vrc = VERR_INVALID_PARAMETER; + break; + } + + uint32_t cbBodyData = 0; /* Data contained in the URB body structure for input URBs. i.e. beyond VRDEUSBREQREAPURBBODY. */ + + requestDevice(pDevice); + + /* Search the queued URB for given handle. */ + REMOTEUSBQURB *qurb = pDevice->pHeadQURBs; + + while (qurb && qurb->u32Handle != pBody->handle) + { + LogFlow(("RemoteUSBBackend::reapURB: searching: %p handle = %d.\n", qurb, qurb->u32Handle)); + qurb = qurb->next; + } + + if (!qurb) + { + LogFlow(("RemoteUSBBackend::reapURB: Queued URB not found, probably already canceled. Skipping the URB.\n")); + } + else + { + LogFlow(("RemoteUSBBackend::reapURB: qurb = %p, u32Err = %d\n", qurb, qurb->u32Err)); + + /* Update the URB error field, if it does not yet indicate an error. */ + if (qurb->u32Err == VUSBSTATUS_OK) + { + if (mClientVersion == VRDE_USB_VERSION_1) + { + switch(pBody->error) + { + case VRDE_USB_XFER_OK: qurb->u32Err = VUSBSTATUS_OK; break; + case VRDE_USB_XFER_STALL: qurb->u32Err = VUSBSTATUS_STALL; break; + case VRDE_USB_XFER_DNR: qurb->u32Err = VUSBSTATUS_DNR; break; + case VRDE_USB_XFER_CRC: qurb->u32Err = VUSBSTATUS_CRC; break; + default: Log(("RemoteUSBBackend::reapURB: Invalid error %d\n", pBody->error)); + qurb->u32Err = VUSBSTATUS_DNR; break; + } + } + else if ( mClientVersion == VRDE_USB_VERSION_2 + || mClientVersion == VRDE_USB_VERSION_3) + { + switch(pBody->error) + { + case VRDE_USB_XFER_OK: qurb->u32Err = VUSBSTATUS_OK; break; + case VRDE_USB_XFER_STALL: qurb->u32Err = VUSBSTATUS_STALL; break; + case VRDE_USB_XFER_DNR: qurb->u32Err = VUSBSTATUS_DNR; break; + case VRDE_USB_XFER_CRC: qurb->u32Err = VUSBSTATUS_CRC; break; + case VRDE_USB_XFER_DO: qurb->u32Err = VUSBSTATUS_DATA_OVERRUN; break; + case VRDE_USB_XFER_DU: qurb->u32Err = VUSBSTATUS_DATA_UNDERRUN; break; + + /* Unmapped errors. */ + case VRDE_USB_XFER_BS: + case VRDE_USB_XFER_DTM: + case VRDE_USB_XFER_PCF: + case VRDE_USB_XFER_UPID: + case VRDE_USB_XFER_BO: + case VRDE_USB_XFER_BU: + case VRDE_USB_XFER_ERR: + default: Log(("RemoteUSBBackend::reapURB: Invalid error %d\n", pBody->error)); + qurb->u32Err = VUSBSTATUS_DNR; break; + } + } + else + { + qurb->u32Err = VUSBSTATUS_DNR; + } + } + + /* Get the URB data. The URB is completed unless the client tells that this is a fragment of an IN URB. */ + bool fURBCompleted = true; + + if (qurb->fInput) + { + if (pBody->len <= cbBody - sizeof(VRDEUSBREQREAPURBBODY)) + cbBodyData = pBody->len; /* VRDE_USB_DIRECTION_IN URBs include some data. */ + else + { + cbBodyData = cbBody - sizeof(VRDEUSBREQREAPURBBODY); + qurb->u32Err = VUSBSTATUS_DNR; + } + + if (qurb->u32Err == VUSBSTATUS_OK) + { + LogFlow(("RemoteUSBBackend::reapURB: copying data %d bytes\n", pBody->len)); + if (pBody->len > qurb->u32Len - qurb->u32TransferredLen) + { + /* Received more data than expected for this URB. If there more fragments follow, + * they will be discarded because the URB handle will not be valid anymore. + */ + qurb->u32Err = VUSBSTATUS_DNR; + qurb->u32TransferredLen = qurb->u32Len; + } + else + { + memcpy ((uint8_t *)qurb->pvData + qurb->u32TransferredLen, &pBody[1], pBody->len); + qurb->u32TransferredLen += pBody->len; + } + + if ( qurb->u32Err == VUSBSTATUS_OK + && (pBody->flags & VRDE_USB_REAP_FLAG_FRAGMENT) != 0) + { + /* If the client sends fragmented packets, accumulate the URB data. */ + fURBCompleted = false; + } + } + } + else + qurb->u32TransferredLen += pBody->len; /* Update the value for OUT URBs. */ + + if (fURBCompleted) + { + /* Move the URB near the head of URB list, so that iface_ReapURB can + * find it faster. Note that the order of completion must be preserved! + */ + if (qurb->prev) + { + /* The URB is not in the head. Unlink it from its current position. */ + qurb->prev->next = qurb->next; + + if (qurb->next) + { + qurb->next->prev = qurb->prev; + } + else + { + pDevice->pTailQURBs = qurb->prev; + } + + /* And insert it to its new place. */ + if (pDevice->pHeadQURBs->fCompleted) + { + /* At least one other completed URB; insert after the + * last completed URB. + */ + REMOTEUSBQURB *prev_qurb = pDevice->pHeadQURBs; + while (prev_qurb->next && prev_qurb->next->fCompleted) + prev_qurb = prev_qurb->next; + + qurb->next = prev_qurb->next; + qurb->prev = prev_qurb; + + if (prev_qurb->next) + prev_qurb->next->prev = qurb; + else + pDevice->pTailQURBs = qurb; + prev_qurb->next = qurb; + } + else + { + /* No other completed URBs; insert at head. */ + qurb->next = pDevice->pHeadQURBs; + qurb->prev = NULL; + + pDevice->pHeadQURBs->prev = qurb; + pDevice->pHeadQURBs = qurb; + } + } + + qurb->u32Len = qurb->u32TransferredLen; /* Update the final length. */ + qurb->fCompleted = true; + } + } + + releaseDevice (pDevice); + + if (pBody->flags & VRDE_USB_REAP_FLAG_LAST) + { + break; + } + + /* There is probably a further URB body. */ + if (cbBodyData > cbBody - sizeof(VRDEUSBREQREAPURBBODY)) + { + vrc = VERR_INVALID_PARAMETER; + break; + } + + cbBody -= sizeof(VRDEUSBREQREAPURBBODY) + cbBodyData; + pBody = (VRDEUSBREQREAPURBBODY *)((uint8_t *)pBody + sizeof(VRDEUSBREQREAPURBBODY) + cbBodyData); + } + + LogFlow(("RemoteUSBBackend::reapURB: returns %Rrc\n", vrc)); + + return vrc; +} +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ |