VirtualBox

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

Last change on this file since 53403 was 53109, checked in by vboxsync, 10 years ago

USB: Detect SS devices on Windows.

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

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