VirtualBox

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

最後變更 在這個檔案從36477是 36477,由 vboxsync 提交於 14 年 前

USB: Also find extra vendor/class descriptor data.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 37.4 KB
 
1/* $Id: USBProxyDevice.cpp 36477 2011-03-30 17:21:29Z vboxsync $ */
2/** @file
3 * USBProxy - USB device proxy.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 /** @todo Here be dragons! Additional descriptors needs copying into pvClass
398 * (RTMemDup be your friend). @bugref{2693} */
399 break;
400 }
401 }
402
403 return 1;
404}
405
406/**
407 * Copy all of a devices config descriptors, this is needed so that the USB
408 * core layer knows all about how to map the different functions on to the
409 * virtual USB bus.
410 */
411static bool copy_config(PUSBPROXYDEV pProxyDev, uint8_t idx, PVUSBDESCCONFIGEX out)
412{
413 PVUSBDESCCONFIG cfg;
414 PVUSBINTERFACE pIf;
415 PVUSBDESCINTERFACEEX ifd;
416 PVUSBDESCENDPOINTEX epd;
417 struct desc_counts cnt;
418 void *descs;
419 size_t tot_len;
420 size_t cbIface;
421 uint32_t i, x;
422
423 descs = GetStdDescSync(pProxyDev, VUSB_DT_CONFIG, idx, 0, VUSB_DT_CONFIG_MIN_LEN);
424 if ( descs == NULL ) {
425 Log(("copy_config: GetStdDescSync failed\n"));
426 return false;
427 }
428
429 cfg = (PVUSBDESCCONFIG)descs;
430 tot_len = RT_LE2H_U16(cfg->wTotalLength);
431
432 if ( !count_descriptors(&cnt, (uint8_t *)descs, tot_len) ) {
433 Log(("copy_config: count_descriptors failed\n"));
434 goto err;
435 }
436
437 if ( cfg->bNumInterfaces != cnt.num_if )
438 Log(("usb-proxy: config%u: bNumInterfaces %u != %u\n",
439 idx, cfg->bNumInterfaces, cnt.num_if));
440
441 Log(("usb-proxy: config%u: %u bytes id=%u ed=%u if=%u\n",
442 idx, tot_len, cnt.num_id, cnt.num_ed, cnt.num_if));
443
444 cbIface = cnt.num_if * sizeof(VUSBINTERFACE)
445 + cnt.num_id * sizeof(VUSBDESCINTERFACEEX)
446 + cnt.num_ed * sizeof(VUSBDESCENDPOINTEX);
447 out->paIfs = (PCVUSBINTERFACE)RTMemAllocZ(cbIface);
448 if ( out->paIfs == NULL ) {
449 free_desc(descs);
450 return false;
451 }
452
453 /* Stash a pointer to the raw config descriptor; we may need bits of it later. */
454 out->pvOriginal = descs;
455
456 pIf = (PVUSBINTERFACE)out->paIfs;
457 ifd = (PVUSBDESCINTERFACEEX)&pIf[cnt.num_if];
458 epd = (PVUSBDESCENDPOINTEX)&ifd[cnt.num_id];
459
460 out->Core.bLength = cfg->bLength;
461 out->Core.bDescriptorType = cfg->bDescriptorType;
462 out->Core.wTotalLength = 0; /* Auto Calculated */
463 out->Core.bNumInterfaces = (uint8_t)cnt.num_if;
464 out->Core.bConfigurationValue = cfg->bConfigurationValue;
465 out->Core.iConfiguration = cfg->iConfiguration;
466 out->Core.bmAttributes = cfg->bmAttributes;
467 out->Core.MaxPower = cfg->MaxPower;
468
469 for(i=0; i < 4; i++)
470 for(x=0; x < 32; x++)
471 if ( cnt.idmap[i] & (1 << x) )
472 if ( !copy_interface(pIf++, (i << 6) | x, &ifd, &epd, (uint8_t *)out->pvOriginal, tot_len) ) {
473 Log(("copy_interface(%d,,) failed\n", pIf - 1));
474 goto err;
475 }
476
477 return true;
478err:
479 Log(("usb-proxy: config%u: Corrupted configuration descriptor\n", idx));
480 free_desc(descs);
481 return false;
482}
483
484
485/**
486 * Edit out masked interface descriptors.
487 *
488 * @param pProxyDev The proxy device
489 */
490static void usbProxyDevEditOutMaskedIfs(PUSBPROXYDEV pProxyDev)
491{
492 unsigned cRemoved = 0;
493
494 PVUSBDESCCONFIGEX paCfgs = pProxyDev->paCfgDescs;
495 for (unsigned iCfg = 0; iCfg < pProxyDev->DevDesc.bNumConfigurations; iCfg++)
496 {
497 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
498 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
499 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
500 if ( paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber < 32
501 && ((1 << paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber) & pProxyDev->fMaskedIfs))
502 {
503 Log(("usb-proxy: removing interface #%d (iIf=%d iAlt=%d) on config #%d (iCfg=%d)\n",
504 paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber, iIf, iAlt, paCfgs[iCfg].Core.bConfigurationValue, iCfg));
505 cRemoved++;
506
507 paCfgs[iCfg].Core.bNumInterfaces--;
508 unsigned cToCopy = paCfgs[iCfg].Core.bNumInterfaces - iIf;
509 if (cToCopy)
510 memmove(&paIfs[iIf], &paIfs[iIf + 1], sizeof(paIfs[0]) * cToCopy);
511 memset(&paIfs[iIf + cToCopy], '\0', sizeof(paIfs[0]));
512 break;
513 }
514 }
515
516 Log(("usb-proxy: edited out %d interface(s).\n", cRemoved));
517}
518
519
520/**
521 * @copydoc PDMUSBREG::pfnUsbReset
522 *
523 * USB Device Proxy: Call OS specific code to reset the device.
524 */
525static DECLCALLBACK(int) usbProxyDevReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
526{
527 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
528
529 if (pProxyDev->fMaskedIfs)
530 {
531 Log(("usbProxyDevReset: pProxyDev=%s - ignoring reset request fMaskedIfs=%#x\n", pUsbIns->pszName, pProxyDev->fMaskedIfs));
532 return VINF_SUCCESS;
533 }
534 LogFlow(("usbProxyDevReset: pProxyDev=%s\n", pUsbIns->pszName));
535 return pProxyDev->pOps->pfnReset(pProxyDev, fResetOnLinux);
536}
537
538
539/**
540 * @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
541 */
542static DECLCALLBACK(PCPDMUSBDESCCACHE) usbProxyDevGetDescriptorCache(PPDMUSBINS pUsbIns)
543{
544 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
545 return &pThis->DescCache;
546}
547
548
549/**
550 * @copydoc PDMUSBREG::pfnUsbSetConfiguration
551 *
552 * USB Device Proxy: Release claimed interfaces, tell the OS+device about the config change, claim the new interfaces.
553 */
554static DECLCALLBACK(int) usbProxyDevSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
555 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
556{
557 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
558 LogFlow(("usbProxyDevSetConfiguration: pProxyDev=%s iActiveCfg=%d bConfigurationValue=%d\n",
559 pUsbIns->pszName, pProxyDev->iActiveCfg, bConfigurationValue));
560
561 /*
562 * Release the current config.
563 */
564 if (pvOldCfgDesc)
565 {
566 PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)pvOldCfgDesc;
567 PCVUSBINTERFACESTATE pOldIfState = (PCVUSBINTERFACESTATE)pvOldIfState;
568 for (unsigned i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
569 if (pOldIfState[i].pCurIfDesc)
570 pProxyDev->pOps->pfnReleaseInterface(pProxyDev, pOldIfState[i].pCurIfDesc->Core.bInterfaceNumber);
571 }
572
573 /*
574 * Do the actual SET_CONFIGURE.
575 * The mess here is because most backends will already have selected a
576 * configuration and there are a bunch of devices which will freak out
577 * if we do SET_CONFIGURE twice with the same value. (PalmOne, TrekStor USB-StickGO, ..)
578 *
579 * After open and reset the backend should use the members iActiveCfg and cIgnoreSetConfigs
580 * to indicate the new configuration state and what to do on the next SET_CONFIGURATION call.
581 */
582 if ( pProxyDev->iActiveCfg != bConfigurationValue
583 || ( bConfigurationValue == 0
584 && pProxyDev->iActiveCfg != -1 /* this test doesn't make sense, we know it's 0 */
585 && pProxyDev->cIgnoreSetConfigs >= 2)
586 || !pProxyDev->cIgnoreSetConfigs)
587 {
588 pProxyDev->cIgnoreSetConfigs = 0;
589 if (!pProxyDev->pOps->pfnSetConfig(pProxyDev, bConfigurationValue))
590 {
591 pProxyDev->iActiveCfg = -1;
592 return VERR_GENERAL_FAILURE;
593 }
594 pProxyDev->iActiveCfg = bConfigurationValue;
595 }
596 else if (pProxyDev->cIgnoreSetConfigs > 0)
597 pProxyDev->cIgnoreSetConfigs--;
598
599 /*
600 * Claim the interfaces.
601 */
602 PCVUSBDESCCONFIGEX pNewCfgDesc = (PCVUSBDESCCONFIGEX)pvNewCfgDesc;
603 Assert(pNewCfgDesc->Core.bConfigurationValue == bConfigurationValue);
604 for (unsigned iIf = 0; iIf < pNewCfgDesc->Core.bNumInterfaces; iIf++)
605 {
606 PCVUSBINTERFACE pIf = &pNewCfgDesc->paIfs[iIf];
607 for (uint32_t iAlt = 0; iAlt < pIf->cSettings; iAlt++)
608 {
609 if (pIf->paSettings[iAlt].Core.bAlternateSetting != 0)
610 continue;
611 pProxyDev->pOps->pfnClaimInterface(pProxyDev, pIf->paSettings[iAlt].Core.bInterfaceNumber);
612 /* ignore failures - the backend deals with that and does the necessary logging. */
613 break;
614 }
615 }
616
617 return VINF_SUCCESS;
618}
619
620
621/**
622 * @copydoc PDMUSBREG::pfnUsbSetInterface
623 *
624 * USB Device Proxy: Call OS specific code to select alternate interface settings.
625 */
626static DECLCALLBACK(int) usbProxyDevSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
627{
628 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
629 LogFlow(("usbProxyDevSetInterface: pProxyDev=%s bInterfaceNumber=%d bAlternateSetting=%d\n",
630 pUsbIns->pszName, bInterfaceNumber, bAlternateSetting));
631
632 /** @todo this is fishy, pfnSetInterface returns true/false from what I can see... */
633 if (pProxyDev->pOps->pfnSetInterface(pProxyDev, bInterfaceNumber, bAlternateSetting) < 0)
634 return VERR_GENERAL_FAILURE;
635 return VINF_SUCCESS;
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 if (!pProxyDev->pOps->pfnClearHaltedEndpoint(pProxyDev, uEndpoint))
651 return VERR_GENERAL_FAILURE;
652 return VINF_SUCCESS;
653}
654
655
656/**
657 * @copydoc PDMUSBREG::pfnUrbQueue
658 *
659 * USB Device Proxy: Call OS specific code.
660 */
661static DECLCALLBACK(int) usbProxyDevUrbQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
662{
663 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
664 if (!pProxyDev->pOps->pfnUrbQueue(pUrb))
665 return pProxyDev->fDetached
666 ? VERR_VUSB_DEVICE_NOT_ATTACHED
667 : VERR_VUSB_FAILED_TO_QUEUE_URB;
668 return VINF_SUCCESS;
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 pProxyDev->pOps->pfnUrbCancel(pUrb);
681 return VINF_SUCCESS;
682}
683
684
685/**
686 * @copydoc PDMUSBREG::pfnUrbReap
687 *
688 * USB Device Proxy: Call OS specific code.
689 */
690static DECLCALLBACK(PVUSBURB) usbProxyDevUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
691{
692 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
693 PVUSBURB pUrb = pProxyDev->pOps->pfnUrbReap(pProxyDev, cMillies);
694 if ( pUrb
695 && pUrb->enmState == VUSBURBSTATE_CANCELLED
696 && pUrb->enmStatus == VUSBSTATUS_OK)
697 pUrb->enmStatus = VUSBSTATUS_DNR;
698 return pUrb;
699}
700
701
702/** @copydoc PDMUSBREG::pfnDestruct */
703static DECLCALLBACK(void) usbProxyDestruct(PPDMUSBINS pUsbIns)
704{
705 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
706 Log(("usbProxyDestruct: destroying pProxyDev=%s\n", pUsbIns->pszName));
707
708 /* close it. */
709 if (pThis->fOpened)
710 {
711 pThis->pOps->pfnClose(pThis);
712 pThis->fOpened = false;
713 }
714
715 /* free the config descriptors. */
716 if (pThis->paCfgDescs)
717 {
718 for (unsigned i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
719 {
720 RTMemFree((void *)pThis->paCfgDescs[i].paIfs);
721 RTMemFree((void *)pThis->paCfgDescs[i].pvOriginal);
722 }
723 /** @todo bugref{2693} cleanup */
724 RTMemFree(pThis->paCfgDescs);
725 pThis->paCfgDescs = NULL;
726 }
727
728 /* free dev */
729 if (&g_szDummyName[0] != pUsbIns->pszName)
730 RTStrFree(pUsbIns->pszName);
731 pUsbIns->pszName = NULL;
732}
733
734
735/**
736 * Helper function used by usbProxyConstruct when
737 * reading a filter from CFG.
738 *
739 * @returns VBox status code.
740 * @param pFilter The filter.
741 * @param enmFieldIdx The filter field indext.
742 * @param pNode The CFGM node.
743 * @param pszExact The exact value name.
744 * @param pszExpr The expression value name.
745 */
746static int usbProxyQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, PCFGMNODE pNode, const char *pszExact, const char *pszExpr)
747{
748 char szTmp[256];
749
750 /* try exact first */
751 uint16_t u16;
752 int rc = CFGMR3QueryU16(pNode, pszExact, &u16);
753 if (RT_SUCCESS(rc))
754 {
755 rc = USBFilterSetNumExact(pFilter, enmFieldIdx, u16, true);
756 AssertRCReturn(rc, rc);
757
758 /* make sure only the exact attribute is present. */
759 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
760 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
761 {
762 szTmp[0] = '\0';
763 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
764 LogRel(("usbProxyConstruct: %s: Both %s and %s are present!\n", szTmp, pszExact, pszExpr));
765 return VERR_INVALID_PARAMETER;
766 }
767 return VINF_SUCCESS;
768 }
769 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
770 {
771 szTmp[0] = '\0';
772 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
773 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExact, rc));
774 return rc;
775 }
776
777 /* expression? */
778 rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
779 if (RT_SUCCESS(rc))
780 {
781 rc = USBFilterSetNumExpression(pFilter, enmFieldIdx, szTmp, true);
782 AssertRCReturn(rc, rc);
783 return VINF_SUCCESS;
784 }
785 if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
786 {
787 szTmp[0] = '\0';
788 CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
789 LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExpr, rc));
790 return rc;
791 }
792
793 return VINF_SUCCESS;
794}
795
796
797/** @copydoc PDMUSBREG::pfnConstruct */
798static DECLCALLBACK(int) usbProxyConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
799{
800 PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
801 LogFlow(("usbProxyConstruct: pUsbIns=%p iInstance=%d\n", pUsbIns, iInstance));
802
803 /*
804 * Initialize the instance data.
805 */
806 pThis->pUsbIns = pUsbIns;
807 pThis->pUsbIns->pszName = g_szDummyName;
808 pThis->iActiveCfg = -1;
809 pThis->fMaskedIfs = 0;
810 pThis->fOpened = false;
811 pThis->fInited = false;
812
813 /*
814 * Read the basic configuration.
815 */
816 char szAddress[1024];
817 int rc = CFGMR3QueryString(pCfg, "Address", szAddress, sizeof(szAddress));
818 AssertRCReturn(rc, rc);
819
820 bool fRemote;
821 rc = CFGMR3QueryBool(pCfg, "Remote", &fRemote);
822 AssertRCReturn(rc, rc);
823
824 void *pvBackend;
825 rc = CFGMR3QueryPtr(pCfg, "pvBackend", &pvBackend);
826 AssertRCReturn(rc, rc);
827
828 /*
829 * Select backend and open the device.
830 */
831 if (!fRemote)
832 pThis->pOps = &g_USBProxyDeviceHost;
833 else
834 pThis->pOps = &g_USBProxyDeviceVRDP;
835 rc = pThis->pOps->pfnOpen(pThis, szAddress, pvBackend);
836 if (RT_FAILURE(rc))
837 return rc;
838 pThis->fOpened = true;
839
840 /*
841 * Get the device descriptor and format the device name (for logging).
842 */
843 if (!usbProxyGetDeviceDesc(pThis, &pThis->DevDesc))
844 {
845 Log(("usbProxyConstruct: usbProxyGetDeviceDesc failed\n"));
846 return VERR_READ_ERROR;
847 }
848
849 RTStrAPrintf(&pUsbIns->pszName, "%p[proxy %04x:%04x]", pThis, pThis->DevDesc.idVendor, pThis->DevDesc.idProduct); /** @todo append the user comment */
850 AssertReturn(pUsbIns->pszName, VERR_NO_MEMORY);
851
852 /*
853 * Get config descriptors.
854 */
855 size_t cbConfigs = pThis->DevDesc.bNumConfigurations * sizeof(pThis->paCfgDescs[0]);
856 pThis->paCfgDescs = (PVUSBDESCCONFIGEX)RTMemAllocZ(cbConfigs);
857 AssertReturn(pThis->paCfgDescs, VERR_NO_MEMORY);
858
859 unsigned i;
860 for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
861 if (!copy_config(pThis, i, (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i]))
862 break;
863 if (i < pThis->DevDesc.bNumConfigurations)
864 {
865 Log(("usbProxyConstruct: copy_config failed, i=%d\n", i));
866 return VERR_READ_ERROR;
867 }
868
869 /*
870 * Pickup best matching global configuration for this device.
871 * The global configuration is organized like this:
872 *
873 * GlobalConfig/Whatever/
874 * |- idVendor = 300
875 * |- idProduct = 300
876 * - Config/
877 *
878 * The first level contains filter attributes which we stuff into a USBFILTER
879 * structure and match against the device info that's available. The highest
880 * ranked match is will be used. If nothing is found, the values will be
881 * queried from the GlobalConfig node (simplifies code and might actually
882 * be useful).
883 */
884 PCFGMNODE pCfgGlobalDev = pCfgGlobal;
885 PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgGlobal);
886 if (pCur)
887 {
888 /*
889 * Create a device filter from the device configuration
890 * descriptor ++. No strings currently.
891 */
892 USBFILTER Device;
893 USBFilterInit(&Device, USBFILTERTYPE_CAPTURE);
894 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_VENDOR_ID, pThis->DevDesc.idVendor, true); AssertRC(rc);
895 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_PRODUCT_ID, pThis->DevDesc.idProduct, true); AssertRC(rc);
896 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_REV, pThis->DevDesc.bcdDevice, true); AssertRC(rc);
897 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_CLASS, pThis->DevDesc.bDeviceClass, true); AssertRC(rc);
898 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_SUB_CLASS, pThis->DevDesc.bDeviceSubClass, true); AssertRC(rc);
899 rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_PROTOCOL, pThis->DevDesc.bDeviceProtocol, true); AssertRC(rc);
900 /** @todo manufacturer, product and serial strings */
901
902 int iBestMatchRate = -1;
903 PCFGMNODE pBestMatch = NULL;
904 for (pCur = CFGMR3GetFirstChild(pCfgGlobal); pCur; pCur = CFGMR3GetNextChild(pCur))
905 {
906 /*
907 * Construct a filter from the attributes in the node.
908 */
909 USBFILTER Filter;
910 USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
911
912 /* numeric */
913 if ( RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_VENDOR_ID, pCur, "idVendor", "idVendorExpr"))
914 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_PRODUCT_ID, pCur, "idProduct", "idProcutExpr"))
915 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_REV, pCur, "bcdDevice", "bcdDeviceExpr"))
916 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_CLASS, pCur, "bDeviceClass", "bDeviceClassExpr"))
917 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pCur, "bDeviceSubClass", "bDeviceSubClassExpr"))
918 || RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pCur, "bDeviceProtocol", "bDeviceProtocolExpr")))
919 continue; /* skip it */
920
921 /* strings */
922 /** @todo manufacturer, product and serial strings */
923
924 /* ignore unknown config values, but not without bitching. */
925 if (!CFGMR3AreValuesValid(pCur,
926 "idVendor\0idVendorExpr\0"
927 "idProduct\0idProductExpr\0"
928 "bcdDevice\0bcdDeviceExpr\0"
929 "bDeviceClass\0bDeviceClassExpr\0"
930 "bDeviceSubClass\0bDeviceSubClassExpr\0"
931 "bDeviceProtocol\0bDeviceProtocolExpr\0"))
932 LogRel(("usbProxyConstruct: Unknown value(s) in config filter (ignored)!\n"));
933
934 /*
935 * Try match it and on match see if it has is a higher rate hit
936 * than the previous match. Quit if its a 100% match.
937 */
938 int iRate = USBFilterMatchRated(&Filter, &Device);
939 if (iRate > iBestMatchRate)
940 {
941 pBestMatch = pCur;
942 iBestMatchRate = iRate;
943 if (iRate >= 100)
944 break;
945 }
946 }
947 if (pBestMatch)
948 pCfgGlobalDev = CFGMR3GetChild(pBestMatch, "Config");
949 if (pCfgGlobalDev)
950 pCfgGlobalDev = pCfgGlobal;
951 }
952
953 /*
954 * Query the rest of the configuration using the global as fallback.
955 */
956 rc = CFGMR3QueryU32(pCfg, "MaskedIfs", &pThis->fMaskedIfs);
957 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
958 rc = CFGMR3QueryU32(pCfgGlobalDev, "MaskedIfs", &pThis->fMaskedIfs);
959 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
960 pThis->fMaskedIfs = 0;
961 else
962 AssertRCReturn(rc, rc);
963
964 bool fForce11Device;
965 rc = CFGMR3QueryBool(pCfg, "Force11Device", &fForce11Device);
966 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
967 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11Device", &fForce11Device);
968 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
969 fForce11Device = false;
970 else
971 AssertRCReturn(rc, rc);
972
973 bool fForce11PacketSize;
974 rc = CFGMR3QueryBool(pCfg, "Force11PacketSize", &fForce11PacketSize);
975 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
976 rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11PacketSize", &fForce11PacketSize);
977 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
978 fForce11PacketSize = false;
979 else
980 AssertRCReturn(rc, rc);
981
982 /*
983 * If we're masking interfaces, edit the descriptors.
984 */
985 bool fEdited = pThis->fMaskedIfs != 0;
986 if (pThis->fMaskedIfs)
987 usbProxyDevEditOutMaskedIfs(pThis);
988
989 /*
990 * Do 2.0 -> 1.1 device edits if requested to do so.
991 */
992 if ( fForce11PacketSize
993 && pThis->DevDesc.bcdUSB >= 0x0200)
994 {
995 PVUSBDESCCONFIGEX paCfgs = pThis->paCfgDescs;
996 for (unsigned iCfg = 0; iCfg < pThis->DevDesc.bNumConfigurations; iCfg++)
997 {
998 PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
999 for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
1000 for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
1001 {
1002 /*
1003 * USB 1.1 defines the max for control, interrupt and bulk to be 64 bytes.
1004 * While isochronous has a max of 1023 bytes.
1005 */
1006 PVUSBDESCENDPOINTEX paEps = (PVUSBDESCENDPOINTEX)paIfs[iIf].paSettings[iAlt].paEndpoints;
1007 for (unsigned iEp = 0; iEp < paIfs[iIf].paSettings[iAlt].Core.bNumEndpoints; iEp++)
1008 {
1009 const uint16_t cbMax = (paEps[iEp].Core.bmAttributes & 3) == 1 /* isoc */
1010 ? 1023
1011 : 64;
1012 if (paEps[iEp].Core.wMaxPacketSize > cbMax)
1013 {
1014 Log(("usb-proxy: pProxyDev=%s correcting wMaxPacketSize from %#x to %#x (mainly for vista)\n",
1015 pUsbIns->pszName, paEps[iEp].Core.wMaxPacketSize, cbMax));
1016 paEps[iEp].Core.wMaxPacketSize = cbMax;
1017 fEdited = true;
1018 }
1019 }
1020 }
1021 }
1022 }
1023
1024 if ( fForce11Device
1025 && pThis->DevDesc.bcdUSB == 0x0200)
1026 {
1027 /*
1028 * Discourages windows from helping you find a 2.0 port.
1029 */
1030 Log(("usb-proxy: %s correcting USB version 2.0 to 1.1 (to avoid Windows warning)\n", pUsbIns->pszName));
1031 pThis->DevDesc.bcdUSB = 0x110;
1032 fEdited = true;
1033 }
1034
1035
1036 /*
1037 * Init the PDM/VUSB descriptor cache.
1038 */
1039 pThis->DescCache.pDevice = &pThis->DevDesc;
1040 pThis->DescCache.paConfigs = pThis->paCfgDescs;
1041 pThis->DescCache.paLanguages = NULL;
1042 pThis->DescCache.cLanguages = 0;
1043 pThis->DescCache.fUseCachedDescriptors = fEdited;
1044 pThis->DescCache.fUseCachedStringsDescriptors = false;
1045
1046 /*
1047 * Call the backend if it wishes to do some more initializing
1048 * after we've read the config and descriptors.
1049 */
1050 if (pThis->pOps->pfnInit)
1051 {
1052 rc = pThis->pOps->pfnInit(pThis);
1053 if (RT_FAILURE(rc))
1054 return rc;
1055 }
1056 pThis->fInited = true;
1057
1058 /*
1059 * We're good!
1060 */
1061 Log(("usb-proxy: created pProxyDev=%s address '%s' fMaskedIfs=%#x (rc=%Rrc)\n",
1062 pUsbIns->pszName, szAddress, pThis->fMaskedIfs, rc));
1063 return VINF_SUCCESS;
1064}
1065
1066
1067/**
1068 * The USB proxy device registration record.
1069 */
1070const PDMUSBREG g_UsbDevProxy =
1071{
1072 /* u32Version */
1073 PDM_USBREG_VERSION,
1074 /* szName */
1075 "USBProxy",
1076 /* pszDescription */
1077 "USB Proxy Device.",
1078 /* fFlags */
1079 0,
1080 /* cMaxInstances */
1081 ~0,
1082 /* cbInstance */
1083 sizeof(USBPROXYDEV),
1084 /* pfnConstruct */
1085 usbProxyConstruct,
1086 /* pfnDestruct */
1087 usbProxyDestruct,
1088 /* pfnVMInitComplete */
1089 NULL,
1090 /* pfnVMPowerOn */
1091 NULL,
1092 /* pfnVMReset */
1093 NULL,
1094 /* pfnVMSuspend */
1095 NULL,
1096 /* pfnVMResume */
1097 NULL,
1098 /* pfnVMPowerOff */
1099 NULL,
1100 /* pfnHotPlugged */
1101 NULL,
1102 /* pfnHotUnplugged */
1103 NULL,
1104 /* pfnDriverAttach */
1105 NULL,
1106 /* pfnDriverDetach */
1107 NULL,
1108 /* pfnQueryInterface */
1109 NULL,
1110 /* pfnUsbReset */
1111 usbProxyDevReset,
1112 /* pfnUsbGetDescriptorCache */
1113 usbProxyDevGetDescriptorCache,
1114 /* pfnUsbSetConfiguration */
1115 usbProxyDevSetConfiguration,
1116 /* pfnUsbSetInterface */
1117 usbProxyDevSetInterface,
1118 /* pfnUsbClearHaltedEndpoint */
1119 usbProxyDevClearHaltedEndpoint,
1120 /* pfnUrbNew */
1121 NULL,
1122 /* pfnUrbQueue */
1123 usbProxyDevUrbQueue,
1124 /* pfnUrbCancel */
1125 usbProxyDevUrbCancel,
1126 /* pfnUrbReap */
1127 usbProxyDevUrbReap,
1128
1129 /* u32TheEnd */
1130 PDM_USBREG_VERSION
1131};
1132
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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