VirtualBox

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

Last change on this file since 47631 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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