VirtualBox

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

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

Devices/USB: First part of the rework, move most of the work to dedicated threads to improve performance

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.5 KB
Line 
1/* $Id: USBProxyDevice.cpp 49814 2013-12-06 21:38:28Z 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
745
746/**
747 * Helper function used by usbProxyConstruct when
748 * reading a filter from CFG.
749 *
750 * @returns VBox status code.
751 * @param pFilter The filter.
752 * @param enmFieldIdx The filter field indext.
753 * @param pNode The CFGM node.
754 * @param pszExact The exact value name.
755 * @param pszExpr The expression value name.
756 */
757static int usbProxyQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, PCFGMNODE pNode, const char *pszExact, const char *pszExpr)
758{
759 char szTmp[256];
760
761 /* try exact first */
762 uint16_t u16;
763 int rc = CFGMR3QueryU16(pNode, pszExact, &u16);
764 if (RT_SUCCESS(rc))
765 {
766 rc = USBFilterSetNumExact(pFilter, enmFieldIdx, u16, true);
767 AssertRCReturn(rc, rc);
768
769 /* make sure only the exact attribute is present. */
770 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
771 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
772 {
773 szTmp[0] = '\0';
774 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
775 LogRel(("usbProxyConstruct: %s: Both %s and %s are present!\n", szTmp, pszExact, pszExpr));
776 return VERR_INVALID_PARAMETER;
777 }
778 return VINF_SUCCESS;
779 }
780 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
781 {
782 szTmp[0] = '\0';
783 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
784 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExact, rc));
785 return rc;
786 }
787
788 /* expression? */
789 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
790 if (RT_SUCCESS(rc))
791 {
792 rc = USBFilterSetNumExpression(pFilter, enmFieldIdx, szTmp, true);
793 AssertRCReturn(rc, rc);
794 return VINF_SUCCESS;
795 }
796 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
797 {
798 szTmp[0] = '\0';
799 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
800 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExpr, rc));
801 return rc;
802 }
803
804 return VINF_SUCCESS;
805}
806
807
808/** @copydoc PDMUSBREG::pfnConstruct */
809static DECLCALLBACK(int) usbProxyConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
810{
811 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
812 LogFlow(("usbProxyConstruct: pUsbIns=%p iInstance=%d\n", pUsbIns, iInstance));
813
814 /*
815 * Initialize the instance data.
816 */
817 pThis->pUsbIns = pUsbIns;
818 pThis->pUsbIns->pszName = g_szDummyName;
819 pThis->iActiveCfg = -1;
820 pThis->fMaskedIfs = 0;
821 pThis->fOpened = false;
822 pThis->fInited = false;
823
824 /*
825 * Read the basic configuration.
826 */
827 char szAddress[1024];
828 int rc = CFGMR3QueryString(pCfg, "Address", szAddress, sizeof(szAddress));
829 AssertRCReturn(rc, rc);
830
831 bool fRemote;
832 rc = CFGMR3QueryBool(pCfg, "Remote", &fRemote);
833 AssertRCReturn(rc, rc);
834
835 void *pvBackend;
836 rc = CFGMR3QueryPtr(pCfg, "pvBackend", &pvBackend);
837 AssertRCReturn(rc, rc);
838
839 /*
840 * Select backend and open the device.
841 */
842 if (!fRemote)
843 pThis->pOps = &g_USBProxyDeviceHost;
844 else
845 pThis->pOps = &g_USBProxyDeviceVRDP;
846 rc = pThis->pOps->pfnOpen(pThis, szAddress, pvBackend);
847 if (RT_FAILURE(rc))
848 return rc;
849 pThis->fOpened = true;
850
851 /*
852 * Get the device descriptor and format the device name (for logging).
853 */
854 if (!usbProxyGetDeviceDesc(pThis, &pThis->DevDesc))
855 {
856 Log(("usbProxyConstruct: usbProxyGetDeviceDesc failed\n"));
857 return VERR_READ_ERROR;
858 }
859
860 RTStrAPrintf(&pUsbIns->pszName, "%p[proxy %04x:%04x]", pThis, pThis->DevDesc.idVendor, pThis->DevDesc.idProduct); /** @todo append the user comment */
861 AssertReturn(pUsbIns->pszName, VERR_NO_MEMORY);
862
863 /*
864 * Get config descriptors.
865 */
866 size_t cbConfigs = pThis->DevDesc.bNumConfigurations * sizeof(pThis->paCfgDescs[0]);
867 pThis->paCfgDescs = (PVUSBDESCCONFIGEX)RTMemAllocZ(cbConfigs);
868 AssertReturn(pThis->paCfgDescs, VERR_NO_MEMORY);
869
870 unsigned i;
871 for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
872 if (!copy_config(pThis, i, (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i]))
873 break;
874 if (i < pThis->DevDesc.bNumConfigurations)
875 {
876 Log(("usbProxyConstruct: copy_config failed, i=%d\n", i));
877 return VERR_READ_ERROR;
878 }
879
880 /*
881 * Pickup best matching global configuration for this device.
882 * The global configuration is organized like this:
883 *
884 * GlobalConfig/Whatever/
885 * |- idVendor = 300
886 * |- idProduct = 300
887 * - Config/
888 *
889 * The first level contains filter attributes which we stuff into a USBFILTER
890 * structure and match against the device info that's available. The highest
891 * ranked match is will be used. If nothing is found, the values will be
892 * queried from the GlobalConfig node (simplifies code and might actually
893 * be useful).
894 */
895 PCFGMNODE pCfgGlobalDev = pCfgGlobal;
896 PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgGlobal);
897 if (pCur)
898 {
899 /*
900 * Create a device filter from the device configuration
901 * descriptor ++. No strings currently.
902 */
903 USBFILTER Device;
904 USBFilterInit(&Device, USBFILTERTYPE_CAPTURE);
905 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_VENDOR_ID, pThis->DevDesc.idVendor, true); AssertRC(rc);
906 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_PRODUCT_ID, pThis->DevDesc.idProduct, true); AssertRC(rc);
907 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_REV, pThis->DevDesc.bcdDevice, true); AssertRC(rc);
908 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_CLASS, pThis->DevDesc.bDeviceClass, true); AssertRC(rc);
909 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_SUB_CLASS, pThis->DevDesc.bDeviceSubClass, true); AssertRC(rc);
910 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_PROTOCOL, pThis->DevDesc.bDeviceProtocol, true); AssertRC(rc);
911 /** @todo manufacturer, product and serial strings */
912
913 int iBestMatchRate = -1;
914 PCFGMNODE pBestMatch = NULL;
915 for (pCur = CFGMR3GetFirstChild(pCfgGlobal); pCur; pCur = CFGMR3GetNextChild(pCur))
916 {
917 /*
918 * Construct a filter from the attributes in the node.
919 */
920 USBFILTER Filter;
921 USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
922
923 /* numeric */
924 if ( RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_VENDOR_ID, pCur, "idVendor", "idVendorExpr"))
925 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_PRODUCT_ID, pCur, "idProduct", "idProcutExpr"))
926 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_REV, pCur, "bcdDevice", "bcdDeviceExpr"))
927 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_CLASS, pCur, "bDeviceClass", "bDeviceClassExpr"))
928 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pCur, "bDeviceSubClass", "bDeviceSubClassExpr"))
929 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pCur, "bDeviceProtocol", "bDeviceProtocolExpr")))
930 continue; /* skip it */
931
932 /* strings */
933 /** @todo manufacturer, product and serial strings */
934
935 /* ignore unknown config values, but not without bitching. */
936 if (!CFGMR3AreValuesValid(pCur,
937 "idVendor\0idVendorExpr\0"
938 "idProduct\0idProductExpr\0"
939 "bcdDevice\0bcdDeviceExpr\0"
940 "bDeviceClass\0bDeviceClassExpr\0"
941 "bDeviceSubClass\0bDeviceSubClassExpr\0"
942 "bDeviceProtocol\0bDeviceProtocolExpr\0"))
943 LogRel(("usbProxyConstruct: Unknown value(s) in config filter (ignored)!\n"));
944
945 /*
946 * Try match it and on match see if it has is a higher rate hit
947 * than the previous match. Quit if its a 100% match.
948 */
949 int iRate = USBFilterMatchRated(&Filter, &Device);
950 if (iRate > iBestMatchRate)
951 {
952 pBestMatch = pCur;
953 iBestMatchRate = iRate;
954 if (iRate >= 100)
955 break;
956 }
957 }
958 if (pBestMatch)
959 pCfgGlobalDev = CFGMR3GetChild(pBestMatch, "Config");
960 if (pCfgGlobalDev)
961 pCfgGlobalDev = pCfgGlobal;
962 }
963
964 /*
965 * Query the rest of the configuration using the global as fallback.
966 */
967 rc = CFGMR3QueryU32(pCfg, "MaskedIfs", &pThis->fMaskedIfs);
968 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
969 rc = CFGMR3QueryU32(pCfgGlobalDev, "MaskedIfs", &pThis->fMaskedIfs);
970 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
971 pThis->fMaskedIfs = 0;
972 else
973 AssertRCReturn(rc, rc);
974
975 bool fForce11Device;
976 rc = CFGMR3QueryBool(pCfg, "Force11Device", &fForce11Device);
977 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
978 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11Device", &fForce11Device);
979 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
980 fForce11Device = false;
981 else
982 AssertRCReturn(rc, rc);
983
984 bool fForce11PacketSize;
985 rc = CFGMR3QueryBool(pCfg, "Force11PacketSize", &fForce11PacketSize);
986 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
987 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11PacketSize", &fForce11PacketSize);
988 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
989 fForce11PacketSize = false;
990 else
991 AssertRCReturn(rc, rc);
992
993 /*
994 * If we're masking interfaces, edit the descriptors.
995 */
996 bool fEdited = pThis->fMaskedIfs != 0;
997 if (pThis->fMaskedIfs)
998 usbProxyDevEditOutMaskedIfs(pThis);
999
1000 /*
1001 * Do 2.0 -> 1.1 device edits if requested to do so.
1002 */
1003 if ( fForce11PacketSize
1004 && pThis->DevDesc.bcdUSB >= 0x0200)
1005 {
1006 PVUSBDESCCONFIGEX paCfgs = pThis->paCfgDescs;
1007 for (unsigned iCfg = 0; iCfg < pThis->DevDesc.bNumConfigurations; iCfg++)
1008 {
1009 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
1010 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
1011 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
1012 {
1013 /*
1014 * USB 1.1 defines the max for control, interrupt and bulk to be 64 bytes.
1015 * While isochronous has a max of 1023 bytes.
1016 */
1017 PVUSBDESCENDPOINTEX paEps = (PVUSBDESCENDPOINTEX)paIfs[iIf].paSettings[iAlt].paEndpoints;
1018 for (unsigned iEp = 0; iEp < paIfs[iIf].paSettings[iAlt].Core.bNumEndpoints; iEp++)
1019 {
1020 const uint16_t cbMax = (paEps[iEp].Core.bmAttributes & 3) == 1 /* isoc */
1021 ? 1023
1022 : 64;
1023 if (paEps[iEp].Core.wMaxPacketSize > cbMax)
1024 {
1025 Log(("usb-proxy: pProxyDev=%s correcting wMaxPacketSize from %#x to %#x (mainly for vista)\n",
1026 pUsbIns->pszName, paEps[iEp].Core.wMaxPacketSize, cbMax));
1027 paEps[iEp].Core.wMaxPacketSize = cbMax;
1028 fEdited = true;
1029 }
1030 }
1031 }
1032 }
1033 }
1034
1035 if ( fForce11Device
1036 && pThis->DevDesc.bcdUSB == 0x0200)
1037 {
1038 /*
1039 * Discourages windows from helping you find a 2.0 port.
1040 */
1041 Log(("usb-proxy: %s correcting USB version 2.0 to 1.1 (to avoid Windows warning)\n", pUsbIns->pszName));
1042 pThis->DevDesc.bcdUSB = 0x110;
1043 fEdited = true;
1044 }
1045
1046
1047 /*
1048 * Init the PDM/VUSB descriptor cache.
1049 */
1050 pThis->DescCache.pDevice = &pThis->DevDesc;
1051 pThis->DescCache.paConfigs = pThis->paCfgDescs;
1052 pThis->DescCache.paLanguages = NULL;
1053 pThis->DescCache.cLanguages = 0;
1054 pThis->DescCache.fUseCachedDescriptors = fEdited;
1055 pThis->DescCache.fUseCachedStringsDescriptors = false;
1056
1057 /*
1058 * Call the backend if it wishes to do some more initializing
1059 * after we've read the config and descriptors.
1060 */
1061 if (pThis->pOps->pfnInit)
1062 {
1063 rc = pThis->pOps->pfnInit(pThis);
1064 if (RT_FAILURE(rc))
1065 return rc;
1066 }
1067 pThis->fInited = true;
1068
1069 /*
1070 * We're good!
1071 */
1072 Log(("usb-proxy: created pProxyDev=%s address '%s' fMaskedIfs=%#x (rc=%Rrc)\n",
1073 pUsbIns->pszName, szAddress, pThis->fMaskedIfs, rc));
1074 return VINF_SUCCESS;
1075}
1076
1077
1078/**
1079 * The USB proxy device registration record.
1080 */
1081const PDMUSBREG g_UsbDevProxy =
1082{
1083 /* u32Version */
1084 PDM_USBREG_VERSION,
1085 /* szName */
1086 "USBProxy",
1087 /* pszDescription */
1088 "USB Proxy Device.",
1089 /* fFlags */
1090 0,
1091 /* cMaxInstances */
1092 ~0U,
1093 /* cbInstance */
1094 sizeof(USBPROXYDEV),
1095 /* pfnConstruct */
1096 usbProxyConstruct,
1097 /* pfnDestruct */
1098 usbProxyDestruct,
1099 /* pfnVMInitComplete */
1100 NULL,
1101 /* pfnVMPowerOn */
1102 NULL,
1103 /* pfnVMReset */
1104 NULL,
1105 /* pfnVMSuspend */
1106 NULL,
1107 /* pfnVMResume */
1108 NULL,
1109 /* pfnVMPowerOff */
1110 NULL,
1111 /* pfnHotPlugged */
1112 NULL,
1113 /* pfnHotUnplugged */
1114 NULL,
1115 /* pfnDriverAttach */
1116 NULL,
1117 /* pfnDriverDetach */
1118 NULL,
1119 /* pfnQueryInterface */
1120 NULL,
1121 /* pfnUsbReset */
1122 usbProxyDevReset,
1123 /* pfnUsbGetDescriptorCache */
1124 usbProxyDevGetDescriptorCache,
1125 /* pfnUsbSetConfiguration */
1126 usbProxyDevSetConfiguration,
1127 /* pfnUsbSetInterface */
1128 usbProxyDevSetInterface,
1129 /* pfnUsbClearHaltedEndpoint */
1130 usbProxyDevClearHaltedEndpoint,
1131 /* pfnUrbNew */
1132 NULL,
1133 /* pfnUrbQueue */
1134 usbProxyDevUrbQueue,
1135 /* pfnUrbCancel */
1136 usbProxyDevUrbCancel,
1137 /* pfnUrbReap */
1138 usbProxyDevUrbReap,
1139 /* pfnWakeup */
1140 usbProxyDevWakeup,
1141
1142 /* u32TheEnd */
1143 PDM_USBREG_VERSION
1144};
1145
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