VirtualBox

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

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

VBoxUSB/win: Minor cleanups and a few more pointer checks after vital memory allocations

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