summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB/win/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/win/lib')
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp1616
2 files changed, 1616 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp b/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp
new file mode 100644
index 00000000..df9c4683
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp
@@ -0,0 +1,1616 @@
+/* $Id: VBoxUsbLib-win.cpp $ */
+/** @file
+ * VBox USB ring-3 Driver Interface library, Windows.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
+#include <iprt/win/windows.h>
+
+#include <VBox/sup.h>
+#include <VBox/types.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+#include <VBox/log.h>
+#include <VBox/usblib.h>
+#include <VBox/usblib-win.h>
+#include <VBox/usb.h>
+#include <VBox/VBoxDrvCfg-win.h>
+#pragma warning (disable:4200) /* shuts up the empty array member warnings */
+#include <iprt/win/setupapi.h>
+#include <usbdi.h>
+#include <hidsdi.h>
+
+/* Defined in Windows 8 DDK (through usbdi.h) but we use Windows 7 DDK to build. */
+#define UsbSuperSpeed 3
+
+#define VBOX_USB_USE_DEVICE_NOTIFICATION
+
+#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
+# include <Dbt.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct _USB_INTERFACE_DESCRIPTOR2
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bInterfaceNumber;
+ UCHAR bAlternateSetting;
+ UCHAR bNumEndpoints;
+ UCHAR bInterfaceClass;
+ UCHAR bInterfaceSubClass;
+ UCHAR bInterfaceProtocol;
+ UCHAR iInterface;
+ USHORT wNumClasses;
+} USB_INTERFACE_DESCRIPTOR2, *PUSB_INTERFACE_DESCRIPTOR2;
+
+typedef struct VBOXUSBGLOBALSTATE
+{
+ HANDLE hMonitor;
+ HANDLE hNotifyEvent;
+ HANDLE hInterruptEvent;
+#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
+ HANDLE hThread;
+ HWND hWnd;
+ HANDLE hTimerQueue;
+ HANDLE hTimer;
+#endif
+} VBOXUSBGLOBALSTATE, *PVBOXUSBGLOBALSTATE;
+
+typedef struct VBOXUSB_STRING_DR_ENTRY
+{
+ struct VBOXUSB_STRING_DR_ENTRY *pNext;
+ UCHAR iDr;
+ USHORT idLang;
+ USB_STRING_DESCRIPTOR StrDr;
+} VBOXUSB_STRING_DR_ENTRY, *PVBOXUSB_STRING_DR_ENTRY;
+
+/**
+ * This represents VBoxUsb device instance
+ */
+typedef struct VBOXUSB_DEV
+{
+ struct VBOXUSB_DEV *pNext;
+ char szName[512];
+ char szDriverRegName[512];
+} VBOXUSB_DEV, *PVBOXUSB_DEV;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXUSBGLOBALSTATE g_VBoxUsbGlobal;
+
+
+int usbLibVuDeviceValidate(PVBOXUSB_DEV pVuDev)
+{
+ HANDLE hOut = INVALID_HANDLE_VALUE;
+
+ hOut = CreateFile(pVuDev->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
+
+ if (hOut == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateFile FAILED to open %s, dwErr (%d)\n", pVuDev->szName, dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ USBSUP_VERSION version = {0};
+ DWORD cbReturned = 0;
+ int rc = VERR_VERSION_MISMATCH;
+
+ do
+ {
+ if (!DeviceIoControl(hOut, SUPUSB_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl SUPUSB_IOCTL_GET_VERSION failed with LastError=%Rwa\n", GetLastError()));
+ break;
+ }
+
+ if ( version.u32Major != USBDRV_MAJOR_VERSION
+#if USBDRV_MINOR_VERSION != 0
+ || version.u32Minor < USBDRV_MINOR_VERSION
+#endif
+ )
+ {
+ AssertMsgFailed(("Invalid version %d:%d vs %d:%d\n", version.u32Major, version.u32Minor, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
+ break;
+ }
+
+ if (!DeviceIoControl(hOut, SUPUSB_IOCTL_IS_OPERATIONAL, NULL, 0, NULL, NULL, &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl SUPUSB_IOCTL_IS_OPERATIONAL failed with LastError=%Rwa\n", GetLastError()));
+ break;
+ }
+
+ rc = VINF_SUCCESS;
+ } while (0);
+
+ CloseHandle(hOut);
+ return rc;
+}
+
+static int usbLibVuDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO hDevInfo, PSP_DEVICE_INTERFACE_DATA pIfData)
+{
+ DWORD cbIfDetailData;
+ int rc = VINF_SUCCESS;
+
+ SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
+ NULL, /* OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData */
+ 0, /* IN DWORD DeviceInterfaceDetailDataSize */
+ &cbIfDetailData,
+ NULL
+ );
+ Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAllocZ(cbIfDetailData);
+ if (!pIfDetailData)
+ {
+ AssertMsgFailed(("RTMemAllocZ failed\n"));
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ DWORD cbDbgRequired;
+ SP_DEVINFO_DATA DevInfoData;
+ DevInfoData.cbSize = sizeof (DevInfoData);
+ /* the cbSize should contain the sizeof a fixed-size part according to the docs */
+ pIfDetailData->cbSize = sizeof (*pIfDetailData);
+ do
+ {
+ if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, pIfData,
+ pIfDetailData,
+ cbIfDetailData,
+ &cbDbgRequired,
+ &DevInfoData))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetDeviceInterfaceDetail, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, cbIfDetailData, dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ strncpy(pVuDev->szName, pIfDetailData->DevicePath, sizeof (pVuDev->szName));
+
+ if (!SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DevInfoData, SPDRP_DRIVER,
+ NULL, /* OUT PDWORD PropertyRegDataType */
+ (PBYTE)pVuDev->szDriverRegName,
+ sizeof (pVuDev->szDriverRegName),
+ &cbDbgRequired))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetDeviceRegistryPropertyA, cbRequired (%d), was (%d), dwErr (%d)\n", cbDbgRequired, sizeof (pVuDev->szDriverRegName), dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ rc = usbLibVuDeviceValidate(pVuDev);
+ AssertRC(rc);
+ } while (0);
+
+ RTMemFree(pIfDetailData);
+ return rc;
+}
+
+static void usbLibVuFreeDevices(PVBOXUSB_DEV pDevInfos)
+{
+ while (pDevInfos)
+ {
+ PVBOXUSB_DEV pNext = pDevInfos->pNext;
+ RTMemFree(pDevInfos);
+ pDevInfos = pNext;
+ }
+}
+
+static int usbLibVuGetDevices(PVBOXUSB_DEV *ppVuDevs, uint32_t *pcVuDevs)
+{
+ *ppVuDevs = NULL;
+ *pcVuDevs = 0;
+
+ HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_CLASS_VBOXUSB,
+ NULL, /* IN PCTSTR Enumerator */
+ NULL, /* IN HWND hwndParent */
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE) /* IN DWORD Flags */
+ );
+ if (hDevInfo == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetupDiGetClassDevs, dwErr (%d)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ for (int i = 0; ; ++i)
+ {
+ SP_DEVICE_INTERFACE_DATA IfData;
+ IfData.cbSize = sizeof (IfData);
+ if (!SetupDiEnumDeviceInterfaces(hDevInfo,
+ NULL, /* IN PSP_DEVINFO_DATA DeviceInfoData */
+ &GUID_CLASS_VBOXUSB, /* IN LPGUID InterfaceClassGuid */
+ i,
+ &IfData))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NO_MORE_ITEMS)
+ break;
+
+ AssertMsgFailed(("SetupDiEnumDeviceInterfaces, dwErr (%d), resuming\n", dwErr));
+ continue;
+ }
+
+ /* we've now got the IfData */
+ PVBOXUSB_DEV pVuDev = (PVBOXUSB_DEV)RTMemAllocZ(sizeof (*pVuDev));
+ if (!pVuDev)
+ {
+ AssertMsgFailed(("RTMemAllocZ failed, resuming\n"));
+ continue;
+ }
+
+ int rc = usbLibVuDevicePopulate(pVuDev, hDevInfo, &IfData);
+ if (!RT_SUCCESS(rc))
+ {
+ AssertMsgFailed(("usbLibVuDevicePopulate failed, rc (%d), resuming\n", rc));
+ continue;
+ }
+
+ pVuDev->pNext = *ppVuDevs;
+ *ppVuDevs = pVuDev;
+ ++*pcVuDevs;
+ }
+
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszDrvKeyName, LPCSTR lpszHubName, PVBOXUSB_STRING_DR_ENTRY pDrList)
+{
+ pDev->bcdUSB = pConInfo->DeviceDescriptor.bcdUSB;
+ pDev->bDeviceClass = pConInfo->DeviceDescriptor.bDeviceClass;
+ pDev->bDeviceSubClass = pConInfo->DeviceDescriptor.bDeviceSubClass;
+ pDev->bDeviceProtocol = pConInfo->DeviceDescriptor.bDeviceProtocol;
+ pDev->idVendor = pConInfo->DeviceDescriptor.idVendor;
+ pDev->idProduct = pConInfo->DeviceDescriptor.idProduct;
+ pDev->bcdDevice = pConInfo->DeviceDescriptor.bcdDevice;
+ pDev->bBus = 0; /** @todo figure out bBus on windows... */
+ pDev->bPort = iPort;
+ /** @todo check which devices are used for primary input (keyboard & mouse) */
+ if (!lpszDrvKeyName || *lpszDrvKeyName == 0)
+ pDev->enmState = USBDEVICESTATE_UNUSED;
+ else
+ pDev->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* Determine the speed the device is operating at. */
+ switch (pConInfo->Speed)
+ {
+ case UsbLowSpeed: pDev->enmSpeed = USBDEVICESPEED_LOW; break;
+ case UsbFullSpeed: pDev->enmSpeed = USBDEVICESPEED_FULL; break;
+ case UsbHighSpeed: pDev->enmSpeed = USBDEVICESPEED_HIGH; break;
+ default: /* If we don't know, most likely it's something new. */
+ case UsbSuperSpeed: pDev->enmSpeed = USBDEVICESPEED_SUPER; break;
+ }
+ /* Unfortunately USB_NODE_CONNECTION_INFORMATION_EX will not report UsbSuperSpeed, and
+ * it's not even defined in the Win7 DDK we use. So we go by the USB version, and
+ * luckily we know that USB3 must mean SuperSpeed. The USB3 spec guarantees this (9.6.1).
+ */
+ if (pDev->bcdUSB >= 0x0300)
+ pDev->enmSpeed = USBDEVICESPEED_SUPER;
+
+ int rc = RTStrAPrintf((char **)&pDev->pszAddress, "%s", lpszDrvKeyName);
+ if (rc < 0)
+ return VERR_NO_MEMORY;
+ pDev->pszBackend = RTStrDup("host");
+ if (!pDev->pszBackend)
+ {
+ RTStrFree((char *)pDev->pszAddress);
+ return VERR_NO_STR_MEMORY;
+ }
+ pDev->pszHubName = RTStrDup(lpszHubName);
+ pDev->bNumConfigurations = 0;
+ pDev->u64SerialHash = 0;
+
+ for (; pDrList; pDrList = pDrList->pNext)
+ {
+ char **ppszString = NULL;
+ if ( pConInfo->DeviceDescriptor.iManufacturer
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iManufacturer)
+ ppszString = (char **)&pDev->pszManufacturer;
+ else if ( pConInfo->DeviceDescriptor.iProduct
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iProduct)
+ ppszString = (char **)&pDev->pszProduct;
+ else if ( pConInfo->DeviceDescriptor.iSerialNumber
+ && pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ ppszString = (char **)&pDev->pszSerialNumber;
+ if (ppszString)
+ {
+ rc = RTUtf16ToUtf8((PCRTUTF16)pDrList->StrDr.bString, ppszString);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(*ppszString);
+ USBLibPurgeEncoding(*ppszString);
+
+ if (pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
+ pDev->u64SerialHash = USBLibHashSerial(*ppszString);
+ }
+ else
+ {
+ AssertMsgFailed(("RTUtf16ToUtf8 failed, rc (%d), resuming\n", rc));
+ *ppszString = NULL;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static void usbLibDevStrFree(LPSTR lpszName)
+{
+ RTStrFree(lpszName);
+}
+
+static int usbLibDevStrDriverKeyGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
+{
+ USB_NODE_CONNECTION_DRIVERKEY_NAME Name;
+ DWORD cbReturned = 0;
+ Name.ConnectionIndex = iPort;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("DeviceIoControl 1 fail dwErr (%d)\n", dwErr));
+#endif
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (Name.ActualLength < sizeof (Name))
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ PUSB_NODE_CONNECTION_DRIVERKEY_NAME pName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)RTMemAllocZ(Name.ActualLength);
+ if (!pName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ pName->ConnectionIndex = iPort;
+ if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->DriverKeyName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("DeviceIoControl 2 fail dwErr (%d)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pName);
+ return rc;
+}
+
+static int usbLibDevStrHubNameGet(HANDLE hHub, ULONG iPort, LPSTR* plpszName)
+{
+ USB_NODE_CONNECTION_NAME Name;
+ DWORD cbReturned = 0;
+ Name.ConnectionIndex = iPort;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (Name.ActualLength < sizeof (Name))
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ PUSB_NODE_CONNECTION_NAME pName = (PUSB_NODE_CONNECTION_NAME)RTMemAllocZ(Name.ActualLength);
+ if (!pName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ pName->ConnectionIndex = iPort;
+ if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pName->NodeName, pName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pName);
+ return rc;
+}
+
+static int usbLibDevStrRootHubNameGet(HANDLE hCtl, LPSTR* plpszName)
+{
+ USB_ROOT_HUB_NAME HubName;
+ DWORD cbReturned = 0;
+ *plpszName = NULL;
+ if (!DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, &HubName, sizeof (HubName), &cbReturned, NULL))
+ {
+ return VERR_GENERAL_FAILURE;
+ }
+ PUSB_ROOT_HUB_NAME pHubName = (PUSB_ROOT_HUB_NAME)RTMemAllocZ(HubName.ActualLength);
+ if (!pHubName)
+ return VERR_OUT_OF_RESOURCES;
+
+ int rc = VINF_SUCCESS;
+ if (DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, pHubName, HubName.ActualLength, &cbReturned, NULL))
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)pHubName->RootHubName, pHubName->ActualLength / sizeof (WCHAR), plpszName, 0, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pHubName);
+ return rc;
+}
+
+static int usbLibDevCfgDrGet(HANDLE hHub, ULONG iPort, ULONG iDr, PUSB_CONFIGURATION_DESCRIPTOR *ppDr)
+{
+ *ppDr = NULL;
+
+ char Buf[sizeof (USB_DESCRIPTOR_REQUEST) + sizeof (USB_CONFIGURATION_DESCRIPTOR)];
+ memset(&Buf, 0, sizeof (Buf));
+
+ PUSB_DESCRIPTOR_REQUEST pCfgDrRq = (PUSB_DESCRIPTOR_REQUEST)Buf;
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)(Buf + sizeof (*pCfgDrRq));
+
+ pCfgDrRq->ConnectionIndex = iPort;
+ pCfgDrRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
+ pCfgDrRq->SetupPacket.wLength = (USHORT)(sizeof (USB_CONFIGURATION_DESCRIPTOR));
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pCfgDrRq, sizeof (Buf),
+ pCfgDrRq, sizeof (Buf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("DeviceIoControl 1 fail dwErr (%d)\n", dwErr));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (sizeof (Buf) != cbReturned)
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pCfgDr->wTotalLength < sizeof (USB_CONFIGURATION_DESCRIPTOR))
+ {
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ DWORD cbRq = sizeof (USB_DESCRIPTOR_REQUEST) + pCfgDr->wTotalLength;
+ PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)RTMemAllocZ(cbRq);
+ Assert(pRq);
+ if (!pRq)
+ return VERR_OUT_OF_RESOURCES;
+
+ int rc = VERR_GENERAL_FAILURE;
+ do
+ {
+ PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)(pRq + 1);
+ pRq->ConnectionIndex = iPort;
+ pRq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | iDr;
+ pRq->SetupPacket.wLength = (USHORT)(cbRq - sizeof (USB_DESCRIPTOR_REQUEST));
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, cbRq,
+ pRq, cbRq,
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("DeviceIoControl 2 fail dwErr (%d)\n", dwErr));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ break;
+ }
+
+ if (cbRq != cbReturned)
+ {
+ AssertFailed();
+ break;
+ }
+
+ if (pDr->wTotalLength != cbRq - sizeof (USB_DESCRIPTOR_REQUEST))
+ {
+ AssertFailed();
+ break;
+ }
+
+ *ppDr = pDr;
+ return VINF_SUCCESS;
+ } while (0);
+
+ RTMemFree(pRq);
+ return rc;
+}
+
+static void usbLibDevCfgDrFree(PUSB_CONFIGURATION_DESCRIPTOR pDr)
+{
+ Assert(pDr);
+ PUSB_DESCRIPTOR_REQUEST pRq = ((PUSB_DESCRIPTOR_REQUEST)pDr)-1;
+ RTMemFree(pRq);
+}
+
+static int usbLibDevStrDrEntryGet(HANDLE hHub, ULONG iPort, ULONG iDr, USHORT idLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ char szBuf[sizeof (USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];
+ RT_ZERO(szBuf);
+
+ PUSB_DESCRIPTOR_REQUEST pRq = (PUSB_DESCRIPTOR_REQUEST)szBuf;
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)(szBuf + sizeof (*pRq));
+ RT_BZERO(pDr, sizeof(USB_STRING_DESCRIPTOR));
+
+ pRq->ConnectionIndex = iPort;
+ pRq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | iDr;
+ pRq->SetupPacket.wIndex = idLang;
+ pRq->SetupPacket.wLength = sizeof (szBuf) - sizeof (*pRq);
+
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, pRq, sizeof (szBuf),
+ pRq, sizeof(szBuf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ LogRel(("Getting USB descriptor failed with error %ld\n", dwErr));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ /* Wrong descriptor type at the requested port index? Bail out. */
+ if (pDr->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
+ return VERR_NOT_FOUND;
+
+ /* Some more sanity checks. */
+ if ( (cbReturned < sizeof (*pDr) + 2)
+ || (!!(pDr->bLength % 2))
+ || (pDr->bLength != cbReturned - sizeof(*pRq)))
+ {
+ AssertMsgFailed(("Sanity check failed for string descriptor: cbReturned=%RI32, cbDevReq=%zu, type=%RU8, len=%RU8, port=%RU32, index=%RU32, lang=%RU32\n",
+ cbReturned, sizeof(*pRq), pDr->bDescriptorType, pDr->bLength, iPort, iDr, idLang));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ PVBOXUSB_STRING_DR_ENTRY pEntry =
+ (PVBOXUSB_STRING_DR_ENTRY)RTMemAllocZ(sizeof(VBOXUSB_STRING_DR_ENTRY) + pDr->bLength + 2);
+ AssertPtr(pEntry);
+ if (!pEntry)
+ return VERR_NO_MEMORY;
+
+ pEntry->pNext = *ppList;
+ pEntry->iDr = iDr;
+ pEntry->idLang = idLang;
+ memcpy(&pEntry->StrDr, pDr, pDr->bLength);
+
+ *ppList = pEntry;
+
+ return VINF_SUCCESS;
+}
+
+static void usbLibDevStrDrEntryFree(PVBOXUSB_STRING_DR_ENTRY pDr)
+{
+ RTMemFree(pDr);
+}
+
+static void usbLibDevStrDrEntryFreeList(PVBOXUSB_STRING_DR_ENTRY pDr)
+{
+ while (pDr)
+ {
+ PVBOXUSB_STRING_DR_ENTRY pNext = pDr->pNext;
+ usbLibDevStrDrEntryFree(pDr);
+ pDr = pNext;
+ }
+}
+
+static int usbLibDevStrDrEntryGetForLangs(HANDLE hHub, ULONG iPort, ULONG iDr, ULONG cIdLang, const USHORT *pIdLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ for (ULONG i = 0; i < cIdLang; ++i)
+ {
+ usbLibDevStrDrEntryGet(hHub, iPort, iDr, pIdLang[i], ppList);
+ }
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevStrDrEntryGetAll(HANDLE hHub, ULONG iPort, PUSB_DEVICE_DESCRIPTOR pDevDr, PUSB_CONFIGURATION_DESCRIPTOR pCfgDr, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ /* Read string descriptor zero to determine what languages are available. */
+ int rc = usbLibDevStrDrEntryGet(hHub, iPort, 0, 0, ppList);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ PUSB_STRING_DESCRIPTOR pLangStrDr = &(*ppList)->StrDr;
+ USHORT *pIdLang = pLangStrDr->bString;
+ ULONG cIdLang = (pLangStrDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof (*pIdLang);
+
+ if (pDevDr->iManufacturer)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iManufacturer, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iProduct)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iProduct, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pDevDr->iSerialNumber, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ PUCHAR pCur = (PUCHAR)pCfgDr;
+ PUCHAR pEnd = pCur + pCfgDr->wTotalLength;
+ while (pCur + sizeof (USB_COMMON_DESCRIPTOR) <= pEnd)
+ {
+ PUSB_COMMON_DESCRIPTOR pCmnDr = (PUSB_COMMON_DESCRIPTOR)pCur;
+ if (pCur + pCmnDr->bLength > pEnd)
+ {
+ AssertFailed();
+ break;
+ }
+
+ /* This is invalid but was seen with a TerraTec Aureon 7.1 USB sound card. */
+ if (!pCmnDr->bLength)
+ break;
+
+ switch (pCmnDr->bDescriptorType)
+ {
+ case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+ {
+ if (pCmnDr->bLength != sizeof (USB_CONFIGURATION_DESCRIPTOR))
+ {
+ AssertFailed();
+ break;
+ }
+ PUSB_CONFIGURATION_DESCRIPTOR pCurCfgDr = (PUSB_CONFIGURATION_DESCRIPTOR)pCmnDr;
+ if (!pCurCfgDr->iConfiguration)
+ break;
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pCurCfgDr->iConfiguration, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ break;
+ }
+ case USB_INTERFACE_DESCRIPTOR_TYPE:
+ {
+ if (pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR) && pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR2))
+ {
+ AssertFailed();
+ break;
+ }
+ PUSB_INTERFACE_DESCRIPTOR pCurIfDr = (PUSB_INTERFACE_DESCRIPTOR)pCmnDr;
+ if (!pCurIfDr->iInterface)
+ break;
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pCurIfDr->iInterface, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ break;
+ }
+ default:
+ break;
+ }
+
+ pCur = pCur + pCmnDr->bLength;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs);
+
+static int usbLibDevGetHubPortDevices(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ int rc = VINF_SUCCESS;
+ char Buf[sizeof (USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof (USB_PIPE_INFO) * 20)];
+ PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo = (PUSB_NODE_CONNECTION_INFORMATION_EX)Buf;
+ //PUSB_PIPE_INFO paPipeInfo = (PUSB_PIPE_INFO)(Buf + sizeof (PUSB_NODE_CONNECTION_INFORMATION_EX));
+ DWORD cbReturned = 0;
+ memset(&Buf, 0, sizeof (Buf));
+ pConInfo->ConnectionIndex = iPort;
+ if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ pConInfo, sizeof (Buf),
+ pConInfo, sizeof (Buf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%d)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pConInfo->ConnectionStatus != DeviceConnected)
+ {
+ /* just ignore & return success */
+ return VWRN_INVALID_HANDLE;
+ }
+
+ if (pConInfo->DeviceIsHub)
+ {
+ LPSTR lpszHubName = NULL;
+ rc = usbLibDevStrHubNameGet(hHub, iPort, &lpszHubName);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibDevGetHubDevices(lpszHubName, ppDevs, pcDevs);
+ usbLibDevStrFree(lpszHubName);
+ AssertRC(rc);
+ return rc;
+ }
+ /* ignore this err */
+ return VINF_SUCCESS;
+ }
+
+ bool fFreeNameBuf = true;
+ char nameEmptyBuf = '\0';
+ LPSTR lpszName = NULL;
+ rc = usbLibDevStrDriverKeyGet(hHub, iPort, &lpszName);
+ Assert(!!lpszName == !!RT_SUCCESS(rc));
+ if (!lpszName)
+ {
+ lpszName = &nameEmptyBuf;
+ fFreeNameBuf = false;
+ }
+
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+ PVBOXUSB_STRING_DR_ENTRY pList = NULL;
+ rc = usbLibDevCfgDrGet(hHub, iPort, 0, &pCfgDr);
+ if (pCfgDr)
+ {
+ rc = usbLibDevStrDrEntryGetAll(hHub, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertRC(rc);
+#endif
+ }
+
+ PUSBDEVICE pDev = (PUSBDEVICE)RTMemAllocZ(sizeof (*pDev));
+ if (RT_LIKELY(pDev))
+ {
+ rc = usbLibDevPopulate(pDev, pConInfo, iPort, lpszName, lpcszHubName, pList);
+ if (RT_SUCCESS(rc))
+ {
+ pDev->pNext = *ppDevs;
+ *ppDevs = pDev;
+ ++*pcDevs;
+ }
+ else
+ RTMemFree(pDev);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (pCfgDr)
+ usbLibDevCfgDrFree(pCfgDr);
+ if (fFreeNameBuf)
+ {
+ Assert(lpszName);
+ usbLibDevStrFree(lpszName);
+ }
+ if (pList)
+ usbLibDevStrDrEntryFreeList(pList);
+
+ return rc;
+}
+
+static int usbLibDevGetHubDevices(LPCSTR lpszName, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ LPSTR lpszDevName = (LPSTR)RTMemAllocZ(strlen(lpszName) + sizeof("\\\\.\\"));
+ HANDLE hDev = INVALID_HANDLE_VALUE;
+ Assert(lpszDevName);
+ if (!lpszDevName)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RESOURCES;
+ }
+
+ int rc = VINF_SUCCESS;
+ strcpy(lpszDevName, "\\\\.\\");
+ strcpy(lpszDevName + sizeof("\\\\.\\") - sizeof (lpszDevName[0]), lpszName);
+ do
+ {
+ DWORD cbReturned = 0;
+ hDev = CreateFile(lpszDevName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hDev == INVALID_HANDLE_VALUE)
+ {
+ AssertFailed();
+ break;
+ }
+
+ USB_NODE_INFORMATION NodeInfo;
+ memset(&NodeInfo, 0, sizeof (NodeInfo));
+ if (!DeviceIoControl(hDev, IOCTL_USB_GET_NODE_INFORMATION,
+ &NodeInfo, sizeof (NodeInfo),
+ &NodeInfo, sizeof (NodeInfo),
+ &cbReturned, NULL))
+ {
+ AssertFailed();
+ break;
+ }
+
+ for (ULONG i = 1; i <= NodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts; ++i)
+ {
+ /* Just skip devices for which we failed to create the device structure. */
+ usbLibDevGetHubPortDevices(hDev, lpszName, i, ppDevs, pcDevs);
+ }
+ } while (0);
+
+ if (hDev != INVALID_HANDLE_VALUE)
+ CloseHandle(hDev);
+
+ RTMemFree(lpszDevName);
+
+ return rc;
+}
+
+static int usbLibDevGetDevices(PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ char CtlName[16];
+ int rc = VINF_SUCCESS;
+
+ for (int i = 0; i < 10; ++i)
+ {
+ RTStrPrintf(CtlName, sizeof(CtlName), "\\\\.\\HCD%d", i);
+ HANDLE hCtl = CreateFile(CtlName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hCtl != INVALID_HANDLE_VALUE)
+ {
+ char* lpszName;
+ rc = usbLibDevStrRootHubNameGet(hCtl, &lpszName);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibDevGetHubDevices(lpszName, ppDevs, pcDevs);
+ AssertRC(rc);
+ usbLibDevStrFree(lpszName);
+ }
+ CloseHandle(hCtl);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+static int usbLibMonDevicesCmp(PUSBDEVICE pDev, PVBOXUSB_DEV pDevInfo)
+{
+ int iDiff;
+ iDiff = strcmp(pDev->pszAddress, pDevInfo->szDriverRegName);
+ return iDiff;
+}
+
+static int usbLibMonDevicesUpdate(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE pDevs, PVBOXUSB_DEV pDevInfos)
+{
+
+ PUSBDEVICE pDevsHead = pDevs;
+ for (; pDevInfos; pDevInfos = pDevInfos->pNext)
+ {
+ for (pDevs = pDevsHead; pDevs; pDevs = pDevs->pNext)
+ {
+ if (usbLibMonDevicesCmp(pDevs, pDevInfos))
+ continue;
+
+ if (!pDevInfos->szDriverRegName[0])
+ {
+ AssertFailed();
+ break;
+ }
+
+ USBSUP_GETDEV Dev = {0};
+ HANDLE hDev = CreateFile(pDevInfos->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);
+ if (hDev == INVALID_HANDLE_VALUE)
+ {
+ AssertFailed();
+ break;
+ }
+
+ DWORD cbReturned = 0;
+ if (!DeviceIoControl(hDev, SUPUSB_IOCTL_GET_DEVICE, &Dev, sizeof (Dev), &Dev, sizeof (Dev), &cbReturned, NULL))
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%d)\n", dwErr));
+#endif
+ Log(("SUPUSB_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
+ CloseHandle(hDev);
+ break;
+ }
+
+ /* we must not close the handle until we request for the device state from the monitor to ensure
+ * the device handle returned by the device driver does not disappear */
+ Assert(Dev.hDevice);
+ USBSUP_GETDEV_MON MonInfo;
+ HVBOXUSBDEVUSR hDevice = Dev.hDevice;
+ if (!DeviceIoControl(pGlobal->hMonitor, SUPUSBFLT_IOCTL_GET_DEVICE, &hDevice, sizeof (hDevice), &MonInfo, sizeof (MonInfo), &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AssertMsgFailed(("Monitor DeviceIoControl failed dwErr (%d)\n", dwErr));
+ Log(("SUPUSBFLT_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
+ CloseHandle(hDev);
+ break;
+ }
+
+ CloseHandle(hDev);
+
+ /* success!! update device info */
+ /* ensure the state returned is valid */
+ Assert( MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST
+ || MonInfo.enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
+ || MonInfo.enmState == USBDEVICESTATE_UNUSED
+ || MonInfo.enmState == USBDEVICESTATE_HELD_BY_PROXY
+ || MonInfo.enmState == USBDEVICESTATE_USED_BY_GUEST);
+ pDevs->enmState = MonInfo.enmState;
+
+ if (pDevs->enmState != USBDEVICESTATE_USED_BY_HOST)
+ {
+ /* only set the interface name if device can be grabbed */
+ RTStrFree(pDevs->pszAltAddress);
+ pDevs->pszAltAddress = (char*)pDevs->pszAddress;
+ pDevs->pszAddress = RTStrDup(pDevInfos->szName);
+ }
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ else
+ {
+ /* dbg breakpoint */
+ AssertFailed();
+ }
+#endif
+
+ /* we've found the device, break in any way */
+ break;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int usbLibGetDevices(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ *ppDevs = NULL;
+ *pcDevs = 0;
+
+ int rc = usbLibDevGetDevices(ppDevs, pcDevs);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXUSB_DEV pDevInfos = NULL;
+ uint32_t cDevInfos = 0;
+ rc = usbLibVuGetDevices(&pDevInfos, &cDevInfos);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibMonDevicesUpdate(pGlobal, *ppDevs, pDevInfos);
+ AssertRC(rc);
+ usbLibVuFreeDevices(pDevInfos);
+ }
+
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+AssertCompile(INFINITE == RT_INDEFINITE_WAIT);
+static int usbLibStateWaitChange(PVBOXUSBGLOBALSTATE pGlobal, RTMSINTERVAL cMillies)
+{
+ HANDLE ahEvents[] = {pGlobal->hNotifyEvent, pGlobal->hInterruptEvent};
+ DWORD dwResult = WaitForMultipleObjects(RT_ELEMENTS(ahEvents), ahEvents,
+ FALSE, /* BOOL bWaitAll */
+ cMillies);
+
+ switch (dwResult)
+ {
+ case WAIT_OBJECT_0:
+ return VINF_SUCCESS;
+ case WAIT_OBJECT_0 + 1:
+ return VERR_INTERRUPTED;
+ case WAIT_TIMEOUT:
+ return VERR_TIMEOUT;
+ default:
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("WaitForMultipleObjects failed, dwErr (%d)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+ }
+}
+
+AssertCompile(RT_INDEFINITE_WAIT == INFINITE);
+AssertCompile(sizeof (RTMSINTERVAL) == sizeof (DWORD));
+USBLIB_DECL(int) USBLibWaitChange(RTMSINTERVAL msWaitTimeout)
+{
+ return usbLibStateWaitChange(&g_VBoxUsbGlobal, msWaitTimeout);
+}
+
+static int usbLibInterruptWaitChange(PVBOXUSBGLOBALSTATE pGlobal)
+{
+ BOOL fRc = SetEvent(pGlobal->hInterruptEvent);
+ if (!fRc)
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("SetEvent failed, dwErr (%d)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+ return VINF_SUCCESS;
+}
+
+USBLIB_DECL(int) USBLibInterruptWaitChange(void)
+{
+ return usbLibInterruptWaitChange(&g_VBoxUsbGlobal);
+}
+
+/*
+USBLIB_DECL(bool) USBLibHasPendingDeviceChanges(void)
+{
+ int rc = USBLibWaitChange(0);
+ return rc == VINF_SUCCESS;
+}
+*/
+
+USBLIB_DECL(int) USBLibGetDevices(PUSBDEVICE *ppDevices, uint32_t *pcbNumDevices)
+{
+ Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
+ return usbLibGetDevices(&g_VBoxUsbGlobal, ppDevices, pcbNumDevices);
+}
+
+USBLIB_DECL(void *) USBLibAddFilter(PCUSBFILTER pFilter)
+{
+ USBSUP_FLTADDOUT FltAddRc;
+ DWORD cbReturned = 0;
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return NULL;
+ }
+
+ Log(("usblibInsertFilter: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_ADD_FILTER,
+ (LPVOID)pFilter, sizeof(*pFilter),
+ &FltAddRc, sizeof(FltAddRc),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("DeviceIoControl failed with dwErr (%d(\n", dwErr));
+ return NULL;
+ }
+
+ if (RT_FAILURE(FltAddRc.rc))
+ {
+ AssertMsgFailed(("Adding filter failed with %d\n", FltAddRc.rc));
+ return NULL;
+ }
+ return (void *)FltAddRc.uId;
+}
+
+
+USBLIB_DECL(void) USBLibRemoveFilter(void *pvId)
+{
+ uintptr_t uId;
+ DWORD cbReturned = 0;
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ return;
+ }
+
+ Log(("usblibRemoveFilter %p\n", pvId));
+
+ uId = (uintptr_t)pvId;
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
+ AssertMsgFailed(("DeviceIoControl failed with LastError=%Rwa\n", GetLastError()));
+}
+
+USBLIB_DECL(int) USBLibRunFilters(void)
+{
+ DWORD cbReturned = 0;
+
+ Assert(g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE);
+
+ if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_RUN_FILTERS,
+ NULL, 0,
+ NULL, 0,
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("DeviceIoControl failed with dwErr (%d(\n", dwErr));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
+
+static VOID CALLBACK usbLibTimerCallback(__in PVOID lpParameter, __in BOOLEAN TimerOrWaitFired)
+{
+ RT_NOREF2(lpParameter, TimerOrWaitFired);
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+}
+
+static void usbLibOnDeviceChange(void)
+{
+ /* we're getting series of events like that especially on device re-attach
+ * (i.e. first for device detach and then for device attach)
+ * unfortunately the event does not tell us what actually happened.
+ * To avoid extra notifications, we delay the SetEvent via a timer
+ * and update the timer if additional notification comes before the timer fires
+ * */
+ if (g_VBoxUsbGlobal.hTimer)
+ {
+ if (!DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(dwErr == ERROR_IO_PENDING, ("DeleteTimerQueueTimer failed, dwErr (%d)\n", dwErr));
+ }
+ }
+
+ if (!CreateTimerQueueTimer(&g_VBoxUsbGlobal.hTimer, g_VBoxUsbGlobal.hTimerQueue,
+ usbLibTimerCallback,
+ NULL,
+ 500, /* ms*/
+ 0,
+ WT_EXECUTEONLYONCE))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateTimerQueueTimer failed, dwErr (%d)\n", dwErr));
+
+ /* call it directly */
+ usbLibTimerCallback(NULL, FALSE);
+ }
+}
+
+static LRESULT CALLBACK usbLibWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DEVICECHANGE:
+ if (wParam == DBT_DEVNODES_CHANGED)
+ {
+ /* we notify change any device arivals/removals on the system
+ * and let the client decide whether the usb change actually happened
+ * so far this is more clean than reporting events from the Monitor
+ * because monitor sees only PDO arrivals/removals,
+ * and by the time PDO is created, device can not
+ * be yet started and fully functional,
+ * so usblib won't be able to pick it up
+ * */
+
+ usbLibOnDeviceChange();
+ }
+ break;
+ case WM_DESTROY:
+ {
+ PostQuitMessage(0);
+ return 0;
+ }
+ }
+ return DefWindowProc (hwnd, uMsg, wParam, lParam);
+}
+
+/** @todo r=bird: Use an IPRT thread? */
+static DWORD WINAPI usbLibMsgThreadProc(__in LPVOID lpParameter)
+{
+ static LPCSTR s_szVBoxUsbWndClassName = "VBoxUsbLibClass";
+ const HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+ RT_NOREF1(lpParameter);
+
+ Assert(g_VBoxUsbGlobal.hWnd == NULL);
+ g_VBoxUsbGlobal.hWnd = NULL;
+
+ /*
+ * Register the Window Class and create the hidden window.
+ */
+ WNDCLASS wc;
+ wc.style = 0;
+ wc.lpfnWndProc = usbLibWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(void *);
+ wc.hInstance = hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = s_szVBoxUsbWndClassName;
+ ATOM atomWindowClass = RegisterClass(&wc);
+ if (atomWindowClass != 0)
+ g_VBoxUsbGlobal.hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ s_szVBoxUsbWndClassName, s_szVBoxUsbWndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+ else
+ AssertMsgFailed(("RegisterClass failed, last error %u\n", GetLastError()));
+
+ /*
+ * Signal the creator thread.
+ */
+ ASMCompilerBarrier();
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+
+ if (g_VBoxUsbGlobal.hWnd)
+ {
+ /* Make sure it's really hidden. */
+ SetWindowPos(g_VBoxUsbGlobal.hWnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ /*
+ * The message pump.
+ */
+ MSG msg;
+ BOOL fRet;
+ while ((fRet = GetMessage(&msg, NULL, 0, 0)) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ Assert(fRet >= 0);
+ }
+
+ if (atomWindowClass != NULL)
+ UnregisterClass(s_szVBoxUsbWndClassName, hInstance);
+
+ return 0;
+}
+
+#endif /* VBOX_USB_USE_DEVICE_NOTIFICATION */
+
+/**
+ * Initialize the USB library
+ *
+ * @returns VBox status code.
+ */
+USBLIB_DECL(int) USBLibInit(void)
+{
+ int rc = VERR_GENERAL_FAILURE;
+
+ Log(("usbproxy: usbLibInit\n"));
+
+ RT_ZERO(g_VBoxUsbGlobal);
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+
+ /*
+ * Create the notification and interrupt event before opening the device.
+ */
+ g_VBoxUsbGlobal.hNotifyEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
+ FALSE, /* BOOL bManualReset */
+#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
+ TRUE, /* BOOL bInitialState */
+#else
+ FALSE, /* set to false since it will be initially used for notification thread startup sync */
+#endif
+ NULL /* LPCTSTR lpName */);
+ if (g_VBoxUsbGlobal.hNotifyEvent)
+ {
+ g_VBoxUsbGlobal.hInterruptEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes */
+ FALSE, /* BOOL bManualReset */
+ FALSE, /* BOOL bInitialState */
+ NULL /* LPCTSTR lpName */);
+ if (g_VBoxUsbGlobal.hInterruptEvent)
+ {
+ /*
+ * Open the USB monitor device, starting if needed.
+ */
+ g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL);
+
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ HRESULT hr = VBoxDrvCfgSvcStart(USBMON_SERVICE_NAME_W);
+ if (hr == S_OK)
+ {
+ g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL);
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("CreateFile failed dwErr(%d)\n", dwErr));
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ }
+ }
+
+ if (g_VBoxUsbGlobal.hMonitor != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Check the USB monitor version.
+ *
+ * Drivers are backwards compatible within the same major
+ * number. We consider the minor version number this library
+ * is compiled with to be the minimum required by the driver.
+ * This is by reasoning that the library uses the full feature
+ * set of the driver it's written for.
+ */
+ USBSUP_VERSION Version = {0};
+ DWORD cbReturned = 0;
+ if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_GET_VERSION,
+ NULL, 0,
+ &Version, sizeof (Version),
+ &cbReturned, NULL))
+ {
+ if ( Version.u32Major == USBMON_MAJOR_VERSION
+#if USBMON_MINOR_VERSION != 0
+ && Version.u32Minor >= USBMON_MINOR_VERSION
+#endif
+ )
+ {
+#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
+ /*
+ * Tell the monitor driver which event object to use
+ * for notifications.
+ */
+ USBSUP_SET_NOTIFY_EVENT SetEvent = {0};
+ Assert(g_VBoxUsbGlobal.hNotifyEvent);
+ SetEvent.u.hEvent = g_VBoxUsbGlobal.hNotifyEvent;
+ if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT,
+ &SetEvent, sizeof(SetEvent),
+ &SetEvent, sizeof(SetEvent),
+ &cbReturned, NULL))
+ {
+ rc = SetEvent.u.rc;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're DONE!
+ */
+ return VINF_SUCCESS;
+ }
+
+ AssertMsgFailed(("SetEvent failed, %Rrc (%d)\n", rc, rc));
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("SetEvent Ioctl failed, dwErr (%d)\n", dwErr));
+ rc = VERR_VERSION_MISMATCH;
+ }
+#else
+ /*
+ * We can not use USB Mon for reliable device add/remove tracking
+ * since once USB Mon is notified about PDO creation and/or IRP_MN_START_DEVICE,
+ * the function device driver may still do some initialization, which might result in
+ * notifying too early.
+ * Instead we use WM_DEVICECHANGE + DBT_DEVNODES_CHANGED to make Windows notify us about
+ * device arivals/removals.
+ * Since WM_DEVICECHANGE is a window message, create a dedicated thread to be used for WndProc and stuff.
+ * The thread would create a window, track windows messages and call usbLibOnDeviceChange on WM_DEVICECHANGE arrival.
+ * See comments in usbLibOnDeviceChange function for detail about using the timer queue.
+ */
+ g_VBoxUsbGlobal.hTimerQueue = CreateTimerQueue();
+ if (g_VBoxUsbGlobal.hTimerQueue)
+ {
+ g_VBoxUsbGlobal.hThread = CreateThread(
+ NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
+ 0, /*__in SIZE_T dwStackSize, */
+ usbLibMsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
+ NULL, /*__in_opt LPVOID lpParameter,*/
+ 0, /*__in DWORD dwCreationFlags,*/
+ NULL /*__out_opt LPDWORD lpThreadId*/
+ );
+ if (g_VBoxUsbGlobal.hThread)
+ {
+ DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hNotifyEvent, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0);
+ if (g_VBoxUsbGlobal.hWnd)
+ {
+ /*
+ * We're DONE!
+ *
+ * Just ensure that the event is set so the
+ * first "wait change" request is processed.
+ */
+ SetEvent(g_VBoxUsbGlobal.hNotifyEvent);
+ return VINF_SUCCESS;
+ }
+
+ dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0);
+ BOOL fRc = CloseHandle(g_VBoxUsbGlobal.hThread); NOREF(fRc);
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsg(fRc, ("CloseHandle for hThread failed dwErr(%d)\n", dwErr));
+ g_VBoxUsbGlobal.hThread = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateThread failed, dwErr (%d)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue, INVALID_HANDLE_VALUE /* see term */);
+ g_VBoxUsbGlobal.hTimerQueue = NULL;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateTimerQueue failed dwErr(%d)\n", dwErr));
+ }
+#endif
+ }
+ else
+ {
+ LogRelFunc(("USB Monitor driver version mismatch! driver=%u.%u library=%u.%u\n",
+ Version.u32Major, Version.u32Minor, USBMON_MAJOR_VERSION, USBMON_MINOR_VERSION));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ rc = VERR_VERSION_MISMATCH;
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("DeviceIoControl failed dwErr(%d)\n", dwErr));
+ rc = VERR_VERSION_MISMATCH;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hMonitor);
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ LogRelFunc(("USB Service not found\n"));
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertFailed();
+#endif
+ rc = VERR_FILE_NOT_FOUND;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
+ g_VBoxUsbGlobal.hInterruptEvent = NULL;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateEvent for InterruptEvent failed dwErr(%d)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateEvent for NotifyEvent failed dwErr(%d)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ /* since main calls us even if USBLibInit fails,
+ * we use hMonitor == INVALID_HANDLE_VALUE as a marker to indicate whether the lib is inited */
+
+ Assert(RT_FAILURE(rc));
+ return rc;
+}
+
+
+/**
+ * Terminate the USB library
+ *
+ * @returns VBox status code.
+ */
+USBLIB_DECL(int) USBLibTerm(void)
+{
+ if (g_VBoxUsbGlobal.hMonitor == INVALID_HANDLE_VALUE)
+ {
+ Assert(g_VBoxUsbGlobal.hInterruptEvent == NULL);
+ Assert(g_VBoxUsbGlobal.hNotifyEvent == NULL);
+ return VINF_SUCCESS;
+ }
+
+ BOOL fRc;
+#ifdef VBOX_USB_USE_DEVICE_NOTIFICATION
+ fRc = PostMessage(g_VBoxUsbGlobal.hWnd, WM_CLOSE, 0, 0);
+ AssertMsg(fRc, ("PostMessage for hWnd failed dwErr(%d)\n", GetLastError()));
+
+ if (g_VBoxUsbGlobal.hThread != NULL)
+ {
+ DWORD dwResult = WaitForSingleObject(g_VBoxUsbGlobal.hThread, INFINITE);
+ Assert(dwResult == WAIT_OBJECT_0); NOREF(dwResult);
+ fRc = CloseHandle(g_VBoxUsbGlobal.hThread);
+ AssertMsg(fRc, ("CloseHandle for hThread failed dwErr(%d)\n", GetLastError()));
+ }
+
+ if (g_VBoxUsbGlobal.hTimer)
+ {
+ fRc = DeleteTimerQueueTimer(g_VBoxUsbGlobal.hTimerQueue, g_VBoxUsbGlobal.hTimer,
+ INVALID_HANDLE_VALUE); /* <-- to block until the timer is completed */
+ AssertMsg(fRc, ("DeleteTimerQueueTimer failed dwErr(%d)\n", GetLastError()));
+ }
+
+ if (g_VBoxUsbGlobal.hTimerQueue)
+ {
+ fRc = DeleteTimerQueueEx(g_VBoxUsbGlobal.hTimerQueue,
+ INVALID_HANDLE_VALUE); /* <-- to block until all timers are completed */
+ AssertMsg(fRc, ("DeleteTimerQueueEx failed dwErr(%d)\n", GetLastError()));
+ }
+#endif /* VBOX_USB_USE_DEVICE_NOTIFICATION */
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hMonitor);
+ AssertMsg(fRc, ("CloseHandle for hMonitor failed dwErr(%d)\n", GetLastError()));
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
+ AssertMsg(fRc, ("CloseHandle for hInterruptEvent failed lasterr=%u\n", GetLastError()));
+ g_VBoxUsbGlobal.hInterruptEvent = NULL;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ AssertMsg(fRc, ("CloseHandle for hNotifyEvent failed dwErr(%d)\n", GetLastError()));
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+
+ return VINF_SUCCESS;
+}
+