VirtualBox

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

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

USBProxyDevice: Fix broken passthrough, pfnSetConfig returns a status code now and not a boolean success indicator

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