VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp@ 69496

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.4 KB
Line 
1/* $Id: VBoxUsbLib-win.cpp 69496 2017-10-28 14:55:58Z vboxsync $ */
2/** @file
3 * VBox USB ring-3 Driver Interface library, Windows.
4 */
5
6/*
7 * Copyright (C) 2011-2017 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
32#include <iprt/win/windows.h>
33
34#include <VBox/sup.h>
35#include <VBox/types.h>
36#include <VBox/err.h>
37#include <VBox/param.h>
38#include <iprt/path.h>
39#include <iprt/assert.h>
40#include <iprt/alloc.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43#include <VBox/log.h>
44#include <VBox/usblib.h>
45#include <VBox/usblib-win.h>
46#include <VBox/usb.h>
47#include <VBox/VBoxDrvCfg-win.h>
48#include <stdio.h>
49#pragma warning (disable:4200) /* shuts up the empty array member warnings */
50#include <iprt/win/setupapi.h>
51#include <usbdi.h>
52#include <hidsdi.h>
53
54#define VBOX_USB_USE_DEVICE_NOTIFICATION
55
56#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
57# include <Dbt.h>
58#endif
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64typedef struct _USB_INTERFACE_DESCRIPTOR2
65{
66 UCHAR bLength;
67 UCHAR bDescriptorType;
68 UCHAR bInterfaceNumber;
69 UCHAR bAlternateSetting;
70 UCHAR bNumEndpoints;
71 UCHAR bInterfaceClass;
72 UCHAR bInterfaceSubClass;
73 UCHAR bInterfaceProtocol;
74 UCHAR iInterface;
75 USHORT wNumClasses;
76} USB_INTERFACE_DESCRIPTOR2, *PUSB_INTERFACE_DESCRIPTOR2;
77
78typedef struct VBOXUSBGLOBALSTATE
79{
80 HANDLE hMonitor;
81 HANDLE hNotifyEvent;
82 HANDLE hInterruptEvent;
83#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
84 HANDLE hThread;
85 HWND hWnd;
86 HANDLE hTimerQueue;
87 HANDLE hTimer;
88#endif
89} VBOXUSBGLOBALSTATE, *PVBOXUSBGLOBALSTATE;
90
91typedef struct VBOXUSB_STRING_DR_ENTRY
92{
93 struct VBOXUSB_STRING_DR_ENTRY *pNext;
94 UCHAR iDr;
95 USHORT idLang;
96 USB_STRING_DESCRIPTOR StrDr;
97} VBOXUSB_STRING_DR_ENTRY, *PVBOXUSB_STRING_DR_ENTRY;
98
99/**
100 * This represents VBoxUsb device instance
101 */
102typedef struct VBOXUSB_DEV
103{
104 struct VBOXUSB_DEV *pNext;
105 char szName[512];
106 char szDriverRegName[512];
107} VBOXUSB_DEV, *PVBOXUSB_DEV;
108
109
110/*********************************************************************************************************************************
111* Global Variables *
112*********************************************************************************************************************************/
113static VBOXUSBGLOBALSTATE g_VBoxUsbGlobal;
114
115
116int usbLibVuDeviceValidate(PVBOXUSB_DEV pVuDev)
117{
118 HANDLE hOut = INVALID_HANDLE_VALUE;
119
120 hOut = CreateFile(pVuDev->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
121 OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
122
123 if (hOut == INVALID_HANDLE_VALUE)
124 {
125 DWORD dwErr = GetLastError(); NOREF(dwErr);
126 AssertMsgFailed(("CreateFile FAILED to open %s, dwErr (%d)\n", pVuDev->szName, dwErr));
127 return VERR_GENERAL_FAILURE;
128 }
129
130 USBSUP_VERSION version = {0};
131 DWORD cbReturned = 0;
132 int rc = VERR_VERSION_MISMATCH;
133
134 do
135 {
136 if (!DeviceIoControl(hOut, SUPUSB_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
137 {
138 AssertMsgFailed(("DeviceIoControl SUPUSB_IOCTL_GET_VERSION failed with LastError=%Rwa\n", GetLastError()));
139 break;
140 }
141
142 if ( version.u32Major != USBDRV_MAJOR_VERSION
143#if USBDRV_MINOR_VERSION != 0
144 || version.u32Minor < USBDRV_MINOR_VERSION
145#endif
146 )
147 {
148 AssertMsgFailed(("Invalid version %d:%d vs %d:%d\n", version.u32Major, version.u32Minor, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
149 break;
150 }
151
152 if (!DeviceIoControl(hOut, SUPUSB_IOCTL_IS_OPERATIONAL, NULL, 0, NULL, NULL, &cbReturned, NULL))
153 {
154 AssertMsgFailed(("DeviceIoControl SUPUSB_IOCTL_IS_OPERATIONAL failed with LastError=%Rwa\n", GetLastError()));
155 break;
156 }
157
158 rc = VINF_SUCCESS;
159 } while (0);
160
161 CloseHandle(hOut);
162 return rc;
163}
164
165static int usbLibVuDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO hDevInfo, PSP_DEVICE_INTERFACE_DATA pIfData)
166{
167 DWORD cbIfDetailData;
168 int rc = VINF_SUCCESS;
169
170 SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
171 NULL, /* OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData */
172 0, /* IN DWORD DeviceInterfaceDetailDataSize */
173 &cbIfDetailData,
174 NULL
175 );
176 Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
177
178 PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAllocZ(cbIfDetailData);
179 if (!pIfDetailData)
180 {
181 AssertMsgFailed(("RTMemAllocZ failed\n"));
182 return VERR_OUT_OF_RESOURCES;
183 }
184
185 DWORD cbDbgRequired;
186 SP_DEVINFO_DATA DevInfoData;
187 DevInfoData.cbSize = sizeof (DevInfoData);
188 /* the cbSize should contain the sizeof a fixed-size part according to the docs */
189 pIfDetailData->cbSize = sizeof (*pIfDetailData);
190 do
191 {
192 if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
193 pIfDetailData,
194 cbIfDetailData,
195 &cbDbgRequired,
196 &DevInfoData))
197 {
198 DWORD dwErr = GetLastError(); NOREF(dwErr);
199 AssertMsgFailed(("SetupDiGetDeviceInterfaceDetail, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, cbIfDetailData, dwErr));
200 rc = VERR_GENERAL_FAILURE;
201 break;
202 }
203
204 strncpy(pVuDev->szName, pIfDetailData->DevicePath, sizeof (pVuDev->szName));
205
206 if (!SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DevInfoData, SPDRP_DRIVER,
207 NULL, /* OUT PDWORD PropertyRegDataType */
208 (PBYTE)pVuDev->szDriverRegName,
209 sizeof (pVuDev->szDriverRegName),
210 &cbDbgRequired))
211 {
212 DWORD dwErr = GetLastError(); NOREF(dwErr);
213 AssertMsgFailed(("SetupDiGetDeviceRegistryPropertyA, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, sizeof (pVuDev->szDriverRegName), dwErr));
214 rc = VERR_GENERAL_FAILURE;
215 break;
216 }
217
218 rc = usbLibVuDeviceValidate(pVuDev);
219 AssertRC(rc);
220 } while (0);
221
222 RTMemFree(pIfDetailData);
223 return rc;
224}
225
226static void usbLibVuFreeDevices(PVBOXUSB_DEV pDevInfos)
227{
228 while (pDevInfos)
229 {
230 PVBOXUSB_DEV pNext = pDevInfos->pNext;
231 RTMemFree(pDevInfos);
232 pDevInfos = pNext;
233 }
234}
235
236static int usbLibVuGetDevices(PVBOXUSB_DEV *ppVuDevs, uint32_t *pcVuDevs)
237{
238 *ppVuDevs = NULL;
239 *pcVuDevs = 0;
240
241 HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_CLASS_VBOXUSB,
242 NULL, /* IN PCTSTR Enumerator */
243 NULL, /* IN HWND hwndParent */
244 (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE) /* IN DWORD Flags */
245 );
246 if (hDevInfo == INVALID_HANDLE_VALUE)
247 {
248 DWORD dwErr = GetLastError(); NOREF(dwErr);
249 AssertMsgFailed(("SetupDiGetClassDevs, dwErr (%d)\n", dwErr));
250 return VERR_GENERAL_FAILURE;
251 }
252
253 for (int i = 0; ; ++i)
254 {
255 SP_DEVICE_INTERFACE_DATA IfData;
256 IfData.cbSize = sizeof (IfData);
257 if (!SetupDiEnumDeviceInterfaces(hDevInfo,
258 NULL, /* IN PSP_DEVINFO_DATA DeviceInfoData */
259 &GUID_CLASS_VBOXUSB, /* IN LPGUID InterfaceClassGuid */
260 i,
261 &IfData))
262 {
263 DWORD dwErr = GetLastError();
264 if (dwErr == ERROR_NO_MORE_ITEMS)
265 break;
266
267 AssertMsgFailed(("SetupDiEnumDeviceInterfaces, dwErr (%d), resuming\n", dwErr));
268 continue;
269 }
270
271 /* we've now got the IfData */
272 PVBOXUSB_DEV pVuDev = (PVBOXUSB_DEV)RTMemAllocZ(sizeof (*pVuDev));
273 if (!pVuDev)
274 {
275 AssertMsgFailed(("RTMemAllocZ failed, resuming\n"));
276 continue;
277 }
278
279 int rc = usbLibVuDevicePopulate(pVuDev, hDevInfo, &IfData);
280 if (!RT_SUCCESS(rc))
281 {
282 AssertMsgFailed(("usbLibVuDevicePopulate failed, rc (%d), resuming\n", rc));
283 continue;
284 }
285
286 pVuDev->pNext = *ppVuDevs;
287 *ppVuDevs = pVuDev;
288 ++*pcVuDevs;
289 }
290
291 SetupDiDestroyDeviceInfoList(hDevInfo);
292
293 return VINF_SUCCESS;
294}
295
296static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszDrvKeyName, LPCSTR lpszHubName, PVBOXUSB_STRING_DR_ENTRY pDrList)
297{
298 pDev->bcdUSB = pConInfo->DeviceDescriptor.bcdUSB;
299 pDev->bDeviceClass = pConInfo->DeviceDescriptor.bDeviceClass;
300 pDev->bDeviceSubClass = pConInfo->DeviceDescriptor.bDeviceSubClass;
301 pDev->bDeviceProtocol = pConInfo->DeviceDescriptor.bDeviceProtocol;
302 pDev->idVendor = pConInfo->DeviceDescriptor.idVendor;
303 pDev->idProduct = pConInfo->DeviceDescriptor.idProduct;
304 pDev->bcdDevice = pConInfo->DeviceDescriptor.bcdDevice;
305 pDev->bBus = 0; /** @todo figure out bBus on windows... */
306 pDev->bPort = iPort;
307 /** @todo check which devices are used for primary input (keyboard & mouse) */
308 if (!lpszDrvKeyName || *lpszDrvKeyName == 0)
309 pDev->enmState = USBDEVICESTATE_UNUSED;
310 else
311 pDev->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
312 pDev->enmSpeed = USBDEVICESPEED_UNKNOWN;
313 int rc = RTStrAPrintf((char **)&pDev->pszAddress, "%s", lpszDrvKeyName);
314 if (rc < 0)
315 return VERR_NO_MEMORY;
316 pDev->pszBackend = RTStrDup("host");
317 if (!pDev->pszBackend)
318 {
319 RTStrFree((char *)pDev->pszAddress);
320 return VERR_NO_STR_MEMORY;
321 }
322 pDev->pszHubName = RTStrDup(lpszHubName);
323 pDev->bNumConfigurations = 0;
324 pDev->u64SerialHash = 0;
325
326 for (; pDrList; pDrList = pDrList->pNext)
327 {
328 char **ppszString = NULL;
329 if ( pConInfo->DeviceDescriptor.iManufacturer
330 && pDrList->iDr == pConInfo->DeviceDescriptor.iManufacturer)
331 ppszString = (char **)&pDev->pszManufacturer;
332 else if ( pConInfo->DeviceDescriptor.iProduct
333 && pDrList->iDr == pConInfo->DeviceDescriptor.iProduct)
334 ppszString = (char **)&pDev->pszProduct;
335 else if ( pConInfo->DeviceDescriptor.iSerialNumber
336 && pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
337 ppszString = (char **)&pDev->pszSerialNumber;
338 if (ppszString)
339 {
340 rc = RTUtf16ToUtf8((PCRTUTF16)pDrList->StrDr.bString, ppszString);
341 if (RT_SUCCESS(rc))
342 {
343 Assert(*ppszString);
344 USBLibPurgeEncoding(*ppszString);
345
346 if (pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
347 pDev->u64SerialHash = USBLibHashSerial(*ppszString);
348 }
349 else
350 {
351 AssertMsgFailed(("RTUtf16ToUtf8 failed, rc (%d), resuming\n", rc));
352 *ppszString = NULL;
353 }
354 }
355 }
356
357 return VINF_SUCCESS;
358}
359
360static void usbLibDevStrFree(LPSTR lpszName)
361{
362 RTStrFree(lpszName);
363}
364
365static int usbLibDevStrDriverKeyGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
366{
367 USB_NODE_CONNECTION_DRIVERKEY_NAME Name;
368 DWORD cbReturned = 0;
369 Name.ConnectionIndex = iPort;
370 *plpszName = NULL;
371 if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
372 {
373#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
374 DWORD dwErr = GetLastError();
375 AssertMsgFailed(("DeviceIoControl 1 fail dwErr (%d)\n", dwErr));
376#endif
377 return VERR_GENERAL_FAILURE;
378 }
379
380 if (Name.ActualLength < sizeof (Name))
381 {
382 AssertFailed();
383 return VERR_OUT_OF_RESOURCES;
384 }
385
386 PUSB_NODE_CONNECTION_DRIVERKEY_NAME pName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)RTMemAllocZ(Name.ActualLength);
387 if (!pName)
388 {
389 AssertFailed();
390 return VERR_OUT_OF_RESOURCES;
391 }
392
393 int rc = VINF_SUCCESS;
394 pName->ConnectionIndex = iPort;
395 if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
396 {
397 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->DriverKeyName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
398 AssertRC(rc);
399 if (RT_SUCCESS(rc))
400 rc = VINF_SUCCESS;
401 }
402 else
403 {
404 DWORD dwErr = GetLastError(); NOREF(dwErr);
405 AssertMsgFailed(("DeviceIoControl 2 fail dwErr (%d)\n", dwErr));
406 rc = VERR_GENERAL_FAILURE;
407 }
408 RTMemFree(pName);
409 return rc;
410}
411
412static int usbLibDevStrHubNameGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
413{
414 USB_NODE_CONNECTION_NAME Name;
415 DWORD cbReturned = 0;
416 Name.ConnectionIndex = iPort;
417 *plpszName = NULL;
418 if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
419 {
420 AssertFailed();
421 return VERR_GENERAL_FAILURE;
422 }
423
424 if (Name.ActualLength < sizeof (Name))
425 {
426 AssertFailed();
427 return VERR_OUT_OF_RESOURCES;
428 }
429
430 PUSB_NODE_CONNECTION_NAME pName = (PUSB_NODE_CONNECTION_NAME)RTMemAllocZ(Name.ActualLength);
431 if (!pName)
432 {
433 AssertFailed();
434 return VERR_OUT_OF_RESOURCES;
435 }
436
437 int rc = VINF_SUCCESS;
438 pName->ConnectionIndex = iPort;
439 if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
440 {
441 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->NodeName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
442 AssertRC(rc);
443 if (RT_SUCCESS(rc))
444 rc = VINF_SUCCESS;
445 }
446 else
447 {
448 AssertFailed();
449 rc = VERR_GENERAL_FAILURE;
450 }
451 RTMemFree(pName);
452 return rc;
453}
454
455static int usbLibDevStrRootHubNameGet(HANDLE hCtl, LPSTR* plpszName)
456{
457 USB_ROOT_HUB_NAME HubName;
458 DWORD cbReturned = 0;
459 *plpszName = NULL;
460 if (!DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, &HubName, sizeof (HubName), &cbReturned, NULL))
461 {
462 return VERR_GENERAL_FAILURE;
463 }
464 PUSB_ROOT_HUB_NAME pHubName = (PUSB_ROOT_HUB_NAME)RTMemAllocZ(HubName.ActualLength);
465 if (!pHubName)
466 return VERR_OUT_OF_RESOURCES;
467
468 int rc = VINF_SUCCESS;
469 if (DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, pHubName, HubName.ActualLength, &cbReturned, NULL))
470 {
471 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pHubName->RootHubName, pHubName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
472 AssertRC(rc);
473 if (RT_SUCCESS(rc))
474 rc = VINF_SUCCESS;
475 }
476 else
477 {
478 rc = VERR_GENERAL_FAILURE;
479 }
480 RTMemFree(pHubName);
481 return rc;
482}
483
484static int usbLibDevCfgDrGet(HANDLE hHub, ULONG iPort, ULONG iDr, PUSB_CONFIGURATION_DESCRIPTOR *ppDr)
485{
486 *ppDr = NULL;
487
488 char Buf[sizeof (USB_DESCRIPTOR_REQUEST) + sizeof (USB_CONFIGURATION_DESCRIPTOR)];
489 memset(&Buf, 0, sizeof (Buf));
490
491 PUSB_DESCRIPTOR_REQUEST pCfgDrRq = (PUSB_DESCRIPTOR_REQUEST)Buf;
492 PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)(Buf + sizeof (*pCfgDrRq));
493
494 pCfgDrRq->ConnectionIndex = iPort;
495 pCfgDrRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
496 pCfgDrRq->SetupPacket.wLength = (USHORT)(sizeof (USB_CONFIGURATION_DESCRIPTOR));
497 DWORD cbReturned = 0;
498 if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pCfgDrRq, sizeof (Buf),
499 pCfgDrRq, sizeof (Buf),
500 &cbReturned, NULL))
501 {
502 DWORD dwErr = GetLastError();
503 LogRelFunc(("DeviceIoControl 1 fail dwErr (%d)\n", dwErr));
504#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
505 AssertFailed();
506#endif
507 return VERR_GENERAL_FAILURE;
508 }
509
510 if (sizeof (Buf) != cbReturned)
511 {
512 AssertFailed();
513 return VERR_GENERAL_FAILURE;
514 }
515
516 if (pCfgDr->wTotalLength < sizeof (USB_CONFIGURATION_DESCRIPTOR))
517 {
518 AssertFailed();
519 return VERR_GENERAL_FAILURE;
520 }
521
522 DWORD cbRq = sizeof (USB_DESCRIPTOR_REQUEST) + pCfgDr->wTotalLength;
523 PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)RTMemAllocZ(cbRq);
524 Assert(pRq);
525 if (!pRq)
526 return VERR_OUT_OF_RESOURCES;
527
528 int rc = VERR_GENERAL_FAILURE;
529 do
530 {
531 PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)(pRq + 1);
532 pRq->ConnectionIndex = iPort;
533 pRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
534 pRq->SetupPacket.wLength = (USHORT)(cbRq - sizeof (USB_DESCRIPTOR_REQUEST));
535 if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, cbRq,
536 pRq, cbRq,
537 &cbReturned, NULL))
538 {
539 DWORD dwErr = GetLastError();
540 LogRelFunc(("DeviceIoControl 2 fail dwErr (%d)\n", dwErr));
541#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
542 AssertFailed();
543#endif
544 break;
545 }
546
547 if (cbRq != cbReturned)
548 {
549 AssertFailed();
550 break;
551 }
552
553 if (pDr->wTotalLength != cbRq - sizeof (USB_DESCRIPTOR_REQUEST))
554 {
555 AssertFailed();
556 break;
557 }
558
559 *ppDr = pDr;
560 return VINF_SUCCESS;
561 } while (0);
562
563 RTMemFree(pRq);
564 return rc;
565}
566
567static void usbLibDevCfgDrFree(PUSB_CONFIGURATION_DESCRIPTOR pDr)
568{
569 Assert(pDr);
570 PUSB_DESCRIPTOR_REQUEST pRq = ((PUSB_DESCRIPTOR_REQUEST)pDr)-1;
571 RTMemFree(pRq);
572}
573
574static int usbLibDevStrDrEntryGet(HANDLE hHub, ULONG iPort, ULONG iDr, USHORT idLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
575{
576 char szBuf[sizeof (USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];
577 RT_ZERO(szBuf);
578
579 PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)szBuf;
580 PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)(szBuf + sizeof (*pRq));
581 RT_BZERO(pDr, sizeof(USB_STRING_DESCRIPTOR));
582
583 pRq->ConnectionIndex = iPort;
584 pRq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | iDr;
585 pRq->SetupPacket.wIndex = idLang;
586 pRq->SetupPacket.wLength = sizeof (szBuf) - sizeof (*pRq);
587
588 DWORD cbReturned = 0;
589 if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, sizeof (szBuf),
590 pRq, sizeof(szBuf),
591 &cbReturned, NULL))
592 {
593 DWORD dwErr = GetLastError();
594 LogRel(("Getting USB descriptor failed with error %ld\n", dwErr));
595 return RTErrConvertFromWin32(dwErr);
596 }
597
598 /* Wrong descriptor type at the requested port index? Bail out. */
599 if (pDr->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
600 return VERR_NOT_FOUND;
601
602 /* Some more sanity checks. */
603 if ( (cbReturned < sizeof (*pDr) + 2)
604 || (!!(pDr->bLength % 2))
605 || (pDr->bLength != cbReturned - sizeof(*pRq)))
606 {
607 AssertMsgFailed(("Sanity check failed for string descriptor: cbReturned=%RI32, cbDevReq=%zu, type=%RU8, len=%RU8, port=%RU32, index=%RU32, lang=%RU32\n",
608 cbReturned, sizeof(*pRq), pDr->bDescriptorType, pDr->bLength, iPort, iDr, idLang));
609 return VERR_INVALID_PARAMETER;
610 }
611
612 PVBOXUSB_STRING_DR_ENTRY pEntry =
613 (PVBOXUSB_STRING_DR_ENTRY)RTMemAllocZ(sizeof(VBOXUSB_STRING_DR_ENTRY) + pDr->bLength + 2);
614 AssertPtr(pEntry);
615 if (!pEntry)
616 return VERR_NO_MEMORY;
617
618 pEntry->pNext = *ppList;
619 pEntry->iDr = iDr;
620 pEntry->idLang = idLang;
621 memcpy(&pEntry->StrDr, pDr, pDr->bLength);
622
623 *ppList = pEntry;
624
625 return VINF_SUCCESS;
626}
627
628static void usbLibDevStrDrEntryFree(PVBOXUSB_STRING_DR_ENTRY pDr)
629{
630 RTMemFree(pDr);
631}
632
633static void usbLibDevStrDrEntryFreeList(PVBOXUSB_STRING_DR_ENTRY pDr)
634{
635 while (pDr)
636 {
637 PVBOXUSB_STRING_DR_ENTRY pNext = pDr->pNext;
638 usbLibDevStrDrEntryFree(pDr);
639 pDr = pNext;
640 }
641}
642
643static int usbLibDevStrDrEntryGetForLangs(HANDLE hHub, ULONG iPort, ULONG iDr, ULONG cIdLang, const USHORT *pIdLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
644{
645 for (ULONG i = 0; i < cIdLang; ++i)
646 {
647 usbLibDevStrDrEntryGet(hHub, iPort, iDr, pIdLang[i], ppList);
648 }
649 return VINF_SUCCESS;
650}
651
652static int usbLibDevStrDrEntryGetAll(HANDLE hHub, ULONG iPort, PUSB_DEVICE_DESCRIPTOR pDevDr, PUSB_CONFIGURATION_DESCRIPTOR pCfgDr, PVBOXUSB_STRING_DR_ENTRY *ppList)
653{
654 /* Read string descriptor zero to determine what languages are available. */
655 int rc = usbLibDevStrDrEntryGet(hHub, iPort, 0, 0, ppList);
656 if (RT_FAILURE(rc))
657 return rc;
658
659 PUSB_STRING_DESCRIPTOR pLangStrDr = &(*ppList)->StrDr;
660 USHORT *pIdLang = pLangStrDr->bString;
661 ULONG cIdLang = (pLangStrDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof (*pIdLang);
662
663 if (pDevDr->iManufacturer)
664 {
665 rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iManufacturer, cIdLang, pIdLang, ppList);
666 AssertRC(rc);
667 }
668
669 if (pDevDr->iProduct)
670 {
671 rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iProduct, cIdLang, pIdLang, ppList);
672 AssertRC(rc);
673 }
674
675 if (pDevDr->iSerialNumber)
676 {
677 rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iSerialNumber, cIdLang, pIdLang, ppList);
678 AssertRC(rc);
679 }
680
681 PUCHAR pCur = (PUCHAR)pCfgDr;
682 PUCHAR pEnd = pCur + pCfgDr->wTotalLength;
683 while (pCur + sizeof (USB_COMMON_DESCRIPTOR) <= pEnd)
684 {
685 PUSB_COMMON_DESCRIPTOR pCmnDr = (PUSB_COMMON_DESCRIPTOR)pCur;
686 if (pCur + pCmnDr->bLength > pEnd)
687 {
688 AssertFailed();
689 break;
690 }
691
692 /* This is invalid but was seen with a TerraTec Aureon 7.1 USB sound card. */
693 if (!pCmnDr->bLength)
694 break;
695
696 switch (pCmnDr->bDescriptorType)
697 {
698 case USB_CONFIGURATION_DESCRIPTOR_TYPE:
699 {
700 if (pCmnDr->bLength != sizeof (USB_CONFIGURATION_DESCRIPTOR))
701 {
702 AssertFailed();
703 break;
704 }
705 PUSB_CONFIGURATION_DESCRIPTOR pCurCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)pCmnDr;
706 if (!pCurCfgDr->iConfiguration)
707 break;
708 rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pCurCfgDr->iConfiguration, cIdLang, pIdLang, ppList);
709 AssertRC(rc);
710 break;
711 }
712 case USB_INTERFACE_DESCRIPTOR_TYPE:
713 {
714 if (pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR) && pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR2))
715 {
716 AssertFailed();
717 break;
718 }
719 PUSB_INTERFACE_DESCRIPTOR pCurIfDr = (PUSB_INTERFACE_DESCRIPTOR)pCmnDr;
720 if (!pCurIfDr->iInterface)
721 break;
722 rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pCurIfDr->iInterface, cIdLang, pIdLang, ppList);
723 AssertRC(rc);
724 break;
725 }
726 default:
727 break;
728 }
729
730 pCur = pCur + pCmnDr->bLength;
731 }
732
733 return VINF_SUCCESS;
734}
735
736static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs);
737
738static int usbLibDevGetHubPortDevices(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
739{
740 int rc = VINF_SUCCESS;
741 char Buf[sizeof (USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof (USB_PIPE_INFO) * 20)];
742 PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo = (PUSB_NODE_CONNECTION_INFORMATION_EX)Buf;
743 //PUSB_PIPE_INFO paPipeInfo = (PUSB_PIPE_INFO)(Buf + sizeof (PUSB_NODE_CONNECTION_INFORMATION_EX));
744 DWORD cbReturned = 0;
745 memset(&Buf, 0, sizeof (Buf));
746 pConInfo->ConnectionIndex = iPort;
747 if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
748 pConInfo, sizeof (Buf),
749 pConInfo, sizeof (Buf),
750 &cbReturned, NULL))
751 {
752 DWORD dwErr = GetLastError(); NOREF(dwErr);
753 AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%d)\n", dwErr));
754 return VERR_GENERAL_FAILURE;
755 }
756
757 if (pConInfo->ConnectionStatus != DeviceConnected)
758 {
759 /* just ignore & return success */
760 return VWRN_INVALID_HANDLE;
761 }
762
763 if (pConInfo->DeviceIsHub)
764 {
765 LPSTR lpszHubName = NULL;
766 rc = usbLibDevStrHubNameGet(hHub, iPort, &lpszHubName);
767 AssertRC(rc);
768 if (RT_SUCCESS(rc))
769 {
770 rc = usbLibDevGetHubDevices(lpszHubName, ppDevs, pcDevs);
771 usbLibDevStrFree(lpszHubName);
772 AssertRC(rc);
773 return rc;
774 }
775 /* ignore this err */
776 return VINF_SUCCESS;
777 }
778
779 bool fFreeNameBuf = true;
780 char nameEmptyBuf = '\0';
781 LPSTR lpszName = NULL;
782 rc = usbLibDevStrDriverKeyGet(hHub, iPort, &lpszName);
783 Assert(!!lpszName == !!RT_SUCCESS(rc));
784 if (!lpszName)
785 {
786 lpszName = &nameEmptyBuf;
787 fFreeNameBuf = false;
788 }
789
790 PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
791 PVBOXUSB_STRING_DR_ENTRY pList = NULL;
792 rc = usbLibDevCfgDrGet(hHub, iPort, 0, &pCfgDr);
793 if (pCfgDr)
794 {
795 rc = usbLibDevStrDrEntryGetAll(hHub, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
796#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
797 AssertRC(rc);
798#endif
799 }
800
801 PUSBDEVICE pDev = (PUSBDEVICE)RTMemAllocZ(sizeof (*pDev));
802 if (RT_LIKELY(pDev))
803 {
804 rc = usbLibDevPopulate(pDev, pConInfo, iPort, lpszName, lpcszHubName, pList);
805 if (RT_SUCCESS(rc))
806 {
807 pDev->pNext = *ppDevs;
808 *ppDevs = pDev;
809 ++*pcDevs;
810 }
811 else
812 RTMemFree(pDev);
813 }
814 else
815 rc = VERR_NO_MEMORY;
816
817 if (pCfgDr)
818 usbLibDevCfgDrFree(pCfgDr);
819 if (fFreeNameBuf)
820 {
821 Assert(lpszName);
822 usbLibDevStrFree(lpszName);
823 }
824 if (pList)
825 usbLibDevStrDrEntryFreeList(pList);
826
827 return rc;
828}
829
830static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
831{
832 LPSTR lpszDevName = (LPSTR)RTMemAllocZ(strlen(lpszName) + sizeof("\\\\.\\"));
833 HANDLE hDev = INVALID_HANDLE_VALUE;
834 Assert(lpszDevName);
835 if (!lpszDevName)
836 {
837 AssertFailed();
838 return VERR_OUT_OF_RESOURCES;
839 }
840
841 int rc = VINF_SUCCESS;
842 strcpy(lpszDevName, "\\\\.\\");
843 strcpy(lpszDevName + sizeof("\\\\.\\") - sizeof (lpszDevName[0]), lpszName);
844 do
845 {
846 DWORD cbReturned = 0;
847 hDev = CreateFile(lpszDevName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
848 if (hDev == INVALID_HANDLE_VALUE)
849 {
850 AssertFailed();
851 break;
852 }
853
854 USB_NODE_INFORMATION NodeInfo;
855 memset(&NodeInfo, 0, sizeof (NodeInfo));
856 if (!DeviceIoControl(hDev, IOCTL_USB_GET_NODE_INFORMATION,
857 &NodeInfo, sizeof (NodeInfo),
858 &NodeInfo, sizeof (NodeInfo),
859 &cbReturned, NULL))
860 {
861 AssertFailed();
862 break;
863 }
864
865 for (ULONG i = 1; i <= NodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts; ++i)
866 {
867 /* Just skip devices for which we failed to create the device structure. */
868 usbLibDevGetHubPortDevices(hDev, lpszName, i, ppDevs, pcDevs);
869 }
870 } while (0);
871
872 if (hDev != INVALID_HANDLE_VALUE)
873 CloseHandle(hDev);
874
875 RTMemFree(lpszDevName);
876
877 return rc;
878}
879
880static int usbLibDevGetDevices(PUSBDEVICE *ppDevs, uint32_t *pcDevs)
881{
882 char CtlName[16];
883 int rc = VINF_SUCCESS;
884
885 for (int i = 0; i < 10; ++i)
886 {
887 sprintf(CtlName, "\\\\.\\HCD%d", i);
888 HANDLE hCtl = CreateFile(CtlName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
889 if (hCtl != INVALID_HANDLE_VALUE)
890 {
891 char* lpszName;
892 rc = usbLibDevStrRootHubNameGet(hCtl, &lpszName);
893 AssertRC(rc);
894 if (RT_SUCCESS(rc))
895 {
896 rc = usbLibDevGetHubDevices(lpszName, ppDevs, pcDevs);
897 AssertRC(rc);
898 usbLibDevStrFree(lpszName);
899 }
900 CloseHandle(hCtl);
901 if (RT_FAILURE(rc))
902 break;
903 }
904 }
905 return VINF_SUCCESS;
906}
907
908#if 0 /* unused */
909static PUSBSUP_GET_DEVICES usbLibMonGetDevRqAlloc(uint32_t cDevs, PDWORD pcbRq)
910{
911 DWORD cbRq = RT_OFFSETOF(USBSUP_GET_DEVICES, aDevices[cDevs]);
912 PUSBSUP_GET_DEVICES pRq = (PUSBSUP_GET_DEVICES)RTMemAllocZ(cbRq);
913 Assert(pRq);
914 if (!pRq)
915 return NULL;
916 pRq->cDevices = cDevs;
917 *pcbRq = cbRq;
918 return pRq;
919}
920#endif
921
922static int usbLibMonDevicesCmp(PUSBDEVICE pDev, PVBOXUSB_DEV pDevInfo)
923{
924 int iDiff;
925 iDiff = strcmp(pDev->pszAddress, pDevInfo->szDriverRegName);
926 return iDiff;
927}
928
929static int usbLibMonDevicesUpdate(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE pDevs, PVBOXUSB_DEV pDevInfos)
930{
931
932 PUSBDEVICE pDevsHead = pDevs;
933 for (; pDevInfos; pDevInfos = pDevInfos->pNext)
934 {
935 for (pDevs = pDevsHead; pDevs; pDevs = pDevs->pNext)
936 {
937 if (usbLibMonDevicesCmp(pDevs, pDevInfos))
938 continue;
939
940 if (!pDevInfos->szDriverRegName[0])
941 {
942 AssertFailed();
943 break;
944 }
945
946 USBSUP_GETDEV Dev = {0};
947 HANDLE hDev = CreateFile(pDevInfos->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
948 OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
949 if (hDev == INVALID_HANDLE_VALUE)
950 {
951 AssertFailed();
952 break;
953 }
954
955 DWORD cbReturned = 0;
956 if (!DeviceIoControl(hDev, SUPUSB_IOCTL_GET_DEVICE, &Dev, sizeof (Dev), &Dev, sizeof (Dev), &cbReturned, NULL))
957 {
958#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
959 DWORD dwErr = GetLastError(); NOREF(dwErr);
960 /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
961 AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%d)\n", dwErr));
962#endif
963 Log(("SUPUSB_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
964 CloseHandle(hDev);
965 break;
966 }
967
968 /* we must not close the handle until we request for the device state from the monitor to ensure
969 * the device handle returned by the device driver does not disappear */
970 Assert(Dev.hDevice);
971 USBSUP_GETDEV_MON MonInfo;
972 HVBOXUSBDEVUSR hDevice = Dev.hDevice;
973 if (!DeviceIoControl(pGlobal->hMonitor, SUPUSBFLT_IOCTL_GET_DEVICE, &hDevice, sizeof (hDevice), &MonInfo, sizeof (MonInfo), &cbReturned, NULL))
974 {
975 DWORD dwErr = GetLastError(); NOREF(dwErr);
976 /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
977 AssertMsgFailed(("Monitor DeviceIoControl failed dwErr (%d)\n", dwErr));
978 Log(("SUPUSBFLT_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
979 CloseHandle(hDev);
980 break;
981 }
982
983 CloseHandle(hDev);
984
985 /* success!! update device info */
986 /* ensure the state returned is valid */
987 Assert( MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST
988 || MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
989 || MonInfo.enmState == USBDEVICESTATE_UNUSED
990 || MonInfo.enmState == USBDEVICESTATE_HELD_BY_PROXY
991 || MonInfo.enmState == USBDEVICESTATE_USED_BY_GUEST);
992 pDevs->enmState = MonInfo.enmState;
993 if (pDevs->bcdUSB == 0x300)
994 /* USB3 spec guarantees this (9.6.1). */
995 pDevs->enmSpeed = USBDEVICESPEED_SUPER;
996 else
997 /* The following is not 100% accurate but we only care about high-speed vs. non-high-speed */
998 pDevs->enmSpeed = Dev.fHiSpeed ? USBDEVICESPEED_HIGH : USBDEVICESPEED_FULL;
999
1000 if (pDevs->enmState != USBDEVICESTATE_USED_BY_HOST)
1001 {
1002 /* only set the interface name if device can be grabbed */
1003 RTStrFree(pDevs->pszAltAddress);
1004 pDevs->pszAltAddress = (char*)pDevs->pszAddress;
1005 pDevs->pszAddress = RTStrDup(pDevInfos->szName);
1006 }
1007#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
1008 else
1009 {
1010 /* dbg breakpoint */
1011 AssertFailed();
1012 }
1013#endif
1014
1015 /* we've found the device, break in any way */
1016 break;
1017 }
1018 }
1019
1020 return VINF_SUCCESS;
1021}
1022
1023static int usbLibGetDevices(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
1024{
1025 *ppDevs = NULL;
1026 *pcDevs = 0;
1027
1028 int rc = usbLibDevGetDevices(ppDevs, pcDevs);
1029 AssertRC(rc);
1030 if (RT_SUCCESS(rc))
1031 {
1032 PVBOXUSB_DEV pDevInfos = NULL;
1033 uint32_t cDevInfos = 0;
1034 rc = usbLibVuGetDevices(&pDevInfos, &cDevInfos);
1035 AssertRC(rc);
1036 if (RT_SUCCESS(rc))
1037 {
1038 rc = usbLibMonDevicesUpdate(pGlobal, *ppDevs, pDevInfos);
1039 AssertRC(rc);
1040 usbLibVuFreeDevices(pDevInfos);
1041 }
1042
1043 return VINF_SUCCESS;
1044 }
1045 return rc;
1046}
1047
1048AssertCompile(INFINITE == RT_INDEFINITE_WAIT);
1049static int usbLibStateWaitChange(PVBOXUSBGLOBALSTATE pGlobal, RTMSINTERVAL cMillies)
1050{
1051 HANDLE ahEvents[] = {pGlobal->hNotifyEvent, pGlobal->hInterruptEvent};
1052 DWORD dwResult = WaitForMultipleObjects(RT_ELEMENTS(ahEvents), ahEvents,
1053 FALSE, /* BOOL bWaitAll */
1054 cMillies);
1055
1056 switch (dwResult)
1057 {
1058 case WAIT_OBJECT_0:
1059 return VINF_SUCCESS;
1060 case WAIT_OBJECT_0 + 1:
1061 return VERR_INTERRUPTED;
1062 case WAIT_TIMEOUT:
1063 return VERR_TIMEOUT;
1064 default:
1065 {
1066 DWORD dwErr = GetLastError(); NOREF(dwErr);
1067 AssertMsgFailed(("WaitForMultipleObjects failed, dwErr (%d)\n", dwErr));
1068 return VERR_GENERAL_FAILURE;
1069 }
1070 }
1071}
1072
1073AssertCompile(RT_INDEFINITE_WAIT == INFINITE);
1074AssertCompile(sizeof (RTMSINTERVAL) == sizeof (DWORD));
1075USBLIB_DECL(int) USBLibWaitChange(RTMSINTERVAL msWaitTimeout)
1076{
1077 return usbLibStateWaitChange(&g_VBoxUsbGlobal, msWaitTimeout);
1078}
1079
1080static int usbLibInterruptWaitChange(PVBOXUSBGLOBALSTATE pGlobal)
1081{
1082 BOOL fRc = SetEvent(pGlobal->hInterruptEvent);
1083 if (!fRc)
1084 {
1085 DWORD dwErr = GetLastError(); NOREF(dwErr);
1086 AssertMsgFailed(("SetEvent failed, dwErr (%d)\n", dwErr));
1087 return VERR_GENERAL_FAILURE;
1088 }
1089 return VINF_SUCCESS;
1090}
1091
1092USBLIB_DECL(int) USBLibInterruptWaitChange(void)
1093{
1094 return usbLibInterruptWaitChange(&g_VBoxUsbGlobal);
1095}
1096
1097/*
1098USBLIB_DECL(bool) USBLibHasPendingDeviceChanges(void)
1099{
1100 int rc = USBLibWaitChange(0);
1101 return rc == VINF_SUCCESS;
1102}
1103*/
1104
1105USBLIB_DECL(int) USBLibGetDevices(PUSBDEVICE *ppDevices, uint32_t *pcbNumDevices)
1106{
1107 Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
1108 return usbLibGetDevices(&g_VBoxUsbGlobal, ppDevices, pcbNumDevices);
1109}
1110
1111USBLIB_DECL(void *) USBLibAddFilter(PCUSBFILTER pFilter)
1112{
1113 USBSUP_FLTADDOUT FltAddRc;
1114 DWORD cbReturned = 0;
1115
1116 if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
1117 {
1118#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
1119 AssertFailed();
1120#endif
1121 return NULL;
1122 }
1123
1124 Log(("usblibInsertFilter: Manufacturer=%s Product=%s Serial=%s\n",
1125 USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
1126 USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
1127 USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
1128
1129 if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_ADD_FILTER,
1130 (LPVOID)pFilter, sizeof(*pFilter),
1131 &FltAddRc, sizeof(FltAddRc),
1132 &cbReturned, NULL))
1133 {
1134 DWORD dwErr = GetLastError(); NOREF(dwErr);
1135 AssertMsgFailed(("DeviceIoControl failed with dwErr (%d(\n", dwErr));
1136 return NULL;
1137 }
1138
1139 if (RT_FAILURE(FltAddRc.rc))
1140 {
1141 AssertMsgFailed(("Adding filter failed with %d\n", FltAddRc.rc));
1142 return NULL;
1143 }
1144 return (void *)FltAddRc.uId;
1145}
1146
1147
1148USBLIB_DECL(void) USBLibRemoveFilter(void *pvId)
1149{
1150 uintptr_t uId;
1151 DWORD cbReturned = 0;
1152
1153 if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
1154 {
1155#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
1156 AssertFailed();
1157#endif
1158 return;
1159 }
1160
1161 Log(("usblibRemoveFilter %p\n", pvId));
1162
1163 uId = (uintptr_t)pvId;
1164 if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
1165 AssertMsgFailed(("DeviceIoControl failed with LastError=%Rwa\n", GetLastError()));
1166}
1167
1168USBLIB_DECL(int) USBLibRunFilters(void)
1169{
1170 DWORD cbReturned = 0;
1171
1172 Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
1173
1174 if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_RUN_FILTERS,
1175 NULL, 0,
1176 NULL, 0,
1177 &cbReturned, NULL))
1178 {
1179 DWORD dwErr = GetLastError();
1180 AssertMsgFailed(("DeviceIoControl failed with dwErr (%d(\n", dwErr));
1181 return RTErrConvertFromWin32(dwErr);
1182 }
1183
1184 return VINF_SUCCESS;
1185}
1186
1187
1188#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
1189
1190static VOID CALLBACK usbLibTimerCallback(__in PVOID lpParameter, __in BOOLEAN TimerOrWaitFired)
1191{
1192 RT_NOREF2(lpParameter, TimerOrWaitFired);
1193 SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
1194}
1195
1196static void usbLibOnDeviceChange(void)
1197{
1198 /* we're getting series of events like that especially on device re-attach
1199 * (i.e. first for device detach and then for device attach)
1200 * unfortunately the event does not tell us what actually happened.
1201 * To avoid extra notifications, we delay the SetEvent via a timer
1202 * and update the timer if additional notification comes before the timer fires
1203 * */
1204 if (g_VBoxUsbGlobal.hTimer)
1205 {
1206 if (!DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer, NULL))
1207 {
1208 DWORD dwErr = GetLastError(); NOREF(dwErr);
1209 AssertMsg(dwErr == ERROR_IO_PENDING, ("DeleteTimerQueueTimer failed, dwErr (%d)\n", dwErr));
1210 }
1211 }
1212
1213 if (!CreateTimerQueueTimer(&g_VBoxUsbGlobal.hTimer, g_VBoxUsbGlobal.hTimerQueue,
1214 usbLibTimerCallback,
1215 NULL,
1216 500, /* ms*/
1217 0,
1218 WT_EXECUTEONLYONCE))
1219 {
1220 DWORD dwErr = GetLastError(); NOREF(dwErr);
1221 AssertMsgFailed(("CreateTimerQueueTimer failed, dwErr (%d)\n", dwErr));
1222
1223 /* call it directly */
1224 usbLibTimerCallback(NULL, FALSE);
1225 }
1226}
1227
1228static LRESULT CALLBACK usbLibWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1229{
1230 switch (uMsg)
1231 {
1232 case WM_DEVICECHANGE:
1233 if (wParam == DBT_DEVNODES_CHANGED)
1234 {
1235 /* we notify change any device arivals/removals on the system
1236 * and let the client decide whether the usb change actually happened
1237 * so far this is more clean than reporting events from the Monitor
1238 * because monitor sees only PDO arrivals/removals,
1239 * and by the time PDO is created, device can not
1240 * be yet started and fully functional,
1241 * so usblib won't be able to pick it up
1242 * */
1243
1244 usbLibOnDeviceChange();
1245 }
1246 break;
1247 case WM_DESTROY:
1248 {
1249 PostQuitMessage(0);
1250 return 0;
1251 }
1252 }
1253 return DefWindowProc (hwnd, uMsg, wParam, lParam);
1254}
1255
1256/** @todo r=bird: Use an IPRT thread? */
1257static DWORD WINAPI usbLibMsgThreadProc(__in LPVOID lpParameter)
1258{
1259 static LPCSTR s_szVBoxUsbWndClassName = "VBoxUsbLibClass";
1260 const HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
1261 RT_NOREF1(lpParameter);
1262
1263 Assert(g_VBoxUsbGlobal.hWnd == NULL);
1264 g_VBoxUsbGlobal.hWnd = NULL;
1265
1266 /*
1267 * Register the Window Class and create the hidden window.
1268 */
1269 WNDCLASS wc;
1270 wc.style = 0;
1271 wc.lpfnWndProc = usbLibWndProc;
1272 wc.cbClsExtra = 0;
1273 wc.cbWndExtra = sizeof(void *);
1274 wc.hInstance = hInstance;
1275 wc.hIcon = NULL;
1276 wc.hCursor = NULL;
1277 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
1278 wc.lpszMenuName = NULL;
1279 wc.lpszClassName = s_szVBoxUsbWndClassName;
1280 ATOM atomWindowClass = RegisterClass(&wc);
1281 if (atomWindowClass != 0)
1282 g_VBoxUsbGlobal.hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
1283 s_szVBoxUsbWndClassName, s_szVBoxUsbWndClassName,
1284 WS_POPUPWINDOW,
1285 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
1286 else
1287 AssertMsgFailed(("RegisterClass failed, last error %u\n", GetLastError()));
1288
1289 /*
1290 * Signal the creator thread.
1291 */
1292 ASMCompilerBarrier();
1293 SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
1294
1295 if (g_VBoxUsbGlobal.hWnd)
1296 {
1297 /* Make sure it's really hidden. */
1298 SetWindowPos(g_VBoxUsbGlobal.hWnd, HWND_TOPMOST, -200, -200, 0, 0,
1299 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
1300
1301 /*
1302 * The message pump.
1303 */
1304 MSG msg;
1305 BOOL fRet;
1306 while ((fRet = GetMessage(&msg, NULL, 0, 0)) > 0)
1307 {
1308 TranslateMessage(&msg);
1309 DispatchMessage(&msg);
1310 }
1311 Assert(fRet >= 0);
1312 }
1313
1314 if (atomWindowClass != NULL)
1315 UnregisterClass(s_szVBoxUsbWndClassName, hInstance);
1316
1317 return 0;
1318}
1319
1320#endif /* VBOX_USB_USE_DEVICE_NOTIFICATION */
1321
1322/**
1323 * Initialize the USB library
1324 *
1325 * @returns VBox status code.
1326 */
1327USBLIB_DECL(int) USBLibInit(void)
1328{
1329 int rc = VERR_GENERAL_FAILURE;
1330
1331 Log(("usbproxy: usbLibInit\n"));
1332
1333 RT_ZERO(g_VBoxUsbGlobal);
1334 g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
1335
1336 /*
1337 * Create the notification and interrupt event before opening the device.
1338 */
1339 g_VBoxUsbGlobal.hNotifyEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
1340 FALSE, /* BOOL bManualReset */
1341#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
1342 TRUE, /* BOOL bInitialState */
1343#else
1344 FALSE, /* set to false since it will be initially used for notification thread startup sync */
1345#endif
1346 NULL /* LPCTSTR lpName */);
1347 if (g_VBoxUsbGlobal.hNotifyEvent)
1348 {
1349 g_VBoxUsbGlobal.hInterruptEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
1350 FALSE, /* BOOL bManualReset */
1351 FALSE, /* BOOL bInitialState */
1352 NULL /* LPCTSTR lpName */);
1353 if (g_VBoxUsbGlobal.hInterruptEvent)
1354 {
1355 /*
1356 * Open the USB monitor device, starting if needed.
1357 */
1358 g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
1359 GENERIC_READ | GENERIC_WRITE,
1360 FILE_SHARE_READ | FILE_SHARE_WRITE,
1361 NULL,
1362 OPEN_EXISTING,
1363 FILE_ATTRIBUTE_SYSTEM,
1364 NULL);
1365
1366 if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
1367 {
1368 HRESULT hr = VBoxDrvCfgSvcStart(USBMON_SERVICE_NAME_W);
1369 if (hr == S_OK)
1370 {
1371 g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
1372 GENERIC_READ | GENERIC_WRITE,
1373 FILE_SHARE_READ | FILE_SHARE_WRITE,
1374 NULL,
1375 OPEN_EXISTING,
1376 FILE_ATTRIBUTE_SYSTEM,
1377 NULL);
1378 if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
1379 {
1380 DWORD dwErr = GetLastError();
1381 LogRelFunc(("CreateFile failed dwErr(%d)\n", dwErr));
1382 rc = VERR_FILE_NOT_FOUND;
1383 }
1384 }
1385 }
1386
1387 if (g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE)
1388 {
1389 /*
1390 * Check the USB monitor version.
1391 *
1392 * Drivers are backwards compatible within the same major
1393 * number. We consider the minor version number this library
1394 * is compiled with to be the minimum required by the driver.
1395 * This is by reasoning that the library uses the full feature
1396 * set of the driver it's written for.
1397 */
1398 USBSUP_VERSION Version = {0};
1399 DWORD cbReturned = 0;
1400 if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_GET_VERSION,
1401 NULL, 0,
1402 &Version, sizeof (Version),
1403 &cbReturned, NULL))
1404 {
1405 if ( Version.u32Major == USBMON_MAJOR_VERSION
1406#if USBMON_MINOR_VERSION != 0
1407 && Version.u32Minor >= USBMON_MINOR_VERSION
1408#endif
1409 )
1410 {
1411#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
1412 /*
1413 * Tell the monitor driver which event object to use
1414 * for notifications.
1415 */
1416 USBSUP_SET_NOTIFY_EVENT SetEvent = {0};
1417 Assert(g_VBoxUsbGlobal.hNotifyEvent);
1418 SetEvent.u.hEvent = g_VBoxUsbGlobal.hNotifyEvent;
1419 if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT,
1420 &SetEvent, sizeof(SetEvent),
1421 &SetEvent, sizeof(SetEvent),
1422 &cbReturned, NULL))
1423 {
1424 rc = SetEvent.u.rc;
1425 if (RT_SUCCESS(rc))
1426 {
1427 /*
1428 * We're DONE!
1429 */
1430 return VINF_SUCCESS;
1431 }
1432
1433 AssertMsgFailed(("SetEvent failed, %Rrc (%d)\n", rc, rc));
1434 }
1435 else
1436 {
1437 DWORD dwErr = GetLastError();
1438 AssertMsgFailed(("SetEvent Ioctl failed, dwErr (%d)\n", dwErr));
1439 rc = VERR_VERSION_MISMATCH;
1440 }
1441#else
1442 /*
1443 * We can not use USB Mon for reliable device add/remove tracking
1444 * since once USB Mon is notified about PDO creation and/or IRP_MN_START_DEVICE,
1445 * the function device driver may still do some initialization, which might result in
1446 * notifying too early.
1447 * Instead we use WM_DEVICECHANGE + DBT_DEVNODES_CHANGED to make Windows notify us about
1448 * device arivals/removals.
1449 * Since WM_DEVICECHANGE is a window message, create a dedicated thread to be used for WndProc and stuff.
1450 * The thread would create a window, track windows messages and call usbLibOnDeviceChange on WM_DEVICECHANGE arrival.
1451 * See comments in usbLibOnDeviceChange function for detail about using the timer queue.
1452 */
1453 g_VBoxUsbGlobal.hTimerQueue = CreateTimerQueue();
1454 if (g_VBoxUsbGlobal.hTimerQueue)
1455 {
1456 g_VBoxUsbGlobal.hThread = CreateThread(
1457 NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
1458 0, /*__in SIZE_T dwStackSize, */
1459 usbLibMsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
1460 NULL, /*__in_opt LPVOID lpParameter,*/
1461 0, /*__in DWORD dwCreationFlags,*/
1462 NULL /*__out_opt LPDWORD lpThreadId*/
1463 );
1464 if (g_VBoxUsbGlobal.hThread)
1465 {
1466 DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hNotifyEvent, INFINITE);
1467 Assert(dwResult == WAIT_OBJECT_0);
1468 if (g_VBoxUsbGlobal.hWnd)
1469 {
1470 /*
1471 * We're DONE!
1472 *
1473 * Just ensure that the event is set so the
1474 * first "wait change" request is processed.
1475 */
1476 SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
1477 return VINF_SUCCESS;
1478 }
1479
1480 dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
1481 Assert(dwResult == WAIT_OBJECT_0);
1482 BOOL fRc = CloseHandle(g_VBoxUsbGlobal.hThread); NOREF(fRc);
1483 DWORD dwErr = GetLastError(); NOREF(dwErr);
1484 AssertMsg(fRc, ("CloseHandle for hThread failed dwErr(%d)\n", dwErr));
1485 g_VBoxUsbGlobal.hThread = INVALID_HANDLE_VALUE;
1486 }
1487 else
1488 {
1489 DWORD dwErr = GetLastError(); NOREF(dwErr);
1490 AssertMsgFailed(("CreateThread failed, dwErr (%d)\n", dwErr));
1491 rc = VERR_GENERAL_FAILURE;
1492 }
1493
1494 DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue, INVALID_HANDLE_VALUE /* see term */);
1495 g_VBoxUsbGlobal.hTimerQueue = NULL;
1496 }
1497 else
1498 {
1499 DWORD dwErr = GetLastError(); NOREF(dwErr);
1500 AssertMsgFailed(("CreateTimerQueue failed dwErr(%d)\n", dwErr));
1501 }
1502#endif
1503 }
1504 else
1505 {
1506 LogRelFunc(("USB Monitor driver version mismatch! driver=%u.%u library=%u.%u\n",
1507 Version.u32Major, Version.u32Minor, USBMON_MAJOR_VERSION, USBMON_MINOR_VERSION));
1508#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
1509 AssertFailed();
1510#endif
1511 rc = VERR_VERSION_MISMATCH;
1512 }
1513 }
1514 else
1515 {
1516 DWORD dwErr = GetLastError(); NOREF(dwErr);
1517 AssertMsgFailed(("DeviceIoControl failed dwErr(%d)\n", dwErr));
1518 rc = VERR_VERSION_MISMATCH;
1519 }
1520
1521 CloseHandle(g_VBoxUsbGlobal.hMonitor);
1522 g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
1523 }
1524 else
1525 {
1526 LogRelFunc(("USB Service not found\n"));
1527#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
1528 AssertFailed();
1529#endif
1530 rc = VERR_FILE_NOT_FOUND;
1531 }
1532
1533 CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
1534 g_VBoxUsbGlobal.hInterruptEvent = NULL;
1535 }
1536 else
1537 {
1538 DWORD dwErr = GetLastError(); NOREF(dwErr);
1539 AssertMsgFailed(("CreateEvent for InterruptEvent failed dwErr(%d)\n", dwErr));
1540 rc = VERR_GENERAL_FAILURE;
1541 }
1542
1543 CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
1544 g_VBoxUsbGlobal.hNotifyEvent = NULL;
1545 }
1546 else
1547 {
1548 DWORD dwErr = GetLastError(); NOREF(dwErr);
1549 AssertMsgFailed(("CreateEvent for NotifyEvent failed dwErr(%d)\n", dwErr));
1550 rc = VERR_GENERAL_FAILURE;
1551 }
1552
1553 /* since main calls us even if USBLibInit fails,
1554 * we use hMonitor == INVALID_HANDLE_VALUE as a marker to indicate whether the lib is inited */
1555
1556 Assert(RT_FAILURE(rc));
1557 return rc;
1558}
1559
1560
1561/**
1562 * Terminate the USB library
1563 *
1564 * @returns VBox status code.
1565 */
1566USBLIB_DECL(int) USBLibTerm(void)
1567{
1568 if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
1569 {
1570 Assert(g_VBoxUsbGlobal.hInterruptEvent == NULL);
1571 Assert(g_VBoxUsbGlobal.hNotifyEvent == NULL);
1572 return VINF_SUCCESS;
1573 }
1574
1575 BOOL fRc;
1576#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
1577 fRc = PostMessage(g_VBoxUsbGlobal.hWnd, WM_CLOSE, 0, 0);
1578 AssertMsg(fRc, ("PostMessage for hWnd failed dwErr(%d)\n", GetLastError()));
1579
1580 if (g_VBoxUsbGlobal.hThread != NULL)
1581 {
1582 DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
1583 Assert(dwResult == WAIT_OBJECT_0); NOREF(dwResult);
1584 fRc = CloseHandle(g_VBoxUsbGlobal.hThread);
1585 AssertMsg(fRc, ("CloseHandle for hThread failed dwErr(%d)\n", GetLastError()));
1586 }
1587
1588 if (g_VBoxUsbGlobal.hTimer)
1589 {
1590 fRc = DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer,
1591 INVALID_HANDLE_VALUE); /* <-- to block until the timer is completed */
1592 AssertMsg(fRc, ("DeleteTimerQueueTimer failed dwErr(%d)\n", GetLastError()));
1593 }
1594
1595 if (g_VBoxUsbGlobal.hTimerQueue)
1596 {
1597 fRc = DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue,
1598 INVALID_HANDLE_VALUE); /* <-- to block until all timers are completed */
1599 AssertMsg(fRc, ("DeleteTimerQueueEx failed dwErr(%d)\n", GetLastError()));
1600 }
1601#endif /* VBOX_USB_USE_DEVICE_NOTIFICATION */
1602
1603 fRc = CloseHandle(g_VBoxUsbGlobal.hMonitor);
1604 AssertMsg(fRc, ("CloseHandle for hMonitor failed dwErr(%d)\n", GetLastError()));
1605 g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
1606
1607 fRc = CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
1608 AssertMsg(fRc, ("CloseHandle for hInterruptEvent failed lasterr=%u\n", GetLastError()));
1609 g_VBoxUsbGlobal.hInterruptEvent = NULL;
1610
1611 fRc = CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
1612 AssertMsg(fRc, ("CloseHandle for hNotifyEvent failed dwErr(%d)\n", GetLastError()));
1613 g_VBoxUsbGlobal.hNotifyEvent = NULL;
1614
1615 return VINF_SUCCESS;
1616}
1617
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