VirtualBox

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

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