RemoteUSBBackend.cpp revision 01827ba01edc252c4e12c19c2cfc5703ff447cf1
/** @file
*
* VirtualBox Remote USB backend
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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_DEV_USB
#include "ConsoleImpl.h"
#include "ConsoleVRDPServer.h"
#include "RemoteUSBBackend.h"
#include "RemoteUSBDeviceImpl.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.
*
*
*
* 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;
void *pvData; /* Pointer to URB data allocated by VUSB. */
void *pvURB; /* Pointer to URB known to VUSB. */
bool fCompleted; /* The URB has been returned back by VRDP client. */
bool fInput; /* This URB receives data from the client. */
* For VRDE_USB_DIRECTION_IN URBs = bytes received.
*/
/* Remote USB device instance data. */
typedef struct _REMOTEUSBDEVICE
{
struct _REMOTEUSBDEVICE *prev;
struct _REMOTEUSBDEVICE *next;
bool fFailed; /* True if an operation has failed for the device. */
{
}
{
}
{
/* @todo reuse URBs. */
if (pQURB)
{
}
return 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",
switch (code)
{
case VRDE_USB_REQ_DEVICE_LIST:
{
} break;
case VRDE_USB_REQ_NEGOTIATE:
{
{
}
else
{
Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n",
}
} break;
case VRDE_USB_REQ_REAP_URB:
{
} 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. */
} break;
case VRDE_USB_REQ_OPEN:
case VRDE_USB_REQ_RESET:
case VRDE_USB_REQ_SET_CONFIG:
{
/*
* Device specific responses with status codes.
*/
{
{
if (!pDevice)
{
}
else
{
}
}
}
else
{
Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n",
}
} break;
default:
{
} 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;
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.
*/
{
AssertFailed();
}
else
{
/* Initialize the device structure. */
if (RT_SUCCESS(rc))
{
{
if (*p == '&')
{
}
else
{
AssertFailed ();
}
}
else
{
AssertFailed ();
}
if (RT_SUCCESS(rc))
{
}
}
}
}
if (RT_SUCCESS(rc))
{
}
else
{
}
return rc;
}
{
{
}
return;
}
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) iface_InterfaceSetting (PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum, uint8_t u8Setting)
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
return VINF_SUCCESS;
}
{
/* 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)
{
}
else
{
}
if (pRemoteURB->next)
{
}
else
{
}
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 */
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
uint32_t u32DataLen = 0;
{
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 = u32Len; // @todo do messages need all information?
}
} break;
case VUSB_DIRECTION_OUT:
{
u32DataLen = u32Len;
} break;
default:
{
AssertFailed();
goto l_leave;
}
}
if (u32Handle == 0)
{
}
switch(u8Type)
{
}
switch(u8Direction)
{
}
if (u32DataLen)
{
}
/* Add at tail of queued urb list. */
qurb->fCompleted = false;
qurb->u32TransferredLen = 0;
if (pDevice->pTailQURBs)
{
}
else
{
/* This is the first URB to be added. */
}
*ppRemoteURB = qurb;
if (RT_FAILURE(rc))
{
}
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;
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
/* Wait for transaction completion. */
if (pThis->pollingEnabledURB ())
{
}
for (;;)
{
/* Scan queued URBs, look for completed. */
while (qurb)
{
if (qurb->fCompleted)
{
/* Remove this completed urb from the queue. */
{
}
else
{
}
{
}
else
{
}
break;
}
}
if ( qurb
|| !pDevice->pHeadQURBs
|| u32Millies == 0
{
/* Got an URB or do not have to wait for an URB. */
break;
}
LogFlow(("RemoteUSBBackend::iface_ReapURB iteration.\n"));
RTThreadSleep (10);
if (pThis->pollingEnabledURB ())
{
}
}
LogFlow(("RemoteUSBBackend::iface_ReapURB completed in %lld ms, qurb = %p\n", RTTimeMilliTS () - u64StartTime, qurb));
if (!qurb)
{
*pu32Len = 0;
*pu32Err = VUSBSTATUS_OK;
}
else
{
#ifdef LOG_ENABLED
{
}
#endif
}
return rc;
}
void RemoteUSBBackend::AddRef (void)
{
cRefs++;
}
void RemoteUSBBackend::Release (void)
{
cRefs--;
if (cRefs <= 0)
{
delete this;
}
}
void RemoteUSBBackend::PollRemoteDevices (void)
{
if ( mfWillBeDeleted
{
/* Unmount all remote USB devices. */
Release ();
return;
}
switch (menmPollRemoteDevicesStatus)
{
{
/* VRDE_USB_VERSION_3: support VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */
/* 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.
*/
} break;
{
LogFlow(("USB::PollRemoteDevices: WaitNegotiateResponse\n"));
/* Do nothing. */
} break;
{
LogFlow(("USB::PollRemoteDevices: SendRequest\n"));
/* Send a request for device list. */
} break;
{
LogFlow(("USB::PollRemoteDevices: WaitResponse\n"));
if (mfHasDeviceList)
{
mConsole->processRemoteUSBDevices (mu32ClientId, (VRDEUSBDEVICEDESC *)mpvDeviceList, mcbDeviceList, mfDescExt);
LogFlow(("USB::PollRemoteDevices: WaitResponse after process\n"));
mfHasDeviceList = false;
}
} break;
{
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.
*/
{
unsigned i;
for (i = 0; i < RT_ELEMENTS(aGuids); i++)
{
{
return true;
}
}
return false;
}
{
unsigned i;
for (i = 0; i < RT_ELEMENTS(aGuids); i++)
{
{
return true;
}
}
return false;
}
{
unsigned i;
for (i = 0; i < RT_ELEMENTS(aGuids); i++)
{
{
break;
}
}
}
RemoteUSBBackend::RemoteUSBBackend(Console *console, ConsoleVRDPServer *server, uint32_t u32ClientId)
:
cRefs (0),
mfHasDeviceList (false),
mcbDeviceList (0),
mfPollURB (true),
mfWillBeDeleted (false),
mClientVersion (0), /* VRDE_USB_VERSION_2: the client version. */
mfDescExt (false) /* VRDE_USB_VERSION_3: VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */
{
if (RT_FAILURE(rc))
{
AssertFailed ();
}
}
{
if (RTCritSectIsInitialized (&mCritsect))
{
}
mServer->usbBackendRemoveFromList (this);
}
{
int rc = VINF_SUCCESS;
LogRel(("Remote USB: Received negotiate response. Flags 0x%02X.\n",
{
Log(("RemoteUSBBackend::negotiateResponse: client requested URB polling.\n"));
mfPollURB = true;
}
else
{
mfPollURB = false;
}
/* VRDE_USB_VERSION_2: check the client version. */
{
/* This could be a client version > 1. */
if (cbRet >= sizeof (VRDEUSBREQNEGOTIATERET_2))
{
{
/* This is OK. The client wants a version supported by the server. */
}
else
{
}
}
else
{
}
}
else
{
/* This is a client version 1. */
}
if (RT_SUCCESS(rc))
{
/* VRDE_USB_VERSION_3: check the client capabilities: VRDE_USB_CLIENT_CAPS_*. */
if (mClientVersion == VRDE_USB_VERSION_3)
{
if (cbRet >= sizeof (VRDEUSBREQNEGOTIATERET_3))
{
}
else
{
}
}
}
return rc;
}
{
if (!mfHasDeviceList)
{
if (cbList > 0)
{
}
mfHasDeviceList = true;
}
return VINF_SUCCESS;
}
void RemoteUSBBackend::request (void)
{
}
void RemoteUSBBackend::release (void)
{
}
{
request ();
{
}
release ();
return pDevice;
}
{
request ();
if (mpDevices)
{
}
release ();
}
{
request ();
{
}
else
{
}
{
}
release ();
}
{
int rc = VINF_SUCCESS;
while (cbBody >= sizeof (VRDEUSBREQREAPURBBODY))
{
Log(("RemoteUSBBackend::reapURB: id = %d, flags = %02X, error = %d, handle %d, len = %d.\n",
{
}
else
{
}
/* Verify client's data. */
|| sizeof (VRDEUSBREQREAPURBBODY) > cbBody
{
LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid reply data. Skipping the reply.\n"));
break;
}
if (!pDevice)
{
LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid device id. Skipping the reply.\n"));
break;
}
/* Search the queued URB for given handle. */
{
}
if (!qurb)
{
LogFlow(("RemoteUSBBackend::reapURB: Queued URB not found, probably already canceled. Skipping the URB.\n"));
}
else
{
/* Update the URB error field. */
if (mClientVersion == VRDE_USB_VERSION_1)
{
{
}
}
else if ( mClientVersion == VRDE_USB_VERSION_2
|| mClientVersion == VRDE_USB_VERSION_3)
{
{
/* 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:
}
}
else
{
}
/* Get the URB data. */
bool fURBCompleted = true;
{
}
{
{
/* 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.
*/
}
else
{
}
{
/* If the client sends fragmented packets, accumulate the URB data. */
fURBCompleted = false;
}
}
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!
*/
{
/* The URB is not in the head. Unlink it from its current position. */
{
}
else
{
}
/* And insert it to its new place. */
{
/* At least one other completed URB; insert after the
* last completed URB.
*/
else
}
else
{
/* No other completed URBs; insert at head. */
}
}
qurb->fCompleted = true;
}
}
{
break;
}
/* There is probably a further URB body. */
if (cbBodySize > cbBody)
{
break;
}
cbBody -= cbBodySize;
}
return rc;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */