VirtualBox

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

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

USB/Proxy: More code cleanup and finish a few todos, also some new ones so it doesn't get boring

  • 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 50234 2014-01-24 22:48:13Z 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.215389.xyz. 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 int rc = VINF_SUCCESS;
56 VUSBURB Urb;
57 AssertCompile(RT_SIZEOFMEMB(VUSBURB, abData) >= _4K);
58 Urb.u32Magic = VUSBURB_MAGIC;
59 Urb.enmState = VUSBURBSTATE_IN_FLIGHT;
60 Urb.pszDesc = (char*)"URB sync";
61 memset(&Urb.VUsb, 0, sizeof(Urb.VUsb));
62 memset(&Urb.Hci, 0, sizeof(Urb.Hci));
63 Urb.Dev.pvPrivate = NULL;
64 Urb.Dev.pNext = NULL;
65 Urb.pUsbIns = pProxyDev->pUsbIns;
66 Urb.DstAddress = 0;
67 Urb.EndPt = 0;
68 Urb.enmType = VUSBXFERTYPE_MSG;
69 Urb.enmDir = VUSBDIRECTION_IN;
70 Urb.fShortNotOk = false;
71 Urb.enmStatus = VUSBSTATUS_INVALID;
72 cbHint = RT_MIN(cbHint, sizeof(Urb.abData) - sizeof(VUSBSETUP));
73 Urb.cbData = cbHint + sizeof(VUSBSETUP);
74
75 PVUSBSETUP pSetup = (PVUSBSETUP)Urb.abData;
76 pSetup->bmRequestType = VUSB_DIR_TO_HOST | VUSB_REQ_STANDARD | VUSB_TO_DEVICE;
77 pSetup->bRequest = VUSB_REQ_GET_DESCRIPTOR;
78 pSetup->wValue = (iDescType << 8) | iIdx;
79 pSetup->wIndex = LangId;
80 pSetup->wLength = cbHint;
81
82 rc = pProxyDev->pOps->pfnUrbQueue(pProxyDev, &Urb);
83 if (RT_FAILURE(rc))
84 break;
85
86 /* Don't wait forever, it's just a simple request that should
87 return immediately. Since we're executing in the EMT thread
88 it's important not to get stuck here. (Some of the builtin
89 iMac devices may not refuse respond for instance.) */
90 PVUSBURB pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, 10000 /* ms */);
91 if (!pUrbReaped)
92 {
93 rc = pProxyDev->pOps->pfnUrbCancel(pProxyDev, &Urb);
94 AssertRC(rc);
95 /** @todo: This breaks the comment above... */
96 pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, RT_INDEFINITE_WAIT);
97 }
98 if (pUrbReaped != &Urb)
99 {
100 Log(("GetStdDescSync: pfnUrbReap failed, pUrbReaped=%p\n", pUrbReaped));
101 break;
102 }
103
104 if (Urb.enmStatus != VUSBSTATUS_OK)
105 {
106 Log(("GetStdDescSync: Urb.enmStatus=%d\n", Urb.enmStatus));
107 break;
108 }
109
110 /*
111 * Check the length, config descriptors have total_length field
112 */
113 uint8_t *pbDesc = (uint8_t *)(pSetup + 1);
114 uint32_t cbDesc;
115 if (iDescType == VUSB_DT_CONFIG)
116 {
117 if (Urb.cbData < sizeof(VUSBSETUP) + 4)
118 {
119 Log(("GetStdDescSync: Urb.cbData=%#x (min 4)\n", Urb.cbData));
120 break;
121 }
122 cbDesc = RT_LE2H_U16(((uint16_t *)pbDesc)[1]);
123 }
124 else
125 {
126 if (Urb.cbData < sizeof(VUSBSETUP) + 1)
127 {
128 Log(("GetStdDescSync: Urb.cbData=%#x (min 1)\n", Urb.cbData));
129 break;
130 }
131 cbDesc = ((uint8_t *)pbDesc)[0];
132 }
133
134 Log(("GetStdDescSync: got Urb.cbData=%u, cbDesc=%u cbHint=%u\n", Urb.cbData, cbDesc, cbHint));
135
136 if ( Urb.cbData == cbHint + sizeof(VUSBSETUP)
137 && cbDesc > Urb.cbData - sizeof(VUSBSETUP))
138 {
139 cbHint = cbDesc;
140 if (cbHint > sizeof(Urb.abData))
141 {
142 AssertMsgFailed(("cbHint=%u\n", cbHint));
143 break;
144 }
145 continue;
146 }
147 Assert(cbDesc <= Urb.cbData - sizeof(VUSBSETUP));
148#ifdef LOG_ENABLED
149 vusbUrbTrace(&Urb, "GetStdDescSync", true);
150#endif
151
152 /*
153 * Fine, we got everything return a heap duplicate of the descriptor.
154 */
155 return RTMemDup(pbDesc, cbDesc);
156 }
157 return NULL;
158}
159
160/**
161 * Frees a descriptor returned by GetStdDescSync().
162 */
163static void free_desc(void *pvDesc)
164{
165 RTMemFree(pvDesc);
166}
167
168/**
169 * Get and a device descriptor and byteswap it appropriately.
170 */
171static bool usbProxyGetDeviceDesc(PUSBPROXYDEV pProxyDev, PVUSBDESCDEVICE pOut)
172{
173 /*
174 * Get the descriptor from the device.
175 */
176 PVUSBDESCDEVICE pIn = (PVUSBDESCDEVICE)GetStdDescSync(pProxyDev, VUSB_DT_DEVICE, 0, 0, VUSB_DT_DEVICE_MIN_LEN);
177 if (!pIn)
178 {
179 Log(("usbProxyGetDeviceDesc: pProxyDev=%s: GetStdDescSync failed\n", pProxyDev->pUsbIns->pszName));
180 return false;
181 }
182 if (pIn->bLength < VUSB_DT_DEVICE_MIN_LEN)
183 {
184 Log(("usb-proxy: pProxyDev=%s: Corrupted device descriptor. bLength=%d\n", pProxyDev->pUsbIns->pszName, pIn->bLength));
185 return false;
186 }
187
188 /*
189 * Convert it.
190 */
191 pOut->bLength = VUSB_DT_DEVICE_MIN_LEN;
192 pOut->bDescriptorType = VUSB_DT_DEVICE;
193 pOut->bcdUSB = RT_LE2H_U16(pIn->bcdUSB);
194 pOut->bDeviceClass = pIn->bDeviceClass;
195 pOut->bDeviceSubClass = pIn->bDeviceSubClass;
196 pOut->bDeviceProtocol = pIn->bDeviceProtocol;
197 pOut->bMaxPacketSize0 = pIn->bMaxPacketSize0;
198 pOut->idVendor = RT_LE2H_U16(pIn->idVendor);
199 pOut->idProduct = RT_LE2H_U16(pIn->idProduct);
200 pOut->bcdDevice = RT_LE2H_U16(pIn->bcdDevice);
201 pOut->iManufacturer = pIn->iManufacturer;
202 pOut->iProduct = pIn->iProduct;
203 pOut->iSerialNumber = pIn->iSerialNumber;
204 pOut->bNumConfigurations = pIn->bNumConfigurations;
205
206 free_desc(pIn);
207 return true;
208}
209
210/**
211 * Count the numbers and types of each kind of descriptor that we need to
212 * copy out of the config descriptor
213 */
214struct desc_counts
215{
216 size_t num_ed, num_id, num_if;
217 /** bitmap (128 bits) */
218 uint32_t idmap[4];
219};
220
221static int count_descriptors(struct desc_counts *cnt, uint8_t *buf, size_t len)
222{
223 PVUSBDESCCONFIG cfg;
224 uint8_t *tmp, *end;
225 uint32_t i, x;
226
227 memset(cnt, 0, sizeof(*cnt));
228
229 end = buf + len;
230
231 cfg = (PVUSBDESCCONFIG)buf;
232 if ( cfg->bLength < VUSB_DT_CONFIG_MIN_LEN )
233 return 0;
234 if ( cfg->bLength > len )
235 return 0;
236
237 for (tmp = buf + cfg->bLength; ((tmp + 1) < end) && *tmp; tmp += *tmp)
238 {
239 uint8_t type;
240 uint32_t ifnum;
241 PVUSBDESCINTERFACE id;
242 PVUSBDESCENDPOINT ed;
243
244 type = *(tmp + 1);
245
246 switch ( type ) {
247 case VUSB_DT_INTERFACE:
248 id = (PVUSBDESCINTERFACE)tmp;
249 if ( id->bLength < VUSB_DT_INTERFACE_MIN_LEN )
250 return 0;
251 cnt->num_id++;
252 ifnum = id->bInterfaceNumber;
253 cnt->idmap[ifnum >> 6] |= (1 << (ifnum & 0x1f));
254 break;
255 case VUSB_DT_ENDPOINT:
256 ed = (PVUSBDESCENDPOINT)tmp;
257 if ( ed->bLength < VUSB_DT_ENDPOINT_MIN_LEN )
258 return 0;
259 cnt->num_ed++;
260 break;
261 default:
262 break;
263 }
264 }
265
266 /* count interfaces */
267 for(i=0; i < RT_ELEMENTS(cnt->idmap); i++)
268 for(x=1; x; x<<=1)
269 if ( cnt->idmap[i] & x )
270 cnt->num_if++;
271
272 return 1;
273}
274
275/* Given the pointer to an interface or endpoint descriptor, find any following
276 * non-standard (vendor or class) descriptors.
277 */
278static const void *collect_stray_bits(uint8_t *this_desc, uint8_t *end, uint16_t *cbExtra)
279{
280 uint8_t *tmp, *buf;
281 uint8_t type;
282
283 Assert(*(this_desc + 1) == VUSB_DT_INTERFACE || *(this_desc + 1) == VUSB_DT_ENDPOINT);
284 buf = this_desc;
285
286 /* Skip the current interface/endpoint descriptor. */
287 buf += *(uint8_t *)buf;
288
289 /* Loop until we find another descriptor we understand. */
290 for (tmp = buf; ((tmp + 1) < end) && *tmp; tmp += *tmp)
291 {
292 type = *(tmp + 1);
293 if (type == VUSB_DT_INTERFACE || type == VUSB_DT_ENDPOINT)
294 break;
295 }
296 *cbExtra = tmp - buf;
297 if (*cbExtra)
298 return buf;
299 else
300 return NULL;
301}
302
303/* Setup a vusb_interface structure given some preallocated structures
304 * to use, (we counted them already)
305 */
306static int copy_interface(PVUSBINTERFACE pIf, uint8_t ifnum,
307 PVUSBDESCINTERFACEEX *id, PVUSBDESCENDPOINTEX *ed,
308 uint8_t *buf, size_t len)
309{
310 PVUSBDESCINTERFACEEX cur_if = NULL;
311 uint32_t altmap[4] = {0,};
312 uint8_t *tmp, *end = buf + len;
313 uint8_t *orig_desc = buf;
314 uint8_t alt;
315 int state;
316 size_t num_ep = 0;
317
318 buf += *(uint8_t *)buf;
319
320 pIf->cSettings = 0;
321 pIf->paSettings = NULL;
322
323 for (tmp = buf, state = 0; ((tmp + 1) < end) && *tmp; tmp += *tmp)
324 {
325 uint8_t type;
326 PVUSBDESCINTERFACE ifd;
327 PVUSBDESCENDPOINT epd;
328 PVUSBDESCENDPOINTEX cur_ep;
329
330 type = tmp[1];
331
332 switch ( type ) {
333 case VUSB_DT_INTERFACE:
334 state = 0;
335 ifd = (PVUSBDESCINTERFACE)tmp;
336
337 /* Ignoring this interface */
338 if ( ifd->bInterfaceNumber != ifnum )
339 break;
340
341 /* Check we didn't see this alternate setting already
342 * because that will break stuff
343 */
344 alt = ifd->bAlternateSetting;
345 if ( altmap[alt >> 6] & (1 << (alt & 0x1f)) )
346 return 0;
347 altmap[alt >> 6] |= (1 << (alt & 0x1f));
348
349 cur_if = *id;
350 (*id)++;
351 if ( pIf->cSettings == 0 )
352 pIf->paSettings = cur_if;
353
354 memcpy(cur_if, ifd, sizeof(cur_if->Core));
355
356 /* Point to additional interface descriptor bytes, if any. */
357 AssertCompile(sizeof(cur_if->Core) == VUSB_DT_INTERFACE_MIN_LEN);
358 if (cur_if->Core.bLength - VUSB_DT_INTERFACE_MIN_LEN > 0)
359 cur_if->pvMore = tmp + VUSB_DT_INTERFACE_MIN_LEN;
360 else
361 cur_if->pvMore = NULL;
362
363 cur_if->pvClass = collect_stray_bits(tmp, end, &cur_if->cbClass);
364
365 pIf->cSettings++;
366
367 state = 1;
368 num_ep = 0;
369 break;
370 case VUSB_DT_ENDPOINT:
371 if ( state == 0 )
372 break;
373
374 epd = (PVUSBDESCENDPOINT)tmp;
375
376 cur_ep = *ed;
377 (*ed)++;
378
379 if ( num_ep == 0 )
380 cur_if->paEndpoints = cur_ep;
381
382 if ( num_ep > cur_if->Core.bNumEndpoints )
383 return 0;
384
385 memcpy(cur_ep, epd, sizeof(cur_ep->Core));
386
387 /* Point to additional endpoint descriptor bytes, if any. */
388 AssertCompile(sizeof(cur_ep->Core) == VUSB_DT_ENDPOINT_MIN_LEN);
389 if (cur_ep->Core.bLength - VUSB_DT_ENDPOINT_MIN_LEN > 0)
390 cur_ep->pvMore = tmp + VUSB_DT_ENDPOINT_MIN_LEN;
391 else
392 cur_ep->pvMore = NULL;
393
394 cur_ep->pvClass = collect_stray_bits(tmp, end, &cur_ep->cbClass);
395
396 cur_ep->Core.wMaxPacketSize = RT_LE2H_U16(cur_ep->Core.wMaxPacketSize);
397
398 num_ep++;
399 break;
400 default:
401 /* Skip unknown descriptors. */
402 break;
403 }
404 }
405
406 return 1;
407}
408
409/**
410 * Copy all of a devices config descriptors, this is needed so that the USB
411 * core layer knows all about how to map the different functions on to the
412 * virtual USB bus.
413 */
414static bool copy_config(PUSBPROXYDEV pProxyDev, uint8_t idx, PVUSBDESCCONFIGEX out)
415{
416 PVUSBDESCCONFIG cfg;
417 PVUSBINTERFACE pIf;
418 PVUSBDESCINTERFACEEX ifd;
419 PVUSBDESCENDPOINTEX epd;
420 struct desc_counts cnt;
421 void *descs;
422 size_t tot_len;
423 size_t cbIface;
424 uint32_t i, x;
425
426 descs = GetStdDescSync(pProxyDev, VUSB_DT_CONFIG, idx, 0, VUSB_DT_CONFIG_MIN_LEN);
427 if ( descs == NULL ) {
428 Log(("copy_config: GetStdDescSync failed\n"));
429 return false;
430 }
431
432 cfg = (PVUSBDESCCONFIG)descs;
433 tot_len = RT_LE2H_U16(cfg->wTotalLength);
434
435 if ( !count_descriptors(&cnt, (uint8_t *)descs, tot_len) ) {
436 Log(("copy_config: count_descriptors failed\n"));
437 goto err;
438 }
439
440 if ( cfg->bNumInterfaces != cnt.num_if )
441 Log(("usb-proxy: config%u: bNumInterfaces %u != %u\n",
442 idx, cfg->bNumInterfaces, cnt.num_if));
443
444 Log(("usb-proxy: config%u: %u bytes id=%u ed=%u if=%u\n",
445 idx, tot_len, cnt.num_id, cnt.num_ed, cnt.num_if));
446
447 cbIface = cnt.num_if * sizeof(VUSBINTERFACE)
448 + cnt.num_id * sizeof(VUSBDESCINTERFACEEX)
449 + cnt.num_ed * sizeof(VUSBDESCENDPOINTEX);
450 out->paIfs = (PCVUSBINTERFACE)RTMemAllocZ(cbIface);
451 if ( out->paIfs == NULL ) {
452 free_desc(descs);
453 return false;
454 }
455
456 /* Stash a pointer to the raw config descriptor; we may need bits of it later. */
457 out->pvOriginal = descs;
458
459 pIf = (PVUSBINTERFACE)out->paIfs;
460 ifd = (PVUSBDESCINTERFACEEX)&pIf[cnt.num_if];
461 epd = (PVUSBDESCENDPOINTEX)&ifd[cnt.num_id];
462
463 out->Core.bLength = cfg->bLength;
464 out->Core.bDescriptorType = cfg->bDescriptorType;
465 out->Core.wTotalLength = 0; /* Auto Calculated */
466 out->Core.bNumInterfaces = (uint8_t)cnt.num_if;
467 out->Core.bConfigurationValue = cfg->bConfigurationValue;
468 out->Core.iConfiguration = cfg->iConfiguration;
469 out->Core.bmAttributes = cfg->bmAttributes;
470 out->Core.MaxPower = cfg->MaxPower;
471
472 for(i=0; i < 4; i++)
473 for(x=0; x < 32; x++)
474 if ( cnt.idmap[i] & (1 << x) )
475 if ( !copy_interface(pIf++, (i << 6) | x, &ifd, &epd, (uint8_t *)out->pvOriginal, tot_len) ) {
476 Log(("copy_interface(%d,,) failed\n", pIf - 1));
477 goto err;
478 }
479
480 return true;
481err:
482 Log(("usb-proxy: config%u: Corrupted configuration descriptor\n", idx));
483 free_desc(descs);
484 return false;
485}
486
487
488/**
489 * Edit out masked interface descriptors.
490 *
491 * @param pProxyDev The proxy device
492 */
493static void usbProxyDevEditOutMaskedIfs(PUSBPROXYDEV pProxyDev)
494{
495 unsigned cRemoved = 0;
496
497 PVUSBDESCCONFIGEX paCfgs = pProxyDev->paCfgDescs;
498 for (unsigned iCfg = 0; iCfg < pProxyDev->DevDesc.bNumConfigurations; iCfg++)
499 {
500 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
501 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
502 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
503 if ( paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber < 32
504 && ((1 << paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber) & pProxyDev->fMaskedIfs))
505 {
506 Log(("usb-proxy: removing interface #%d (iIf=%d iAlt=%d) on config #%d (iCfg=%d)\n",
507 paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber, iIf, iAlt, paCfgs[iCfg].Core.bConfigurationValue, iCfg));
508 cRemoved++;
509
510 paCfgs[iCfg].Core.bNumInterfaces--;
511 unsigned cToCopy = paCfgs[iCfg].Core.bNumInterfaces - iIf;
512 if (cToCopy)
513 memmove(&paIfs[iIf], &paIfs[iIf + 1], sizeof(paIfs[0]) * cToCopy);
514 memset(&paIfs[iIf + cToCopy], '\0', sizeof(paIfs[0]));
515 break;
516 }
517 }
518
519 Log(("usb-proxy: edited out %d interface(s).\n", cRemoved));
520}
521
522
523/**
524 * @copydoc PDMUSBREG::pfnUsbReset
525 *
526 * USB Device Proxy: Call OS specific code to reset the device.
527 */
528static DECLCALLBACK(int) usbProxyDevReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
529{
530 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
531
532 if (pProxyDev->fMaskedIfs)
533 {
534 Log(("usbProxyDevReset: pProxyDev=%s - ignoring reset request fMaskedIfs=%#x\n", pUsbIns->pszName, pProxyDev->fMaskedIfs));
535 return VINF_SUCCESS;
536 }
537 LogFlow(("usbProxyDevReset: pProxyDev=%s\n", pUsbIns->pszName));
538 return pProxyDev->pOps->pfnReset(pProxyDev, fResetOnLinux);
539}
540
541
542/**
543 * @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
544 */
545static DECLCALLBACK(PCPDMUSBDESCCACHE) usbProxyDevGetDescriptorCache(PPDMUSBINS pUsbIns)
546{
547 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
548 return &pThis->DescCache;
549}
550
551
552/**
553 * @copydoc PDMUSBREG::pfnUsbSetConfiguration
554 *
555 * USB Device Proxy: Release claimed interfaces, tell the OS+device about the config change, claim the new interfaces.
556 */
557static DECLCALLBACK(int) usbProxyDevSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
558 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
559{
560 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
561 LogFlow(("usbProxyDevSetConfiguration: pProxyDev=%s iActiveCfg=%d bConfigurationValue=%d\n",
562 pUsbIns->pszName, pProxyDev->iActiveCfg, bConfigurationValue));
563
564 /*
565 * Release the current config.
566 */
567 if (pvOldCfgDesc)
568 {
569 PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)pvOldCfgDesc;
570 PCVUSBINTERFACESTATE pOldIfState = (PCVUSBINTERFACESTATE)pvOldIfState;
571 for (unsigned i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
572 if (pOldIfState[i].pCurIfDesc)
573 pProxyDev->pOps->pfnReleaseInterface(pProxyDev, pOldIfState[i].pCurIfDesc->Core.bInterfaceNumber);
574 }
575
576 /*
577 * Do the actual SET_CONFIGURE.
578 * The mess here is because most backends will already have selected a
579 * configuration and there are a bunch of devices which will freak out
580 * if we do SET_CONFIGURE twice with the same value. (PalmOne, TrekStor USB-StickGO, ..)
581 *
582 * After open and reset the backend should use the members iActiveCfg and cIgnoreSetConfigs
583 * to indicate the new configuration state and what to do on the next SET_CONFIGURATION call.
584 */
585 if ( pProxyDev->iActiveCfg != bConfigurationValue
586 || ( bConfigurationValue == 0
587 && pProxyDev->iActiveCfg != -1 /* this test doesn't make sense, we know it's 0 */
588 && pProxyDev->cIgnoreSetConfigs >= 2)
589 || !pProxyDev->cIgnoreSetConfigs)
590 {
591 pProxyDev->cIgnoreSetConfigs = 0;
592 if (!pProxyDev->pOps->pfnSetConfig(pProxyDev, bConfigurationValue))
593 {
594 pProxyDev->iActiveCfg = -1;
595 return VERR_GENERAL_FAILURE;
596 }
597 pProxyDev->iActiveCfg = bConfigurationValue;
598 }
599 else if (pProxyDev->cIgnoreSetConfigs > 0)
600 pProxyDev->cIgnoreSetConfigs--;
601
602 /*
603 * Claim the interfaces.
604 */
605 PCVUSBDESCCONFIGEX pNewCfgDesc = (PCVUSBDESCCONFIGEX)pvNewCfgDesc;
606 Assert(pNewCfgDesc->Core.bConfigurationValue == bConfigurationValue);
607 for (unsigned iIf = 0; iIf < pNewCfgDesc->Core.bNumInterfaces; iIf++)
608 {
609 PCVUSBINTERFACE pIf = &pNewCfgDesc->paIfs[iIf];
610 for (uint32_t iAlt = 0; iAlt < pIf->cSettings; iAlt++)
611 {
612 if (pIf->paSettings[iAlt].Core.bAlternateSetting != 0)
613 continue;
614 pProxyDev->pOps->pfnClaimInterface(pProxyDev, pIf->paSettings[iAlt].Core.bInterfaceNumber);
615 /* ignore failures - the backend deals with that and does the necessary logging. */
616 break;
617 }
618 }
619
620 return VINF_SUCCESS;
621}
622
623
624/**
625 * @copydoc PDMUSBREG::pfnUsbSetInterface
626 *
627 * USB Device Proxy: Call OS specific code to select alternate interface settings.
628 */
629static DECLCALLBACK(int) usbProxyDevSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
630{
631 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
632 LogFlow(("usbProxyDevSetInterface: pProxyDev=%s bInterfaceNumber=%d bAlternateSetting=%d\n",
633 pUsbIns->pszName, bInterfaceNumber, bAlternateSetting));
634
635 return pProxyDev->pOps->pfnSetInterface(pProxyDev, bInterfaceNumber, bAlternateSetting);
636}
637
638
639/**
640 * @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
641 *
642 * USB Device Proxy: Call OS specific code to clear the endpoint.
643 */
644static DECLCALLBACK(int) usbProxyDevClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
645{
646 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
647 LogFlow(("usbProxyDevClearHaltedEndpoint: pProxyDev=%s uEndpoint=%u\n",
648 pUsbIns->pszName, uEndpoint));
649
650 return pProxyDev->pOps->pfnClearHaltedEndpoint(pProxyDev, uEndpoint);
651}
652
653
654/**
655 * @copydoc PDMUSBREG::pfnUrbQueue
656 *
657 * USB Device Proxy: Call OS specific code.
658 */
659static DECLCALLBACK(int) usbProxyDevUrbQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
660{
661 int rc = VINF_SUCCESS;
662 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
663 rc = pProxyDev->pOps->pfnUrbQueue(pProxyDev, pUrb);
664 if (RT_FAILURE(rc))
665 return pProxyDev->fDetached
666 ? VERR_VUSB_DEVICE_NOT_ATTACHED
667 : VERR_VUSB_FAILED_TO_QUEUE_URB;
668 return rc;
669}
670
671
672/**
673 * @copydoc PDMUSBREG::pfnUrbCancel
674 *
675 * USB Device Proxy: Call OS specific code.
676 */
677static DECLCALLBACK(int) usbProxyDevUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
678{
679 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
680 return pProxyDev->pOps->pfnUrbCancel(pProxyDev, pUrb);
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