VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/USBProxyDevice.cpp@ 50233

Last change on this file since 50233 was 50229, checked in by vboxsync, 11 years ago

Removed a bit too much

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.8 KB
Line 
1/* $Id: USBProxyDevice.cpp 50229 2014-01-24 21:22:26Z vboxsync $ */
2/** @file
3 * USBProxy - USB device proxy.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
23#include <VBox/usb.h>
24#include <VBox/usbfilter.h>
25#include <VBox/vmm/pdm.h>
26#include <VBox/err.h>
27#include <iprt/alloc.h>
28#include <iprt/string.h>
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include "USBProxyDevice.h"
32#include "VUSBInternal.h"
33#include "VBoxDD.h"
34
35
36/*******************************************************************************
37* Global Variables *
38*******************************************************************************/
39/** A dummy name used early during the construction phase to avoid log crashes. */
40static char g_szDummyName[] = "proxy xxxx:yyyy";
41
42
43
44/* Synchronously obtain a standard USB descriptor for a device, used in order
45 * to grab configuration descriptors when we first add the device
46 */
47static void *GetStdDescSync(PUSBPROXYDEV pProxyDev, uint8_t iDescType, uint8_t iIdx, uint16_t LangId, uint16_t cbHint)
48{
49 LogFlow(("GetStdDescSync: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
50 for (;;)
51 {
52 /*
53 * Setup a MSG URB, queue and reap it.
54 */
55 VUSBURB Urb;
56 AssertCompile(RT_SIZEOFMEMB(VUSBURB, abData) >= _4K);
57 Urb.u32Magic = VUSBURB_MAGIC;
58 Urb.enmState = VUSBURBSTATE_IN_FLIGHT;
59 Urb.pszDesc = (char*)"URB sync";
60 memset(&Urb.VUsb, 0, sizeof(Urb.VUsb));
61 memset(&Urb.Hci, 0, sizeof(Urb.Hci));
62 Urb.Dev.pvPrivate = NULL;
63 Urb.Dev.pNext = NULL;
64 Urb.pUsbIns = pProxyDev->pUsbIns;
65 Urb.DstAddress = 0;
66 Urb.EndPt = 0;
67 Urb.enmType = VUSBXFERTYPE_MSG;
68 Urb.enmDir = VUSBDIRECTION_IN;
69 Urb.fShortNotOk = false;
70 Urb.enmStatus = VUSBSTATUS_INVALID;
71 cbHint = RT_MIN(cbHint, sizeof(Urb.abData) - sizeof(VUSBSETUP));
72 Urb.cbData = cbHint + sizeof(VUSBSETUP);
73
74 PVUSBSETUP pSetup = (PVUSBSETUP)Urb.abData;
75 pSetup->bmRequestType = VUSB_DIR_TO_HOST | VUSB_REQ_STANDARD | VUSB_TO_DEVICE;
76 pSetup->bRequest = VUSB_REQ_GET_DESCRIPTOR;
77 pSetup->wValue = (iDescType << 8) | iIdx;
78 pSetup->wIndex = LangId;
79 pSetup->wLength = cbHint;
80
81 if (!pProxyDev->pOps->pfnUrbQueue(&Urb))
82 break;
83
84 /* Don't wait forever, it's just a simple request that should
85 return immediately. Since we're executing in the EMT thread
86 it's important not to get stuck here. (Some of the builtin
87 iMac devices may not refuse respond for instance.) */
88 PVUSBURB pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, 10000 /* ms */);
89 if (!pUrbReaped)
90 {
91 pProxyDev->pOps->pfnUrbCancel(&Urb);
92 pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, RT_INDEFINITE_WAIT);
93 }
94 if (pUrbReaped != &Urb)
95 {
96 Log(("GetStdDescSync: pfnUrbReap failed, pUrbReaped=%p\n", pUrbReaped));
97 break;
98 }
99
100 if (Urb.enmStatus != VUSBSTATUS_OK)
101 {
102 Log(("GetStdDescSync: Urb.enmStatus=%d\n", Urb.enmStatus));
103 break;
104 }
105
106 /*
107 * Check the length, config descriptors have total_length field
108 */
109 uint8_t *pbDesc = (uint8_t *)(pSetup + 1);
110 uint32_t cbDesc;
111 if (iDescType == VUSB_DT_CONFIG)
112 {
113 if (Urb.cbData < sizeof(VUSBSETUP) + 4)
114 {
115 Log(("GetStdDescSync: Urb.cbData=%#x (min 4)\n", Urb.cbData));
116 break;
117 }
118 cbDesc = RT_LE2H_U16(((uint16_t *)pbDesc)[1]);
119 }
120 else
121 {
122 if (Urb.cbData < sizeof(VUSBSETUP) + 1)
123 {
124 Log(("GetStdDescSync: Urb.cbData=%#x (min 1)\n", Urb.cbData));
125 break;
126 }
127 cbDesc = ((uint8_t *)pbDesc)[0];
128 }
129
130 Log(("GetStdDescSync: got Urb.cbData=%u, cbDesc=%u cbHint=%u\n", Urb.cbData, cbDesc, cbHint));
131
132 if ( Urb.cbData == cbHint + sizeof(VUSBSETUP)
133 && cbDesc > Urb.cbData - sizeof(VUSBSETUP))
134 {
135 cbHint = cbDesc;
136 if (cbHint > sizeof(Urb.abData))
137 {
138 AssertMsgFailed(("cbHint=%u\n", cbHint));
139 break;
140 }
141 continue;
142 }
143 Assert(cbDesc <= Urb.cbData - sizeof(VUSBSETUP));
144#ifdef LOG_ENABLED
145 vusbUrbTrace(&Urb, "GetStdDescSync", true);
146#endif
147
148 /*
149 * Fine, we got everything return a heap duplicate of the descriptor.
150 */
151 return RTMemDup(pbDesc, cbDesc);
152 }
153 return NULL;
154}
155
156/**
157 * Frees a descriptor returned by GetStdDescSync().
158 */
159static void free_desc(void *pvDesc)
160{
161 RTMemFree(pvDesc);
162}
163
164/**
165 * Get and a device descriptor and byteswap it appropriately.
166 */
167static bool usbProxyGetDeviceDesc(PUSBPROXYDEV pProxyDev, PVUSBDESCDEVICE pOut)
168{
169 /*
170 * Get the descriptor from the device.
171 */
172 PVUSBDESCDEVICE pIn = (PVUSBDESCDEVICE)GetStdDescSync(pProxyDev, VUSB_DT_DEVICE, 0, 0, VUSB_DT_DEVICE_MIN_LEN);
173 if (!pIn)
174 {
175 Log(("usbProxyGetDeviceDesc: pProxyDev=%s: GetStdDescSync failed\n", pProxyDev->pUsbIns->pszName));
176 return false;
177 }
178 if (pIn->bLength < VUSB_DT_DEVICE_MIN_LEN)
179 {
180 Log(("usb-proxy: pProxyDev=%s: Corrupted device descriptor. bLength=%d\n", pProxyDev->pUsbIns->pszName, pIn->bLength));
181 return false;
182 }
183
184 /*
185 * Convert it.
186 */
187 pOut->bLength = VUSB_DT_DEVICE_MIN_LEN;
188 pOut->bDescriptorType = VUSB_DT_DEVICE;
189 pOut->bcdUSB = RT_LE2H_U16(pIn->bcdUSB);
190 pOut->bDeviceClass = pIn->bDeviceClass;
191 pOut->bDeviceSubClass = pIn->bDeviceSubClass;
192 pOut->bDeviceProtocol = pIn->bDeviceProtocol;
193 pOut->bMaxPacketSize0 = pIn->bMaxPacketSize0;
194 pOut->idVendor = RT_LE2H_U16(pIn->idVendor);
195 pOut->idProduct = RT_LE2H_U16(pIn->idProduct);
196 pOut->bcdDevice = RT_LE2H_U16(pIn->bcdDevice);
197 pOut->iManufacturer = pIn->iManufacturer;
198 pOut->iProduct = pIn->iProduct;
199 pOut->iSerialNumber = pIn->iSerialNumber;
200 pOut->bNumConfigurations = pIn->bNumConfigurations;
201
202 free_desc(pIn);
203 return true;
204}
205
206/**
207 * Count the numbers and types of each kind of descriptor that we need to
208 * copy out of the config descriptor
209 */
210struct desc_counts
211{
212 size_t num_ed, num_id, num_if;
213 /** bitmap (128 bits) */
214 uint32_t idmap[4];
215};
216
217static int count_descriptors(struct desc_counts *cnt, uint8_t *buf, size_t len)
218{
219 PVUSBDESCCONFIG cfg;
220 uint8_t *tmp, *end;
221 uint32_t i, x;
222
223 memset(cnt, 0, sizeof(*cnt));
224
225 end = buf + len;
226
227 cfg = (PVUSBDESCCONFIG)buf;
228 if ( cfg->bLength < VUSB_DT_CONFIG_MIN_LEN )
229 return 0;
230 if ( cfg->bLength > len )
231 return 0;
232
233 for (tmp = buf + cfg->bLength; ((tmp + 1) < end) && *tmp; tmp += *tmp)
234 {
235 uint8_t type;
236 uint32_t ifnum;
237 PVUSBDESCINTERFACE id;
238 PVUSBDESCENDPOINT ed;
239
240 type = *(tmp + 1);
241
242 switch ( type ) {
243 case VUSB_DT_INTERFACE:
244 id = (PVUSBDESCINTERFACE)tmp;
245 if ( id->bLength < VUSB_DT_INTERFACE_MIN_LEN )
246 return 0;
247 cnt->num_id++;
248 ifnum = id->bInterfaceNumber;
249 cnt->idmap[ifnum >> 6] |= (1 << (ifnum & 0x1f));
250 break;
251 case VUSB_DT_ENDPOINT:
252 ed = (PVUSBDESCENDPOINT)tmp;
253 if ( ed->bLength < VUSB_DT_ENDPOINT_MIN_LEN )
254 return 0;
255 cnt->num_ed++;
256 break;
257 default:
258 break;
259 }
260 }
261
262 /* count interfaces */
263 for(i=0; i < RT_ELEMENTS(cnt->idmap); i++)
264 for(x=1; x; x<<=1)
265 if ( cnt->idmap[i] & x )
266 cnt->num_if++;
267
268 return 1;
269}
270
271/* Given the pointer to an interface or endpoint descriptor, find any following
272 * non-standard (vendor or class) descriptors.
273 */
274static const void *collect_stray_bits(uint8_t *this_desc, uint8_t *end, uint16_t *cbExtra)
275{
276 uint8_t *tmp, *buf;
277 uint8_t type;
278
279 Assert(*(this_desc + 1) == VUSB_DT_INTERFACE || *(this_desc + 1) == VUSB_DT_ENDPOINT);
280 buf = this_desc;
281
282 /* Skip the current interface/endpoint descriptor. */
283 buf += *(uint8_t *)buf;
284
285 /* Loop until we find another descriptor we understand. */
286 for (tmp = buf; ((tmp + 1) < end) && *tmp; tmp += *tmp)
287 {
288 type = *(tmp + 1);
289 if (type == VUSB_DT_INTERFACE || type == VUSB_DT_ENDPOINT)
290 break;
291 }
292 *cbExtra = tmp - buf;
293 if (*cbExtra)
294 return buf;
295 else
296 return NULL;
297}
298
299/* Setup a vusb_interface structure given some preallocated structures
300 * to use, (we counted them already)
301 */
302static int copy_interface(PVUSBINTERFACE pIf, uint8_t ifnum,
303 PVUSBDESCINTERFACEEX *id, PVUSBDESCENDPOINTEX *ed,
304 uint8_t *buf, size_t len)
305{
306 PVUSBDESCINTERFACEEX cur_if = NULL;
307 uint32_t altmap[4] = {0,};
308 uint8_t *tmp, *end = buf + len;
309 uint8_t *orig_desc = buf;
310 uint8_t alt;
311 int state;
312 size_t num_ep = 0;
313
314 buf += *(uint8_t *)buf;
315
316 pIf->cSettings = 0;
317 pIf->paSettings = NULL;
318
319 for (tmp = buf, state = 0; ((tmp + 1) < end) && *tmp; tmp += *tmp)
320 {
321 uint8_t type;
322 PVUSBDESCINTERFACE ifd;
323 PVUSBDESCENDPOINT epd;
324 PVUSBDESCENDPOINTEX cur_ep;
325
326 type = tmp[1];
327
328 switch ( type ) {
329 case VUSB_DT_INTERFACE:
330 state = 0;
331 ifd = (PVUSBDESCINTERFACE)tmp;
332
333 /* Ignoring this interface */
334 if ( ifd->bInterfaceNumber != ifnum )
335 break;
336
337 /* Check we didn't see this alternate setting already
338 * because that will break stuff
339 */
340 alt = ifd->bAlternateSetting;
341 if ( altmap[alt >> 6] & (1 << (alt & 0x1f)) )
342 return 0;
343 altmap[alt >> 6] |= (1 << (alt & 0x1f));
344
345 cur_if = *id;
346 (*id)++;
347 if ( pIf->cSettings == 0 )
348 pIf->paSettings = cur_if;
349
350 memcpy(cur_if, ifd, sizeof(cur_if->Core));
351
352 /* Point to additional interface descriptor bytes, if any. */
353 AssertCompile(sizeof(cur_if->Core) == VUSB_DT_INTERFACE_MIN_LEN);
354 if (cur_if->Core.bLength - VUSB_DT_INTERFACE_MIN_LEN > 0)
355 cur_if->pvMore = tmp + VUSB_DT_INTERFACE_MIN_LEN;
356 else
357 cur_if->pvMore = NULL;
358
359 cur_if->pvClass = collect_stray_bits(tmp, end, &cur_if->cbClass);
360
361 pIf->cSettings++;
362
363 state = 1;
364 num_ep = 0;
365 break;
366 case VUSB_DT_ENDPOINT:
367 if ( state == 0 )
368 break;
369
370 epd = (PVUSBDESCENDPOINT)tmp;
371
372 cur_ep = *ed;
373 (*ed)++;
374
375 if ( num_ep == 0 )
376 cur_if->paEndpoints = cur_ep;
377
378 if ( num_ep > cur_if->Core.bNumEndpoints )
379 return 0;
380
381 memcpy(cur_ep, epd, sizeof(cur_ep->Core));
382
383 /* Point to additional endpoint descriptor bytes, if any. */
384 AssertCompile(sizeof(cur_ep->Core) == VUSB_DT_ENDPOINT_MIN_LEN);
385 if (cur_ep->Core.bLength - VUSB_DT_ENDPOINT_MIN_LEN > 0)
386 cur_ep->pvMore = tmp + VUSB_DT_ENDPOINT_MIN_LEN;
387 else
388 cur_ep->pvMore = NULL;
389
390 cur_ep->pvClass = collect_stray_bits(tmp, end, &cur_ep->cbClass);
391
392 cur_ep->Core.wMaxPacketSize = RT_LE2H_U16(cur_ep->Core.wMaxPacketSize);
393
394 num_ep++;
395 break;
396 default:
397 /* Skip unknown descriptors. */
398 break;
399 }
400 }
401
402 return 1;
403}
404
405/**
406 * Copy all of a devices config descriptors, this is needed so that the USB
407 * core layer knows all about how to map the different functions on to the
408 * virtual USB bus.
409 */
410static bool copy_config(PUSBPROXYDEV pProxyDev, uint8_t idx, PVUSBDESCCONFIGEX out)
411{
412 PVUSBDESCCONFIG cfg;
413 PVUSBINTERFACE pIf;
414 PVUSBDESCINTERFACEEX ifd;
415 PVUSBDESCENDPOINTEX epd;
416 struct desc_counts cnt;
417 void *descs;
418 size_t tot_len;
419 size_t cbIface;
420 uint32_t i, x;
421
422 descs = GetStdDescSync(pProxyDev, VUSB_DT_CONFIG, idx, 0, VUSB_DT_CONFIG_MIN_LEN);
423 if ( descs == NULL ) {
424 Log(("copy_config: GetStdDescSync failed\n"));
425 return false;
426 }
427
428 cfg = (PVUSBDESCCONFIG)descs;
429 tot_len = RT_LE2H_U16(cfg->wTotalLength);
430
431 if ( !count_descriptors(&cnt, (uint8_t *)descs, tot_len) ) {
432 Log(("copy_config: count_descriptors failed\n"));
433 goto err;
434 }
435
436 if ( cfg->bNumInterfaces != cnt.num_if )
437 Log(("usb-proxy: config%u: bNumInterfaces %u != %u\n",
438 idx, cfg->bNumInterfaces, cnt.num_if));
439
440 Log(("usb-proxy: config%u: %u bytes id=%u ed=%u if=%u\n",
441 idx, tot_len, cnt.num_id, cnt.num_ed, cnt.num_if));
442
443 cbIface = cnt.num_if * sizeof(VUSBINTERFACE)
444 + cnt.num_id * sizeof(VUSBDESCINTERFACEEX)
445 + cnt.num_ed * sizeof(VUSBDESCENDPOINTEX);
446 out->paIfs = (PCVUSBINTERFACE)RTMemAllocZ(cbIface);
447 if ( out->paIfs == NULL ) {
448 free_desc(descs);
449 return false;
450 }
451
452 /* Stash a pointer to the raw config descriptor; we may need bits of it later. */
453 out->pvOriginal = descs;
454
455 pIf = (PVUSBINTERFACE)out->paIfs;
456 ifd = (PVUSBDESCINTERFACEEX)&pIf[cnt.num_if];
457 epd = (PVUSBDESCENDPOINTEX)&ifd[cnt.num_id];
458
459 out->Core.bLength = cfg->bLength;
460 out->Core.bDescriptorType = cfg->bDescriptorType;
461 out->Core.wTotalLength = 0; /* Auto Calculated */
462 out->Core.bNumInterfaces = (uint8_t)cnt.num_if;
463 out->Core.bConfigurationValue = cfg->bConfigurationValue;
464 out->Core.iConfiguration = cfg->iConfiguration;
465 out->Core.bmAttributes = cfg->bmAttributes;
466 out->Core.MaxPower = cfg->MaxPower;
467
468 for(i=0; i < 4; i++)
469 for(x=0; x < 32; x++)
470 if ( cnt.idmap[i] & (1 << x) )
471 if ( !copy_interface(pIf++, (i << 6) | x, &ifd, &epd, (uint8_t *)out->pvOriginal, tot_len) ) {
472 Log(("copy_interface(%d,,) failed\n", pIf - 1));
473 goto err;
474 }
475
476 return true;
477err:
478 Log(("usb-proxy: config%u: Corrupted configuration descriptor\n", idx));
479 free_desc(descs);
480 return false;
481}
482
483
484/**
485 * Edit out masked interface descriptors.
486 *
487 * @param pProxyDev The proxy device
488 */
489static void usbProxyDevEditOutMaskedIfs(PUSBPROXYDEV pProxyDev)
490{
491 unsigned cRemoved = 0;
492
493 PVUSBDESCCONFIGEX paCfgs = pProxyDev->paCfgDescs;
494 for (unsigned iCfg = 0; iCfg < pProxyDev->DevDesc.bNumConfigurations; iCfg++)
495 {
496 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
497 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
498 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
499 if ( paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber < 32
500 && ((1 << paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber) & pProxyDev->fMaskedIfs))
501 {
502 Log(("usb-proxy: removing interface #%d (iIf=%d iAlt=%d) on config #%d (iCfg=%d)\n",
503 paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber, iIf, iAlt, paCfgs[iCfg].Core.bConfigurationValue, iCfg));
504 cRemoved++;
505
506 paCfgs[iCfg].Core.bNumInterfaces--;
507 unsigned cToCopy = paCfgs[iCfg].Core.bNumInterfaces - iIf;
508 if (cToCopy)
509 memmove(&paIfs[iIf], &paIfs[iIf + 1], sizeof(paIfs[0]) * cToCopy);
510 memset(&paIfs[iIf + cToCopy], '\0', sizeof(paIfs[0]));
511 break;
512 }
513 }
514
515 Log(("usb-proxy: edited out %d interface(s).\n", cRemoved));
516}
517
518
519/**
520 * @copydoc PDMUSBREG::pfnUsbReset
521 *
522 * USB Device Proxy: Call OS specific code to reset the device.
523 */
524static DECLCALLBACK(int) usbProxyDevReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
525{
526 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
527
528 if (pProxyDev->fMaskedIfs)
529 {
530 Log(("usbProxyDevReset: pProxyDev=%s - ignoring reset request fMaskedIfs=%#x\n", pUsbIns->pszName, pProxyDev->fMaskedIfs));
531 return VINF_SUCCESS;
532 }
533 LogFlow(("usbProxyDevReset: pProxyDev=%s\n", pUsbIns->pszName));
534 return pProxyDev->pOps->pfnReset(pProxyDev, fResetOnLinux);
535}
536
537
538/**
539 * @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
540 */
541static DECLCALLBACK(PCPDMUSBDESCCACHE) usbProxyDevGetDescriptorCache(PPDMUSBINS pUsbIns)
542{
543 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
544 return &pThis->DescCache;
545}
546
547
548/**
549 * @copydoc PDMUSBREG::pfnUsbSetConfiguration
550 *
551 * USB Device Proxy: Release claimed interfaces, tell the OS+device about the config change, claim the new interfaces.
552 */
553static DECLCALLBACK(int) usbProxyDevSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
554 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
555{
556 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
557 LogFlow(("usbProxyDevSetConfiguration: pProxyDev=%s iActiveCfg=%d bConfigurationValue=%d\n",
558 pUsbIns->pszName, pProxyDev->iActiveCfg, bConfigurationValue));
559
560 /*
561 * Release the current config.
562 */
563 if (pvOldCfgDesc)
564 {
565 PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)pvOldCfgDesc;
566 PCVUSBINTERFACESTATE pOldIfState = (PCVUSBINTERFACESTATE)pvOldIfState;
567 for (unsigned i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
568 if (pOldIfState[i].pCurIfDesc)
569 pProxyDev->pOps->pfnReleaseInterface(pProxyDev, pOldIfState[i].pCurIfDesc->Core.bInterfaceNumber);
570 }
571
572 /*
573 * Do the actual SET_CONFIGURE.
574 * The mess here is because most backends will already have selected a
575 * configuration and there are a bunch of devices which will freak out
576 * if we do SET_CONFIGURE twice with the same value. (PalmOne, TrekStor USB-StickGO, ..)
577 *
578 * After open and reset the backend should use the members iActiveCfg and cIgnoreSetConfigs
579 * to indicate the new configuration state and what to do on the next SET_CONFIGURATION call.
580 */
581 if ( pProxyDev->iActiveCfg != bConfigurationValue
582 || ( bConfigurationValue == 0
583 && pProxyDev->iActiveCfg != -1 /* this test doesn't make sense, we know it's 0 */
584 && pProxyDev->cIgnoreSetConfigs >= 2)
585 || !pProxyDev->cIgnoreSetConfigs)
586 {
587 pProxyDev->cIgnoreSetConfigs = 0;
588 if (!pProxyDev->pOps->pfnSetConfig(pProxyDev, bConfigurationValue))
589 {
590 pProxyDev->iActiveCfg = -1;
591 return VERR_GENERAL_FAILURE;
592 }
593 pProxyDev->iActiveCfg = bConfigurationValue;
594 }
595 else if (pProxyDev->cIgnoreSetConfigs > 0)
596 pProxyDev->cIgnoreSetConfigs--;
597
598 /*
599 * Claim the interfaces.
600 */
601 PCVUSBDESCCONFIGEX pNewCfgDesc = (PCVUSBDESCCONFIGEX)pvNewCfgDesc;
602 Assert(pNewCfgDesc->Core.bConfigurationValue == bConfigurationValue);
603 for (unsigned iIf = 0; iIf < pNewCfgDesc->Core.bNumInterfaces; iIf++)
604 {
605 PCVUSBINTERFACE pIf = &pNewCfgDesc->paIfs[iIf];
606 for (uint32_t iAlt = 0; iAlt < pIf->cSettings; iAlt++)
607 {
608 if (pIf->paSettings[iAlt].Core.bAlternateSetting != 0)
609 continue;
610 pProxyDev->pOps->pfnClaimInterface(pProxyDev, pIf->paSettings[iAlt].Core.bInterfaceNumber);
611 /* ignore failures - the backend deals with that and does the necessary logging. */
612 break;
613 }
614 }
615
616 return VINF_SUCCESS;
617}
618
619
620/**
621 * @copydoc PDMUSBREG::pfnUsbSetInterface
622 *
623 * USB Device Proxy: Call OS specific code to select alternate interface settings.
624 */
625static DECLCALLBACK(int) usbProxyDevSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
626{
627 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
628 LogFlow(("usbProxyDevSetInterface: pProxyDev=%s bInterfaceNumber=%d bAlternateSetting=%d\n",
629 pUsbIns->pszName, bInterfaceNumber, bAlternateSetting));
630
631 /** @todo this is fishy, pfnSetInterface returns true/false from what I can see... */
632 if (pProxyDev->pOps->pfnSetInterface(pProxyDev, bInterfaceNumber, bAlternateSetting) < 0)
633 return VERR_GENERAL_FAILURE;
634 return VINF_SUCCESS;
635}
636
637
638/**
639 * @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
640 *
641 * USB Device Proxy: Call OS specific code to clear the endpoint.
642 */
643static DECLCALLBACK(int) usbProxyDevClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
644{
645 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
646 LogFlow(("usbProxyDevClearHaltedEndpoint: pProxyDev=%s uEndpoint=%u\n",
647 pUsbIns->pszName, uEndpoint));
648
649 if (!pProxyDev->pOps->pfnClearHaltedEndpoint(pProxyDev, uEndpoint))
650 return VERR_GENERAL_FAILURE;
651 return VINF_SUCCESS;
652}
653
654
655/**
656 * @copydoc PDMUSBREG::pfnUrbQueue
657 *
658 * USB Device Proxy: Call OS specific code.
659 */
660static DECLCALLBACK(int) usbProxyDevUrbQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
661{
662 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
663 if (!pProxyDev->pOps->pfnUrbQueue(pUrb))
664 return pProxyDev->fDetached
665 ? VERR_VUSB_DEVICE_NOT_ATTACHED
666 : VERR_VUSB_FAILED_TO_QUEUE_URB;
667 return VINF_SUCCESS;
668}
669
670
671/**
672 * @copydoc PDMUSBREG::pfnUrbCancel
673 *
674 * USB Device Proxy: Call OS specific code.
675 */
676static DECLCALLBACK(int) usbProxyDevUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
677{
678 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
679 pProxyDev->pOps->pfnUrbCancel(pUrb);
680 return VINF_SUCCESS;
681}
682
683
684/**
685 * @copydoc PDMUSBREG::pfnUrbReap
686 *
687 * USB Device Proxy: Call OS specific code.
688 */
689static DECLCALLBACK(PVUSBURB) usbProxyDevUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
690{
691 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
692 PVUSBURB pUrb = pProxyDev->pOps->pfnUrbReap(pProxyDev, cMillies);
693 if ( pUrb
694 && pUrb->enmState == VUSBURBSTATE_CANCELLED
695 && pUrb->enmStatus == VUSBSTATUS_OK)
696 pUrb->enmStatus = VUSBSTATUS_DNR;
697 return pUrb;
698}
699
700
701/**
702 * @copydoc PDMUSBREG::pfnWakeup
703 *
704 * USB Device Proxy: Call OS specific code.
705 */
706static DECLCALLBACK(int) usbProxyDevWakeup(PPDMUSBINS pUsbIns)
707{
708 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
709
710 return pProxyDev->pOps->pfnWakeup(pProxyDev);
711}
712
713
714/** @copydoc PDMUSBREG::pfnDestruct */
715static DECLCALLBACK(void) usbProxyDestruct(PPDMUSBINS pUsbIns)
716{
717 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
718 Log(("usbProxyDestruct: destroying pProxyDev=%s\n", pUsbIns->pszName));
719
720 /* close it. */
721 if (pThis->fOpened)
722 {
723 pThis->pOps->pfnClose(pThis);
724 pThis->fOpened = false;
725 }
726
727 /* free the config descriptors. */
728 if (pThis->paCfgDescs)
729 {
730 for (unsigned i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
731 {
732 RTMemFree((void *)pThis->paCfgDescs[i].paIfs);
733 RTMemFree((void *)pThis->paCfgDescs[i].pvOriginal);
734 }
735 RTMemFree(pThis->paCfgDescs);
736 pThis->paCfgDescs = NULL;
737 }
738
739 /* free dev */
740 if (&g_szDummyName[0] != pUsbIns->pszName)
741 RTStrFree(pUsbIns->pszName);
742 pUsbIns->pszName = NULL;
743
744 if (pThis->pvInstanceDataR3)
745 RTMemFree(pThis->pvInstanceDataR3);
746}
747
748
749/**
750 * Helper function used by usbProxyConstruct when
751 * reading a filter from CFG.
752 *
753 * @returns VBox status code.
754 * @param pFilter The filter.
755 * @param enmFieldIdx The filter field indext.
756 * @param pNode The CFGM node.
757 * @param pszExact The exact value name.
758 * @param pszExpr The expression value name.
759 */
760static int usbProxyQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, PCFGMNODE pNode, const char *pszExact, const char *pszExpr)
761{
762 char szTmp[256];
763
764 /* try exact first */
765 uint16_t u16;
766 int rc = CFGMR3QueryU16(pNode, pszExact, &u16);
767 if (RT_SUCCESS(rc))
768 {
769 rc = USBFilterSetNumExact(pFilter, enmFieldIdx, u16, true);
770 AssertRCReturn(rc, rc);
771
772 /* make sure only the exact attribute is present. */
773 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
774 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
775 {
776 szTmp[0] = '\0';
777 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
778 LogRel(("usbProxyConstruct: %s: Both %s and %s are present!\n", szTmp, pszExact, pszExpr));
779 return VERR_INVALID_PARAMETER;
780 }
781 return VINF_SUCCESS;
782 }
783 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
784 {
785 szTmp[0] = '\0';
786 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
787 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExact, rc));
788 return rc;
789 }
790
791 /* expression? */
792 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
793 if (RT_SUCCESS(rc))
794 {
795 rc = USBFilterSetNumExpression(pFilter, enmFieldIdx, szTmp, true);
796 AssertRCReturn(rc, rc);
797 return VINF_SUCCESS;
798 }
799 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
800 {
801 szTmp[0] = '\0';
802 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
803 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExpr, rc));
804 return rc;
805 }
806
807 return VINF_SUCCESS;
808}
809
810
811/** @copydoc PDMUSBREG::pfnConstruct */
812static DECLCALLBACK(int) usbProxyConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
813{
814 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
815 LogFlow(("usbProxyConstruct: pUsbIns=%p iInstance=%d\n", pUsbIns, iInstance));
816
817 /*
818 * Initialize the instance data.
819 */
820 pThis->pUsbIns = pUsbIns;
821 pThis->pUsbIns->pszName = g_szDummyName;
822 pThis->iActiveCfg = -1;
823 pThis->fMaskedIfs = 0;
824 pThis->fOpened = false;
825 pThis->fInited = false;
826
827 /*
828 * Read the basic configuration.
829 */
830 char szAddress[1024];
831 int rc = CFGMR3QueryString(pCfg, "Address", szAddress, sizeof(szAddress));
832 AssertRCReturn(rc, rc);
833
834 bool fRemote;
835 rc = CFGMR3QueryBool(pCfg, "Remote", &fRemote);
836 AssertRCReturn(rc, rc);
837
838 void *pvBackend;
839 rc = CFGMR3QueryPtr(pCfg, "pvBackend", &pvBackend);
840 AssertRCReturn(rc, rc);
841
842 /*
843 * Select backend and open the device.
844 */
845 if (!fRemote)
846 pThis->pOps = &g_USBProxyDeviceHost;
847 else
848 pThis->pOps = &g_USBProxyDeviceVRDP;
849
850 pThis->pvInstanceDataR3 = RTMemAllocZ(pThis->pOps->cbBackend);
851 if (!pThis->pvInstanceDataR3)
852 return PDMUSB_SET_ERROR(pUsbIns, VERR_NO_MEMORY, N_("USBProxy: can't allocate memory for host backend"));
853
854 rc = pThis->pOps->pfnOpen(pThis, szAddress, pvBackend);
855 if (RT_FAILURE(rc))
856 return rc;
857 pThis->fOpened = true;
858
859 /*
860 * Get the device descriptor and format the device name (for logging).
861 */
862 if (!usbProxyGetDeviceDesc(pThis, &pThis->DevDesc))
863 {
864 Log(("usbProxyConstruct: usbProxyGetDeviceDesc failed\n"));
865 return VERR_READ_ERROR;
866 }
867
868 RTStrAPrintf(&pUsbIns->pszName, "%p[proxy %04x:%04x]", pThis, pThis->DevDesc.idVendor, pThis->DevDesc.idProduct); /** @todo append the user comment */
869 AssertReturn(pUsbIns->pszName, VERR_NO_MEMORY);
870
871 /*
872 * Get config descriptors.
873 */
874 size_t cbConfigs = pThis->DevDesc.bNumConfigurations * sizeof(pThis->paCfgDescs[0]);
875 pThis->paCfgDescs = (PVUSBDESCCONFIGEX)RTMemAllocZ(cbConfigs);
876 AssertReturn(pThis->paCfgDescs, VERR_NO_MEMORY);
877
878 unsigned i;
879 for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
880 if (!copy_config(pThis, i, (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i]))
881 break;
882 if (i < pThis->DevDesc.bNumConfigurations)
883 {
884 Log(("usbProxyConstruct: copy_config failed, i=%d\n", i));
885 return VERR_READ_ERROR;
886 }
887
888 /*
889 * Pickup best matching global configuration for this device.
890 * The global configuration is organized like this:
891 *
892 * GlobalConfig/Whatever/
893 * |- idVendor = 300
894 * |- idProduct = 300
895 * - Config/
896 *
897 * The first level contains filter attributes which we stuff into a USBFILTER
898 * structure and match against the device info that's available. The highest
899 * ranked match is will be used. If nothing is found, the values will be
900 * queried from the GlobalConfig node (simplifies code and might actually
901 * be useful).
902 */
903 PCFGMNODE pCfgGlobalDev = pCfgGlobal;
904 PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgGlobal);
905 if (pCur)
906 {
907 /*
908 * Create a device filter from the device configuration
909 * descriptor ++. No strings currently.
910 */
911 USBFILTER Device;
912 USBFilterInit(&Device, USBFILTERTYPE_CAPTURE);
913 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_VENDOR_ID, pThis->DevDesc.idVendor, true); AssertRC(rc);
914 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_PRODUCT_ID, pThis->DevDesc.idProduct, true); AssertRC(rc);
915 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_REV, pThis->DevDesc.bcdDevice, true); AssertRC(rc);
916 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_CLASS, pThis->DevDesc.bDeviceClass, true); AssertRC(rc);
917 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_SUB_CLASS, pThis->DevDesc.bDeviceSubClass, true); AssertRC(rc);
918 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_PROTOCOL, pThis->DevDesc.bDeviceProtocol, true); AssertRC(rc);
919 /** @todo manufacturer, product and serial strings */
920
921 int iBestMatchRate = -1;
922 PCFGMNODE pBestMatch = NULL;
923 for (pCur = CFGMR3GetFirstChild(pCfgGlobal); pCur; pCur = CFGMR3GetNextChild(pCur))
924 {
925 /*
926 * Construct a filter from the attributes in the node.
927 */
928 USBFILTER Filter;
929 USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
930
931 /* numeric */
932 if ( RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_VENDOR_ID, pCur, "idVendor", "idVendorExpr"))
933 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_PRODUCT_ID, pCur, "idProduct", "idProcutExpr"))
934 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_REV, pCur, "bcdDevice", "bcdDeviceExpr"))
935 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_CLASS, pCur, "bDeviceClass", "bDeviceClassExpr"))
936 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pCur, "bDeviceSubClass", "bDeviceSubClassExpr"))
937 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pCur, "bDeviceProtocol", "bDeviceProtocolExpr")))
938 continue; /* skip it */
939
940 /* strings */
941 /** @todo manufacturer, product and serial strings */
942
943 /* ignore unknown config values, but not without bitching. */
944 if (!CFGMR3AreValuesValid(pCur,
945 "idVendor\0idVendorExpr\0"
946 "idProduct\0idProductExpr\0"
947 "bcdDevice\0bcdDeviceExpr\0"
948 "bDeviceClass\0bDeviceClassExpr\0"
949 "bDeviceSubClass\0bDeviceSubClassExpr\0"
950 "bDeviceProtocol\0bDeviceProtocolExpr\0"))
951 LogRel(("usbProxyConstruct: Unknown value(s) in config filter (ignored)!\n"));
952
953 /*
954 * Try match it and on match see if it has is a higher rate hit
955 * than the previous match. Quit if its a 100% match.
956 */
957 int iRate = USBFilterMatchRated(&Filter, &Device);
958 if (iRate > iBestMatchRate)
959 {
960 pBestMatch = pCur;
961 iBestMatchRate = iRate;
962 if (iRate >= 100)
963 break;
964 }
965 }
966 if (pBestMatch)
967 pCfgGlobalDev = CFGMR3GetChild(pBestMatch, "Config");
968 if (pCfgGlobalDev)
969 pCfgGlobalDev = pCfgGlobal;
970 }
971
972 /*
973 * Query the rest of the configuration using the global as fallback.
974 */
975 rc = CFGMR3QueryU32(pCfg, "MaskedIfs", &pThis->fMaskedIfs);
976 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
977 rc = CFGMR3QueryU32(pCfgGlobalDev, "MaskedIfs", &pThis->fMaskedIfs);
978 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
979 pThis->fMaskedIfs = 0;
980 else
981 AssertRCReturn(rc, rc);
982
983 bool fForce11Device;
984 rc = CFGMR3QueryBool(pCfg, "Force11Device", &fForce11Device);
985 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
986 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11Device", &fForce11Device);
987 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
988 fForce11Device = false;
989 else
990 AssertRCReturn(rc, rc);
991
992 bool fForce11PacketSize;
993 rc = CFGMR3QueryBool(pCfg, "Force11PacketSize", &fForce11PacketSize);
994 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
995 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11PacketSize", &fForce11PacketSize);
996 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
997 fForce11PacketSize = false;
998 else
999 AssertRCReturn(rc, rc);
1000
1001 /*
1002 * If we're masking interfaces, edit the descriptors.
1003 */
1004 bool fEdited = pThis->fMaskedIfs != 0;
1005 if (pThis->fMaskedIfs)
1006 usbProxyDevEditOutMaskedIfs(pThis);
1007
1008 /*
1009 * Do 2.0 -> 1.1 device edits if requested to do so.
1010 */
1011 if ( fForce11PacketSize
1012 && pThis->DevDesc.bcdUSB >= 0x0200)
1013 {
1014 PVUSBDESCCONFIGEX paCfgs = pThis->paCfgDescs;
1015 for (unsigned iCfg = 0; iCfg < pThis->DevDesc.bNumConfigurations; iCfg++)
1016 {
1017 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
1018 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
1019 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
1020 {
1021 /*
1022 * USB 1.1 defines the max for control, interrupt and bulk to be 64 bytes.
1023 * While isochronous has a max of 1023 bytes.
1024 */
1025 PVUSBDESCENDPOINTEX paEps = (PVUSBDESCENDPOINTEX)paIfs[iIf].paSettings[iAlt].paEndpoints;
1026 for (unsigned iEp = 0; iEp < paIfs[iIf].paSettings[iAlt].Core.bNumEndpoints; iEp++)
1027 {
1028 const uint16_t cbMax = (paEps[iEp].Core.bmAttributes & 3) == 1 /* isoc */
1029 ? 1023
1030 : 64;
1031 if (paEps[iEp].Core.wMaxPacketSize > cbMax)
1032 {
1033 Log(("usb-proxy: pProxyDev=%s correcting wMaxPacketSize from %#x to %#x (mainly for vista)\n",
1034 pUsbIns->pszName, paEps[iEp].Core.wMaxPacketSize, cbMax));
1035 paEps[iEp].Core.wMaxPacketSize = cbMax;
1036 fEdited = true;
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043 if ( fForce11Device
1044 && pThis->DevDesc.bcdUSB == 0x0200)
1045 {
1046 /*
1047 * Discourages windows from helping you find a 2.0 port.
1048 */
1049 Log(("usb-proxy: %s correcting USB version 2.0 to 1.1 (to avoid Windows warning)\n", pUsbIns->pszName));
1050 pThis->DevDesc.bcdUSB = 0x110;
1051 fEdited = true;
1052 }
1053
1054
1055 /*
1056 * Init the PDM/VUSB descriptor cache.
1057 */
1058 pThis->DescCache.pDevice = &pThis->DevDesc;
1059 pThis->DescCache.paConfigs = pThis->paCfgDescs;
1060 pThis->DescCache.paLanguages = NULL;
1061 pThis->DescCache.cLanguages = 0;
1062 pThis->DescCache.fUseCachedDescriptors = fEdited;
1063 pThis->DescCache.fUseCachedStringsDescriptors = false;
1064
1065 /*
1066 * Call the backend if it wishes to do some more initializing
1067 * after we've read the config and descriptors.
1068 */
1069 if (pThis->pOps->pfnInit)
1070 {
1071 rc = pThis->pOps->pfnInit(pThis);
1072 if (RT_FAILURE(rc))
1073 return rc;
1074 }
1075 pThis->fInited = true;
1076
1077 /*
1078 * We're good!
1079 */
1080 Log(("usb-proxy: created pProxyDev=%s address '%s' fMaskedIfs=%#x (rc=%Rrc)\n",
1081 pUsbIns->pszName, szAddress, pThis->fMaskedIfs, rc));
1082 return VINF_SUCCESS;
1083}
1084
1085
1086/**
1087 * The USB proxy device registration record.
1088 */
1089const PDMUSBREG g_UsbDevProxy =
1090{
1091 /* u32Version */
1092 PDM_USBREG_VERSION,
1093 /* szName */
1094 "USBProxy",
1095 /* pszDescription */
1096 "USB Proxy Device.",
1097 /* fFlags */
1098 0,
1099 /* cMaxInstances */
1100 ~0U,
1101 /* cbInstance */
1102 sizeof(USBPROXYDEV),
1103 /* pfnConstruct */
1104 usbProxyConstruct,
1105 /* pfnDestruct */
1106 usbProxyDestruct,
1107 /* pfnVMInitComplete */
1108 NULL,
1109 /* pfnVMPowerOn */
1110 NULL,
1111 /* pfnVMReset */
1112 NULL,
1113 /* pfnVMSuspend */
1114 NULL,
1115 /* pfnVMResume */
1116 NULL,
1117 /* pfnVMPowerOff */
1118 NULL,
1119 /* pfnHotPlugged */
1120 NULL,
1121 /* pfnHotUnplugged */
1122 NULL,
1123 /* pfnDriverAttach */
1124 NULL,
1125 /* pfnDriverDetach */
1126 NULL,
1127 /* pfnQueryInterface */
1128 NULL,
1129 /* pfnUsbReset */
1130 usbProxyDevReset,
1131 /* pfnUsbGetDescriptorCache */
1132 usbProxyDevGetDescriptorCache,
1133 /* pfnUsbSetConfiguration */
1134 usbProxyDevSetConfiguration,
1135 /* pfnUsbSetInterface */
1136 usbProxyDevSetInterface,
1137 /* pfnUsbClearHaltedEndpoint */
1138 usbProxyDevClearHaltedEndpoint,
1139 /* pfnUrbNew */
1140 NULL,
1141 /* pfnUrbQueue */
1142 usbProxyDevUrbQueue,
1143 /* pfnUrbCancel */
1144 usbProxyDevUrbCancel,
1145 /* pfnUrbReap */
1146 usbProxyDevUrbReap,
1147 /* pfnWakeup */
1148 usbProxyDevWakeup,
1149
1150 /* u32TheEnd */
1151 PDM_USBREG_VERSION
1152};
1153
Note: See TracBrowser for help on using the repository browser.

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