RemoteUSBBackend.cpp revision 8f99c70cb179542005a5564026d7d5365032435c
/** @file
*
* VirtualBox Remote USB backend
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_USB_REMOTE
#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.
*
*
* @subsection 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 rc = RTCritSectEnter(&pDevice->critsect);
AssertRC(rc);
}
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)
{
int rc = 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:
{
rc = pThis->saveDeviceList (pvRet, cbRet);
} break;
case VRDE_USB_REQ_NEGOTIATE:
{
if (pvRet && cbRet >= sizeof (VRDEUSBREQNEGOTIATERET))
{
VRDEUSBREQNEGOTIATERET *pret = (VRDEUSBREQNEGOTIATERET *)pvRet;
rc = pThis->negotiateResponse (pret, cbRet);
}
else
{
Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n",
pvRet, cbRet, sizeof (VRDEUSBREQNEGOTIATERET)));
rc = VERR_INVALID_PARAMETER;
}
} break;
case VRDE_USB_REQ_REAP_URB:
{
rc = pThis->reapURB (pvRet, cbRet);
LogFlow(("USBClientResponseCallback: reap URB, rc = %Rrc.\n", rc));
} 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));
rc = 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 rc;
}
/*
* Backend entry points.
*/
static DECLCALLBACK(int) iface_Open (PREMOTEUSBBACKEND pInstance, const char *pszAddress, size_t cbAddress, PREMOTEUSBDEVICE *ppDevice)
{
int rc = VINF_SUCCESS;
RemoteUSBBackend *pThis = (RemoteUSBBackend *)pInstance;
REMOTEUSBDEVICE *pDevice = (REMOTEUSBDEVICE *)RTMemAllocZ (sizeof (REMOTEUSBDEVICE));
if (!pDevice)
{
rc = 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();
rc = VERR_INVALID_PARAMETER;
}
else
{
/* Initialize the device structure. */
pDevice->pOwner = pThis;
pDevice->fWokenUp = false;
rc = RTCritSectInit(&pDevice->critsect);
AssertRC(rc);
if (RT_SUCCESS(rc))
{
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 ();
rc = VERR_INVALID_PARAMETER;
}
}
else
{
AssertFailed ();
rc = VERR_INVALID_PARAMETER;
}
if (RT_SUCCESS(rc))
{
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(rc))
{
*ppDevice = pDevice;
pThis->addDevice (pDevice);
}
else
{
RTMemFree (pDevice);
}
return rc;
}
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 rc = 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)
{
rc = 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();
rc = 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(); rc = 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(); rc = 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(rc))
{
qurbFree (qurb);
}
return rc;
}
/* 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 rc = 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 rc;
}
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->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->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 rc = RTCritSectInit (&mCritsect);
if (RT_FAILURE(rc))
{
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 rc = 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));
rc = VERR_NOT_SUPPORTED;
}
}
else
{
LogRel(("VRDP: ERROR: invalid remote USB negotiate request packet size %d.\n", cbRet));
rc = VERR_NOT_SUPPORTED;
}
}
else
{
/* This is a client version 1. */
mClientVersion = VRDE_USB_VERSION_1;
}
if (RT_SUCCESS(rc))
{
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));
rc = VERR_NOT_SUPPORTED;
}
}
menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_SendRequest;
}
return rc;
}
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 rc = RTCritSectEnter(&mCritsect);
AssertRC(rc);
}
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 rc = VINF_SUCCESS;
LogFlow(("RemoteUSBBackend::reapURB: pvBody = %p, cbBody = %d\n", pvBody, cbBody));
VRDEUSBREQREAPURBBODY *pBody = (VRDEUSBREQREAPURBBODY *)pvBody;
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
|| sizeof (VRDEUSBREQREAPURBBODY) > cbBody
|| pBody->handle == 0)
{
LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid reply data. Skipping the reply.\n"));
rc = VERR_INVALID_PARAMETER;
break;
}
PREMOTEUSBDEVICE pDevice = deviceFromId (pBody->id);
if (!pDevice)
{
LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid device id. Skipping the reply.\n"));
rc = VERR_INVALID_PARAMETER;
break;
}
uint32_t cbBodyData = 0; /* Data contained in the URB body structure for input URBs. */
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\n", qurb));
/* Update the URB error field. */
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. */
bool fURBCompleted = true;
if (qurb->fInput)
{
cbBodyData = pBody->len; /* VRDE_USB_DIRECTION_IN URBs include some data. */
}
if ( qurb->u32Err == VUSBSTATUS_OK
&& qurb->fInput)
{
LogFlow(("RemoteUSBBackend::reapURB: copying data %d bytes\n", pBody->len));
uint32_t u32DataLen = qurb->u32TransferredLen + pBody->len;
if (u32DataLen > qurb->u32Len)
{
/* 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;
}
else
{
memcpy ((uint8_t *)qurb->pvData + qurb->u32TransferredLen, &pBody[1], 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;
}
}
qurb->u32TransferredLen += pBody->len; /* Update the value for all 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. */
uint32_t cbBodySize = sizeof (VRDEUSBREQREAPURBBODY) + cbBodyData;
if (cbBodySize > cbBody)
{
rc = VERR_INVALID_PARAMETER;
break;
}
pBody = (VRDEUSBREQREAPURBBODY *)((uint8_t *)pBody + cbBodySize);
cbBody -= cbBodySize;
}
LogFlow(("RemoteUSBBackend::reapURB: returns %Rrc\n", rc));
return rc;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */