VirtualBox

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

Last change on this file since 60277 was 60277, checked in by vboxsync, 9 years ago

HostDrivers: try to purge all usage of __FUNCTION__

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette