VirtualBox

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

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

Don't need stdio.h that much.

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