VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/RemoteUSBBackend.cpp@ 63147

Last change on this file since 63147 was 62485, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.3 KB
Line 
1/* $Id: RemoteUSBBackend.cpp 62485 2016-07-22 18:36:43Z vboxsync $ */
2/** @file
3 * VirtualBox Remote USB backend
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_USB_REMOTE
19#include "ConsoleImpl.h"
20#include "ConsoleVRDPServer.h"
21#include "RemoteUSBBackend.h"
22#include "RemoteUSBDeviceImpl.h"
23
24#include <VBox/RemoteDesktop/VRDE.h>
25#include <VBox/vrdpusb.h>
26#include <VBox/err.h>
27#include <VBox/log.h>
28
29#include <VBox/vusb.h>
30
31#include <iprt/time.h>
32
33/** @page pg_vrdb_usb Async Remote USB
34 *
35 *
36 * USB backend functions are called in EMT so care must be taken to prevent
37 * delays in the functions execution.
38 *
39 * Among 11 backend functions 10 just return a success indicator.
40 *
41 * Such a function usually will check pending error code and if everything is ok,
42 * submit asynchronous RDP request and return success immediately.
43 *
44 * On actual completion of each request, the status will be saved as
45 * pending, so in case of an error all further functions will fail with
46 * device disconnected condition.
47 * @todo May be a device disconnect notification for console is required?
48 *
49 * The only remaining function that needs special processing is
50 * the reap_urb. It has a timeout parameter.
51 * Normally, the timeout is 0, as result of polling from VUSB frame timer.
52 * It is ok for async processing, the backend will periodically reap urbs from client.
53 * And already reaped URBs from client will be returned for the call.
54 * Exceptions:
55 * 1) during device initialization, when obtaining device descriptions
56 * the timeout is -1, and the request is expected to be processed synchronously.
57 * It looks like only 3 URBs with some information are retrieved that way.
58 * Probably, one can return this information in DEVICE_LIST together with the
59 * device description and when such request are submitted, just return
60 * the prefetched data.
61 * 2) during suspend timeout is non zero (10 or less milliseconds),
62 * and URB's are reaped for about 1 second. But here network delays
63 * will not affect the timeout, so it is ok.
64 *
65 *
66 * @subsection sub_vrdb_usb_dad Device attaching/detaching
67 *
68 * Devices are attached when client is connected or when a new device is connected to client.
69 * Devices are detached when client is disconnected (all devices) or a device is disconnected
70 * the client side.
71 *
72 * The backend polls the client for list of attached USB devices from RemoteUSBThread.
73 *
74 */
75
76/* Queued URB submitted to VRDP client. */
77typedef struct _REMOTEUSBQURB
78{
79 struct _REMOTEUSBQURB *next;
80 struct _REMOTEUSBQURB *prev;
81
82 PREMOTEUSBDEVICE pDevice; /* Device, the URB is queued for. */
83
84 uint32_t u32Handle; /* The handle of the URB. Generated by the Remote USB backend. */
85
86 void *pvData; /* Pointer to URB data allocated by VUSB. */
87 void *pvURB; /* Pointer to URB known to VUSB. */
88
89 uint32_t u32Len; /* Data length returned by the VRDP client. */
90 uint32_t u32Err; /* URB error code returned by the VRDP client. */
91
92 bool fCompleted; /* The URB has been returned back by VRDP client. */
93 bool fInput; /* This URB receives data from the client. */
94
95 uint32_t u32TransferredLen; /* For VRDE_USB_DIRECTION_OUT URBs = bytes written.
96 * For VRDE_USB_DIRECTION_IN URBs = bytes received.
97 */
98} REMOTEUSBQURB;
99
100/* Remote USB device instance data. */
101typedef struct _REMOTEUSBDEVICE
102{
103 struct _REMOTEUSBDEVICE *prev;
104 struct _REMOTEUSBDEVICE *next;
105
106 RemoteUSBBackend *pOwner;
107
108 VRDEUSBDEVID id; /* The remote identifier, assigned by client. */
109
110 uint32_t u32ClientId; /* The identifier of the remote client. */
111
112 REMOTEUSBQURB *pHeadQURBs; /* List of URBs queued for the device. */
113 REMOTEUSBQURB *pTailQURBs;
114
115 volatile uint32_t hURB; /* Source for URB's handles. */
116 bool fFailed; /* True if an operation has failed for the device. */
117 RTCRITSECT critsect; /* Protects the queued urb list. */
118 volatile bool fWokenUp; /* Flag whther the reaper was woken up. */
119} REMOTEUSBDEVICE;
120
121
122
123static void requestDevice(REMOTEUSBDEVICE *pDevice)
124{
125 int rc = RTCritSectEnter(&pDevice->critsect);
126 AssertRC(rc);
127}
128
129static void releaseDevice(REMOTEUSBDEVICE *pDevice)
130{
131 RTCritSectLeave(&pDevice->critsect);
132}
133
134static REMOTEUSBQURB *qurbAlloc(PREMOTEUSBDEVICE pDevice)
135{
136 /* @todo reuse URBs. */
137 REMOTEUSBQURB *pQURB = (REMOTEUSBQURB *)RTMemAllocZ (sizeof (REMOTEUSBQURB));
138
139 if (pQURB)
140 {
141 pQURB->pDevice = pDevice;
142 }
143
144 return pQURB;
145}
146
147static void qurbFree (REMOTEUSBQURB *pQURB)
148{
149 RTMemFree (pQURB);
150 return;
151}
152
153
154/* Called by VRDP server when the client responds to a request on USB channel. */
155DECLCALLBACK(int) USBClientResponseCallback(void *pv, uint32_t u32ClientId, uint8_t code, const void *pvRet, uint32_t cbRet)
156{
157 int rc = VINF_SUCCESS;
158
159 LogFlow(("USBClientResponseCallback: id = %d, pv = %p, code = %d, pvRet = %p, cbRet = %d\n",
160 u32ClientId, pv, code, pvRet, cbRet));
161
162 RemoteUSBBackend *pThis = (RemoteUSBBackend *)pv;
163
164 switch (code)
165 {
166 case VRDE_USB_REQ_DEVICE_LIST:
167 {
168 rc = pThis->saveDeviceList(pvRet, cbRet);
169 } break;
170
171 case VRDE_USB_REQ_NEGOTIATE:
172 {
173 if (pvRet && cbRet >= sizeof(VRDEUSBREQNEGOTIATERET))
174 {
175 VRDEUSBREQNEGOTIATERET *pret = (VRDEUSBREQNEGOTIATERET *)pvRet;
176
177 rc = pThis->negotiateResponse(pret, cbRet);
178 }
179 else
180 {
181 Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n",
182 pvRet, cbRet, sizeof(VRDEUSBREQNEGOTIATERET)));
183
184 rc = VERR_INVALID_PARAMETER;
185 }
186 } break;
187
188 case VRDE_USB_REQ_REAP_URB:
189 {
190 rc = pThis->reapURB(pvRet, cbRet);
191
192 LogFlow(("USBClientResponseCallback: reap URB, rc = %Rrc.\n", rc));
193 } break;
194
195 case VRDE_USB_REQ_QUEUE_URB:
196 case VRDE_USB_REQ_CLOSE:
197 case VRDE_USB_REQ_CANCEL_URB:
198 {
199 /* Do nothing, actually this should not happen. */
200 Log(("USBClientResponseCallback: WARNING: response to a request %d is not expected!!!\n", code));
201 } break;
202
203 case VRDE_USB_REQ_OPEN:
204 case VRDE_USB_REQ_RESET:
205 case VRDE_USB_REQ_SET_CONFIG:
206 case VRDE_USB_REQ_CLAIM_INTERFACE:
207 case VRDE_USB_REQ_RELEASE_INTERFACE:
208 case VRDE_USB_REQ_INTERFACE_SETTING:
209 case VRDE_USB_REQ_CLEAR_HALTED_EP:
210 {
211 /*
212 * Device specific responses with status codes.
213 */
214 if (pvRet && cbRet >= sizeof(VRDEUSBREQRETHDR))
215 {
216 VRDEUSBREQRETHDR *pret = (VRDEUSBREQRETHDR *)pvRet;
217
218 if (pret->status != VRDE_USB_STATUS_SUCCESS)
219 {
220 REMOTEUSBDEVICE *pDevice = pThis->deviceFromId(pret->id);
221
222 if (!pDevice)
223 {
224 Log(("USBClientResponseCallback: WARNING: invalid device id %08X.\n", pret->id));
225 rc = VERR_INVALID_PARAMETER;
226 }
227 else
228 {
229 Log(("USBClientResponseCallback: WARNING: the operation failed, status %d\n", pret->status));
230 pDevice->fFailed = true;
231 }
232 }
233 }
234 else
235 {
236 Log(("USBClientResponseCallback: WARNING: not enough data in response: pv = %p, cb = %d, expected %d.\n",
237 pvRet, cbRet, sizeof(VRDEUSBREQRETHDR)));
238 }
239 } break;
240
241 default:
242 {
243 Log(("USBClientResponseCallback: WARNING: invalid code %d\n", code));
244 } break;
245 }
246
247 return rc;
248}
249
250/*
251 * Backend entry points.
252 */
253static DECLCALLBACK(int) iface_Open(PREMOTEUSBBACKEND pInstance, const char *pszAddress,
254 size_t cbAddress, PREMOTEUSBDEVICE *ppDevice)
255{
256 int rc = VINF_SUCCESS;
257
258 RemoteUSBBackend *pThis = (RemoteUSBBackend *)pInstance;
259
260 REMOTEUSBDEVICE *pDevice = (REMOTEUSBDEVICE *)RTMemAllocZ(sizeof(REMOTEUSBDEVICE));
261
262 if (!pDevice)
263 {
264 rc = VERR_NO_MEMORY;
265 }
266 else
267 {
268 /* Parse given address string to find the device identifier.
269 * The format is "REMOTEUSB0xAAAABBBB&0xCCCCDDDD", where AAAABBBB is hex device identifier
270 * and CCCCDDDD is hex client id.
271 */
272 if (strncmp(pszAddress, REMOTE_USB_BACKEND_PREFIX_S, REMOTE_USB_BACKEND_PREFIX_LEN) != 0)
273 {
274 AssertFailed();
275 rc = VERR_INVALID_PARAMETER;
276 }
277 else
278 {
279 /* Initialize the device structure. */
280 pDevice->pOwner = pThis;
281 pDevice->fWokenUp = false;
282
283 rc = RTCritSectInit(&pDevice->critsect);
284 AssertRC(rc);
285
286 if (RT_SUCCESS(rc))
287 {
288 pDevice->id = RTStrToUInt32(&pszAddress[REMOTE_USB_BACKEND_PREFIX_LEN]);
289
290 size_t l = strlen(pszAddress);
291
292 if (l >= REMOTE_USB_BACKEND_PREFIX_LEN + strlen("0x12345678&0x87654321"))
293 {
294 const char *p = &pszAddress[REMOTE_USB_BACKEND_PREFIX_LEN + strlen("0x12345678")];
295 if (*p == '&')
296 {
297 pDevice->u32ClientId = RTStrToUInt32(p + 1);
298 }
299 else
300 {
301 AssertFailed();
302 rc = VERR_INVALID_PARAMETER;
303 }
304 }
305 else
306 {
307 AssertFailed();
308 rc = VERR_INVALID_PARAMETER;
309 }
310
311 if (RT_SUCCESS(rc))
312 {
313 VRDE_USB_REQ_OPEN_PARM parm;
314
315 parm.code = VRDE_USB_REQ_OPEN;
316 parm.id = pDevice->id;
317
318 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
319 }
320 }
321 }
322 }
323
324 if (RT_SUCCESS(rc))
325 {
326 *ppDevice = pDevice;
327
328 pThis->addDevice(pDevice);
329 }
330 else
331 {
332 RTMemFree(pDevice);
333 }
334
335 return rc;
336}
337
338static DECLCALLBACK(void) iface_Close(PREMOTEUSBDEVICE pDevice)
339{
340 RemoteUSBBackend *pThis = pDevice->pOwner;
341
342 VRDE_USB_REQ_CLOSE_PARM parm;
343
344 parm.code = VRDE_USB_REQ_CLOSE;
345 parm.id = pDevice->id;
346
347 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
348
349 pThis->removeDevice(pDevice);
350
351 if (RTCritSectIsInitialized(&pDevice->critsect))
352 {
353 RTCritSectDelete(&pDevice->critsect);
354 }
355
356 RTMemFree(pDevice);
357
358 return;
359}
360
361static DECLCALLBACK(int) iface_Reset(PREMOTEUSBDEVICE pDevice)
362{
363 RemoteUSBBackend *pThis = pDevice->pOwner;
364
365 if (pDevice->fFailed)
366 {
367 return VERR_VUSB_DEVICE_NOT_ATTACHED;
368 }
369
370 VRDE_USB_REQ_RESET_PARM parm;
371
372 parm.code = VRDE_USB_REQ_RESET;
373 parm.id = pDevice->id;
374
375 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
376
377 return VINF_SUCCESS;
378}
379
380static DECLCALLBACK(int) iface_SetConfig(PREMOTEUSBDEVICE pDevice, uint8_t u8Cfg)
381{
382 RemoteUSBBackend *pThis = pDevice->pOwner;
383
384 if (pDevice->fFailed)
385 {
386 return VERR_VUSB_DEVICE_NOT_ATTACHED;
387 }
388
389 VRDE_USB_REQ_SET_CONFIG_PARM parm;
390
391 parm.code = VRDE_USB_REQ_SET_CONFIG;
392 parm.id = pDevice->id;
393 parm.configuration = u8Cfg;
394
395 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
396
397 return VINF_SUCCESS;
398}
399
400static DECLCALLBACK(int) iface_ClaimInterface(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum)
401{
402 RemoteUSBBackend *pThis = pDevice->pOwner;
403
404 if (pDevice->fFailed)
405 {
406 return VERR_VUSB_DEVICE_NOT_ATTACHED;
407 }
408
409 VRDE_USB_REQ_CLAIM_INTERFACE_PARM parm;
410
411 parm.code = VRDE_USB_REQ_CLAIM_INTERFACE;
412 parm.id = pDevice->id;
413 parm.iface = u8Ifnum;
414
415 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
416
417 return VINF_SUCCESS;
418}
419
420static DECLCALLBACK(int) iface_ReleaseInterface(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum)
421{
422 RemoteUSBBackend *pThis = pDevice->pOwner;
423
424 if (pDevice->fFailed)
425 {
426 return VERR_VUSB_DEVICE_NOT_ATTACHED;
427 }
428
429 VRDE_USB_REQ_RELEASE_INTERFACE_PARM parm;
430
431 parm.code = VRDE_USB_REQ_RELEASE_INTERFACE;
432 parm.id = pDevice->id;
433 parm.iface = u8Ifnum;
434
435 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
436
437 return VINF_SUCCESS;
438}
439
440static DECLCALLBACK(int) iface_InterfaceSetting(PREMOTEUSBDEVICE pDevice, uint8_t u8Ifnum, uint8_t u8Setting)
441{
442 RemoteUSBBackend *pThis = pDevice->pOwner;
443
444 if (pDevice->fFailed)
445 {
446 return VERR_VUSB_DEVICE_NOT_ATTACHED;
447 }
448
449 VRDE_USB_REQ_INTERFACE_SETTING_PARM parm;
450
451 parm.code = VRDE_USB_REQ_INTERFACE_SETTING;
452 parm.id = pDevice->id;
453 parm.iface = u8Ifnum;
454 parm.setting = u8Setting;
455
456 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
457
458 return VINF_SUCCESS;
459}
460
461static DECLCALLBACK(int) iface_ClearHaltedEP(PREMOTEUSBDEVICE pDevice, uint8_t u8Ep)
462{
463 RemoteUSBBackend *pThis = pDevice->pOwner;
464
465 if (pDevice->fFailed)
466 {
467 return VERR_VUSB_DEVICE_NOT_ATTACHED;
468 }
469
470 VRDE_USB_REQ_CLEAR_HALTED_EP_PARM parm;
471
472 parm.code = VRDE_USB_REQ_CLEAR_HALTED_EP;
473 parm.id = pDevice->id;
474 parm.ep = u8Ep;
475
476 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
477
478 return VINF_SUCCESS;
479}
480
481static DECLCALLBACK(void) iface_CancelURB(PREMOTEUSBDEVICE pDevice, PREMOTEUSBQURB pRemoteURB)
482{
483 RemoteUSBBackend *pThis = pDevice->pOwner;
484
485 VRDE_USB_REQ_CANCEL_URB_PARM parm;
486
487 parm.code = VRDE_USB_REQ_CANCEL_URB;
488 parm.id = pDevice->id;
489 parm.handle = pRemoteURB->u32Handle;
490
491 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
492
493 requestDevice(pDevice);
494
495 /* Remove this urb from the queue. It is safe because if
496 * client will return the URB, it will be just ignored
497 * in reapURB.
498 */
499 if (pRemoteURB->prev)
500 {
501 pRemoteURB->prev->next = pRemoteURB->next;
502 }
503 else
504 {
505 pDevice->pHeadQURBs = pRemoteURB->next;
506 }
507
508 if (pRemoteURB->next)
509 {
510 pRemoteURB->next->prev = pRemoteURB->prev;
511 }
512 else
513 {
514 pDevice->pTailQURBs = pRemoteURB->prev;
515 }
516
517 qurbFree(pRemoteURB);
518
519 releaseDevice(pDevice);
520
521 return;
522}
523
524static DECLCALLBACK(int) iface_QueueURB(PREMOTEUSBDEVICE pDevice, uint8_t u8Type, uint8_t u8Ep, uint8_t u8Direction,
525 uint32_t u32Len, void *pvData, void *pvURB, PREMOTEUSBQURB *ppRemoteURB)
526{
527 int rc = VINF_SUCCESS;
528
529#ifdef DEBUG_sunlover
530 LogFlow(("RemoteUSBBackend::iface_QueueURB: u8Type = %d, u8Ep = %d, u8Direction = %d, data\n%.*Rhxd\n",
531 u8Type, u8Ep, u8Direction, u32Len, pvData));
532#endif /* DEBUG_sunlover */
533
534 if (pDevice->fFailed)
535 {
536 return VERR_VUSB_DEVICE_NOT_ATTACHED;
537 }
538
539 RemoteUSBBackend *pThis = pDevice->pOwner;
540
541 VRDE_USB_REQ_QUEUE_URB_PARM parm;
542 uint32_t u32Handle = 0;
543 uint32_t u32DataLen = 0;
544
545 REMOTEUSBQURB *qurb = qurbAlloc(pDevice);
546
547 if (qurb == NULL)
548 {
549 rc = VERR_NO_MEMORY;
550 goto l_leave;
551 }
552
553 /*
554 * Compute length of data which need to be transferred to the client.
555 */
556 switch(u8Direction)
557 {
558 case VUSB_DIRECTION_IN:
559 {
560 if (u8Type == VUSBXFERTYPE_MSG)
561 {
562 u32DataLen = 8; /* 8 byte header. */
563 // u32DataLen = u32Len; // @todo do messages need all information?
564 }
565 } break;
566
567 case VUSB_DIRECTION_OUT:
568 {
569 u32DataLen = u32Len;
570 } break;
571
572 default:
573 {
574 AssertFailed();
575 rc = VERR_INVALID_PARAMETER;
576 goto l_leave;
577 }
578 }
579
580 parm.code = VRDE_USB_REQ_QUEUE_URB;
581 parm.id = pDevice->id;
582
583 u32Handle = pDevice->hURB++;
584 if (u32Handle == 0)
585 {
586 u32Handle = pDevice->hURB++;
587 }
588
589 LogFlow(("RemoteUSBBackend::iface_QueueURB: handle = %d\n", u32Handle));
590
591 parm.handle = u32Handle;
592
593 switch(u8Type)
594 {
595 case VUSBXFERTYPE_CTRL: parm.type = VRDE_USB_TRANSFER_TYPE_CTRL; break;
596 case VUSBXFERTYPE_ISOC: parm.type = VRDE_USB_TRANSFER_TYPE_ISOC; break;
597 case VUSBXFERTYPE_BULK: parm.type = VRDE_USB_TRANSFER_TYPE_BULK; break;
598 case VUSBXFERTYPE_INTR: parm.type = VRDE_USB_TRANSFER_TYPE_INTR; break;
599 case VUSBXFERTYPE_MSG: parm.type = VRDE_USB_TRANSFER_TYPE_MSG; break;
600 default: AssertFailed(); rc = VERR_INVALID_PARAMETER; goto l_leave;
601 }
602
603 parm.ep = u8Ep;
604
605 switch(u8Direction)
606 {
607 case VUSB_DIRECTION_SETUP: AssertFailed(); parm.direction = VRDE_USB_DIRECTION_SETUP; break;
608 case VUSB_DIRECTION_IN: parm.direction = VRDE_USB_DIRECTION_IN; break;
609 case VUSB_DIRECTION_OUT: parm.direction = VRDE_USB_DIRECTION_OUT; break;
610 default: AssertFailed(); rc = VERR_INVALID_PARAMETER; goto l_leave;
611 }
612
613 parm.urblen = u32Len;
614 parm.datalen = u32DataLen;
615
616 if (u32DataLen)
617 {
618 parm.data = pvData;
619 }
620
621 requestDevice (pDevice);
622
623 /* Add at tail of queued urb list. */
624 qurb->next = NULL;
625 qurb->prev = pDevice->pTailQURBs;
626 qurb->u32Err = VRDE_USB_XFER_OK;
627 qurb->u32Len = u32Len;
628 qurb->pvData = pvData;
629 qurb->pvURB = pvURB;
630 qurb->u32Handle = u32Handle;
631 qurb->fCompleted = false;
632 qurb->fInput = (u8Direction == VUSB_DIRECTION_IN);
633 qurb->u32TransferredLen = 0;
634
635 if (pDevice->pTailQURBs)
636 {
637 Assert(pDevice->pTailQURBs->next == NULL);
638 pDevice->pTailQURBs->next = qurb;
639 }
640 else
641 {
642 /* This is the first URB to be added. */
643 Assert(pDevice->pHeadQURBs == NULL);
644 pDevice->pHeadQURBs = qurb;
645 }
646
647 pDevice->pTailQURBs = qurb;
648
649 releaseDevice(pDevice);
650
651 *ppRemoteURB = qurb;
652
653 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
654
655l_leave:
656 if (RT_FAILURE(rc))
657 {
658 qurbFree(qurb);
659 }
660
661 return rc;
662}
663
664/* The function checks the URB queue for completed URBs. Also if the client
665 * has requested URB polling, the function will send URB poll requests.
666 */
667static DECLCALLBACK(int) iface_ReapURB(PREMOTEUSBDEVICE pDevice, uint32_t u32Millies, void **ppvURB,
668 uint32_t *pu32Len, uint32_t *pu32Err)
669{
670 int rc = VINF_SUCCESS;
671
672 LogFlow(("RemoteUSBBackend::iface_ReapURB %d ms\n", u32Millies));
673
674 if (pDevice->fFailed)
675 {
676 return VERR_VUSB_DEVICE_NOT_ATTACHED;
677 }
678
679 RemoteUSBBackend *pThis = pDevice->pOwner;
680
681 /* Wait for transaction completion. */
682 uint64_t u64StartTime = RTTimeMilliTS();
683
684 if (pThis->pollingEnabledURB())
685 {
686 VRDE_USB_REQ_REAP_URB_PARM parm;
687
688 parm.code = VRDE_USB_REQ_REAP_URB;
689
690 pThis->VRDPServer()->SendUSBRequest(pDevice->u32ClientId, &parm, sizeof(parm));
691 }
692
693 REMOTEUSBQURB *qurb = NULL;
694
695 for (;;)
696 {
697 uint32_t u32ClientId;
698
699 if (ASMAtomicXchgBool(&pDevice->fWokenUp, false))
700 break;
701
702 /* Scan queued URBs, look for completed. */
703 requestDevice(pDevice);
704
705 u32ClientId = pDevice->u32ClientId;
706
707 qurb = pDevice->pHeadQURBs;
708
709 while (qurb)
710 {
711 if (qurb->fCompleted)
712 {
713 /* Remove this completed urb from the queue. */
714 if (qurb->prev)
715 {
716 qurb->prev->next = qurb->next;
717 }
718 else
719 {
720 pDevice->pHeadQURBs = qurb->next;
721 }
722
723 if (qurb->next)
724 {
725 qurb->next->prev = qurb->prev;
726 }
727 else
728 {
729 pDevice->pTailQURBs = qurb->prev;
730 }
731
732 qurb->next = NULL;
733 qurb->prev = NULL;
734
735 break;
736 }
737
738 qurb = qurb->next;
739 }
740
741 releaseDevice(pDevice);
742
743 if ( qurb
744 || !pDevice->pHeadQURBs
745 || u32Millies == 0
746 || pDevice->fFailed
747 || (RTTimeMilliTS() - u64StartTime >= (uint64_t)u32Millies))
748 {
749 /* Got an URB or do not have to wait for an URB. */
750 break;
751 }
752
753 LogFlow(("RemoteUSBBackend::iface_ReapURB iteration.\n"));
754
755 RTThreadSleep(10);
756
757 if (pThis->pollingEnabledURB())
758 {
759 VRDE_USB_REQ_REAP_URB_PARM parm;
760
761 parm.code = VRDE_USB_REQ_REAP_URB;
762
763 pThis->VRDPServer()->SendUSBRequest(u32ClientId, &parm, sizeof(parm));
764 }
765 }
766
767 LogFlow(("RemoteUSBBackend::iface_ReapURB completed in %lld ms, qurb = %p\n", RTTimeMilliTS () - u64StartTime, qurb));
768
769 if (!qurb)
770 {
771 *ppvURB = NULL;
772 *pu32Len = 0;
773 *pu32Err = VUSBSTATUS_OK;
774 }
775 else
776 {
777 *ppvURB = qurb->pvURB;
778 *pu32Len = qurb->u32Len;
779 *pu32Err = qurb->u32Err;
780
781#ifdef LOG_ENABLED
782 Log(("URB len = %d, data = %p\n", qurb->u32Len, qurb->pvURB));
783 if (qurb->u32Len)
784 {
785 Log(("Received URB content:\n%.*Rhxd\n", qurb->u32Len, qurb->pvData));
786 }
787#endif
788
789 qurbFree(qurb);
790 }
791
792 return rc;
793}
794
795static DECLCALLBACK(int) iface_Wakeup(PREMOTEUSBDEVICE pDevice)
796{
797 ASMAtomicXchgBool(&pDevice->fWokenUp, true);
798 return VINF_SUCCESS;
799}
800
801void RemoteUSBBackend::AddRef(void)
802{
803 cRefs++;
804}
805
806void RemoteUSBBackend::Release(void)
807{
808 cRefs--;
809
810 if (cRefs <= 0)
811 {
812 delete this;
813 }
814}
815
816void RemoteUSBBackend::PollRemoteDevices(void)
817{
818 if ( mfWillBeDeleted
819 && menmPollRemoteDevicesStatus != PollRemoteDevicesStatus_Dereferenced)
820 {
821 /* Unmount all remote USB devices. */
822 mConsole->i_processRemoteUSBDevices(mu32ClientId, NULL, 0, false);
823
824 menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_Dereferenced;
825
826 Release();
827
828 return;
829 }
830
831 switch(menmPollRemoteDevicesStatus)
832 {
833 case PollRemoteDevicesStatus_Negotiate:
834 {
835 VRDEUSBREQNEGOTIATEPARM parm;
836
837 parm.code = VRDE_USB_REQ_NEGOTIATE;
838 parm.version = VRDE_USB_VERSION;
839 /* VRDE_USB_VERSION_3: support VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */
840 parm.flags = VRDE_USB_SERVER_CAPS_PORT_VERSION;
841
842 mServer->SendUSBRequest(mu32ClientId, &parm, sizeof(parm));
843
844 /* Reference the object. When the client disconnects and
845 * the backend is about to be deleted, the method must be called
846 * to disconnect the USB devices (as stated above).
847 */
848 AddRef();
849
850 /* Goto the disabled state. When a response will be received
851 * the state will be changed to the SendRequest.
852 */
853 menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_WaitNegotiateResponse;
854 } break;
855
856 case PollRemoteDevicesStatus_WaitNegotiateResponse:
857 {
858 LogFlow(("USB::PollRemoteDevices: WaitNegotiateResponse\n"));
859 /* Do nothing. */
860 } break;
861
862 case PollRemoteDevicesStatus_SendRequest:
863 {
864 LogFlow(("USB::PollRemoteDevices: SendRequest\n"));
865
866 /* Send a request for device list. */
867 VRDE_USB_REQ_DEVICE_LIST_PARM parm;
868
869 parm.code = VRDE_USB_REQ_DEVICE_LIST;
870
871 mServer->SendUSBRequest(mu32ClientId, &parm, sizeof(parm));
872
873 menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_WaitResponse;
874 } break;
875
876 case PollRemoteDevicesStatus_WaitResponse:
877 {
878 LogFlow(("USB::PollRemoteDevices: WaitResponse\n"));
879
880 if (mfHasDeviceList)
881 {
882 mConsole->i_processRemoteUSBDevices(mu32ClientId, (VRDEUSBDEVICEDESC *)mpvDeviceList, mcbDeviceList, mfDescExt);
883 LogFlow(("USB::PollRemoteDevices: WaitResponse after process\n"));
884
885 menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_SendRequest;
886
887 mfHasDeviceList = false;
888 }
889 } break;
890
891 case PollRemoteDevicesStatus_Dereferenced:
892 {
893 LogFlow(("USB::PollRemoteDevices: Dereferenced\n"));
894 /* Do nothing. */
895 } break;
896
897 default:
898 {
899 AssertFailed();
900 } break;
901 }
902}
903
904void RemoteUSBBackend::NotifyDelete(void)
905{
906 mfWillBeDeleted = true;
907}
908
909/*
910 * The backend maintains a list of UUIDs of devices
911 * which are managed by the backend.
912 */
913bool RemoteUSBBackend::addUUID(const Guid *pUuid)
914{
915 unsigned i;
916 for (i = 0; i < RT_ELEMENTS(aGuids); i++)
917 {
918 if (aGuids[i].isZero())
919 {
920 aGuids[i] = *pUuid;
921 return true;
922 }
923 }
924
925 return false;
926}
927
928bool RemoteUSBBackend::findUUID(const Guid *pUuid)
929{
930 unsigned i;
931 for (i = 0; i < RT_ELEMENTS(aGuids); i++)
932 {
933 if (aGuids[i] == *pUuid)
934 {
935 return true;
936 }
937 }
938
939 return false;
940}
941
942void RemoteUSBBackend::removeUUID(const Guid *pUuid)
943{
944 unsigned i;
945 for (i = 0; i < RT_ELEMENTS(aGuids); i++)
946 {
947 if (aGuids[i] == *pUuid)
948 {
949 aGuids[i].clear();
950 break;
951 }
952 }
953}
954
955RemoteUSBBackend::RemoteUSBBackend(Console *console, ConsoleVRDPServer *server, uint32_t u32ClientId)
956 :
957 mConsole(console),
958 mServer(server),
959 cRefs(0),
960 mu32ClientId(u32ClientId),
961 mfHasDeviceList(false),
962 mpvDeviceList(NULL),
963 mcbDeviceList(0),
964 menmPollRemoteDevicesStatus(PollRemoteDevicesStatus_Negotiate),
965 mfPollURB(true),
966 mpDevices(NULL),
967 mfWillBeDeleted(false),
968 mClientVersion(0), /* VRDE_USB_VERSION_2: the client version. */
969 mfDescExt(false) /* VRDE_USB_VERSION_3: VRDE_USB_REQ_DEVICE_LIST_EXT_RET. */
970{
971 Assert(console);
972 Assert(server);
973
974 int rc = RTCritSectInit(&mCritsect);
975
976 if (RT_FAILURE(rc))
977 {
978 AssertFailed();
979 RT_ZERO(mCritsect);
980 }
981
982 mCallback.pInstance = (PREMOTEUSBBACKEND)this;
983 mCallback.pfnOpen = iface_Open;
984 mCallback.pfnClose = iface_Close;
985 mCallback.pfnReset = iface_Reset;
986 mCallback.pfnSetConfig = iface_SetConfig;
987 mCallback.pfnClaimInterface = iface_ClaimInterface;
988 mCallback.pfnReleaseInterface = iface_ReleaseInterface;
989 mCallback.pfnInterfaceSetting = iface_InterfaceSetting;
990 mCallback.pfnQueueURB = iface_QueueURB;
991 mCallback.pfnReapURB = iface_ReapURB;
992 mCallback.pfnClearHaltedEP = iface_ClearHaltedEP;
993 mCallback.pfnCancelURB = iface_CancelURB;
994 mCallback.pfnWakeup = iface_Wakeup;
995}
996
997RemoteUSBBackend::~RemoteUSBBackend()
998{
999 Assert(cRefs == 0);
1000
1001 if (RTCritSectIsInitialized(&mCritsect))
1002 {
1003 RTCritSectDelete(&mCritsect);
1004 }
1005
1006 RTMemFree(mpvDeviceList);
1007
1008 mServer->usbBackendRemoveFromList(this);
1009}
1010
1011int RemoteUSBBackend::negotiateResponse(const VRDEUSBREQNEGOTIATERET *pret, uint32_t cbRet)
1012{
1013 int rc = VINF_SUCCESS;
1014
1015 Log(("RemoteUSBBackend::negotiateResponse: flags = %02X.\n", pret->flags));
1016
1017 LogRel(("Remote USB: Received negotiate response. Flags 0x%02X.\n",
1018 pret->flags));
1019
1020 if (pret->flags & VRDE_USB_CAPS_FLAG_POLL)
1021 {
1022 Log(("RemoteUSBBackend::negotiateResponse: client requested URB polling.\n"));
1023 mfPollURB = true;
1024 }
1025 else
1026 {
1027 mfPollURB = false;
1028 }
1029
1030 /* VRDE_USB_VERSION_2: check the client version. */
1031 if (pret->flags & VRDE_USB_CAPS2_FLAG_VERSION)
1032 {
1033 /* This could be a client version > 1. */
1034 if (cbRet >= sizeof(VRDEUSBREQNEGOTIATERET_2))
1035 {
1036 VRDEUSBREQNEGOTIATERET_2 *pret2 = (VRDEUSBREQNEGOTIATERET_2 *)pret;
1037
1038 if (pret2->u32Version <= VRDE_USB_VERSION)
1039 {
1040 /* This is OK. The client wants a version supported by the server. */
1041 mClientVersion = pret2->u32Version;
1042 }
1043 else
1044 {
1045 LogRel(("VRDP: ERROR: unsupported remote USB protocol client version %d.\n", pret2->u32Version));
1046 rc = VERR_NOT_SUPPORTED;
1047 }
1048 }
1049 else
1050 {
1051 LogRel(("VRDP: ERROR: invalid remote USB negotiate request packet size %d.\n", cbRet));
1052 rc = VERR_NOT_SUPPORTED;
1053 }
1054 }
1055 else
1056 {
1057 /* This is a client version 1. */
1058 mClientVersion = VRDE_USB_VERSION_1;
1059 }
1060
1061 if (RT_SUCCESS(rc))
1062 {
1063 LogRel(("VRDP: remote USB protocol version %d.\n", mClientVersion));
1064
1065 /* VRDE_USB_VERSION_3: check the client capabilities: VRDE_USB_CLIENT_CAPS_*. */
1066 if (mClientVersion == VRDE_USB_VERSION_3)
1067 {
1068 if (cbRet >= sizeof(VRDEUSBREQNEGOTIATERET_3))
1069 {
1070 VRDEUSBREQNEGOTIATERET_3 *pret3 = (VRDEUSBREQNEGOTIATERET_3 *)pret;
1071
1072 mfDescExt = (pret3->u32Flags & VRDE_USB_CLIENT_CAPS_PORT_VERSION) != 0;
1073 }
1074 else
1075 {
1076 LogRel(("VRDP: ERROR: invalid remote USB negotiate request packet size %d.\n", cbRet));
1077 rc = VERR_NOT_SUPPORTED;
1078 }
1079 }
1080
1081 menmPollRemoteDevicesStatus = PollRemoteDevicesStatus_SendRequest;
1082 }
1083
1084 return rc;
1085}
1086
1087int RemoteUSBBackend::saveDeviceList(const void *pvList, uint32_t cbList)
1088{
1089 Log(("RemoteUSBBackend::saveDeviceList: pvList = %p, cbList = %d\n", pvList, cbList));
1090
1091 if (!mfHasDeviceList)
1092 {
1093 RTMemFree(mpvDeviceList);
1094 mpvDeviceList = NULL;
1095
1096 mcbDeviceList = cbList;
1097
1098 if (cbList > 0)
1099 {
1100 mpvDeviceList = RTMemAlloc(cbList);
1101 memcpy(mpvDeviceList, pvList, cbList);
1102 }
1103
1104 mfHasDeviceList = true;
1105 }
1106
1107 return VINF_SUCCESS;
1108}
1109
1110void RemoteUSBBackend::request(void)
1111{
1112 int rc = RTCritSectEnter(&mCritsect);
1113 AssertRC(rc);
1114}
1115
1116void RemoteUSBBackend::release(void)
1117{
1118 RTCritSectLeave(&mCritsect);
1119}
1120
1121PREMOTEUSBDEVICE RemoteUSBBackend::deviceFromId(VRDEUSBDEVID id)
1122{
1123 request();
1124
1125 REMOTEUSBDEVICE *pDevice = mpDevices;
1126
1127 while (pDevice && pDevice->id != id)
1128 {
1129 pDevice = pDevice->next;
1130 }
1131
1132 release();
1133
1134 return pDevice;
1135}
1136
1137void RemoteUSBBackend::addDevice(PREMOTEUSBDEVICE pDevice)
1138{
1139 request();
1140
1141 pDevice->next = mpDevices;
1142
1143 if (mpDevices)
1144 {
1145 mpDevices->prev = pDevice;
1146 }
1147
1148 mpDevices = pDevice;
1149
1150 release();
1151}
1152
1153void RemoteUSBBackend::removeDevice(PREMOTEUSBDEVICE pDevice)
1154{
1155 request();
1156
1157 if (pDevice->prev)
1158 {
1159 pDevice->prev->next = pDevice->next;
1160 }
1161 else
1162 {
1163 mpDevices = pDevice->next;
1164 }
1165
1166 if (pDevice->next)
1167 {
1168 pDevice->next->prev = pDevice->prev;
1169 }
1170
1171 release();
1172}
1173
1174int RemoteUSBBackend::reapURB(const void *pvBody, uint32_t cbBody)
1175{
1176 int rc = VINF_SUCCESS;
1177
1178 LogFlow(("RemoteUSBBackend::reapURB: pvBody = %p, cbBody = %d\n", pvBody, cbBody));
1179
1180 VRDEUSBREQREAPURBBODY *pBody = (VRDEUSBREQREAPURBBODY *)pvBody;
1181
1182 while (cbBody >= sizeof(VRDEUSBREQREAPURBBODY))
1183 {
1184 Log(("RemoteUSBBackend::reapURB: id = %d, flags = %02X, error = %d, handle %d, len = %d.\n",
1185 pBody->id, pBody->flags, pBody->error, pBody->handle, pBody->len));
1186
1187 uint8_t fu8ReapValidFlags;
1188
1189 if (mClientVersion == VRDE_USB_VERSION_1 || mClientVersion == VRDE_USB_VERSION_2)
1190 {
1191 fu8ReapValidFlags = VRDE_USB_REAP_VALID_FLAGS;
1192 }
1193 else
1194 {
1195 fu8ReapValidFlags = VRDE_USB_REAP_VALID_FLAGS_3;
1196 }
1197
1198 /* Verify client's data. */
1199 if ( (pBody->flags & ~fu8ReapValidFlags) != 0
1200 || sizeof(VRDEUSBREQREAPURBBODY) > cbBody
1201 || pBody->handle == 0)
1202 {
1203 LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid reply data. Skipping the reply.\n"));
1204 rc = VERR_INVALID_PARAMETER;
1205 break;
1206 }
1207
1208 PREMOTEUSBDEVICE pDevice = deviceFromId(pBody->id);
1209
1210 if (!pDevice)
1211 {
1212 LogFlow(("RemoteUSBBackend::reapURB: WARNING: invalid device id. Skipping the reply.\n"));
1213 rc = VERR_INVALID_PARAMETER;
1214 break;
1215 }
1216
1217 uint32_t cbBodyData = 0; /* Data contained in the URB body structure for input URBs. */
1218
1219 requestDevice(pDevice);
1220
1221 /* Search the queued URB for given handle. */
1222 REMOTEUSBQURB *qurb = pDevice->pHeadQURBs;
1223
1224 while (qurb && qurb->u32Handle != pBody->handle)
1225 {
1226 LogFlow(("RemoteUSBBackend::reapURB: searching: %p handle = %d.\n", qurb, qurb->u32Handle));
1227 qurb = qurb->next;
1228 }
1229
1230 if (!qurb)
1231 {
1232 LogFlow(("RemoteUSBBackend::reapURB: Queued URB not found, probably already canceled. Skipping the URB.\n"));
1233 }
1234 else
1235 {
1236 LogFlow(("RemoteUSBBackend::reapURB: qurb = %p\n", qurb));
1237
1238 /* Update the URB error field. */
1239 if (mClientVersion == VRDE_USB_VERSION_1)
1240 {
1241 switch(pBody->error)
1242 {
1243 case VRDE_USB_XFER_OK: qurb->u32Err = VUSBSTATUS_OK; break;
1244 case VRDE_USB_XFER_STALL: qurb->u32Err = VUSBSTATUS_STALL; break;
1245 case VRDE_USB_XFER_DNR: qurb->u32Err = VUSBSTATUS_DNR; break;
1246 case VRDE_USB_XFER_CRC: qurb->u32Err = VUSBSTATUS_CRC; break;
1247 default: Log(("RemoteUSBBackend::reapURB: Invalid error %d\n", pBody->error));
1248 qurb->u32Err = VUSBSTATUS_DNR; break;
1249 }
1250 }
1251 else if ( mClientVersion == VRDE_USB_VERSION_2
1252 || mClientVersion == VRDE_USB_VERSION_3)
1253 {
1254 switch(pBody->error)
1255 {
1256 case VRDE_USB_XFER_OK: qurb->u32Err = VUSBSTATUS_OK; break;
1257 case VRDE_USB_XFER_STALL: qurb->u32Err = VUSBSTATUS_STALL; break;
1258 case VRDE_USB_XFER_DNR: qurb->u32Err = VUSBSTATUS_DNR; break;
1259 case VRDE_USB_XFER_CRC: qurb->u32Err = VUSBSTATUS_CRC; break;
1260 case VRDE_USB_XFER_DO: qurb->u32Err = VUSBSTATUS_DATA_OVERRUN; break;
1261 case VRDE_USB_XFER_DU: qurb->u32Err = VUSBSTATUS_DATA_UNDERRUN; break;
1262
1263 /* Unmapped errors. */
1264 case VRDE_USB_XFER_BS:
1265 case VRDE_USB_XFER_DTM:
1266 case VRDE_USB_XFER_PCF:
1267 case VRDE_USB_XFER_UPID:
1268 case VRDE_USB_XFER_BO:
1269 case VRDE_USB_XFER_BU:
1270 case VRDE_USB_XFER_ERR:
1271 default: Log(("RemoteUSBBackend::reapURB: Invalid error %d\n", pBody->error));
1272 qurb->u32Err = VUSBSTATUS_DNR; break;
1273 }
1274 }
1275 else
1276 {
1277 qurb->u32Err = VUSBSTATUS_DNR;
1278 }
1279
1280 /* Get the URB data. */
1281 bool fURBCompleted = true;
1282
1283 if (qurb->fInput)
1284 {
1285 cbBodyData = pBody->len; /* VRDE_USB_DIRECTION_IN URBs include some data. */
1286 }
1287
1288 if ( qurb->u32Err == VUSBSTATUS_OK
1289 && qurb->fInput)
1290 {
1291 LogFlow(("RemoteUSBBackend::reapURB: copying data %d bytes\n", pBody->len));
1292
1293 uint32_t u32DataLen = qurb->u32TransferredLen + pBody->len;
1294
1295 if (u32DataLen > qurb->u32Len)
1296 {
1297 /* Received more data than expected for this URB. If there more fragments follow,
1298 * they will be discarded because the URB handle will not be valid anymore.
1299 */
1300 qurb->u32Err = VUSBSTATUS_DNR;
1301 }
1302 else
1303 {
1304 memcpy ((uint8_t *)qurb->pvData + qurb->u32TransferredLen, &pBody[1], pBody->len);
1305 }
1306
1307 if ( qurb->u32Err == VUSBSTATUS_OK
1308 && (pBody->flags & VRDE_USB_REAP_FLAG_FRAGMENT) != 0)
1309 {
1310 /* If the client sends fragmented packets, accumulate the URB data. */
1311 fURBCompleted = false;
1312 }
1313 }
1314
1315 qurb->u32TransferredLen += pBody->len; /* Update the value for all URBs. */
1316
1317 if (fURBCompleted)
1318 {
1319 /* Move the URB near the head of URB list, so that iface_ReapURB can
1320 * find it faster. Note that the order of completion must be preserved!
1321 */
1322 if (qurb->prev)
1323 {
1324 /* The URB is not in the head. Unlink it from its current position. */
1325 qurb->prev->next = qurb->next;
1326
1327 if (qurb->next)
1328 {
1329 qurb->next->prev = qurb->prev;
1330 }
1331 else
1332 {
1333 pDevice->pTailQURBs = qurb->prev;
1334 }
1335
1336 /* And insert it to its new place. */
1337 if (pDevice->pHeadQURBs->fCompleted)
1338 {
1339 /* At least one other completed URB; insert after the
1340 * last completed URB.
1341 */
1342 REMOTEUSBQURB *prev_qurb = pDevice->pHeadQURBs;
1343 while (prev_qurb->next && prev_qurb->next->fCompleted)
1344 prev_qurb = prev_qurb->next;
1345
1346 qurb->next = prev_qurb->next;
1347 qurb->prev = prev_qurb;
1348
1349 if (prev_qurb->next)
1350 prev_qurb->next->prev = qurb;
1351 else
1352 pDevice->pTailQURBs = qurb;
1353 prev_qurb->next = qurb;
1354 }
1355 else
1356 {
1357 /* No other completed URBs; insert at head. */
1358 qurb->next = pDevice->pHeadQURBs;
1359 qurb->prev = NULL;
1360
1361 pDevice->pHeadQURBs->prev = qurb;
1362 pDevice->pHeadQURBs = qurb;
1363 }
1364 }
1365
1366 qurb->u32Len = qurb->u32TransferredLen; /* Update the final length. */
1367 qurb->fCompleted = true;
1368 }
1369 }
1370
1371 releaseDevice (pDevice);
1372
1373 if (pBody->flags & VRDE_USB_REAP_FLAG_LAST)
1374 {
1375 break;
1376 }
1377
1378 /* There is probably a further URB body. */
1379 uint32_t cbBodySize = sizeof (VRDEUSBREQREAPURBBODY) + cbBodyData;
1380
1381 if (cbBodySize > cbBody)
1382 {
1383 rc = VERR_INVALID_PARAMETER;
1384 break;
1385 }
1386
1387 pBody = (VRDEUSBREQREAPURBBODY *)((uint8_t *)pBody + cbBodySize);
1388 cbBody -= cbBodySize;
1389 }
1390
1391 LogFlow(("RemoteUSBBackend::reapURB: returns %Rrc\n", rc));
1392
1393 return rc;
1394}
1395/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette