summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB/win
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/win')
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp256
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp215
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk204
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp247
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h113
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h95
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp428
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h89
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf88
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h103
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp357
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h218
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc70
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp274
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h46
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp427
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h51
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp1593
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h97
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp2221
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf98
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp1763
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h75
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp218
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h96
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp1568
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h77
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc70
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp384
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def74
37 files changed, 11615 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp
new file mode 100644
index 00000000..0debc44c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBInstall.cpp
@@ -0,0 +1,256 @@
+/* $Id: USBInstall.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver installation, Installation code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxUSBMon"
+/** Win32 Device name. */
+#define DEVICE_NAME "\\\\.\\VBoxUSBMon"
+/** NT Device name. */
+#define DEVICE_NAME_NT L"\\Device\\VBoxUSBMon"
+/** Win32 Symlink name. */
+#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxUSBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+int usblibOsCreateService(void);
+
+
+static DECLCALLBACK(void) vboxUsbLog(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ switch (enmSeverity)
+ {
+ case VBOXDRVCFG_LOG_SEVERITY_FLOW:
+ case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
+ break;
+ case VBOXDRVCFG_LOG_SEVERITY_REL:
+ RTMsgInfo("%s", pszMsg);
+ break;
+ default:
+ break;
+ }
+}
+
+static DECLCALLBACK(void) vboxUsbPanic(void *pvPanic)
+{
+ RT_NOREF1(pvPanic);
+#ifndef DEBUG_bird
+ AssertFailed();
+#endif
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ RTMsgInfo("USB installation");
+
+ VBoxDrvCfgLoggerSet(vboxUsbLog, NULL);
+ VBoxDrvCfgPanicSet(vboxUsbPanic, NULL);
+
+ rc = usblibOsCreateService();
+ if (RT_SUCCESS(rc))
+ {
+ /* Build the path to the INF file: */
+ char szInfFile[RTPATH_MAX];
+ rc = RTProcGetExecutablePath(szInfFile, sizeof(szInfFile)) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
+ if (RT_SUCCESS(rc))
+ {
+ RTPathStripFilename(szInfFile);
+ rc = RTPathAppend(szInfFile, sizeof(szInfFile), "VBoxUSB.inf");
+ }
+ PRTUTF16 pwszInfFile = NULL;
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUtf16(szInfFile, &pwszInfFile);
+ if (RT_SUCCESS(rc))
+ {
+ /* Install the INF file: */
+ HRESULT hr = VBoxDrvCfgInfInstall(pwszInfFile);
+ if (hr == S_OK)
+ RTMsgInfo("Installation successful!");
+ else
+ {
+ RTMsgError("Installation failed: %Rhrc", hr);
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ RTUtf16Free(pwszInfFile);
+ }
+ else
+ RTMsgError("Failed to construct INF path: %Rrc", rc);
+ }
+ else
+ RTMsgError("Service creation failed: %Rrc", rc);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Changes the USB driver service to specified driver path.
+ *
+ * @returns 0 on success.
+ * @returns < 0 on failure.
+ */
+int usblibOsChangeService(const char *pszDriverPath)
+{
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertPtr(pszDriverPath);
+ AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", dwLastError));
+ if (hSMgrCreate)
+ {
+ SC_HANDLE hService = OpenService(hSMgrCreate,
+ SERVICE_NAME,
+ GENERIC_ALL);
+ dwLastError = GetLastError();
+ if (hService == NULL)
+ {
+ AssertMsg(hService, ("OpenService failed! LastError=%Rwa, pszDriver=%s\n", dwLastError, pszDriverPath));
+ rc = RTErrConvertFromWin32(dwLastError);
+ }
+ else
+ {
+ /* We only gonna change the driver image path, the rest remains like it already is */
+ if (ChangeServiceConfig(hService,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ pszDriverPath,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL))
+ {
+ RTPrintf("Changed service config to new driver path: %s\n", pszDriverPath);
+ }
+ else
+ {
+ AssertMsg(hService, ("ChangeServiceConfig failed! LastError=%Rwa, pszDriver=%s\n", dwLastError, pszDriverPath));
+ rc = RTErrConvertFromWin32(dwLastError);
+ }
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ }
+
+ CloseServiceHandle(hSMgrCreate);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates the service.
+ *
+ * @returns 0 on success.
+ * @returns < 0 on failure.
+ */
+int usblibOsCreateService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertMsg(hSMgrCreate, ("OpenSCManager(,,create) failed rc=%d\n", dwLastError));
+ if (hSMgrCreate)
+ {
+ char szDriver[RTPATH_MAX];
+ rc = RTPathExecDir(szDriver, sizeof(szDriver) - sizeof("\\VBoxUSBMon.sys"));
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szDriver, "\\VBoxUSBMon.sys");
+ RTPrintf("Creating USB monitor driver service with path %s ...\n", szDriver);
+ SC_HANDLE hService = CreateService(hSMgrCreate,
+ SERVICE_NAME,
+ "VBox USB Monitor Driver",
+ SERVICE_QUERY_STATUS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ szDriver,
+ NULL, NULL, NULL, NULL, NULL);
+ dwLastError = GetLastError();
+ if (dwLastError == ERROR_SERVICE_EXISTS)
+ {
+ RTPrintf("USB monitor driver service already exists, skipping creation.\n");
+ rc = usblibOsChangeService(szDriver);
+ }
+ else
+ {
+ AssertMsg(hService, ("CreateService failed! LastError=%Rwa, szDriver=%s\n", dwLastError, szDriver));
+ rc = RTErrConvertFromWin32(dwLastError);
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ }
+ }
+ CloseServiceHandle(hSMgrCreate);
+ }
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp
new file mode 100644
index 00000000..3dc0d6fe
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Install/USBUninstall.cpp
@@ -0,0 +1,215 @@
+/* $Id: USBUninstall.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver uninstallation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The support service name. */
+#define SERVICE_NAME "VBoxUSBMon"
+/** Win32 Device name. */
+#define DEVICE_NAME "\\\\.\\VBoxUSBMon"
+/** NT Device name. */
+#define DEVICE_NAME_NT L"\\Device\\VBoxUSBMon"
+/** Win32 Symlink name. */
+#define DEVICE_NAME_DOS L"\\DosDevices\\VBoxUSBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int usblibOsStopService(void);
+static int usblibOsDeleteService(void);
+
+
+static DECLCALLBACK(void) vboxUsbLog(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ switch (enmSeverity)
+ {
+ case VBOXDRVCFG_LOG_SEVERITY_FLOW:
+ case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
+ break;
+ case VBOXDRVCFG_LOG_SEVERITY_REL:
+ RTMsgInfo("%s", pszMsg);
+ break;
+ default:
+ break;
+ }
+}
+
+static DECLCALLBACK(void) vboxUsbPanic(void *pvPanic)
+{
+ RT_NOREF1(pvPanic);
+#ifndef DEBUG_bird
+ AssertFailed();
+#endif
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+ RTMsgInfo("USB uninstallation\n");
+
+ VBoxDrvCfgLoggerSet(vboxUsbLog, NULL);
+ VBoxDrvCfgPanicSet(vboxUsbPanic, NULL);
+
+ usblibOsStopService();
+ usblibOsDeleteService();
+
+ HRESULT hr = VBoxDrvCfgInfUninstallAllF(L"USB", L"USB\\VID_80EE&PID_CAFE", SUOI_FORCEDELETE);
+ if (hr != S_OK)
+ return RTMsgErrorExitFailure("SetupUninstallOEMInf failed: %Rhrc\n", hr);
+
+ RTMsgInfo("USB uninstallation succeeded!");
+ return 0;
+}
+
+
+/**
+ * Stops a possibly running service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int usblibOsStopService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Stop the service.
+ */
+ SERVICE_STATUS Status;
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
+ {
+ /*
+ * Wait for finish about 1 minute.
+ * It should be enough for work with driver verifier
+ */
+ int iWait = 600;
+ while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else
+ AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
+ }
+ else
+ AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", GetLastError(), Status.dwCurrentState));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
+
+
+/**
+ * Deletes the service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+static int usblibOsDeleteService(void)
+{
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, DELETE);
+ if (hService)
+ {
+ /*
+ * Delete the service.
+ */
+ if (DeleteService(hService))
+ rc = 0;
+ else
+ AssertMsgFailed(("DeleteService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk
new file mode 100644
index 00000000..4211147b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/Makefile.kmk
@@ -0,0 +1,204 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Windows USB drivers.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox 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.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+LIBRARIES.win += usbd
+SYSMODS.win += VBoxUSB VBoxUSBMon
+PROGRAMS.win += USBInstall USBUninstall USBTest
+INSTALLS.win += install-infs
+
+#
+# usbd
+#
+usbd_TEMPLATE = VBoxR0Drv
+usbd_SOURCES = usbd/usbd.def
+
+#
+# VBoxUSB
+#
+VBoxUSB_TEMPLATE = VBoxR0Drv
+ifdef VBOX_SIGNING_MODE
+ VBoxUSB_INSTTYPE = none
+ VBoxUSB_DEBUG_INSTTYPE = both
+endif
+VBoxUSB_SDKS = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ VBoxUSB_DEFS := IN_RT_R0 IN_SUP_R0 VBOX_DBG_LOG_NAME="USBDev"
+else
+ VBoxUSB_DEFS := IN_RT_R0 IN_SUP_R0 VBOX_DBG_LOG_NAME=\"USBDev\"
+endif
+VBoxUSB_LDFLAGS.x86 = -Entry:DriverEntry@8
+VBoxUSB_LDFLAGS.amd64 = -Entry:DriverEntry
+VBoxUSB_SOURCES = \
+ dev/VBoxUsbDev.cpp \
+ dev/VBoxUsbRt.cpp \
+ dev/VBoxUsbPnP.cpp \
+ dev/VBoxUsbPwr.cpp \
+ cmn/VBoxUsbTool.cpp \
+ cmn/VBoxDrvTool.cpp \
+ dev/VBoxUsbDev.rc
+VBoxUSB_LIBS = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(usbd_1_TARGET)
+
+#
+# VBoxUSBMon
+#
+VBoxUSBMon_TEMPLATE = VBoxR0Drv
+ifdef VBOX_SIGNING_MODE
+ VBoxUSBMon_INSTTYPE = none
+ VBoxUSBMon_DEBUG_INSTTYPE = both
+endif
+VBoxUSBMon_INCS := $(PATH_SUB_CURRENT)/..
+VBoxUSBMon_SDKS = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK_INCS)
+VBoxUSBMon_DEFS = IN_RT_R0 IN_SUP_R0 NTDDI_WINNT=_NTDDI_VISTA VBOXUSBFILTERMGR_USB_SPINLOCK
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ VBoxUSBMon_DEFS += VBOX_DBG_LOG_NAME="USBMon"
+else
+ VBoxUSBMon_DEFS += VBOX_DBG_LOG_NAME=\"USBMon\"
+endif
+VBoxUSBMon_LDFLAGS.x86 = -Entry:DriverEntry@8
+VBoxUSBMon_LDFLAGS.amd64 = -Entry:DriverEntry
+ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUSBMon_DEFS += VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+endif
+VBoxUSBMon_SOURCES = \
+ mon/VBoxUsbMon.cpp \
+ mon/VBoxUsbFlt.cpp \
+ mon/VBoxUsbHook.cpp \
+ cmn/VBoxUsbTool.cpp \
+ cmn/VBoxDrvTool.cpp \
+ ../USBFilter.cpp \
+ ../VBoxUSBFilterMgr.cpp \
+ mon/VBoxUsbMon.rc
+VBoxUSBMon_LIBS = \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) \
+ $(usbd_1_TARGET)
+if1of ($(KBUILD_TYPE), debug)
+ VBoxUSBMon_DEFS += LOG_ENABLED VBOX_USB_WITH_VERBOSE_LOGGING
+endif
+
+#
+# Template for USBInstalls and friends.
+#
+TEMPLATE_VBoxUsbR3 = Template for USBInstalls, USBUninstall and USBTest
+TEMPLATE_VBoxUsbR3_EXTENDS = VBoxR3Exe
+TEMPLATE_VBoxUsbR3_SDKS = $(TEMPLATE_VBoxR3Exe_SDKS) ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxWinNewDevLib
+TEMPLATE_VBoxUsbR3_CXXFLAGS = $(TEMPLATE_VBoxR3Exe_CXXFLAGS) -Gz
+TEMPLATE_VBoxUsbR3_CFLAGS = $(TEMPLATE_VBoxR3Exe_CFLAGS) -Gz
+TEMPLATE_VBoxUsbR3_LIBS = $(TEMPLATE_VBoxR3Exe_LIBS) \
+ $(PATH_STAGE_LIB)/VBoxDrvCfgExe$(VBOX_SUFF_LIB) \
+ $(LIB_RUNTIME)
+
+#
+# USBInstall
+#
+USBInstall_TEMPLATE = VBoxUsbR3
+USBInstall_SOURCES = Install/USBInstall.cpp
+
+#
+# USBUninstall
+#
+USBUninstall_TEMPLATE = VBoxUsbR3
+USBUninstall_SOURCES = Install/USBUninstall.cpp
+
+#
+# USBTest
+#
+USBTest_TEMPLATE = VBoxUsbR3
+USBTest_DEFS = IN_USBLIB
+USBTest_SOURCES = \
+ testcase/USBTest.cpp \
+ ../USBFilter.cpp
+
+#
+# Install the INF files.
+#
+install-infs_TEMPLATE = VBoxR0DrvInfCat
+install-infs_SOURCES = \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf
+install-infs_CLEAN = $(install-infs_SOURCES)
+install-infs_BLDDIRS = \
+ $(PATH_TARGET)/VBoxUSBCat.dir \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir
+
+$(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf: $(PATH_SUB_CURRENT)/dev/VBoxUSB.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,install-infs,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+$(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf: $(PATH_SUB_CURRENT)/mon/VBoxUSBMon.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,install-infs,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ifdef VBOX_SIGNING_MODE
+ install-infs_SOURCES += \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat=>VBoxUSB-PreW10.cat \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat=>VBoxUSBMon-PreW10.cat \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys
+
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys: $$(VBoxUSB_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.cat: \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.inf \
+ $(PATH_TARGET)/VBoxUSBCat.dir/VBoxUSB.sys
+ $(call MSG_TOOL,Inf2Cat,VBoxUSB-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys: $$(VBoxUSBMon_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.cat: \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.inf \
+ $(PATH_TARGET)/VBoxUSBMonCat.dir/VBoxUSBMon.sys
+ $(call MSG_TOOL,Inf2Cat,VBoxUSBMon-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+endif # signing
+
+# generate rules
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp
new file mode 100644
index 00000000..63ed595b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.cpp
@@ -0,0 +1,247 @@
+/* $Id: VBoxDrvTool.cpp $ */
+/** @file
+ * Windows Driver R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxDrvTool.h"
+
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+#include "../../../win/VBoxDbgLog.h"
+
+#define VBOXDRVTOOL_MEMTAG 'TDBV'
+
+static PVOID vboxDrvToolMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXDRVTOOL_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+static PVOID vboxDrvToolMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxDrvToolMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+static VOID vboxDrvToolMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXDRVTOOL_MEMTAG);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKeyU(OUT PHANDLE phKey, IN PUNICODE_STRING pName, IN ACCESS_MASK fAccess)
+{
+ OBJECT_ATTRIBUTES ObjAttr;
+
+ InitializeObjectAttributes(&ObjAttr, pName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ return ZwOpenKey(phKey, fAccess, &ObjAttr);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess)
+{
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+
+ return VBoxDrvToolRegOpenKeyU(phKey, &RtlStr, fAccess);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegCloseKey(IN HANDLE hKey)
+{
+ return ZwClose(hKey);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PULONG pDword)
+{
+ struct
+ {
+ KEY_VALUE_PARTIAL_INFORMATION Info;
+ UCHAR Buf[32]; /* should be enough */
+ } Buf;
+ ULONG cbBuf;
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+ NTSTATUS Status = ZwQueryValueKey(hKey,
+ &RtlStr,
+ KeyValuePartialInformation,
+ &Buf.Info,
+ sizeof(Buf),
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (Buf.Info.Type == REG_DWORD)
+ {
+ Assert(Buf.Info.DataLength == 4);
+ *pDword = *((PULONG)Buf.Info.Data);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_INVALID_PARAMETER;
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT ULONG val)
+{
+ UNICODE_STRING RtlStr;
+ RtlInitUnicodeString(&RtlStr, pName);
+ return ZwSetValueKey(hKey, &RtlStr,
+ NULL, /* IN ULONG TitleIndex OPTIONAL, reserved */
+ REG_DWORD,
+ &val,
+ sizeof(val));
+}
+
+static NTSTATUS vboxDrvToolIoCompletionSetEvent(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF2(pDevObj, pIrp);
+ PKEVENT pEvent = (PKEVENT)pvContext;
+ KeSetEvent(pEvent, 0, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostAsync(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
+{
+ IoSetCompletionRoutine(pIrp, vboxDrvToolIoCompletionSetEvent, pEvent, TRUE, TRUE, TRUE);
+ return IoCallDriver(pDevObj, pIrp);
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSync(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ KEVENT Event;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event);
+ if (Status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = pIrp->IoStatus.Status;
+ }
+ return Status;
+}
+
+/* !!!NOTE: the caller MUST be the IRP owner!!! *
+ * !! one can not post threaded IRPs this way!! */
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSyncWithTimeout(PDEVICE_OBJECT pDevObj, PIRP pIrp, ULONG dwTimeoutMs)
+{
+ KEVENT Event;
+ LOG(("post irp (0x%p) to DevObj(0x%p) with timeout (%u)", pIrp, pDevObj, dwTimeoutMs));
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ NTSTATUS Status = VBoxDrvToolIoPostAsync(pDevObj, pIrp, &Event);
+ if (Status == STATUS_PENDING)
+ {
+ LARGE_INTEGER Interval;
+ PLARGE_INTEGER pInterval = NULL;
+ if (dwTimeoutMs != RT_INDEFINITE_WAIT)
+ {
+ Interval.QuadPart = -(int64_t) dwTimeoutMs /* ms */ * 10000;
+ pInterval = &Interval;
+ }
+
+ Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, pInterval);
+ if (Status == STATUS_TIMEOUT)
+ {
+ WARN(("irp (0x%p) to DevObj(0x%p) was not completed within timeout (%u), cancelling", pIrp, pDevObj, dwTimeoutMs));
+ if (!IoCancelIrp(pIrp))
+ {
+ /* this may happen, but this is something the caller with timeout is not expecting */
+ WARN(("IoCancelIrp failed"));
+ }
+
+ /* wait for the IRP to complete */
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ }
+ else
+ {
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("uunexpected Status (0x%x)", Status));
+ }
+
+ /* by this time the IRP is completed */
+ Status = pIrp->IoStatus.Status;
+ LOG(("Pending IRP(0x%p) completed with status(0x%x)", pIrp, Status));
+ }
+ else
+ {
+ LOG(("IRP(0x%p) completed with status(0x%x)", pIrp, Status));
+ }
+ return Status;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolRefWaitEqual(PVBOXDRVTOOL_REF pRef, uint32_t u32Val)
+{
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = -(int64_t) 2 /* ms */ * 10000;
+ uint32_t cRefs;
+ size_t loops = 0;
+ KTIMER kTimer;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ KeInitializeTimer(&kTimer);
+
+ while ((cRefs = ASMAtomicReadU32(&pRef->cRefs)) > u32Val && loops < 256)
+ {
+ Assert(cRefs >= u32Val);
+ Assert(cRefs < UINT32_MAX/2);
+
+ KeSetTimer(&kTimer, Interval, NULL);
+ status = KeWaitForSingleObject(&kTimer, Executive, KernelMode, false, NULL);
+ Assert(NT_SUCCESS(status));
+ loops++;
+ }
+}
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolStrCopy(PUNICODE_STRING pDst, CONST PUNICODE_STRING pSrc)
+{
+ USHORT cbLength = pSrc->Length + sizeof (pDst->Buffer[0]);
+ pDst->Buffer = (PWCHAR)vboxDrvToolMemAlloc(cbLength);
+ Assert(pDst->Buffer);
+ if (pDst->Buffer)
+ {
+ RtlMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length);
+ pDst->Buffer[pSrc->Length / sizeof (pDst->Buffer[0])] = L'\0';
+ pDst->Length = pSrc->Length;
+ pDst->MaximumLength = cbLength;
+ return STATUS_SUCCESS;
+ }
+ return STATUS_NO_MEMORY;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolStrFree(PUNICODE_STRING pStr)
+{
+ vboxDrvToolMemFree(pStr->Buffer);
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h
new file mode 100644
index 00000000..f953f8eb
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxDrvTool.h
@@ -0,0 +1,113 @@
+/* $Id: VBoxDrvTool.h $ */
+/** @file
+ * Windows Driver R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <iprt/stdint.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/nt/wdm.h>
+
+
+RT_C_DECLS_BEGIN
+
+#if 0
+/* enable this in case we include this in a dll*/
+# ifdef IN_VBOXDRVTOOL
+# define VBOXDRVTOOL_DECL(a_Type) DECLEXPORT(a_Type)
+# else
+# define VBOXDRVTOOL_DECL(a_Type) DECLIMPORT(a_Type)
+# endif
+#else
+/*enable this in case we include this in a static lib*/
+# define VBOXDRVTOOL_DECL(a_Type) a_Type VBOXCALL
+#endif
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKeyU(OUT PHANDLE phKey, IN PUNICODE_STRING pName, IN ACCESS_MASK fAccess);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegCloseKey(IN HANDLE hKey);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PULONG pDword);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT ULONG val);
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostAsync(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSync(PDEVICE_OBJECT pDevObj, PIRP pIrp);
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolIoPostSyncWithTimeout(PDEVICE_OBJECT pDevObj, PIRP pIrp, ULONG dwTimeoutMs);
+DECLINLINE(NTSTATUS) VBoxDrvToolIoComplete(PIRP pIrp, NTSTATUS Status, ULONG ulInfo)
+{
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = ulInfo;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+typedef struct VBOXDRVTOOL_REF
+{
+ volatile uint32_t cRefs;
+} VBOXDRVTOOL_REF, *PVBOXDRVTOOL_REF;
+
+DECLINLINE(void) VBoxDrvToolRefInit(PVBOXDRVTOOL_REF pRef)
+{
+ pRef->cRefs = 1;
+}
+
+DECLINLINE(uint32_t) VBoxDrvToolRefRetain(PVBOXDRVTOOL_REF pRef)
+{
+ Assert(pRef->cRefs);
+ Assert(pRef->cRefs < UINT32_MAX / 2);
+ return ASMAtomicIncU32(&pRef->cRefs);
+}
+
+DECLINLINE(uint32_t) VBoxDrvToolRefRelease(PVBOXDRVTOOL_REF pRef)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pRef->cRefs);
+ Assert(cRefs < UINT32_MAX/2);
+ return cRefs;
+}
+
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolRefWaitEqual(PVBOXDRVTOOL_REF pRef, uint32_t u32Val);
+
+VBOXDRVTOOL_DECL(NTSTATUS) VBoxDrvToolStrCopy(PUNICODE_STRING pDst, CONST PUNICODE_STRING pSrc);
+VBOXDRVTOOL_DECL(VOID) VBoxDrvToolStrFree(PUNICODE_STRING pStr);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxDrvTool_h */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h
new file mode 100644
index 00000000..9bc69afb
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbIdc.h
@@ -0,0 +1,95 @@
+/* $Id: VBoxUsbIdc.h $ */
+/** @file
+ * Windows USB Proxy - Monitor Driver communication interface.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOXUSBIDC_VERSION_MAJOR 1
+#define VBOXUSBIDC_VERSION_MINOR 0
+
+#define VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x618, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP CTL_CODE(FILE_DEVICE_UNKNOWN, 0x619, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN CTL_CODE(FILE_DEVICE_UNKNOWN, 0x61A, METHOD_NEITHER, FILE_WRITE_ACCESS)
+#define VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STATE_CHANGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x61B, METHOD_NEITHER, FILE_WRITE_ACCESS)
+
+typedef struct
+{
+ uint32_t u32Major;
+ uint32_t u32Minor;
+} VBOXUSBIDC_VERSION, *PVBOXUSBIDC_VERSION;
+
+typedef void *HVBOXUSBIDCDEV;
+
+/* the initial device state is USBDEVICESTATE_HELD_BY_PROXY */
+typedef struct VBOXUSBIDC_PROXY_STARTUP
+{
+ union
+ {
+ /* in: device PDO */
+ PDEVICE_OBJECT pPDO;
+ /* out: device handle to be used for subsequent USBSUP_PROXY_XXX calls */
+ HVBOXUSBIDCDEV hDev;
+ } u;
+} VBOXUSBIDC_PROXY_STARTUP, *PVBOXUSBIDC_PROXY_STARTUP;
+
+typedef struct VBOXUSBIDC_PROXY_TEARDOWN
+{
+ HVBOXUSBIDCDEV hDev;
+} VBOXUSBIDC_PROXY_TEARDOWN, *PVBOXUSBIDC_PROXY_TEARDOWN;
+
+typedef enum
+{
+ VBOXUSBIDC_PROXY_STATE_UNKNOWN = 0,
+ VBOXUSBIDC_PROXY_STATE_IDLE,
+ VBOXUSBIDC_PROXY_STATE_INITIAL = VBOXUSBIDC_PROXY_STATE_IDLE,
+ VBOXUSBIDC_PROXY_STATE_USED_BY_GUEST
+} VBOXUSBIDC_PROXY_STATE;
+
+typedef struct VBOXUSBIDC_PROXY_STATE_CHANGE
+{
+ HVBOXUSBIDCDEV hDev;
+ VBOXUSBIDC_PROXY_STATE enmState;
+} VBOXUSBIDC_PROXY_STATE_CHANGE, *PVBOXUSBIDC_PROXY_STATE_CHANGE;
+
+NTSTATUS VBoxUsbIdcInit();
+VOID VBoxUsbIdcTerm();
+NTSTATUS VBoxUsbIdcProxyStarted(PDEVICE_OBJECT pPDO, HVBOXUSBIDCDEV *phDev);
+NTSTATUS VBoxUsbIdcProxyStopped(HVBOXUSBIDCDEV hDev);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbIdc_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp
new file mode 100644
index 00000000..67383ea0
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.cpp
@@ -0,0 +1,428 @@
+/* $Id: VBoxUsbTool.cpp $ */
+/** @file
+ * Windows USB R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define INITGUID
+#include "VBoxUsbTool.h"
+#include <usbbusif.h>
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <VBox/log.h>
+#include <VBox/usblib.h>
+
+#include "../../../win/VBoxDbgLog.h"
+
+#define VBOXUSBTOOL_MEMTAG 'TUBV'
+
+static PVOID vboxUsbToolMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBTOOL_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+static PVOID vboxUsbToolMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxUsbToolMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+static VOID vboxUsbToolMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSBTOOL_MEMTAG);
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAlloc(USHORT u16Function, USHORT cbSize)
+{
+ PURB pUrb = (PURB)vboxUsbToolMemAlloc(cbSize);
+ Assert(pUrb);
+ if (!pUrb)
+ return NULL;
+
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAllocZ(USHORT u16Function, USHORT cbSize)
+{
+ PURB pUrb = (PURB)vboxUsbToolMemAllocZ(cbSize);
+ Assert(pUrb);
+ if (!pUrb)
+ return NULL;
+
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbReinit(PURB pUrb, USHORT cbSize, USHORT u16Function)
+{
+ Assert(pUrb->UrbHeader.Length == cbSize);
+ if (pUrb->UrbHeader.Length < cbSize)
+ return NULL;
+ pUrb->UrbHeader.Length = cbSize;
+ pUrb->UrbHeader.Function = u16Function;
+ return pUrb;
+}
+
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolUrbFree(PURB pUrb)
+{
+ vboxUsbToolMemFree(pUrb);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolUrbPost(PDEVICE_OBJECT pDevObj, PURB pUrb, ULONG dwTimeoutMs)
+{
+ if (dwTimeoutMs == RT_INDEFINITE_WAIT)
+ return VBoxUsbToolIoInternalCtlSendSync(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL);
+ return VBoxUsbToolIoInternalCtlSendSyncWithTimeout(pDevObj, IOCTL_INTERNAL_USB_SUBMIT_URB, pUrb, NULL, dwTimeoutMs);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDescriptor(PDEVICE_OBJECT pDevObj, void *pvBuffer, int cbBuffer, int Type, int iIndex, int LangId, ULONG dwTimeoutMs)
+{
+ NTSTATUS Status;
+ USHORT cbUrb = sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST);
+ PURB pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, cbUrb);
+ if (!pUrb)
+ {
+ WARN(("allocating URB failed"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ PUSB_COMMON_DESCRIPTOR pCmn = (PUSB_COMMON_DESCRIPTOR)pvBuffer;
+ pCmn->bLength = cbBuffer;
+ pCmn->bDescriptorType = Type;
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE;
+ pUrb->UrbHeader.Length = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);
+ pUrb->UrbControlDescriptorRequest.TransferBufferLength = cbBuffer;
+ pUrb->UrbControlDescriptorRequest.TransferBuffer = pvBuffer;
+ pUrb->UrbControlDescriptorRequest.Index = (UCHAR)iIndex;
+ pUrb->UrbControlDescriptorRequest.DescriptorType = (UCHAR)Type;
+ pUrb->UrbControlDescriptorRequest.LanguageId = (USHORT)LangId;
+
+ Status = VBoxUsbToolUrbPost(pDevObj, pUrb, dwTimeoutMs);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("VBoxUsbToolUrbPost failed Status (0x%x)", Status));
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolStringDescriptorToUnicodeString(PUSB_STRING_DESCRIPTOR pDr, PUNICODE_STRING pUnicode)
+{
+ /* for some reason the string dr sometimes contains a non-null terminated string
+ * although we zeroed up the complete descriptor buffer
+ * this is why RtlInitUnicodeString won't work
+ * we need to init the scting length based on dr length */
+ pUnicode->Buffer = pDr->bString;
+ pUnicode->Length = pUnicode->MaximumLength = pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString);
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *pszResult, ULONG cbResult,
+ int iIndex, int LangId, ULONG dwTimeoutMs)
+{
+ char aBuf[MAXIMUM_USB_STRING_LENGTH];
+ AssertCompile(sizeof (aBuf) <= UINT8_MAX);
+ UCHAR cbBuf = (UCHAR)sizeof (aBuf);
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
+
+ Assert(pszResult);
+ *pszResult = 0;
+
+ memset(pDr, 0, cbBuf);
+ pDr->bLength = cbBuf;
+ pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
+
+ NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, iIndex, LangId, dwTimeoutMs);
+ if (NT_SUCCESS(Status))
+ {
+ if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
+ {
+ int rc = RTUtf16ToUtf8Ex(pDr->bString, (pDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof(RTUTF16),
+ &pszResult, cbResult, NULL /*pcch*/);
+ if (RT_SUCCESS(rc))
+ {
+ USBLibPurgeEncoding(pszResult);
+ Status = STATUS_SUCCESS;
+ }
+ else
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ else
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetLangID(PDEVICE_OBJECT pDevObj, int *pLangId, ULONG dwTimeoutMs)
+{
+ char aBuf[MAXIMUM_USB_STRING_LENGTH];
+ AssertCompile(sizeof (aBuf) <= UINT8_MAX);
+ UCHAR cbBuf = (UCHAR)sizeof (aBuf);
+ PUSB_STRING_DESCRIPTOR pDr = (PUSB_STRING_DESCRIPTOR)&aBuf;
+
+ Assert(pLangId);
+ *pLangId = 0;
+
+ memset(pDr, 0, cbBuf);
+ pDr->bLength = cbBuf;
+ pDr->bDescriptorType = USB_STRING_DESCRIPTOR_TYPE;
+
+ NTSTATUS Status = VBoxUsbToolGetDescriptor(pDevObj, pDr, cbBuf, USB_STRING_DESCRIPTOR_TYPE, 0, 0, dwTimeoutMs);
+ if (NT_SUCCESS(Status))
+ {
+ /* Just grab the first lang ID if available. In 99% cases, it will be US English (0x0409).*/
+ if (pDr->bLength >= sizeof (USB_STRING_DESCRIPTOR))
+ {
+ AssertCompile(sizeof (pDr->bString[0]) == sizeof (uint16_t));
+ *pLangId = pDr->bString[0];
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDeviceSpeed(PDEVICE_OBJECT pDevObj, BOOLEAN *pbIsHigh)
+{
+ Assert(pbIsHigh);
+ *pbIsHigh = FALSE;
+
+ PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
+ Assert(pIrp);
+ if (!pIrp)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ USB_BUS_INTERFACE_USBDI_V1 BusIf;
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_PNP;
+ pSl->MinorFunction = IRP_MN_QUERY_INTERFACE;
+ pSl->Parameters.QueryInterface.InterfaceType = &USB_BUS_INTERFACE_USBDI_GUID;
+ pSl->Parameters.QueryInterface.Size = sizeof (BusIf);
+ pSl->Parameters.QueryInterface.Version = USB_BUSIF_USBDI_VERSION_1;
+ pSl->Parameters.QueryInterface.Interface = (PINTERFACE)&BusIf;
+ pSl->Parameters.QueryInterface.InterfaceSpecificData = NULL;
+
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevObj, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ if (NT_SUCCESS(Status))
+ {
+ *pbIsHigh = BusIf.IsDeviceHighSpeed(BusIf.BusContext);
+ BusIf.InterfaceDereference(BusIf.BusContext);
+ }
+ IoFreeIrp(pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolPipeClear(PDEVICE_OBJECT pDevObj, HANDLE hPipe, bool fReset)
+{
+ if (!hPipe)
+ {
+ Log(("Resetting the control pipe??\n"));
+ return STATUS_SUCCESS;
+ }
+ USHORT u16Function = fReset ? URB_FUNCTION_RESET_PIPE : URB_FUNCTION_ABORT_PIPE;
+ PURB pUrb = VBoxUsbToolUrbAlloc(u16Function, sizeof (struct _URB_PIPE_REQUEST));
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAlloc failed!\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ pUrb->UrbPipeRequest.PipeHandle = hPipe;
+ pUrb->UrbPipeRequest.Reserved = 0;
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status) || !USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbToolRequest failed with %x (%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolCurrentFrame(PDEVICE_OBJECT pDevObj, PIRP pIrp, PULONG piFrame)
+{
+ struct _URB_GET_CURRENT_FRAME_NUMBER Urb;
+ Urb.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;
+ Urb.Hdr.Length = sizeof(Urb);
+ Urb.FrameNumber = (ULONG)-1;
+
+ Assert(piFrame);
+ *piFrame = (ULONG)-1;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+ pSl->Parameters.Others.Argument1 = (PVOID)&Urb;
+ pSl->Parameters.Others.Argument2 = NULL;
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, (PURB)&Urb, RT_INDEFINITE_WAIT);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ {
+ *piFrame = Urb.FrameNumber;
+ }
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolDevUnconfigure(PDEVICE_OBJECT pDevObj)
+{
+ USHORT cbUrb = sizeof (struct _URB_SELECT_CONFIGURATION);
+ PURB pUrb = VBoxUsbToolUrbAlloc(URB_FUNCTION_SELECT_CONFIGURATION, cbUrb);
+ Assert(pUrb);
+ if (!pUrb)
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ UsbBuildSelectConfigurationRequest(pUrb, (USHORT)cbUrb, NULL);
+
+ NTSTATUS Status = VBoxUsbToolUrbPost(pDevObj, pUrb, RT_INDEFINITE_WAIT);
+ Assert(NT_SUCCESS(Status));
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(PIRP) VBoxUsbToolIoBuildAsyncInternalCtl(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
+{
+ PIRP pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
+ Assert(pIrp);
+ if (!pIrp)
+ {
+ return NULL;
+ }
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = NULL;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->MinorFunction = 0;
+ pSl->Parameters.DeviceIoControl.IoControlCode = uCtl;
+ pSl->Parameters.Others.Argument1 = pvArg1;
+ pSl->Parameters.Others.Argument2 = pvArg2;
+ return pIrp;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSyncWithTimeout(PDEVICE_OBJECT pDevObj, ULONG uCtl,
+ void *pvArg1, void *pvArg2, ULONG dwTimeoutMs)
+{
+ /* since we're going to cancel the irp on timeout, we should allocate our own IRP rather than using the threaded one
+ * */
+ PIRP pIrp = VBoxUsbToolIoBuildAsyncInternalCtl(pDevObj, uCtl, pvArg1, pvArg2);
+ if (!pIrp)
+ {
+ WARN(("VBoxUsbToolIoBuildAsyncInternalCtl failed"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NTSTATUS Status = VBoxDrvToolIoPostSyncWithTimeout(pDevObj, pIrp, dwTimeoutMs);
+
+ IoFreeIrp(pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendAsync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2,
+ PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus)
+{
+ NTSTATUS Status;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pSl;
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ pIrp = IoBuildDeviceIoControlRequest(uCtl, pDevObj, NULL, 0, NULL, 0, TRUE, pEvent, pIoStatus);
+ if (!pIrp)
+ {
+ WARN(("IoBuildDeviceIoControlRequest failed!!\n"));
+ pIoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
+ pIoStatus->Information = 0;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Get the next stack location as that is used for the new irp */
+ pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->Parameters.Others.Argument1 = pvArg1;
+ pSl->Parameters.Others.Argument2 = pvArg2;
+
+ Status = IoCallDriver(pDevObj, pIrp);
+
+ return Status;
+}
+
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2)
+{
+ IO_STATUS_BLOCK IoStatus = {0};
+ KEVENT Event;
+ NTSTATUS Status;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ LOG(("Sending sync Ctl pDevObj(0x%p), uCtl(0x%x), pvArg1(0x%p), pvArg2(0x%p)", pDevObj, uCtl, pvArg1, pvArg2));
+
+ Status = VBoxUsbToolIoInternalCtlSendAsync(pDevObj, uCtl, pvArg1, pvArg2, &Event, &IoStatus);
+
+ if (Status == STATUS_PENDING)
+ {
+ LOG(("VBoxUsbToolIoInternalCtlSendAsync returned pending for pDevObj(0x%p)", pDevObj));
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ LOG(("Pending VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
+ }
+ else
+ {
+ LOG(("VBoxUsbToolIoInternalCtlSendAsync completed with Status (0x%x) for pDevObj(0x%p)", Status, pDevObj));
+ }
+
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h
new file mode 100644
index 00000000..e6c6c236
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/cmn/VBoxUsbTool.h
@@ -0,0 +1,89 @@
+/* $Id: VBoxUsbTool.h $ */
+/** @file
+ * Windows USB R0 Tooling.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxDrvTool.h"
+
+RT_C_DECLS_BEGIN
+#pragma warning( disable : 4200 )
+#include <usbdi.h>
+#pragma warning( default : 4200 )
+#include <usbdlib.h>
+
+RT_C_DECLS_END
+
+#if 0
+/* enable this in case we include this in a dll*/
+# ifdef IN_VBOXUSBTOOL
+# define VBOXUSBTOOL_DECL(a_Type) DECLEXPORT(a_Type)
+# else
+# define VBOXUSBTOOL_DECL(a_Type) DECLIMPORT(a_Type)
+# endif
+#else
+/*enable this in case we include this in a static lib*/
+# define VBOXUSBTOOL_DECL(a_Type) a_Type VBOXCALL
+#endif
+
+
+RT_C_DECLS_BEGIN
+
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAlloc(USHORT u16Function, USHORT cbSize);
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbAllocZ(USHORT u16Function, USHORT cbSize);
+VBOXUSBTOOL_DECL(PURB) VBoxUsbToolUrbReinit(PURB pUrb, USHORT cbSize, USHORT u16Function);
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolUrbFree(PURB pUrb);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolUrbPost(PDEVICE_OBJECT pDevObj, PURB pUrb, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDescriptor(PDEVICE_OBJECT pDevObj, void *pvBuffer, int cbBuffer, int Type, int iIndex, int LangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *pszResult, ULONG cbResult, int iIndex, int LangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetLangID(PDEVICE_OBJECT pDevObj, int *pLangId, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolGetDeviceSpeed(PDEVICE_OBJECT pDevObj, BOOLEAN *pbIsHigh);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolPipeClear(PDEVICE_OBJECT pDevObj, HANDLE hPipe, bool fReset);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolCurrentFrame(PDEVICE_OBJECT pDevObj, PIRP pIrp, PULONG piFrame);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolDevUnconfigure(PDEVICE_OBJECT pDevObj);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendAsync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2, PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSync(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2);
+VBOXUSBTOOL_DECL(PIRP) VBoxUsbToolIoBuildAsyncInternalCtl(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2);
+VBOXUSBTOOL_DECL(NTSTATUS) VBoxUsbToolIoInternalCtlSendSyncWithTimeout(PDEVICE_OBJECT pDevObj, ULONG uCtl, void *pvArg1, void *pvArg2, ULONG dwTimeoutMs);
+VBOXUSBTOOL_DECL(VOID) VBoxUsbToolStringDescriptorToUnicodeString(PUSB_STRING_DESCRIPTOR pDr, PUNICODE_STRING pUnicode);
+
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_cmn_VBoxUsbTool_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf
new file mode 100644
index 00000000..9b580b98
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUSB.inf
@@ -0,0 +1,88 @@
+; $Id: VBoxUSB.inf $
+;; @file
+; VBox host drivers - USB drivers - Win32 USB device
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox 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.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=USB
+ClassGUID={36FC9E60-C465-11CF-8056-444553540000}
+provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+;cat CatalogFile=VBoxUSB.cat
+
+[SourceDisksNames]
+1=%Disk_Description%,,,
+
+[SourceDisksFiles]
+VBoxUSB.sys = 1
+
+[Manufacturer]
+%ORACLE%=VBoxUSB@COMMA-NT-ARCH@
+
+[VBoxUSB@DOT-NT-ARCH@]
+%USB\VID_80EE&PID_CAFE.DeviceDesc%=VBoxUSB.Dev, USB\VID_80EE&PID_CAFE
+
+[DestinationDirs]
+VBoxUSB.Files.Ext = 10,System32\Drivers
+
+[VBoxUSB.Dev.NT]
+CopyFiles=VBoxUSB.Files.Ext
+
+[VBoxUSB.Dev.NT.Services]
+Addservice = VBoxUSB, 0x00000002, VBoxUSB.AddService
+
+[VBoxUSB.AddService]
+DisplayName = %VBoxUSB.SvcDesc%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %10%\System32\Drivers\VBoxUSB.sys
+AddReg = VBoxUSB.AddReg
+LoadOrderGroup = Base
+
+[VBoxUSB.AddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,VBoxUSB.sys
+
+[VBoxUSB.Files.Ext]
+VBoxUSB.sys
+
+;---------------------------------------------------------------;
+
+[Strings]
+ORACLE="Oracle Corporation"
+Disk_Description="VBoxUSB Installation Disk"
+USB\VID_80EE&PID_CAFE.DeviceDesc="VirtualBox USB"
+VBoxUSB.SvcDesc="VirtualBox USB"
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h
new file mode 100644
index 00000000..57eeeafa
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbCmn.h
@@ -0,0 +1,103 @@
+/* $Id: VBoxUsbCmn.h $ */
+/** @file
+ * VBoxUsmCmn.h - USB device. Common defs
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "../cmn/VBoxDrvTool.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <iprt/cdefs.h>
+#include <iprt/asm.h>
+
+#include <VBox/usblib-win.h>
+
+#define VBOXUSB_CFG_IDLE_TIME_MS 5000
+
+typedef struct VBOXUSBDEV_EXT *PVBOXUSBDEV_EXT;
+
+RT_C_DECLS_BEGIN
+
+#ifdef _WIN64
+#define DECLSPEC_USBIMPORT DECLSPEC_IMPORT
+#else
+#define DECLSPEC_USBIMPORT
+
+#define USBD_ParseDescriptors _USBD_ParseDescriptors
+#define USBD_ParseConfigurationDescriptorEx _USBD_ParseConfigurationDescriptorEx
+#define USBD_CreateConfigurationRequestEx _USBD_CreateConfigurationRequestEx
+#endif
+
+DECLSPEC_USBIMPORT PUSB_COMMON_DESCRIPTOR
+USBD_ParseDescriptors(
+ IN PVOID DescriptorBuffer,
+ IN ULONG TotalLength,
+ IN PVOID StartPosition,
+ IN LONG DescriptorType
+ );
+
+DECLSPEC_USBIMPORT PUSB_INTERFACE_DESCRIPTOR
+USBD_ParseConfigurationDescriptorEx(
+ IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
+ IN PVOID StartPosition,
+ IN LONG InterfaceNumber,
+ IN LONG AlternateSetting,
+ IN LONG InterfaceClass,
+ IN LONG InterfaceSubClass,
+ IN LONG InterfaceProtocol
+ );
+
+DECLSPEC_USBIMPORT PURB
+USBD_CreateConfigurationRequestEx(
+ IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
+ IN PUSBD_INTERFACE_LIST_ENTRY InterfaceList
+ );
+
+RT_C_DECLS_END
+
+DECLHIDDEN(PVOID) vboxUsbMemAlloc(SIZE_T cbBytes);
+DECLHIDDEN(PVOID) vboxUsbMemAllocZ(SIZE_T cbBytes);
+DECLHIDDEN(VOID) vboxUsbMemFree(PVOID pvMem);
+
+#include "VBoxUsbRt.h"
+#include "VBoxUsbPnP.h"
+#include "VBoxUsbPwr.h"
+#include "VBoxUsbDev.h"
+
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbCmn_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp
new file mode 100644
index 00000000..680f5d2f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.cpp
@@ -0,0 +1,357 @@
+/* $Id: VBoxUsbDev.cpp $ */
+/** @file
+ * VBoxUsbDev.cpp - USB device.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbCmn.h"
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSB_MEMTAG 'bUBV'
+
+
+
+DECLHIDDEN(PVOID) vboxUsbMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSB_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+DECLHIDDEN(PVOID) vboxUsbMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = vboxUsbMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+DECLHIDDEN(VOID) vboxUsbMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSB_MEMTAG);
+}
+
+VBOXUSB_GLOBALS g_VBoxUsbGlobals = {0};
+
+static NTSTATUS vboxUsbDdiAddDevice(PDRIVER_OBJECT pDriverObject,
+ PDEVICE_OBJECT pPDO)
+{
+ PDEVICE_OBJECT pFDO = NULL;
+ NTSTATUS Status = IoCreateDevice(pDriverObject,
+ sizeof (VBOXUSBDEV_EXT),
+ NULL, /* IN PUNICODE_STRING pDeviceName OPTIONAL */
+ FILE_DEVICE_UNKNOWN, /* IN DEVICE_TYPE DeviceType */
+ FILE_AUTOGENERATED_DEVICE_NAME, /* IN ULONG DeviceCharacteristics */
+ FALSE, /* IN BOOLEAN fExclusive */
+ &pFDO);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pFDO->DeviceExtension;
+ /* init Device Object bits */
+ pFDO->Flags |= DO_DIRECT_IO;
+ if (pPDO->Flags & DO_POWER_PAGABLE)
+ pFDO->Flags |= DO_POWER_PAGABLE;
+
+
+ /* now init our state bits */
+
+ pDevExt->cHandles = 0;
+
+ pDevExt->pFDO = pFDO;
+ pDevExt->pPDO = pPDO;
+ pDevExt->pLowerDO = IoAttachDeviceToDeviceStack(pFDO, pPDO);
+ Assert(pDevExt->pLowerDO);
+ if (pDevExt->pLowerDO)
+ {
+ vboxUsbDdiStateInit(pDevExt);
+ Status = vboxUsbRtInit(pDevExt);
+ if (Status == STATUS_SUCCESS)
+ {
+ /* we're done! */
+ pFDO->Flags &= ~DO_DEVICE_INITIALIZING;
+ return STATUS_SUCCESS;
+ }
+
+ IoDetachDevice(pDevExt->pLowerDO);
+ }
+ else
+ Status = STATUS_NO_SUCH_DEVICE;
+
+ IoDeleteDevice(pFDO);
+ }
+
+ return Status;
+}
+
+static VOID vboxUsbDdiUnload(PDRIVER_OBJECT pDriverObject)
+{
+ RT_NOREF1(pDriverObject);
+ LogRel(("VBoxUsb::DriverUnload. Built Date (%s) Time (%s)\n", __DATE__, __TIME__));
+ VBoxDrvToolStrFree(&g_VBoxUsbGlobals.RegPath);
+
+ vboxUsbRtGlobalsTerm();
+
+ PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
+ if (pLogger)
+ {
+ RTLogDestroy(pLogger);
+ }
+ pLogger = RTLogSetDefaultInstance(NULL);
+ if (pLogger)
+ {
+ RTLogDestroy(pLogger);
+ }
+}
+
+static NTSTATUS vboxUsbDispatchCreate(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ NTSTATUS Status = STATUS_INVALID_HANDLE;
+ do
+ {
+ if (vboxUsbPnPStateGet(pDevExt) != ENMVBOXUSB_PNPSTATE_STARTED)
+ {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ break;
+ }
+
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ if (!pFObj)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ pFObj->FsContext = NULL;
+
+ if (pFObj->FileName.Length)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtCreate(pDevExt, pIrp);
+ if (!NT_SUCCESS(Status))
+ {
+ AssertFailed();
+ break;
+ }
+
+ ASMAtomicIncU32(&pDevExt->cHandles);
+ Status = STATUS_SUCCESS;
+ break;
+ } while (0);
+
+ Status = VBoxDrvToolIoComplete(pIrp, Status, 0);
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ NTSTATUS Status = STATUS_SUCCESS;
+#ifdef VBOX_STRICT
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ Assert(pFObj);
+ Assert(!pFObj->FileName.Length);
+#endif
+ Status = vboxUsbRtClose(pDevExt, pIrp);
+ if (NT_SUCCESS(Status))
+ {
+ ASMAtomicDecU32(&pDevExt->cHandles);
+ }
+ else
+ {
+ AssertFailed();
+ }
+ Status = VBoxDrvToolIoComplete(pIrp, Status, 0);
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (vboxUsbDdiStateRetainIfStarted(pDevExt))
+ return vboxUsbRtDispatch(pDevExt, pIrp);
+ return VBoxDrvToolIoComplete(pIrp, STATUS_INVALID_DEVICE_STATE, 0);
+}
+
+static NTSTATUS vboxUsbDispatchCleanup(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ RT_NOREF1(pDeviceObject);
+ return VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+}
+
+static NTSTATUS vboxUsbDevAccessDeviedDispatchStub(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ {
+ VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+ return STATUS_DELETE_PENDING;
+ }
+
+ NTSTATUS Status = VBoxDrvToolIoComplete(pIrp, STATUS_ACCESS_DENIED, 0);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchSystemControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ {
+ VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+ return STATUS_DELETE_PENDING;
+ }
+
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ NTSTATUS Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+#ifdef DEBUG_misha
+ AssertFailed();
+#endif
+ return vboxUsbDevAccessDeviedDispatchStub(pDeviceObject, pIrp);
+}
+
+static NTSTATUS vboxUsbDispatchWrite(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+#ifdef DEBUG_misha
+ AssertFailed();
+#endif
+ return vboxUsbDevAccessDeviedDispatchStub(pDeviceObject, pIrp);
+}
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
+{
+ LogRel(("VBoxUsb::DriverEntry. Built Date (%s) Time (%s)\n", __DATE__, __TIME__));
+
+ NTSTATUS Status = vboxUsbRtGlobalsInit();
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = VBoxDrvToolStrCopy(&g_VBoxUsbGlobals.RegPath, pRegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ g_VBoxUsbGlobals.pDrvObj = pDriverObject;
+
+ pDriverObject->DriverExtension->AddDevice = vboxUsbDdiAddDevice;
+
+ pDriverObject->DriverUnload = vboxUsbDdiUnload;
+
+ pDriverObject->MajorFunction[IRP_MJ_CREATE] = vboxUsbDispatchCreate;
+ pDriverObject->MajorFunction[IRP_MJ_CLOSE] = vboxUsbDispatchClose;
+ pDriverObject->MajorFunction[IRP_MJ_READ] = vboxUsbDispatchRead;
+ pDriverObject->MajorFunction[IRP_MJ_WRITE] = vboxUsbDispatchWrite;
+ pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vboxUsbDispatchDeviceControl;
+ pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = vboxUsbDispatchCleanup;
+ pDriverObject->MajorFunction[IRP_MJ_POWER] = vboxUsbDispatchPower;
+ pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vboxUsbDispatchSystemControl;
+ pDriverObject->MajorFunction[IRP_MJ_PNP] = vboxUsbDispatchPnP;
+
+ return STATUS_SUCCESS;
+ }
+ vboxUsbRtGlobalsTerm();
+ }
+
+ LogRel(("VBoxUsb::DriverEntry. failed with Status (0x%x)\n", Status));
+
+ return Status;
+}
+
+#ifdef VBOX_STRICT
+DECLHIDDEN(VOID) vboxUsbPnPStateGbgChange(ENMVBOXUSB_PNPSTATE enmOldState, ENMVBOXUSB_PNPSTATE enmNewState)
+{
+ /* *ensure the state change is valid */
+ switch (enmNewState)
+ {
+ case ENMVBOXUSB_PNPSTATE_STARTED:
+ Assert( enmOldState == ENMVBOXUSB_PNPSTATE_START_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_STOPPED
+ || enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+ break;
+ case ENMVBOXUSB_PNPSTATE_STOP_PENDING:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_STOPPED:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+ break;
+ case ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_REMOVE_PENDING:
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STARTED);
+ break;
+ case ENMVBOXUSB_PNPSTATE_REMOVED:
+ Assert( enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+}
+#endif
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h
new file mode 100644
index 00000000..7dd19d86
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.h
@@ -0,0 +1,218 @@
+/* $Id: VBoxUsbDev.h $ */
+/** @file
+ * VBoxUsbDev.h - USB device.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbCmn.h"
+#include <VBox/cdefs.h>
+#include <iprt/assert.h>
+
+typedef struct VBOXUSB_GLOBALS
+{
+ PDRIVER_OBJECT pDrvObj;
+ UNICODE_STRING RegPath;
+ VBOXUSBRT_IDC RtIdc;
+} VBOXUSB_GLOBALS, *PVBOXUSB_GLOBALS;
+
+extern VBOXUSB_GLOBALS g_VBoxUsbGlobals;
+
+/* pnp state decls */
+typedef enum
+{
+ ENMVBOXUSB_PNPSTATE_UNKNOWN = 0,
+ ENMVBOXUSB_PNPSTATE_START_PENDING,
+ ENMVBOXUSB_PNPSTATE_STARTED,
+ ENMVBOXUSB_PNPSTATE_STOP_PENDING,
+ ENMVBOXUSB_PNPSTATE_STOPPED,
+ ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED,
+ ENMVBOXUSB_PNPSTATE_REMOVE_PENDING,
+ ENMVBOXUSB_PNPSTATE_REMOVED,
+ ENMVBOXUSB_PNPSTATE_FORSEDWORD = 0x8fffffff
+} ENMVBOXUSB_PNPSTATE;
+AssertCompile(sizeof (ENMVBOXUSB_PNPSTATE) == sizeof (uint32_t));
+
+#ifdef VBOX_STRICT
+DECLHIDDEN(VOID) vboxUsbPnPStateGbgChange(ENMVBOXUSB_PNPSTATE enmOld, ENMVBOXUSB_PNPSTATE enmNew);
+# define VBOXUSB_PNP_GBG_STATE_CHANGE(_old, _new) vboxUsbPnPStateGbgChange((_old), (_new))
+#else
+# define VBOXUSB_PNP_GBG_STATE_CHANGE(_old, _new) do { } while (0)
+#endif
+
+
+typedef struct VBOXUSB_PNPSTATE
+{
+ /* Current state */
+ volatile ENMVBOXUSB_PNPSTATE Curr;
+ /* Previous state, used to restore state info on cancell stop device */
+ ENMVBOXUSB_PNPSTATE Prev;
+} VBOXUSB_PNPSTATE, *PVBOXUSB_PNPSTATE;
+
+typedef struct VBOXUSBDEV_DDISTATE
+{
+ /* Lock */
+ KSPIN_LOCK Lock;
+ VBOXDRVTOOL_REF Ref;
+ VBOXUSB_PNPSTATE PnPState;
+ VBOXUSB_PWRSTATE PwrState;
+ /* current dev caps */
+ DEVICE_CAPABILITIES DevCaps;
+} VBOXUSBDEV_DDISTATE, *PVBOXUSBDEV_DDISTATE;
+
+typedef struct VBOXUSBDEV_EXT
+{
+ PDEVICE_OBJECT pFDO;
+ PDEVICE_OBJECT pPDO;
+ PDEVICE_OBJECT pLowerDO;
+
+ VBOXUSBDEV_DDISTATE DdiState;
+
+ uint32_t cHandles;
+
+ VBOXUSB_RT Rt;
+
+} VBOXUSBDEV_EXT, *PVBOXUSBDEV_EXT;
+
+/* pnp state api */
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateGet(PVBOXUSBDEV_EXT pDevExt)
+{
+ return (ENMVBOXUSB_PNPSTATE)ASMAtomicUoReadU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr);
+}
+
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateSet(PVBOXUSBDEV_EXT pDevExt, ENMVBOXUSB_PNPSTATE enmState)
+{
+ KIRQL Irql;
+ ENMVBOXUSB_PNPSTATE enmOldState;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &Irql);
+ pDevExt->DdiState.PnPState.Prev = (ENMVBOXUSB_PNPSTATE)ASMAtomicUoReadU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr);
+ ASMAtomicWriteU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr, (uint32_t)enmState);
+ pDevExt->DdiState.PnPState.Curr = enmState;
+ enmOldState = pDevExt->DdiState.PnPState.Prev;
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, Irql);
+ VBOXUSB_PNP_GBG_STATE_CHANGE(enmOldState, enmState);
+ return enmState;
+}
+
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbPnPStateRestore(PVBOXUSBDEV_EXT pDevExt)
+{
+ ENMVBOXUSB_PNPSTATE enmNewState, enmOldState;
+ KIRQL Irql;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &Irql);
+ enmOldState = pDevExt->DdiState.PnPState.Curr;
+ enmNewState = pDevExt->DdiState.PnPState.Prev;
+ ASMAtomicWriteU32((volatile uint32_t*)&pDevExt->DdiState.PnPState.Curr, (uint32_t)pDevExt->DdiState.PnPState.Prev);
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, Irql);
+ VBOXUSB_PNP_GBG_STATE_CHANGE(enmOldState, enmNewState);
+ Assert(enmNewState == ENMVBOXUSB_PNPSTATE_STARTED);
+ Assert(enmOldState == ENMVBOXUSB_PNPSTATE_STOP_PENDING
+ || enmOldState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING);
+ return enmNewState;
+}
+
+DECLINLINE(VOID) vboxUsbPnPStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ pDevExt->DdiState.PnPState.Curr = pDevExt->DdiState.PnPState.Prev = ENMVBOXUSB_PNPSTATE_START_PENDING;
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ KeInitializeSpinLock(&pDevExt->DdiState.Lock);
+ VBoxDrvToolRefInit(&pDevExt->DdiState.Ref);
+ vboxUsbPwrStateInit(pDevExt);
+ vboxUsbPnPStateInit(pDevExt);
+}
+
+DECLINLINE(bool) vboxUsbDdiStateRetainIfStarted(PVBOXUSBDEV_EXT pDevExt)
+{
+ KIRQL oldIrql;
+ bool bRetained = true;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &oldIrql);
+ if (vboxUsbPnPStateGet(pDevExt) == ENMVBOXUSB_PNPSTATE_STARTED)
+ {
+ VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+ }
+ else
+ {
+ bRetained = false;
+ }
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, oldIrql);
+ return bRetained;
+}
+
+/* if device is removed - does nothing and returns zero,
+ * otherwise increments a ref counter and returns the current pnp state
+ * NOTE: never returns ENMVBOXUSB_PNPSTATE_REMOVED
+ * */
+DECLINLINE(ENMVBOXUSB_PNPSTATE) vboxUsbDdiStateRetainIfNotRemoved(PVBOXUSBDEV_EXT pDevExt)
+{
+ KIRQL oldIrql;
+ ENMVBOXUSB_PNPSTATE enmState;
+ KeAcquireSpinLock(&pDevExt->DdiState.Lock, &oldIrql);
+ enmState = vboxUsbPnPStateGet(pDevExt);
+ if (enmState != ENMVBOXUSB_PNPSTATE_REMOVED)
+ {
+ VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+ }
+ KeReleaseSpinLock(&pDevExt->DdiState.Lock, oldIrql);
+ return enmState != ENMVBOXUSB_PNPSTATE_REMOVED ? enmState : (ENMVBOXUSB_PNPSTATE)0;
+}
+
+DECLINLINE(uint32_t) vboxUsbDdiStateRetain(PVBOXUSBDEV_EXT pDevExt)
+{
+ return VBoxDrvToolRefRetain(&pDevExt->DdiState.Ref);
+}
+
+DECLINLINE(uint32_t) vboxUsbDdiStateRelease(PVBOXUSBDEV_EXT pDevExt)
+{
+ return VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateReleaseAndWaitCompleted(PVBOXUSBDEV_EXT pDevExt)
+{
+ VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+ VBoxDrvToolRefWaitEqual(&pDevExt->DdiState.Ref, 1);
+}
+
+DECLINLINE(VOID) vboxUsbDdiStateReleaseAndWaitRemoved(PVBOXUSBDEV_EXT pDevExt)
+{
+ VBoxDrvToolRefRelease(&pDevExt->DdiState.Ref);
+ VBoxDrvToolRefWaitEqual(&pDevExt->DdiState.Ref, 0);
+}
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbDev_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc
new file mode 100644
index 00000000..83ccd753
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbDev.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxUsbDev.rc $ */
+/** @file
+ * VBoxUSB - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox USB Driver\0"
+ VALUE "InternalName", "VBoxUSB\0"
+ VALUE "OriginalFilename", "VBoxUSB.sys\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp
new file mode 100644
index 00000000..18a068c8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.cpp
@@ -0,0 +1,274 @@
+/* $Id: VBoxUsbPnP.cpp $ */
+/** @file
+ * USB PnP Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxUsbCmn.h"
+
+static NTSTATUS vboxUsbPnPMnStartDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbRtStart(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STARTED);
+ }
+ }
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STOP_PENDING);
+
+ vboxUsbDdiStateReleaseAndWaitCompleted(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ return IoCallDriver(pDevExt->pLowerDO, pIrp);
+}
+
+static NTSTATUS vboxUsbPnPMnStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_STOPPED);
+
+ vboxUsbRtClear(pDevExt);
+
+ NTSTATUS Status = VBoxUsbToolDevUnconfigure(pDevExt->pLowerDO);
+ Assert(NT_SUCCESS(Status));
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnCancelStopDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ if (NT_SUCCESS(Status) && enmState == ENMVBOXUSB_PNPSTATE_STOP_PENDING)
+ {
+ vboxUsbPnPStateRestore(pDevExt);
+ }
+
+ Status = STATUS_SUCCESS;
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_REMOVE_PENDING);
+
+ vboxUsbDdiStateReleaseAndWaitCompleted(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ return IoCallDriver(pDevExt->pLowerDO, pIrp);
+}
+
+static NTSTATUS vboxUsbPnPRmDev(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = vboxUsbRtRm(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+ if (enmState != ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED)
+ {
+ Status = vboxUsbPnPRmDev(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+ }
+
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_REMOVED);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ vboxUsbDdiStateReleaseAndWaitRemoved(pDevExt);
+
+ vboxUsbRtClear(pDevExt);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ IoDetachDevice(pDevExt->pLowerDO);
+ IoDeleteDevice(pDevExt->pFDO);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnCancelRemoveDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbPnPStateGet(pDevExt);
+ NTSTATUS Status = STATUS_SUCCESS;
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+
+ Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+
+ if (NT_SUCCESS(Status) &&
+ enmState == ENMVBOXUSB_PNPSTATE_REMOVE_PENDING)
+ {
+ vboxUsbPnPStateRestore(pDevExt);
+ }
+
+ Status = STATUS_SUCCESS;
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnSurpriseRemoval(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ vboxUsbPnPStateSet(pDevExt, ENMVBOXUSB_PNPSTATE_SURPRISE_REMOVED);
+
+ NTSTATUS Status = vboxUsbPnPRmDev(pDevExt);
+ Assert(Status == STATUS_SUCCESS);
+
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrp->IoStatus.Information = 0;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnQueryCapabilities(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PDEVICE_CAPABILITIES pDevCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
+
+ if (pDevCaps->Version < 1 || pDevCaps->Size < sizeof (*pDevCaps))
+ {
+ AssertFailed();
+ /** @todo return more appropriate status ?? */
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pDevCaps->SurpriseRemovalOK = TRUE;
+ pIrp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ NTSTATUS Status = VBoxDrvToolIoPostSync(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ {
+ pDevCaps->SurpriseRemovalOK = 1;
+ pDevExt->DdiState.DevCaps = *pDevCaps;
+ }
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPnPMnDefault(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ NTSTATUS Status;
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ if (!vboxUsbDdiStateRetainIfNotRemoved(pDevExt))
+ return VBoxDrvToolIoComplete(pIrp, STATUS_DELETE_PENDING, 0);
+
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_START_DEVICE:
+ return vboxUsbPnPMnStartDevice(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ return vboxUsbPnPMnQueryStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_STOP_DEVICE:
+ return vboxUsbPnPMnStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ return vboxUsbPnPMnCancelStopDevice(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ return vboxUsbPnPMnQueryRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_REMOVE_DEVICE:
+ return vboxUsbPnPMnRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ return vboxUsbPnPMnCancelRemoveDevice(pDevExt, pIrp);
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ return vboxUsbPnPMnSurpriseRemoval(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ return vboxUsbPnPMnQueryCapabilities(pDevExt, pIrp);
+
+ default:
+ return vboxUsbPnPMnDefault(pDevExt, pIrp);
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h
new file mode 100644
index 00000000..c1799080
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPnP.h
@@ -0,0 +1,46 @@
+/* $Id: VBoxUsbPnP.h $ */
+/** @file
+ * USB PnP Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+#include "VBoxUsbCmn.h"
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPnP_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp
new file mode 100644
index 00000000..35b57159
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.cpp
@@ -0,0 +1,427 @@
+/* $Id: VBoxUsbPwr.cpp $ */
+/** @file
+ * USB Power state Handling
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "VBoxUsbCmn.h"
+
+#include <iprt/assert.h>
+
+DECLHIDDEN(VOID) vboxUsbPwrStateInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ POWER_STATE PowerState;
+ PowerState.SystemState = PowerSystemWorking;
+ PowerState.DeviceState = PowerDeviceD0;
+ PoSetPowerState(pDevExt->pFDO, DevicePowerState, PowerState);
+ pDevExt->DdiState.PwrState.PowerState = PowerState;
+ pDevExt->DdiState.PwrState.PowerDownLevel = PowerDeviceUnspecified;
+}
+
+static NTSTATUS vboxUsbPwrMnDefault(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ NTSTATUS Status;
+ PoStartNextPowerIrp(pIrp);
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrMnPowerSequence(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+}
+
+typedef struct VBOXUSB_PWRDEV_CTX
+{
+ PVBOXUSBDEV_EXT pDevExt;
+ PIRP pIrp;
+} VBOXUSB_PWRDEV_CTX, *PVBOXUSB_PWRDEV_CTX;
+
+static VOID vboxUsbPwrIoDeviceCompletion(IN PDEVICE_OBJECT pDeviceObject,
+ IN UCHAR MinorFunction,
+ IN POWER_STATE PowerState,
+ IN PVOID pvContext,
+ IN PIO_STATUS_BLOCK pIoStatus)
+{
+ RT_NOREF3(pDeviceObject, MinorFunction, PowerState);
+ PVBOXUSB_PWRDEV_CTX pDevCtx = (PVBOXUSB_PWRDEV_CTX)pvContext;
+ PVBOXUSBDEV_EXT pDevExt = pDevCtx->pDevExt;
+ PIRP pIrp = pDevCtx->pIrp;
+ pIrp->IoStatus.Status = pIoStatus->Status;
+ pIrp->IoStatus.Information = 0;
+
+ PoStartNextPowerIrp(pIrp);
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ vboxUsbDdiStateRelease(pDevExt);
+
+ vboxUsbMemFree(pDevCtx);
+}
+
+static NTSTATUS vboxUsbPwrIoRequestDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ POWER_STATE PwrState;
+ PwrState.SystemState = pSl->Parameters.Power.State.SystemState;
+ PwrState.DeviceState = pDevExt->DdiState.DevCaps.DeviceState[PwrState.SystemState];
+
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PVBOXUSB_PWRDEV_CTX pDevCtx = (PVBOXUSB_PWRDEV_CTX)vboxUsbMemAlloc(sizeof (*pDevCtx));
+ Assert(pDevCtx);
+ if (pDevCtx)
+ {
+ pDevCtx->pDevExt = pDevExt;
+ pDevCtx->pIrp = pIrp;
+
+ Status = PoRequestPowerIrp(pDevExt->pPDO, pSl->MinorFunction, PwrState,
+ vboxUsbPwrIoDeviceCompletion, pDevCtx, NULL);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ vboxUsbMemFree(pDevCtx);
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ vboxUsbDdiStateRelease(pDevExt);
+
+ /* the "real" Status is stored in pIrp->IoStatus.Status,
+ * return success here to complete the Io */
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostSysCompletion(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF1(pDevObj);
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pvContext;
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ pDevExt->DdiState.PwrState.PowerState.SystemState = pSl->Parameters.Power.State.SystemState;
+ break;
+
+ default:
+ break;
+ }
+
+ return vboxUsbPwrIoRequestDev(pDevExt, pIrp);
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ IoMarkIrpPending(pIrp);
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostSysCompletion, pDevExt, TRUE, TRUE, TRUE);
+ NTSTATUS Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status)); NOREF(Status);
+ return STATUS_PENDING;
+}
+
+static NTSTATUS vboxUsbPwrQueryPowerSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ /*PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ SYSTEM_POWER_STATE enmSysPState = pSl->Parameters.Power.State.SystemState;*/
+
+ return vboxUsbPwrIoPostSys(pDevExt, pIrp);
+}
+
+static NTSTATUS vboxUsbPwrIoPostDevCompletion(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pvContext)
+{
+ RT_NOREF1(pDevObj);
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pvContext;
+
+ if (pIrp->PendingReturned)
+ IoMarkIrpPending(pIrp);
+
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ pDevExt->DdiState.PwrState.PowerState.DeviceState = pSl->Parameters.Power.State.DeviceState;
+ PoSetPowerState(pDevExt->pFDO, DevicePowerState, pSl->Parameters.Power.State);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PoStartNextPowerIrp(pIrp);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbPwrIoPostDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ IoMarkIrpPending(pIrp);
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostDevCompletion, pDevExt, TRUE, TRUE, TRUE);
+ NTSTATUS Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ Assert(NT_SUCCESS(Status)); RT_NOREF_PV(Status);
+ return STATUS_PENDING;
+}
+
+typedef struct VBOXUSB_IOASYNC_CTX
+{
+ PIO_WORKITEM pWrkItem;
+ PIRP pIrp;
+} VBOXUSB_IOASYNC_CTX, *PVBOXUSB_IOASYNC_CTX;
+
+static VOID vboxUsbPwrIoWaitCompletionAndPostAsyncWorker(IN PDEVICE_OBJECT pDeviceObject, IN PVOID pvContext)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ PVBOXUSB_IOASYNC_CTX pCtx = (PVBOXUSB_IOASYNC_CTX)pvContext;
+ PIRP pIrp = pCtx->pIrp;
+
+ vboxUsbPwrIoPostDev(pDevExt, pIrp);
+
+ IoFreeWorkItem(pCtx->pWrkItem);
+ vboxUsbMemFree(pCtx);
+}
+
+static NTSTATUS vboxUsbPwrIoWaitCompletionAndPostAsync(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PVBOXUSB_IOASYNC_CTX pCtx = (PVBOXUSB_IOASYNC_CTX)vboxUsbMemAlloc(sizeof (*pCtx));
+ Assert(pCtx);
+ if (pCtx)
+ {
+ PIO_WORKITEM pWrkItem = IoAllocateWorkItem(pDevExt->pFDO);
+ Assert(pWrkItem);
+ if (pWrkItem)
+ {
+ pCtx->pWrkItem = pWrkItem;
+ pCtx->pIrp = pIrp;
+ IoMarkIrpPending(pIrp);
+ IoQueueWorkItem(pWrkItem, vboxUsbPwrIoWaitCompletionAndPostAsyncWorker, DelayedWorkQueue, pCtx);
+ return STATUS_PENDING;
+ }
+ vboxUsbMemFree(pCtx);
+ }
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrQueryPowerDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ DEVICE_POWER_STATE enmDevPState = pSl->Parameters.Power.State.DeviceState;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (enmDevPState >= pDevExt->DdiState.PwrState.PowerState.DeviceState)
+ {
+ Status = vboxUsbPwrIoWaitCompletionAndPostAsync(pDevExt, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return Status;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+
+ PoStartNextPowerIrp(pIrp);
+
+ if (NT_SUCCESS(Status))
+ {
+ IoSkipCurrentIrpStackLocation(pIrp);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ }
+ else
+ {
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbPwrMnQueryPower(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.Power.Type)
+ {
+ case SystemPowerState:
+ return vboxUsbPwrQueryPowerSys(pDevExt, pIrp);
+
+ case DevicePowerState:
+ return vboxUsbPwrQueryPowerDev(pDevExt, pIrp);
+
+ default:
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+
+ }
+}
+
+static NTSTATUS vboxUsbPwrSetPowerSys(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ /*PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ SYSTEM_POWER_STATE enmSysPState = pSl->Parameters.Power.State.SystemState;*/
+
+ return vboxUsbPwrIoPostSys(pDevExt, pIrp);
+}
+
+static NTSTATUS vboxUsbPwrSetPowerDev(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ DEVICE_POWER_STATE enmDevPState = pSl->Parameters.Power.State.DeviceState;
+ DEVICE_POWER_STATE enmCurDevPState = pDevExt->DdiState.PwrState.PowerState.DeviceState;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (enmDevPState > enmCurDevPState && enmCurDevPState == PowerDeviceD0)
+ {
+ Status = vboxUsbPwrIoWaitCompletionAndPostAsync(pDevExt, pIrp);
+ Assert(NT_SUCCESS(Status));
+ if (NT_SUCCESS(Status))
+ return Status;
+ }
+
+ PoStartNextPowerIrp(pIrp);
+
+ if (NT_SUCCESS(Status))
+ {
+ IoCopyCurrentIrpStackLocationToNext(pIrp);
+ IoSetCompletionRoutine(pIrp, vboxUsbPwrIoPostDevCompletion, pDevExt, TRUE, TRUE, TRUE);
+ Status = PoCallDriver(pDevExt->pLowerDO, pIrp);
+ }
+ else
+ {
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ vboxUsbDdiStateRelease(pDevExt);
+ }
+
+ return Status;
+}
+
+
+static NTSTATUS vboxUsbPwrMnSetPower(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.Power.Type)
+ {
+ case SystemPowerState:
+ return vboxUsbPwrSetPowerSys(pDevExt, pIrp);
+
+ case DevicePowerState:
+ return vboxUsbPwrSetPowerDev(pDevExt, pIrp);
+
+ default:
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+ }
+}
+
+static NTSTATUS vboxUsbPwrMnWaitWake(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+}
+
+
+static NTSTATUS vboxUsbPwrDispatch(IN PVBOXUSBDEV_EXT pDevExt, IN PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+
+ switch (pSl->MinorFunction)
+ {
+ case IRP_MN_POWER_SEQUENCE:
+ return vboxUsbPwrMnPowerSequence(pDevExt, pIrp);
+
+ case IRP_MN_QUERY_POWER:
+ return vboxUsbPwrMnQueryPower(pDevExt, pIrp);
+
+ case IRP_MN_SET_POWER:
+ return vboxUsbPwrMnSetPower(pDevExt, pIrp);
+
+ case IRP_MN_WAIT_WAKE:
+ return vboxUsbPwrMnWaitWake(pDevExt, pIrp);
+
+ default:
+// AssertFailed();
+ return vboxUsbPwrMnDefault(pDevExt, pIrp);
+ }
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPower(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
+{
+ PVBOXUSBDEV_EXT pDevExt = (PVBOXUSBDEV_EXT)pDeviceObject->DeviceExtension;
+ ENMVBOXUSB_PNPSTATE enmState = vboxUsbDdiStateRetainIfNotRemoved(pDevExt);
+ switch (enmState)
+ {
+ case ENMVBOXUSB_PNPSTATE_REMOVED:
+ PoStartNextPowerIrp(pIrp);
+
+ pIrp->IoStatus.Status = STATUS_DELETE_PENDING;
+ pIrp->IoStatus.Information = 0;
+
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return STATUS_DELETE_PENDING;
+
+ case ENMVBOXUSB_PNPSTATE_START_PENDING:
+ PoStartNextPowerIrp(pIrp);
+ IoSkipCurrentIrpStackLocation(pIrp);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ return PoCallDriver(pDevExt->pLowerDO, pIrp);
+
+ default:
+ return vboxUsbPwrDispatch(pDevExt, pIrp);
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h
new file mode 100644
index 00000000..792187ed
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbPwr.h
@@ -0,0 +1,51 @@
+/* $Id: VBoxUsbPwr.h $ */
+/** @file
+ * USB Power state Handling
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+typedef struct VBOXUSB_PWRSTATE
+{
+ POWER_STATE PowerState;
+ ULONG PowerDownLevel;
+} VBOXUSB_PWRSTATE, *PVBOXUSB_PWRSTATE;
+
+DECLHIDDEN(VOID) vboxUsbPwrStateInit(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbDispatchPower(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbPwr_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp
new file mode 100644
index 00000000..f4e82a33
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.cpp
@@ -0,0 +1,1593 @@
+/* $Id: VBoxUsbRt.cpp $ */
+/** @file
+ * VBox USB R0 runtime
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbCmn.h"
+#include "../cmn/VBoxUsbIdc.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <VBox/usblib-win.h>
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define _USBD_ /** @todo r=bird: What is this?? */
+
+#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008
+
+#define VBOXUSB_MAGIC 0xABCF1423
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXUSB_URB_CONTEXT
+{
+ PURB pUrb;
+ PMDL pMdlBuf;
+ PVBOXUSBDEV_EXT pDevExt;
+ PVOID pOut;
+ ULONG ulTransferType;
+ ULONG ulMagic;
+} VBOXUSB_URB_CONTEXT, * PVBOXUSB_URB_CONTEXT;
+
+typedef struct VBOXUSB_SETUP
+{
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} VBOXUSB_SETUP, *PVBOXUSB_SETUP;
+
+
+
+static bool vboxUsbRtCtxSetOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ bool fRc = ASMAtomicCmpXchgPtr(&pDevExt->Rt.pOwner, pFObj, NULL);
+ if (fRc)
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) acquired\n", pFObj));
+ else
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) FAILED!!\n", pFObj));
+ return fRc;
+}
+
+static bool vboxUsbRtCtxReleaseOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ bool fRc = ASMAtomicCmpXchgPtr(&pDevExt->Rt.pOwner, NULL, pFObj);
+ if (fRc)
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) released\n", pFObj));
+ else
+ LogFunc(("pDevExt (0x%x) Owner(0x%x) release: is NOT an owner\n", pFObj));
+ return fRc;
+}
+
+static bool vboxUsbRtCtxIsOwner(PVBOXUSBDEV_EXT pDevExt, PFILE_OBJECT pFObj)
+{
+ PFILE_OBJECT pOwner = (PFILE_OBJECT)ASMAtomicReadPtr((void *volatile *)(&pDevExt->Rt.pOwner));
+ return pOwner == pFObj;
+}
+
+static NTSTATUS vboxUsbRtIdcSubmit(ULONG uCtl, void *pvBuffer)
+{
+ /* we just reuse the standard usb tooling for simplicity here */
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(g_VBoxUsbGlobals.RtIdc.pDevice, uCtl, pvBuffer, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtIdcInit()
+{
+ UNICODE_STRING UniName;
+ RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS Status = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbGlobals.RtIdc.pFile, &g_VBoxUsbGlobals.RtIdc.pDevice);
+ if (NT_SUCCESS(Status))
+ {
+ VBOXUSBIDC_VERSION Version;
+ vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION, &Version);
+ if (NT_SUCCESS(Status))
+ {
+ if ( Version.u32Major == VBOXUSBIDC_VERSION_MAJOR
+#if VBOXUSBIDC_VERSION_MINOR != 0
+ && Version.u32Minor >= VBOXUSBIDC_VERSION_MINOR
+#endif
+ )
+ return STATUS_SUCCESS;
+ AssertFailed();
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ /* this will as well dereference the dev obj */
+ ObDereferenceObject(g_VBoxUsbGlobals.RtIdc.pFile);
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ memset(&g_VBoxUsbGlobals.RtIdc, 0, sizeof (g_VBoxUsbGlobals.RtIdc));
+ return Status;
+}
+
+static VOID vboxUsbRtIdcTerm()
+{
+ Assert(g_VBoxUsbGlobals.RtIdc.pFile);
+ Assert(g_VBoxUsbGlobals.RtIdc.pDevice);
+ ObDereferenceObject(g_VBoxUsbGlobals.RtIdc.pFile);
+ memset(&g_VBoxUsbGlobals.RtIdc, 0, sizeof (g_VBoxUsbGlobals.RtIdc));
+}
+
+static NTSTATUS vboxUsbRtIdcReportDevStart(PDEVICE_OBJECT pPDO, HVBOXUSBIDCDEV *phDev)
+{
+ VBOXUSBIDC_PROXY_STARTUP Start;
+ Start.u.pPDO = pPDO;
+
+ *phDev = NULL;
+
+ NTSTATUS Status = vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP, &Start);
+ Assert(Status == STATUS_SUCCESS);
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ *phDev = Start.u.hDev;
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtIdcReportDevStop(HVBOXUSBIDCDEV hDev)
+{
+ VBOXUSBIDC_PROXY_TEARDOWN Stop;
+ Stop.hDev = hDev;
+
+ NTSTATUS Status = vboxUsbRtIdcSubmit(VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN, &Stop);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtGlobalsInit()
+{
+ return vboxUsbRtIdcInit();
+}
+
+DECLHIDDEN(VOID) vboxUsbRtGlobalsTerm()
+{
+ vboxUsbRtIdcTerm();
+}
+
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtInit(PVBOXUSBDEV_EXT pDevExt)
+{
+ RtlZeroMemory(&pDevExt->Rt, sizeof (pDevExt->Rt));
+ NTSTATUS Status = IoRegisterDeviceInterface(pDevExt->pPDO, &GUID_CLASS_VBOXUSB,
+ NULL, /* IN PUNICODE_STRING ReferenceString OPTIONAL */
+ &pDevExt->Rt.IfName);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbRtIdcReportDevStart(pDevExt->pPDO, &pDevExt->Rt.hMonDev);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ Assert(pDevExt->Rt.hMonDev);
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS tmpStatus = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, FALSE);
+ Assert(tmpStatus == STATUS_SUCCESS);
+ if (NT_SUCCESS(tmpStatus))
+ {
+ RtlFreeUnicodeString(&pDevExt->Rt.IfName);
+ }
+ }
+ return Status;
+}
+
+/**
+ * Free cached USB device/configuration descriptors
+ *
+ * @param pDevExt USB DevExt pointer
+ */
+static void vboxUsbRtFreeCachedDescriptors(PVBOXUSBDEV_EXT pDevExt)
+{
+ if (pDevExt->Rt.devdescr)
+ {
+ vboxUsbMemFree(pDevExt->Rt.devdescr);
+ pDevExt->Rt.devdescr = NULL;
+ }
+ for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
+ {
+ if (pDevExt->Rt.cfgdescr[i])
+ {
+ vboxUsbMemFree(pDevExt->Rt.cfgdescr[i]);
+ pDevExt->Rt.cfgdescr[i] = NULL;
+ }
+ }
+}
+
+/**
+ * Free per-device interface info
+ *
+ * @param pDevExt USB DevExt pointer
+ * @param fAbortPipes If true, also abort any open pipes
+ */
+static void vboxUsbRtFreeInterfaces(PVBOXUSBDEV_EXT pDevExt, BOOLEAN fAbortPipes)
+{
+ unsigned i;
+ unsigned j;
+
+ /*
+ * Free old interface info
+ */
+ if (pDevExt->Rt.pVBIfaceInfo)
+ {
+ for (i=0;i<pDevExt->Rt.uNumInterfaces;i++)
+ {
+ if (pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo)
+ {
+ if (fAbortPipes)
+ {
+ for (j=0; j<pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ Log(("Aborting Pipe %d handle %x address %x\n", j,
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle,
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].EndpointAddress));
+ VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle, FALSE);
+ }
+ }
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo);
+ }
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo = NULL;
+ if (pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo);
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = NULL;
+ }
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo);
+ pDevExt->Rt.pVBIfaceInfo = NULL;
+ }
+}
+
+DECLHIDDEN(VOID) vboxUsbRtClear(PVBOXUSBDEV_EXT pDevExt)
+{
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ vboxUsbRtFreeInterfaces(pDevExt, FALSE);
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtRm(PVBOXUSBDEV_EXT pDevExt)
+{
+ if (!pDevExt->Rt.IfName.Buffer)
+ return STATUS_SUCCESS;
+
+ NTSTATUS Status = vboxUsbRtIdcReportDevStop(pDevExt->Rt.hMonDev);
+ Assert(Status == STATUS_SUCCESS);
+ Status = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, FALSE);
+ Assert(Status == STATUS_SUCCESS);
+ if (NT_SUCCESS(Status))
+ {
+ RtlFreeUnicodeString(&pDevExt->Rt.IfName);
+ pDevExt->Rt.IfName.Buffer = NULL;
+ }
+ return Status;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtStart(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = IoSetDeviceInterfaceState(&pDevExt->Rt.IfName, TRUE);
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtCacheDescriptors(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+// uint32_t uTotalLength;
+// unsigned i;
+
+ /* Read device descriptor */
+ Assert(!pDevExt->Rt.devdescr);
+ pDevExt->Rt.devdescr = (PUSB_DEVICE_DESCRIPTOR)vboxUsbMemAlloc(sizeof (USB_DEVICE_DESCRIPTOR));
+ if (pDevExt->Rt.devdescr)
+ {
+ memset(pDevExt->Rt.devdescr, 0, sizeof (USB_DEVICE_DESCRIPTOR));
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.devdescr, sizeof (USB_DEVICE_DESCRIPTOR), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ Assert(pDevExt->Rt.devdescr->bNumConfigurations > 0);
+ PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)vboxUsbMemAlloc(sizeof (USB_CONFIGURATION_DESCRIPTOR));
+ Assert(pDr);
+ if (pDr)
+ {
+ UCHAR i = 0;
+ for (; i < pDevExt->Rt.devdescr->bNumConfigurations; ++i)
+ {
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof (USB_CONFIGURATION_DESCRIPTOR), USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+
+ USHORT uTotalLength = pDr->wTotalLength;
+ pDevExt->Rt.cfgdescr[i] = (PUSB_CONFIGURATION_DESCRIPTOR)vboxUsbMemAlloc(uTotalLength);
+ if (!pDevExt->Rt.cfgdescr[i])
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.cfgdescr[i], uTotalLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+ }
+
+ vboxUsbMemFree(pDr);
+
+ if (NT_SUCCESS(Status))
+ return Status;
+
+ /* recources will be freed in vboxUsbRtFreeCachedDescriptors below */
+ }
+ }
+
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ }
+
+ /* shoud be only on fail here */
+ Assert(!NT_SUCCESS(Status));
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchClaimDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLAIMDEV pDev = (PUSBSUP_CLAIMDEV)pIrp->AssociatedIrp.SystemBuffer;
+ ULONG cbOut = 0;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if ( !pDev
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pDev)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != sizeof (*pDev))
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxSetOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ pDev->fClaimed = false;
+ cbOut = sizeof (*pDev);
+ break;
+ }
+
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ Status = vboxUsbRtCacheDescriptors(pDevExt);
+ if (NT_SUCCESS(Status))
+ {
+ pDev->fClaimed = true;
+ cbOut = sizeof (*pDev);
+ }
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, cbOut);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchReleaseDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ NTSTATUS Status= STATUS_SUCCESS;
+
+ if (vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ vboxUsbRtFreeCachedDescriptors(pDevExt);
+ bool fRc = vboxUsbRtCtxReleaseOwner(pDevExt, pFObj);
+ Assert(fRc); NOREF(fRc);
+ }
+ else
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtGetDeviceDescription(PVBOXUSBDEV_EXT pDevExt)
+{
+ NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
+ PUSB_DEVICE_DESCRIPTOR pDr = (PUSB_DEVICE_DESCRIPTOR)vboxUsbMemAllocZ(sizeof (USB_DEVICE_DESCRIPTOR));
+ if (pDr)
+ {
+ Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof(*pDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ pDevExt->Rt.idVendor = pDr->idVendor;
+ pDevExt->Rt.idProduct = pDr->idProduct;
+ pDevExt->Rt.bcdDevice = pDr->bcdDevice;
+ pDevExt->Rt.szSerial[0] = 0;
+
+ if (pDr->iSerialNumber
+#ifdef DEBUG
+ || pDr->iProduct || pDr->iManufacturer
+#endif
+ )
+ {
+ int langId;
+ Status = VBoxUsbToolGetLangID(pDevExt->pLowerDO, &langId, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status))
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDevExt->pLowerDO, pDevExt->Rt.szSerial, sizeof (pDevExt->Rt.szSerial),
+ pDr->iSerialNumber, langId, RT_INDEFINITE_WAIT);
+ }
+ else
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ vboxUsbMemFree(pDr);
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchGetDevice(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PUSBSUP_GETDEV pDev = (PUSBSUP_GETDEV)pIrp->AssociatedIrp.SystemBuffer;
+ ULONG cbOut = 0;
+
+ /* don't check for owner since this request is allowed for non-owners as well */
+ NTSTATUS Status;
+ if ( pDev
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == sizeof(*pDev)
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == sizeof(*pDev))
+ {
+ /* Even if we don't return it, we need to query the HS flag for later use. */
+ Status = VBoxUsbToolGetDeviceSpeed(pDevExt->pLowerDO, &pDevExt->Rt.fIsHighSpeed);
+ if (NT_SUCCESS(Status))
+ {
+ pDev->hDevice = pDevExt->Rt.hMonDev;
+ cbOut = sizeof (*pDev);
+ }
+ }
+ else
+ Status = STATUS_INVALID_PARAMETER;
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, cbOut);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbReset(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ NTSTATUS rcNt;
+ if (pFObj)
+ {
+ if (vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ if ( pIrp->AssociatedIrp.SystemBuffer == NULL
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == 0
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == 0)
+ {
+ rcNt = VBoxUsbToolIoInternalCtlSendSync(pDevExt->pLowerDO, IOCTL_INTERNAL_USB_RESET_PORT, NULL, NULL);
+ Assert(NT_SUCCESS(rcNt));
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_ACCESS_DENIED;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rcNt = STATUS_INVALID_PARAMETER;
+ }
+
+ Assert(rcNt != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, rcNt, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return rcNt;
+}
+
+static PUSB_CONFIGURATION_DESCRIPTOR vboxUsbRtFindConfigDesc(PVBOXUSBDEV_EXT pDevExt, uint8_t uConfiguration)
+{
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+
+ for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
+ {
+ if (pDevExt->Rt.cfgdescr[i])
+ {
+ if (pDevExt->Rt.cfgdescr[i]->bConfigurationValue == uConfiguration)
+ {
+ pCfgDr = pDevExt->Rt.cfgdescr[i];
+ break;
+ }
+ }
+ }
+
+ return pCfgDr;
+}
+
+static NTSTATUS vboxUsbRtSetConfig(PVBOXUSBDEV_EXT pDevExt, uint8_t uConfiguration)
+{
+ PURB pUrb = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+ uint32_t i;
+
+ if (!uConfiguration)
+ {
+ pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_SELECT_CONFIGURATION, sizeof (struct _URB_SELECT_CONFIGURATION));
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAlloc failed\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ vboxUsbRtFreeInterfaces(pDevExt, TRUE);
+
+ pUrb->UrbSelectConfiguration.ConfigurationDescriptor = NULL;
+
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ pDevExt->Rt.hConfiguration = pUrb->UrbSelectConfiguration.ConfigurationHandle;
+ pDevExt->Rt.uConfigValue = uConfiguration;
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+
+ VBoxUsbToolUrbFree(pUrb);
+
+ return Status;
+ }
+
+/** @todo r=bird: Need to write a script for fixing these kind of clueless use
+ * of AssertMsgFailed (into AssertMsgReturn). The __FUNCTION__ is just
+ * the topping it off - the assertion message includes function, file and
+ * line number. Duh! */
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = vboxUsbRtFindConfigDesc(pDevExt, uConfiguration);
+ if (!pCfgDr)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUSBFindConfigDesc did not find cfg (%d)\n", uConfiguration));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ PUSBD_INTERFACE_LIST_ENTRY pIfLe = (PUSBD_INTERFACE_LIST_ENTRY)vboxUsbMemAllocZ((pCfgDr->bNumInterfaces + 1) * sizeof(USBD_INTERFACE_LIST_ENTRY));
+ if (!pIfLe)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAllocZ for pIfLe failed\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ for (i = 0; i < pCfgDr->bNumInterfaces; i++)
+ {
+ pIfLe[i].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, i, 0, -1, -1, -1);
+ if (!pIfLe[i].InterfaceDescriptor)
+ {
+ AssertMsgFailed((__FUNCTION__": interface %d not found\n", i));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ pIfLe[pCfgDr->bNumInterfaces].InterfaceDescriptor = NULL;
+
+ if (NT_SUCCESS(Status))
+ {
+ pUrb = USBD_CreateConfigurationRequestEx(pCfgDr, pIfLe);
+ if (pUrb)
+ {
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ vboxUsbRtFreeInterfaces(pDevExt, FALSE);
+
+ pDevExt->Rt.hConfiguration = pUrb->UrbSelectConfiguration.ConfigurationHandle;
+ pDevExt->Rt.uConfigValue = uConfiguration;
+ pDevExt->Rt.uNumInterfaces = pCfgDr->bNumInterfaces;
+
+ pDevExt->Rt.pVBIfaceInfo = (VBOXUSB_IFACE_INFO*)vboxUsbMemAllocZ(pDevExt->Rt.uNumInterfaces * sizeof (VBOXUSB_IFACE_INFO));
+ if (pDevExt->Rt.pVBIfaceInfo)
+ {
+ Assert(NT_SUCCESS(Status));
+ for (i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ size_t uTotalIfaceInfoLength = GET_USBD_INTERFACE_SIZE(pIfLe[i].Interface->NumberOfPipes);
+ pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
+ if (!pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if (pIfLe[i].Interface->NumberOfPipes > 0)
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = (VBOXUSB_PIPE_INFO *)vboxUsbMemAlloc(pIfLe[i].Interface->NumberOfPipes * sizeof(VBOXUSB_PIPE_INFO));
+ if (!pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+ }
+ else
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = NULL;
+ }
+
+ RtlCopyMemory(pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo, pIfLe[i].Interface, uTotalIfaceInfoLength);
+
+ for (ULONG j = 0; j < pIfLe[i].Interface->NumberOfPipes; j++)
+ {
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].EndpointAddress = pIfLe[i].Interface->Pipes[j].EndpointAddress;
+ pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].NextScheduledFrame = 0;
+ }
+ }
+
+// if (NT_SUCCESS(Status))
+// {
+//
+// }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAllocZ failed\n"));
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+ ExFreePool(pUrb);
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": USBD_CreateConfigurationRequestEx failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ vboxUsbMemFree(pIfLe);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbSetConfig(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_SET_CONFIG pCfg = (PUSBSUP_SET_CONFIG)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCfg
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCfg)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtSetConfig(pDevExt, pCfg->bConfigurationValue);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtSetInterface(PVBOXUSBDEV_EXT pDevExt, uint32_t InterfaceNumber, int AlternateSetting)
+{
+ AssertMsgReturn(pDevExt->Rt.uConfigValue, ("Can't select an interface without an active configuration\n"),
+ STATUS_INVALID_PARAMETER);
+ AssertMsgReturn(InterfaceNumber < pDevExt->Rt.uNumInterfaces, ("InterfaceNumber %d too high!!\n", InterfaceNumber),
+ STATUS_INVALID_PARAMETER);
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = vboxUsbRtFindConfigDesc(pDevExt, pDevExt->Rt.uConfigValue);
+ AssertMsgReturn(pCfgDr, ("configuration %d not found!!\n", pDevExt->Rt.uConfigValue),
+ STATUS_INVALID_PARAMETER);
+ PUSB_INTERFACE_DESCRIPTOR pIfDr = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, InterfaceNumber, AlternateSetting, -1, -1, -1);
+ AssertMsgReturn(pIfDr, ("invalid interface %d or alternate setting %d\n", InterfaceNumber, AlternateSetting),
+ STATUS_UNSUCCESSFUL);
+
+ USHORT uUrbSize = GET_SELECT_INTERFACE_REQUEST_SIZE(pIfDr->bNumEndpoints);
+ ULONG uTotalIfaceInfoLength = GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints);
+ NTSTATUS Status = STATUS_SUCCESS;
+ PURB pUrb;
+ PUSBD_INTERFACE_INFORMATION pNewIFInfo = NULL;
+ VBOXUSB_PIPE_INFO *pNewPipeInfo = NULL;
+
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo)
+ {
+ /* Clear pipes associated with the interface, else Windows may hang. */
+ for (ULONG i = 0; i < pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo->NumberOfPipes; i++)
+ VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo->Pipes[i].PipeHandle, FALSE);
+ }
+
+ do {
+ /* First allocate all the structures we'll need. */
+ pUrb = VBoxUsbToolUrbAllocZ(0, uUrbSize);
+ if (!pUrb)
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbAllocZ failed\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ pNewIFInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
+ if (!pNewIFInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": Failed allocating interface storage\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+
+ if (pIfDr->bNumEndpoints > 0)
+ {
+ pNewPipeInfo = (VBOXUSB_PIPE_INFO *)vboxUsbMemAlloc(pIfDr->bNumEndpoints * sizeof(VBOXUSB_PIPE_INFO));
+ if (!pNewPipeInfo)
+ {
+ AssertMsgFailed((__FUNCTION__": Failed allocating pipe info storage\n"));
+ Status = STATUS_NO_MEMORY;
+ break;
+ }
+ }
+ else
+ pNewPipeInfo = NULL;
+
+ /* Now that we have all the bits, select the interface. */
+ UsbBuildSelectInterfaceRequest(pUrb, uUrbSize, pDevExt->Rt.hConfiguration, InterfaceNumber, AlternateSetting);
+ pUrb->UrbSelectInterface.Interface.Length = GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints);
+
+ Status = VBoxUsbToolUrbPost(pDevExt->pLowerDO, pUrb, RT_INDEFINITE_WAIT);
+ if (NT_SUCCESS(Status) && USBD_SUCCESS(pUrb->UrbHeader.Status))
+ {
+ /* Free the old memory and put new in. */
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo);
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo = pNewIFInfo;
+ if (pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo)
+ vboxUsbMemFree(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo);
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo = pNewPipeInfo;
+ pNewPipeInfo = NULL; pNewIFInfo = NULL; /* Don't try to free it again. */
+
+ USBD_INTERFACE_INFORMATION *pIfInfo = &pUrb->UrbSelectInterface.Interface;
+ memcpy(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo, pIfInfo, GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints));
+
+ Assert(pIfInfo->NumberOfPipes == pIfDr->bNumEndpoints);
+ for (ULONG i = 0; i < pIfInfo->NumberOfPipes; i++)
+ {
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo[i].EndpointAddress = pIfInfo->Pipes[i].EndpointAddress;
+ pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo[i].NextScheduledFrame = 0;
+ }
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x) usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ }
+ } while (0);
+
+ /* Clean up. */
+ if (pUrb)
+ VBoxUsbToolUrbFree(pUrb);
+ if (pNewIFInfo)
+ vboxUsbMemFree(pNewIFInfo);
+ if (pNewPipeInfo)
+ vboxUsbMemFree(pNewPipeInfo);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbSelectInterface(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_SELECT_INTERFACE pIf = (PUSBSUP_SELECT_INTERFACE)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pIf
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pIf)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtSetInterface(pDevExt, pIf->bInterfaceNumber, pIf->bAlternateSetting);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static HANDLE vboxUsbRtGetPipeHandle(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress)
+{
+ if (EndPointAddress == 0)
+ return pDevExt->Rt.hPipe0;
+
+ for (ULONG i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ for (ULONG j = 0; j < pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ /* Note that bit 7 determines pipe direction, but is still significant
+ * because endpoints may be numbered like 0x01, 0x81, 0x02, 0x82 etc.
+ */
+ if (pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].EndpointAddress == EndPointAddress)
+ return pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle;
+ }
+ }
+ return 0;
+}
+
+static VBOXUSB_PIPE_INFO* vboxUsbRtGetPipeInfo(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress)
+{
+ for (ULONG i = 0; i < pDevExt->Rt.uNumInterfaces; i++)
+ {
+ for (ULONG j = 0; j < pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->NumberOfPipes; j++)
+ {
+ if (pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].EndpointAddress == EndPointAddress)
+ return &pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j];
+ }
+ }
+ return NULL;
+}
+
+
+
+static NTSTATUS vboxUsbRtClearEndpoint(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress, bool fReset)
+{
+ NTSTATUS Status = VBoxUsbToolPipeClear(pDevExt->pLowerDO, vboxUsbRtGetPipeHandle(pDevExt, EndPointAddress), fReset);
+ if (!NT_SUCCESS(Status))
+ {
+ AssertMsgFailed((__FUNCTION__": VBoxUsbToolPipeClear failed Status (0x%x)\n", Status));
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbClearEndpoint(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLEAR_ENDPOINT pCe = (PUSBSUP_CLEAR_ENDPOINT)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCe
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCe)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtClearEndpoint(pDevExt, pCe->bEndpoint, TRUE);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchUsbAbortEndpoint(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_CLEAR_ENDPOINT pCe = (PUSBSUP_CLEAR_ENDPOINT)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pCe
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pCe)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != 0)
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = vboxUsbRtClearEndpoint(pDevExt, pCe->bEndpoint, FALSE);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtUrbSendCompletion(PDEVICE_OBJECT pDevObj, IRP *pIrp, void *pvContext)
+{
+ RT_NOREF1(pDevObj);
+
+ if (!pvContext)
+ {
+ AssertMsgFailed((__FUNCTION__": context is NULL\n"));
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ PVBOXUSB_URB_CONTEXT pContext = (PVBOXUSB_URB_CONTEXT)pvContext;
+
+ if (pContext->ulMagic != VBOXUSB_MAGIC)
+ {
+ AssertMsgFailed((__FUNCTION__": Invalid context magic\n"));
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ PURB pUrb = pContext->pUrb;
+ PMDL pMdlBuf = pContext->pMdlBuf;
+ PUSBSUP_URB pUrbInfo = (PUSBSUP_URB)pContext->pOut;
+ PVBOXUSBDEV_EXT pDevExt = pContext->pDevExt;
+
+ if (!pUrb || !pMdlBuf || !pUrbInfo || !pDevExt)
+ {
+ AssertMsgFailed((__FUNCTION__": Invalid args\n"));
+ if (pDevExt)
+ vboxUsbDdiStateRelease(pDevExt);
+ pIrp->IoStatus.Information = 0;
+ return STATUS_CONTINUE_COMPLETION;
+ }
+
+ NTSTATUS Status = pIrp->IoStatus.Status;
+ if (Status == STATUS_SUCCESS)
+ {
+ switch(pUrb->UrbHeader.Status)
+ {
+ case USBD_STATUS_CRC:
+ pUrbInfo->error = USBSUP_XFER_CRC;
+ break;
+ case USBD_STATUS_SUCCESS:
+ pUrbInfo->error = USBSUP_XFER_OK;
+ break;
+ case USBD_STATUS_STALL_PID:
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ break;
+ case USBD_STATUS_INVALID_URB_FUNCTION:
+ case USBD_STATUS_INVALID_PARAMETER:
+ AssertMsgFailed((__FUNCTION__": sw error, urb Status (0x%x)\n", pUrb->UrbHeader.Status));
+ case USBD_STATUS_DEV_NOT_RESPONDING:
+ default:
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ break;
+ }
+
+ switch(pContext->ulTransferType)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ pUrbInfo->len = pUrb->UrbControlTransfer.TransferBufferLength;
+ /* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
+ * the first 8 bytes of the buffer is the setup packet so the real
+ * data length is therefore urb->len - 8
+ */
+ pUrbInfo->len += sizeof (pUrb->UrbControlTransfer.SetupPacket);
+
+ /* If a control URB was successfully completed on the default control
+ * pipe, stash away the handle. When submitting the URB, we don't need
+ * to know (and initially don't have) the handle. If we want to abort
+ * the default control pipe, we *have* to have a handle. This is how we
+ * find out what the handle is.
+ */
+ if (!pUrbInfo->ep && (pDevExt->Rt.hPipe0 == NULL))
+ {
+ pDevExt->Rt.hPipe0 = pUrb->UrbControlTransfer.PipeHandle;
+ }
+
+ break;
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ pUrbInfo->len = pUrb->UrbIsochronousTransfer.TransferBufferLength;
+ break;
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ if (pUrbInfo->dir == USBSUP_DIRECTION_IN && pUrbInfo->error == USBSUP_XFER_OK
+ && !(pUrbInfo->flags & USBSUP_FLAG_SHORT_OK)
+ && pUrbInfo->len > pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength
+ )
+ {
+ /* If we don't use the USBD_SHORT_TRANSFER_OK flag, the returned buffer lengths are
+ * wrong for short transfers (always a multiple of max packet size?). So we just figure
+ * out if this was a data underrun on our own.
+ */
+ pUrbInfo->error = USBSUP_XFER_UNDERRUN;
+ }
+ pUrbInfo->len = pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ pUrbInfo->len = 0;
+
+ LogFunc(("URB failed Status (0x%x) urb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
+#ifdef DEBUG
+ switch(pContext->ulTransferType)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ LogRel(("Msg (CTRL) length=%d\n", pUrb->UrbControlTransfer.TransferBufferLength));
+ break;
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ LogRel(("ISOC length=%d\n", pUrb->UrbIsochronousTransfer.TransferBufferLength));
+ break;
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ LogRel(("BULK/INTR length=%d\n", pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength));
+ break;
+ }
+#endif
+ switch(pUrb->UrbHeader.Status)
+ {
+ case USBD_STATUS_CRC:
+ pUrbInfo->error = USBSUP_XFER_CRC;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_STALL_PID:
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_DEV_NOT_RESPONDING:
+ case USBD_STATUS_DEVICE_GONE:
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ Status = STATUS_SUCCESS;
+ break;
+ case ((USBD_STATUS)0xC0010000L): // USBD_STATUS_CANCELED - too bad usbdi.h and usb.h aren't consistent!
+ /// @todo What the heck are we really supposed to do here?
+ pUrbInfo->error = USBSUP_XFER_STALL;
+ Status = STATUS_SUCCESS;
+ break;
+ case USBD_STATUS_BAD_START_FRAME: // This one really shouldn't happen
+ case USBD_STATUS_ISOCH_REQUEST_FAILED:
+ pUrbInfo->error = USBSUP_XFER_NAC;
+ Status = STATUS_SUCCESS;
+ break;
+ default:
+ AssertMsgFailed((__FUNCTION__": err Status (0x%x) (0x%x)\n", Status, pUrb->UrbHeader.Status));
+ pUrbInfo->error = USBSUP_XFER_DNR;
+ Status = STATUS_SUCCESS;
+ break;
+ }
+ }
+ // For isochronous transfers, always update the individual packets
+ if (pContext->ulTransferType == USBSUP_TRANSFER_TYPE_ISOC)
+ {
+ Assert(pUrbInfo->numIsoPkts == pUrb->UrbIsochronousTransfer.NumberOfPackets);
+ for (ULONG i = 0; i < pUrbInfo->numIsoPkts; ++i)
+ {
+ Assert(pUrbInfo->aIsoPkts[i].off == pUrb->UrbIsochronousTransfer.IsoPacket[i].Offset);
+ pUrbInfo->aIsoPkts[i].cb = (uint16_t)pUrb->UrbIsochronousTransfer.IsoPacket[i].Length;
+ switch (pUrb->UrbIsochronousTransfer.IsoPacket[i].Status)
+ {
+ case USBD_STATUS_SUCCESS:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_OK;
+ break;
+ case USBD_STATUS_NOT_ACCESSED:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_NAC;
+ break;
+ default:
+ pUrbInfo->aIsoPkts[i].stat = USBSUP_XFER_STALL;
+ break;
+ }
+ }
+ }
+
+ MmUnlockPages(pMdlBuf);
+ IoFreeMdl(pMdlBuf);
+
+ vboxUsbMemFree(pContext);
+
+ vboxUsbDdiStateRelease(pDevExt);
+
+ Assert(pIrp->IoStatus.Status != STATUS_IO_TIMEOUT);
+ pIrp->IoStatus.Information = sizeof(*pUrbInfo);
+ pIrp->IoStatus.Status = Status;
+ return STATUS_CONTINUE_COMPLETION;
+}
+
+static NTSTATUS vboxUsbRtUrbSend(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp, PUSBSUP_URB pUrbInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVBOXUSB_URB_CONTEXT pContext = NULL;
+ PMDL pMdlBuf = NULL;
+ ULONG cbUrb;
+
+ Assert(pUrbInfo);
+ if (pUrbInfo->type == USBSUP_TRANSFER_TYPE_ISOC)
+ {
+ Assert(pUrbInfo->numIsoPkts <= 8);
+ cbUrb = GET_ISO_URB_SIZE(pUrbInfo->numIsoPkts);
+ }
+ else
+ cbUrb = sizeof (URB);
+
+ do
+ {
+ pContext = (PVBOXUSB_URB_CONTEXT)vboxUsbMemAllocZ(cbUrb + sizeof (VBOXUSB_URB_CONTEXT));
+ if (!pContext)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbMemAlloc failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ PURB pUrb = (PURB)(pContext + 1);
+ HANDLE hPipe = NULL;
+ if (pUrbInfo->ep)
+ {
+ hPipe = vboxUsbRtGetPipeHandle(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
+ if (!hPipe)
+ {
+ AssertMsgFailed((__FUNCTION__": vboxUsbRtGetPipeHandle failed for endpoint (0x%x)\n", pUrbInfo->ep));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ pMdlBuf = IoAllocateMdl(pUrbInfo->buf, (ULONG)pUrbInfo->len, FALSE, FALSE, NULL);
+ if (!pMdlBuf)
+ {
+ AssertMsgFailed((__FUNCTION__": IoAllocateMdl failed for buffer (0x%p) length (%d)\n", pUrbInfo->buf, pUrbInfo->len));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ __try
+ {
+ MmProbeAndLockPages(pMdlBuf, KernelMode, IoModifyAccess);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = GetExceptionCode();
+ IoFreeMdl(pMdlBuf);
+ pMdlBuf = NULL;
+ AssertMsgFailed((__FUNCTION__": Exception Code (0x%x)\n", Status));
+ break;
+ }
+
+ /* For some reason, passing a MDL in the URB does not work reliably. Notably
+ * the iPhone when used with iTunes fails.
+ */
+ PVOID pBuffer = MmGetSystemAddressForMdlSafe(pMdlBuf, NormalPagePriority);
+ if (!pBuffer)
+ {
+ AssertMsgFailed((__FUNCTION__": MmGetSystemAddressForMdlSafe failed\n"));
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ switch (pUrbInfo->type)
+ {
+ case USBSUP_TRANSFER_TYPE_MSG:
+ {
+ pUrb->UrbHeader.Function = URB_FUNCTION_CONTROL_TRANSFER;
+ pUrb->UrbHeader.Length = sizeof (struct _URB_CONTROL_TRANSFER);
+ pUrb->UrbControlTransfer.PipeHandle = hPipe;
+ pUrb->UrbControlTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbControlTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+ pUrb->UrbControlTransfer.UrbLink = 0;
+
+ if (!hPipe)
+ pUrb->UrbControlTransfer.TransferFlags |= USBD_DEFAULT_PIPE_TRANSFER;
+
+ /* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
+ * the first 8 bytes of the buffer is the setup packet so the real
+ * data length is therefore pUrb->len - 8
+ */
+ //PVBOXUSB_SETUP pSetup = (PVBOXUSB_SETUP)pUrb->UrbControlTransfer.SetupPacket;
+ memcpy(pUrb->UrbControlTransfer.SetupPacket, pBuffer, min(sizeof (pUrb->UrbControlTransfer.SetupPacket), pUrbInfo->len));
+
+ if (pUrb->UrbControlTransfer.TransferBufferLength <= sizeof (pUrb->UrbControlTransfer.SetupPacket))
+ pUrb->UrbControlTransfer.TransferBufferLength = 0;
+ else
+ pUrb->UrbControlTransfer.TransferBufferLength -= sizeof (pUrb->UrbControlTransfer.SetupPacket);
+
+ pUrb->UrbControlTransfer.TransferBuffer = (uint8_t *)pBuffer + sizeof(pUrb->UrbControlTransfer.SetupPacket);
+ pUrb->UrbControlTransfer.TransferBufferMDL = 0;
+ pUrb->UrbControlTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
+ break;
+ }
+ case USBSUP_TRANSFER_TYPE_ISOC:
+ {
+ Assert(hPipe);
+ VBOXUSB_PIPE_INFO *pPipeInfo = vboxUsbRtGetPipeInfo(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
+ if (pPipeInfo == NULL)
+ {
+ /* Can happen if the isoc request comes in too early or late. */
+ AssertMsgFailed((__FUNCTION__": pPipeInfo not found\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
+ pUrb->UrbHeader.Length = (USHORT)cbUrb;
+ pUrb->UrbIsochronousTransfer.PipeHandle = hPipe;
+ pUrb->UrbIsochronousTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbIsochronousTransfer.TransferBufferMDL = 0;
+ pUrb->UrbIsochronousTransfer.TransferBuffer = pBuffer;
+ pUrb->UrbIsochronousTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+ pUrb->UrbIsochronousTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK; // May be implied already
+ pUrb->UrbIsochronousTransfer.NumberOfPackets = pUrbInfo->numIsoPkts;
+ pUrb->UrbIsochronousTransfer.ErrorCount = 0;
+ pUrb->UrbIsochronousTransfer.UrbLink = 0;
+
+ Assert(pUrbInfo->numIsoPkts == pUrb->UrbIsochronousTransfer.NumberOfPackets);
+ for (ULONG i = 0; i < pUrbInfo->numIsoPkts; ++i)
+ {
+ pUrb->UrbIsochronousTransfer.IsoPacket[i].Offset = pUrbInfo->aIsoPkts[i].off;
+ pUrb->UrbIsochronousTransfer.IsoPacket[i].Length = pUrbInfo->aIsoPkts[i].cb;
+ }
+
+ /* We have to schedule the URBs ourselves. There is an ASAP flag but
+ * that can only be reliably used after pipe creation/reset, ie. it's
+ * almost completely useless.
+ */
+ ULONG iFrame, iStartFrame;
+ VBoxUsbToolCurrentFrame(pDevExt->pLowerDO, pIrp, &iFrame);
+ iFrame += 2;
+ iStartFrame = pPipeInfo->NextScheduledFrame;
+ if ((iFrame < iStartFrame) || (iStartFrame > iFrame + 512))
+ iFrame = iStartFrame;
+ /* For full-speed devices, there must be one transfer per frame (Windows USB
+ * stack requirement), but URBs can contain multiple packets. For high-speed or
+ * faster transfers, we expect one URB per frame, regardless of the interval.
+ */
+ if (pDevExt->Rt.devdescr->bcdUSB < 0x300 && !pDevExt->Rt.fIsHighSpeed)
+ pPipeInfo->NextScheduledFrame = iFrame + pUrbInfo->numIsoPkts;
+ else
+ pPipeInfo->NextScheduledFrame = iFrame + 1;
+ pUrb->UrbIsochronousTransfer.StartFrame = iFrame;
+ break;
+ }
+ case USBSUP_TRANSFER_TYPE_BULK:
+ case USBSUP_TRANSFER_TYPE_INTR:
+ {
+ Assert(pUrbInfo->dir != USBSUP_DIRECTION_SETUP);
+ Assert(pUrbInfo->dir == USBSUP_DIRECTION_IN || pUrbInfo->type == USBSUP_TRANSFER_TYPE_BULK);
+ Assert(hPipe);
+
+ pUrb->UrbHeader.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
+ pUrb->UrbHeader.Length = sizeof (struct _URB_BULK_OR_INTERRUPT_TRANSFER);
+ pUrb->UrbBulkOrInterruptTransfer.PipeHandle = hPipe;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength = (ULONG)pUrbInfo->len;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBufferMDL = 0;
+ pUrb->UrbBulkOrInterruptTransfer.TransferBuffer = pBuffer;
+ pUrb->UrbBulkOrInterruptTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
+
+ if (pUrb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
+ pUrb->UrbBulkOrInterruptTransfer.TransferFlags |= (USBD_SHORT_TRANSFER_OK);
+
+ pUrb->UrbBulkOrInterruptTransfer.UrbLink = 0;
+ break;
+ }
+ default:
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+
+ pContext->pDevExt = pDevExt;
+ pContext->pMdlBuf = pMdlBuf;
+ pContext->pUrb = pUrb;
+ pContext->pOut = pUrbInfo;
+ pContext->ulTransferType = pUrbInfo->type;
+ pContext->ulMagic = VBOXUSB_MAGIC;
+
+ PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ pSl->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+ pSl->Parameters.Others.Argument1 = pUrb;
+ pSl->Parameters.Others.Argument2 = NULL;
+
+ IoSetCompletionRoutine(pIrp, vboxUsbRtUrbSendCompletion, pContext, TRUE, TRUE, TRUE);
+ IoMarkIrpPending(pIrp);
+ Status = IoCallDriver(pDevExt->pLowerDO, pIrp);
+ AssertMsg(NT_SUCCESS(Status), (__FUNCTION__": IoCallDriver failed Status (0x%x)\n", Status));
+ return STATUS_PENDING;
+ } while (0);
+
+ Assert(!NT_SUCCESS(Status));
+
+ if (pMdlBuf)
+ {
+ if (pMdlBuf->MdlFlags & MDL_PAGES_LOCKED)
+ MmUnlockPages(pMdlBuf);
+
+ IoFreeMdl(pMdlBuf);
+ }
+
+ if (pContext)
+ vboxUsbMemFree(pContext);
+
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchSendUrb(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ PUSBSUP_URB pUrbInfo = (PUSBSUP_URB)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status;
+
+ do
+ {
+ if (!pFObj)
+ {
+ AssertFailed();
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!vboxUsbRtCtxIsOwner(pDevExt, pFObj))
+ {
+ AssertFailed();
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ if ( !pUrbInfo
+ || pSl->Parameters.DeviceIoControl.InputBufferLength != sizeof (*pUrbInfo)
+ || pSl->Parameters.DeviceIoControl.OutputBufferLength != sizeof (*pUrbInfo))
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ return vboxUsbRtUrbSend(pDevExt, pIrp, pUrbInfo);
+ } while (0);
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchIsOperational(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ VBoxDrvToolIoComplete(pIrp, STATUS_SUCCESS, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbRtDispatchGetVersion(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PUSBSUP_VERSION pVer= (PUSBSUP_VERSION)pIrp->AssociatedIrp.SystemBuffer;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if ( pVer
+ && pSl->Parameters.DeviceIoControl.InputBufferLength == 0
+ && pSl->Parameters.DeviceIoControl.OutputBufferLength == sizeof(*pVer))
+ {
+ pVer->u32Major = USBDRV_MAJOR_VERSION;
+ pVer->u32Minor = USBDRV_MINOR_VERSION;
+ }
+ else
+ {
+ AssertMsgFailed((__FUNCTION__": STATUS_INVALID_PARAMETER\n"));
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ Assert(Status != STATUS_PENDING);
+ VBoxDrvToolIoComplete(pIrp, Status, sizeof (*pVer));
+ vboxUsbDdiStateRelease(pDevExt);
+ return Status;
+}
+
+static NTSTATUS vboxUsbRtDispatchDefault(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ VBoxDrvToolIoComplete(pIrp, STATUS_INVALID_DEVICE_REQUEST, 0);
+ vboxUsbDdiStateRelease(pDevExt);
+ return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtCreate(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ RT_NOREF1(pDevExt);
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ AssertReturn(pFObj, STATUS_INVALID_PARAMETER);
+ return STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtClose(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFObj = pSl->FileObject;
+ Assert(pFObj);
+
+ vboxUsbRtCtxReleaseOwner(pDevExt, pFObj);
+
+ return STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtDispatch(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ switch (pSl->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case SUPUSB_IOCTL_USB_CLAIM_DEVICE:
+ return vboxUsbRtDispatchClaimDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_RELEASE_DEVICE:
+ return vboxUsbRtDispatchReleaseDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_GET_DEVICE:
+ return vboxUsbRtDispatchGetDevice(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_RESET:
+ return vboxUsbRtDispatchUsbReset(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_SET_CONFIG:
+ return vboxUsbRtDispatchUsbSetConfig(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_SELECT_INTERFACE:
+ return vboxUsbRtDispatchUsbSelectInterface(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_CLEAR_ENDPOINT:
+ return vboxUsbRtDispatchUsbClearEndpoint(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_USB_ABORT_ENDPOINT:
+ return vboxUsbRtDispatchUsbAbortEndpoint(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_SEND_URB:
+ return vboxUsbRtDispatchSendUrb(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_IS_OPERATIONAL:
+ return vboxUsbRtDispatchIsOperational(pDevExt, pIrp);
+
+ case SUPUSB_IOCTL_GET_VERSION:
+ return vboxUsbRtDispatchGetVersion(pDevExt, pIrp);
+
+ default:
+ return vboxUsbRtDispatchDefault(pDevExt, pIrp);
+ }
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h
new file mode 100644
index 00000000..c835890d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/dev/VBoxUsbRt.h
@@ -0,0 +1,97 @@
+/* $Id: VBoxUsbRt.h $ */
+/** @file
+ * VBox USB R0 runtime
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbCmn.h"
+#include "../cmn/VBoxUsbIdc.h"
+
+#define VBOXUSBRT_MAX_CFGS 4
+
+typedef struct VBOXUSB_PIPE_INFO {
+ UCHAR EndpointAddress;
+ ULONG NextScheduledFrame;
+} VBOXUSB_PIPE_INFO;
+
+typedef struct VBOXUSB_IFACE_INFO {
+ USBD_INTERFACE_INFORMATION *pInterfaceInfo;
+ VBOXUSB_PIPE_INFO *pPipeInfo;
+} VBOXUSB_IFACE_INFO;
+
+typedef struct VBOXUSB_RT
+{
+ UNICODE_STRING IfName;
+
+ HANDLE hPipe0;
+ HANDLE hConfiguration;
+ uint32_t uConfigValue;
+
+ uint32_t uNumInterfaces;
+ USB_DEVICE_DESCRIPTOR *devdescr;
+ USB_CONFIGURATION_DESCRIPTOR *cfgdescr[VBOXUSBRT_MAX_CFGS];
+
+ VBOXUSB_IFACE_INFO *pVBIfaceInfo;
+
+ uint16_t idVendor, idProduct, bcdDevice;
+ char szSerial[MAX_USB_SERIAL_STRING];
+ BOOLEAN fIsHighSpeed;
+
+ HVBOXUSBIDCDEV hMonDev;
+ PFILE_OBJECT pOwner;
+} VBOXUSB_RT, *PVBOXUSB_RT;
+
+typedef struct VBOXUSBRT_IDC
+{
+ PDEVICE_OBJECT pDevice;
+ PFILE_OBJECT pFile;
+} VBOXUSBRT_IDC, *PVBOXUSBRT_IDC;
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtGlobalsInit();
+DECLHIDDEN(VOID) vboxUsbRtGlobalsTerm();
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtInit(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(VOID) vboxUsbRtClear(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbRtRm(PVBOXUSBDEV_EXT pDevExt);
+DECLHIDDEN(NTSTATUS) vboxUsbRtStart(PVBOXUSBDEV_EXT pDevExt);
+
+DECLHIDDEN(NTSTATUS) vboxUsbRtDispatch(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+DECLHIDDEN(NTSTATUS) vboxUsbRtCreate(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+DECLHIDDEN(NTSTATUS) vboxUsbRtClose(PVBOXUSBDEV_EXT pDevExt, PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_dev_VBoxUsbRt_h */
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..817b26ed
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/lib/VBoxUsbLib-win.cpp
@@ -0,0 +1,2221 @@
+/* $Id: VBoxUsbLib-win.cpp $ */
+/** @file
+ * VBox USB ring-3 Driver Interface library, Windows.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* 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>
+#include <Dbt.h>
+
+/* Defined in Windows 8 DDK (through usbdi.h) but we use Windows 7 DDK to build. */
+#define UsbSuperSpeed 3
+
+#ifdef VBOX_WITH_NEW_USB_ENUM
+# include <cfgmgr32.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;
+ HANDLE hThread;
+ HWND hWnd;
+ HANDLE hTimerQueue;
+ HANDLE hTimer;
+} 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;
+
+
+static void usbLibVuFreeDevices(PVBOXUSB_DEV pDevInfos)
+{
+ while (pDevInfos)
+ {
+ PVBOXUSB_DEV pNext = pDevInfos->pNext;
+ RTMemFree(pDevInfos);
+ pDevInfos = pNext;
+ }
+}
+
+/* Check that a proxied device responds the way we expect it to. */
+static int usbLibVuDeviceValidate(PVBOXUSB_DEV pVuDev)
+{
+ HANDLE hOut = INVALID_HANDLE_VALUE;
+ DWORD dwErr;
+
+ 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)
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("Failed to open `%s' (dwErr=%u)!\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))
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSB_IOCTL_GET_VERSION failed on `%s' (dwErr=%u)!\n", pVuDev->szName, dwErr));
+ break;
+ }
+
+ if ( version.u32Major != USBDRV_MAJOR_VERSION
+#if USBDRV_MINOR_VERSION != 0
+ || version.u32Minor < USBDRV_MINOR_VERSION
+#endif
+ )
+ {
+ AssertFailed();
+ LogRelFunc(("Invalid version %d:%d (%s) vs %d:%d (library)!\n", version.u32Major, version.u32Minor, pVuDev->szName, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
+ break;
+ }
+
+ if (!DeviceIoControl(hOut, SUPUSB_IOCTL_IS_OPERATIONAL, NULL, 0, NULL, NULL, &cbReturned, NULL))
+ {
+ dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSB_IOCTL_IS_OPERATIONAL failed on `%s' (dwErr=%u)!\n", pVuDev->szName, dwErr));
+ break;
+ }
+
+ rc = VINF_SUCCESS;
+ } while (0);
+
+ CloseHandle(hOut);
+ return rc;
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+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);
+ LogRelFunc(("Found VBoxUSB on `%s' (rc=%d)\n", pVuDev->szName, rc));
+ AssertRC(rc);
+ } while (0);
+
+ RTMemFree(pIfDetailData);
+ return rc;
+}
+
+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 (%u)\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 (%u), 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;
+}
+#else
+
+static PSP_DEVICE_INTERFACE_DETAIL_DATA usbLibGetDevDetail(HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData, PSP_DEVINFO_DATA DevInfoData);
+static void *usbLibGetRegistryProperty(HDEVINFO InfoSet, const PSP_DEVINFO_DATA DevData, DWORD Property);
+
+/* Populate the data for a single proxied USB device. */
+static int usbLibVUsbDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData)
+{
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData = NULL;
+ SP_DEVINFO_DATA DeviceData;
+ LPCSTR Location;
+ int rc = VINF_SUCCESS;
+
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ /* The interface detail includes the device path. */
+ DetailData = usbLibGetDevDetail(InfoSet, InterfaceData, &DeviceData);
+ if (DetailData)
+ {
+ strncpy(pVuDev->szName, DetailData->DevicePath, sizeof(pVuDev->szName));
+
+ /* The location is used as a unique identifier for cross-referencing the two lists. */
+ Location = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_DRIVER);
+ if (Location)
+ {
+ strncpy(pVuDev->szDriverRegName, Location, sizeof(pVuDev->szDriverRegName));
+ rc = usbLibVuDeviceValidate(pVuDev);
+ LogRelFunc(("Found VBoxUSB on `%s' (rc=%d)\n", pVuDev->szName, rc));
+ AssertRC(rc);
+
+ RTMemFree((void *)Location);
+ }
+ else
+ {
+ /* Errors will be logged by usbLibGetRegistryProperty(). */
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ RTMemFree(DetailData);
+ }
+ else
+ {
+ /* Errors will be logged by usbLibGetDevDetail(). */
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+
+ return rc;
+}
+
+/* Enumerate proxied USB devices (with VBoxUSB.sys loaded). */
+static int usbLibEnumVUsbDevices(PVBOXUSB_DEV *ppVuDevs, uint32_t *pcVuDevs)
+{
+ SP_DEVICE_INTERFACE_DATA InterfaceData;
+ HDEVINFO InfoSet;
+ DWORD DeviceIndex;
+ DWORD dwErr;
+
+ *ppVuDevs = NULL;
+ *pcVuDevs = 0;
+
+ /* Enumerate all present devices which support the GUID_CLASS_VBOXUSB interface. */
+ InfoSet = SetupDiGetClassDevs(&GUID_CLASS_VBOXUSB, NULL, NULL,
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+ if (InfoSet == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwErr = GetLastError();
+ LogRelFunc(("SetupDiGetClassDevs for GUID_CLASS_VBOXUSB failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+ }
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ DeviceIndex = 0;
+
+ /* Loop over the enumerated list. */
+ while (SetupDiEnumDeviceInterfaces(InfoSet, NULL, &GUID_CLASS_VBOXUSB, DeviceIndex, &InterfaceData))
+ {
+ /* we've now got the IfData */
+ PVBOXUSB_DEV pVuDev = (PVBOXUSB_DEV)RTMemAllocZ(sizeof (*pVuDev));
+ if (!pVuDev)
+ {
+ AssertFailed();
+ LogRelFunc(("RTMemAllocZ failed\n"));
+ break;
+ }
+
+ int rc = usbLibVUsbDevicePopulate(pVuDev, InfoSet, &InterfaceData);
+ if (RT_SUCCESS(rc))
+ {
+ pVuDev->pNext = *ppVuDevs;
+ *ppVuDevs = pVuDev;
+ ++*pcVuDevs;
+ }
+ else /* Skip this device but continue enumerating. */
+ AssertMsgFailed(("usbLibVuDevicePopulate failed, rc=%d\n", rc));
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ ++DeviceIndex;
+ }
+
+ /* Paranoia. */
+ dwErr = GetLastError();
+ if (dwErr != ERROR_NO_MORE_ITEMS)
+ {
+ LogRelFunc(("SetupDiEnumDeviceInterfaces failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ }
+
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return VINF_SUCCESS;
+}
+
+static uint16_t usbLibParseHexNumU16(LPCSTR *ppStr)
+{
+ const char *pStr = *ppStr;
+ char c;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ c = *pStr;
+ u = c >= 'A' ? c - 'A' + 10 : c - '0'; /* Hex digit to number. */
+ num |= u << (12 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszLocation, 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; /* The hub numbering is not very useful on Windows. Skip it. */
+ pDev->bPort = iPort;
+
+ /* The port path/location uniquely identifies the port. */
+ pDev->pszPortPath = RTStrDup(lpszLocation);
+ if (!pDev->pszPortPath)
+ return VERR_NO_STR_MEMORY;
+
+ /* If there is no DriverKey, the device is unused because there's no driver. */
+ 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;
+
+ /* If there's no DriverKey, jam in an empty string to avoid NULL pointers. */
+ if (!lpszDrvKeyName)
+ pDev->pszAddress = RTStrDup("");
+ else
+ pDev->pszAddress = RTStrDup(lpszDrvKeyName);
+
+ 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)
+ {
+ int 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;
+}
+#endif
+
+static void usbLibDevStrFree(LPSTR lpszName)
+{
+ RTStrFree(lpszName);
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+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
+ AssertMsgFailed(("DeviceIoControl 1 fail dwErr (%u)\n", GetLastError()));
+#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 (%u)\n", dwErr));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTMemFree(pName);
+ return rc;
+}
+#endif
+
+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, LPCSTR lpcszHubName, 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(("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION #1 failed (dwErr=%u) on hub %s port %d\n", dwErr, lpcszHubName, iPort));
+#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(("IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION #2 failed (dwErr=%u) on hub %s port %d\n", dwErr, lpcszHubName, iPort));
+#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, LPCSTR lpcszHubName, 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 (id %u) failed (dwErr=%u) on hub %s port %d\n", iDr, dwErr, lpcszHubName, iPort));
+ 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, LPCSTR lpcszHubName, ULONG iPort, ULONG iDr, ULONG cIdLang, const USHORT *pIdLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
+{
+ for (ULONG i = 0; i < cIdLang; ++i)
+ {
+ usbLibDevStrDrEntryGet(hHub, lpcszHubName, iPort, iDr, pIdLang[i], ppList);
+ }
+ return VINF_SUCCESS;
+}
+
+static int usbLibDevStrDrEntryGetAll(HANDLE hHub, LPCSTR lpcszHubName, 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, lpcszHubName, 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, lpcszHubName, iPort, pDevDr->iManufacturer, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iProduct)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, iPort, pDevDr->iProduct, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ rc = usbLibDevStrDrEntryGetForLangs(hHub, lpcszHubName, 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, lpcszHubName, 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, lpcszHubName, iPort, pCurIfDr->iInterface, cIdLang, pIdLang, ppList);
+ AssertRC(rc);
+ break;
+ }
+ default:
+ break;
+ }
+
+ pCur = pCur + pCmnDr->bLength;
+ }
+
+ return VINF_SUCCESS;
+}
+
+#ifndef VBOX_WITH_NEW_USB_ENUM
+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);
+ LogRel(("Getting USB connection information failed (dwErr=%u) on hub %s\n", dwErr, lpcszHubName));
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed (dwErr=%u)\n", dwErr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pConInfo->ConnectionStatus != DeviceConnected)
+ {
+ /* just ignore & return success */
+ return VWRN_INVALID_HANDLE;
+ }
+
+ if (pConInfo->DeviceIsHub)
+ {
+ LPSTR lpszChildHubName = NULL;
+ rc = usbLibDevStrHubNameGet(hHub, iPort, &lpszChildHubName);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibDevGetHubDevices(lpszChildHubName, ppDevs, pcDevs);
+ usbLibDevStrFree(lpszChildHubName);
+ 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)
+ {
+ LogRelFunc(("No DriverKey on hub %s port %d\n", lpcszHubName, iPort));
+ lpszName = &nameEmptyBuf;
+ fFreeNameBuf = false;
+ }
+
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+ PVBOXUSB_STRING_DR_ENTRY pList = NULL;
+ rc = usbLibDevCfgDrGet(hHub, lpcszHubName, iPort, 0, &pCfgDr);
+ if (pCfgDr)
+ {
+ rc = usbLibDevStrDrEntryGetAll(hHub, lpcszHubName, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertRC(rc); // this can fail if device suspended
+#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))
+ {
+ LogRel(("Getting USB node information failed (dwErr=%u) on hub %s\n", GetLastError(), lpszName));
+ 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;
+}
+#endif
+
+#ifdef VBOX_WITH_NEW_USB_ENUM
+
+/* Get a registry property for a device given its HDEVINFO + SP_DEVINFO_DATA. */
+static void *usbLibGetRegistryProperty(HDEVINFO InfoSet, const PSP_DEVINFO_DATA DevData, DWORD Property)
+{
+ BOOL rc;
+ DWORD dwReqLen;
+ void *PropertyData;
+
+ /* How large a buffer do we need? */
+ rc = SetupDiGetDeviceRegistryProperty(InfoSet, DevData, Property,
+ NULL, NULL, 0, &dwReqLen);
+ if (!rc && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+ {
+ LogRelFunc(("Failed to query buffer size, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ PropertyData = RTMemAlloc(dwReqLen);
+ if (!PropertyData)
+ return NULL;
+
+ /* Get the actual property data. */
+ rc = SetupDiGetDeviceRegistryProperty(InfoSet, DevData, Property,
+ NULL, (PBYTE)PropertyData, dwReqLen, &dwReqLen);
+ if (!rc)
+ {
+ LogRelFunc(("Failed to get property data, error %ld\n", GetLastError()));
+ RTMemFree(PropertyData);
+ return NULL;
+ }
+ return PropertyData;
+}
+
+/* Given a HDEVINFO and SP_DEVICE_INTERFACE_DATA, get the interface detail data and optionally device info data. */
+static PSP_DEVICE_INTERFACE_DETAIL_DATA usbLibGetDevDetail(HDEVINFO InfoSet, PSP_DEVICE_INTERFACE_DATA InterfaceData, PSP_DEVINFO_DATA DevInfoData)
+{
+ BOOL rc;
+ DWORD dwReqLen;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
+
+ rc = SetupDiGetDeviceInterfaceDetail(InfoSet, InterfaceData, NULL, 0, &dwReqLen, DevInfoData);
+ if (!rc && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+ {
+ LogRelFunc(("Failed to get interface detail size, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ DetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAlloc(dwReqLen);
+ if (!DetailData)
+ return NULL;
+
+ memset(DetailData, 0, dwReqLen);
+ DetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ rc = SetupDiGetDeviceInterfaceDetail(InfoSet, InterfaceData, DetailData, dwReqLen, &dwReqLen, DevInfoData);
+ if (!rc)
+ {
+ LogRelFunc(("Failed to get interface detail, error %ld\n", GetLastError()));
+ RTMemFree(DetailData);
+ }
+
+ return DetailData;
+}
+
+/* Given a hub's PnP device instance, find its device path (file name). */
+static LPCSTR usbLibGetHubPathFromInstanceID(LPCSTR InstanceID)
+{
+ HDEVINFO InfoSet;
+ SP_DEVICE_INTERFACE_DATA InterfaceData;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
+ BOOL rc;
+ LPSTR DevicePath = NULL;
+
+ /* Enumerate the DevInst's USB hub interface. */
+ InfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_HUB, InstanceID, NULL,
+ DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+ if (InfoSet == INVALID_HANDLE_VALUE)
+ {
+ LogRelFunc(("Failed to get interface for InstID %se, error %ld\n", InstanceID, GetLastError()));
+ return NULL;
+ }
+
+ memset(&InterfaceData, 0, sizeof(InterfaceData));
+ InterfaceData.cbSize = sizeof(InterfaceData);
+ rc = SetupDiEnumDeviceInterfaces(InfoSet, 0, &GUID_DEVINTERFACE_USB_HUB, 0, &InterfaceData);
+ if (!rc)
+ {
+ DWORD dwErr = GetLastError();
+
+ /* The parent device might not be a hub; that is valid, ignore such errors. */
+ if (dwErr != ERROR_NO_MORE_ITEMS)
+ LogRelFunc(("Failed to get interface data for InstID %s, error %ld\n", InstanceID, dwErr));
+ SetupDiDestroyDeviceInfoList(InfoSet);
+ return NULL;
+ }
+
+ DetailData = usbLibGetDevDetail(InfoSet, &InterfaceData, NULL);
+ if (!DetailData)
+ {
+ SetupDiDestroyDeviceInfoList(InfoSet);
+ return NULL;
+ }
+
+ /* Copy the device path out of the interface detail. */
+ DevicePath = RTStrDup(DetailData->DevicePath);
+ RTMemFree(DetailData);
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return DevicePath;
+}
+
+
+/* Use the Configuration Manager (CM) to get a devices's parent given its DEVINST and
+ * turn it into a PnP device instance ID string.
+ */
+static LPCSTR usbLibGetParentInstanceID(DEVINST DevInst)
+{
+ LPSTR InstanceID;
+ DEVINST ParentInst;
+ ULONG ulReqChars;
+ ULONG ulReqBytes;
+ CONFIGRET cr;
+
+ /* First get the parent DEVINST. */
+ cr = CM_Get_Parent(&ParentInst, DevInst, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get parent instance, error %ld\n", GetLastError()));
+ return NULL;
+ }
+
+ /* Then convert it to the instance ID string. */
+ cr = CM_Get_Device_ID_Size(&ulReqChars, ParentInst, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get device ID size (DevInst=%X), error %ld\n", DevInst, GetLastError()));
+ return NULL;
+ }
+
+ /* CM_Get_Device_ID_Size gives us the size in characters without terminating null. */
+ ulReqBytes = (ulReqChars + 1) * sizeof(char);
+ InstanceID = (LPSTR)RTMemAlloc(ulReqBytes);
+ if (!InstanceID)
+ return NULL;
+
+ cr = CM_Get_Device_ID(ParentInst, InstanceID, ulReqBytes, 0);
+ if (cr != CR_SUCCESS)
+ {
+ LogRelFunc(("Failed to get device ID (DevInst=%X), error %ld\n", DevInst, GetLastError()));
+ RTMemFree(InstanceID);
+ return NULL;
+ }
+
+ return InstanceID;
+}
+
+/* Process a single USB device that's being enumerated and grab its hub-specific data. */
+static int usbLibDevGetDevice(LPCSTR lpcszHubFile, ULONG iPort, LPCSTR lpcszLocation, LPCSTR lpcszDriverKey, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ HANDLE HubDevice;
+ BYTE abConBuf[sizeof(USB_NODE_CONNECTION_INFORMATION_EX)];
+ PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo = PUSB_NODE_CONNECTION_INFORMATION_EX(abConBuf);
+ int rc = VINF_SUCCESS;
+ DWORD cbReturned = 0;
+
+ /* Validate inputs. */
+ if ((iPort < 1) || (iPort > 255))
+ {
+ LogRelFunc(("Port index out of range (%u)\n", iPort));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!lpcszHubFile)
+ {
+ LogRelFunc(("Hub path is NULL!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!lpcszLocation)
+ {
+ LogRelFunc(("Location NULL!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Try opening the hub file so we can send IOCTLs to it. */
+ HubDevice = CreateFile(lpcszHubFile, GENERIC_WRITE, FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ if (HubDevice == INVALID_HANDLE_VALUE)
+ {
+ LogRelFunc(("Failed to open hub `%s' (dwErr=%u)\n", lpcszHubFile, GetLastError()));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /* The shenanigans with abConBuf are due to USB_NODE_CONNECTION_INFORMATION_EX
+ * containing a zero-sized array, triggering compiler warnings.
+ */
+ memset(pConInfo, 0, sizeof(abConBuf));
+ pConInfo->ConnectionIndex = iPort;
+
+ /* We expect that IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX is always available
+ * on any supported Windows version and hardware.
+ * NB: IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 is Win8 and later only.
+ */
+ if (!DeviceIoControl(HubDevice, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ pConInfo, sizeof(abConBuf), pConInfo, sizeof(abConBuf),
+ &cbReturned, NULL))
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ LogRel(("IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX failed (dwErr=%u) on hub %s, port %d\n", dwErr, lpcszHubFile, iPort));
+ AssertMsg(dwErr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed dwErr (%u)\n", dwErr));
+ CloseHandle(HubDevice);
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (pConInfo->ConnectionStatus != DeviceConnected)
+ {
+ /* Ignore this, can't do anything with it. */
+ LogFunc(("Device is not connected, skipping.\n"));
+ CloseHandle(HubDevice);
+ return VINF_SUCCESS;
+ }
+
+ if (pConInfo->DeviceIsHub)
+ {
+ /* We're ignoring hubs, just skip this. */
+ LogFunc(("Device is a hub, skipping.\n"));
+ CloseHandle(HubDevice);
+ return VINF_SUCCESS;
+ }
+
+ PUSB_CONFIGURATION_DESCRIPTOR pCfgDr = NULL;
+ PVBOXUSB_STRING_DR_ENTRY pList = NULL;
+ rc = usbLibDevCfgDrGet(HubDevice, lpcszHubFile, iPort, 0, &pCfgDr);
+ if (pCfgDr)
+ {
+ rc = usbLibDevStrDrEntryGetAll(HubDevice, lpcszHubFile, iPort, &pConInfo->DeviceDescriptor, pCfgDr, &pList);
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ AssertRC(rc); // this can fail if device suspended
+#endif
+ }
+
+ /* At this point we're done with the hub device. */
+ CloseHandle(HubDevice);
+
+ PUSBDEVICE pDev = (PUSBDEVICE)RTMemAllocZ(sizeof (*pDev));
+ if (RT_LIKELY(pDev))
+ {
+ rc = usbLibDevPopulate(pDev, pConInfo, iPort, lpcszLocation, lpcszDriverKey, lpcszHubFile, pList);
+ if (RT_SUCCESS(rc))
+ {
+ pDev->pNext = *ppDevs;
+ *ppDevs = pDev;
+ ++*pcDevs;
+ }
+ else
+ RTMemFree(pDev);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (pCfgDr)
+ usbLibDevCfgDrFree(pCfgDr);
+ if (pList)
+ usbLibDevStrDrEntryFreeList(pList);
+
+ return rc;
+}
+
+
+/*
+ * Enumerate the USB devices in the host system. Since we do not care about the hierarchical
+ * structure of root hubs, other hubs, and devices, we just ask the USB PnP enumerator to
+ * give us all it has. This includes hubs (though not root hubs), as well as multiple child
+ * interfaces of multi-interface USB devices, which we filter out. It also includes USB
+ * devices with no driver, which is notably something we cannot get by enumerating via
+ * GUID_DEVINTERFACE_USB_DEVICE.
+ *
+ * This approach also saves us some trouble relative to enumerating devices via hub IOCTLs and
+ * then hunting through the PnP manager to find them. Instead, we look up the device's parent
+ * which (for devices we're interested in) is always a hub, and that allows us to obtain
+ * USB-specific data (descriptors, speeds, etc.) when combined with the devices PnP "address"
+ * (USB port on parent hub).
+ *
+ * NB: Every USB device known to the Windows PnP Manager will have a device instance ID. Typically
+ * it also has a DriverKey but only if it has a driver installed. Hence we ignore the DriverKey, at
+ * least prior to capturing (once VBoxUSB.sys is installed, a DriverKey must by definition be
+ * present). Also note that the device instance ID changes for captured devices since we change
+ * their USB VID/PID, though it is unique at any given point.
+ *
+ * The location information should be a reliable way of identifying a device and does not change
+ * with driver installs, capturing, etc. USB device location information is only available on
+ * Windows Vista and later; earlier Windows version had no reliable way of cross-referencing the
+ * USB IOCTL and PnP Manager data.
+ */
+static int usbLibEnumDevices(PUSBDEVICE *ppDevs, uint32_t *pcDevs)
+{
+ HDEVINFO InfoSet;
+ DWORD DeviceIndex;
+ LPDWORD Address;
+ SP_DEVINFO_DATA DeviceData;
+ LPCSTR ParentInstID;
+ LPCSTR HubPath = NULL;
+ LPCSTR Location;
+ LPCSTR DriverKey;
+
+ /* Ask the USB PnP enumerator for all it has. */
+ InfoSet = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
+
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ DeviceIndex = 0;
+
+ /* Enumerate everything in the info set. */
+ while (SetupDiEnumDeviceInfo(InfoSet, DeviceIndex, &DeviceData))
+ {
+ /* Use the CM API to get the parent instance ID. */
+ ParentInstID = usbLibGetParentInstanceID(DeviceData.DevInst);
+
+ /* Now figure out the hub's file path fron the instance ID, if there is one. */
+ if (ParentInstID)
+ HubPath = usbLibGetHubPathFromInstanceID(ParentInstID);
+
+ /* If there's no hub interface on the parent, then this might be a child
+ * device of a multi-interface device. Either way, we're not interested.
+ */
+ if (HubPath)
+ {
+ /* The location information uniquely identifies the USB device, (hub/port). */
+ Location = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_LOCATION_PATHS);
+
+ /* The software key aka DriverKey. This will be NULL for devices with no driver
+ * and allows us to distinguish between 'busy' (driver installed) and 'available'
+ * (no driver) devices.
+ */
+ DriverKey = (LPCSTR)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_DRIVER);
+
+ /* The device's PnP Manager "address" is the port number on the parent hub. */
+ Address = (LPDWORD)usbLibGetRegistryProperty(InfoSet, &DeviceData, SPDRP_ADDRESS);
+ if (Address && Location) /* NB: DriverKey may be NULL! */
+ {
+ usbLibDevGetDevice(HubPath, *Address, Location, DriverKey, ppDevs, pcDevs);
+ }
+ RTMemFree((void *)HubPath);
+
+ if (Location)
+ RTMemFree((void *)Location);
+ if (DriverKey)
+ RTMemFree((void *)DriverKey);
+ if (Address)
+ RTMemFree((void *)Address);
+ }
+
+ /* Clean up after this device. */
+ if (ParentInstID)
+ RTMemFree((void *)ParentInstID);
+
+ ++DeviceIndex;
+ memset(&DeviceData, 0, sizeof(DeviceData));
+ DeviceData.cbSize = sizeof(DeviceData);
+ }
+
+ if (InfoSet)
+ SetupDiDestroyDeviceInfoList(InfoSet);
+
+ return VINF_SUCCESS;
+}
+
+#else
+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;
+}
+#endif
+
+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))
+ {
+ DWORD dwErr = GetLastError();
+#ifdef VBOX_WITH_ANNOYING_USB_ASSERTIONS
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AsserFailed();
+#endif
+ LogRelFunc(("SUPUSB_IOCTL_GET_DEVICE failed on '%s' (dwErr=%u)!\n", pDevInfos->szName, dwErr));
+ 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();
+ /* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_GET_DEVICE failed for '%s' (hDevice=%p, dwErr=%u)!\n", pDevInfos->szName, hDevice, dwErr));
+ 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;
+
+ LogRelFunc(("Starting USB device enumeration\n"));
+#ifdef VBOX_WITH_NEW_USB_ENUM
+ int rc = usbLibEnumDevices(ppDevs, pcDevs);
+#else
+ int rc = usbLibDevGetDevices(ppDevs, pcDevs);
+#endif
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXUSB_DEV pDevInfos = NULL;
+ uint32_t cDevInfos = 0;
+#ifdef VBOX_WITH_NEW_USB_ENUM
+ rc = usbLibEnumVUsbDevices(&pDevInfos, &cDevInfos);
+#else
+ rc = usbLibVuGetDevices(&pDevInfos, &cDevInfos);
+#endif
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbLibMonDevicesUpdate(pGlobal, *ppDevs, pDevInfos);
+ AssertRC(rc);
+ usbLibVuFreeDevices(pDevInfos);
+ }
+
+ LogRelFunc(("Found %u USB devices, %u captured\n", *pcDevs, cDevInfos));
+ 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 (%u)\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 (%u)\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();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_ADD_FILTER failed (dwErr=%u)!\n", dwErr));
+ return NULL;
+ }
+
+ if (RT_FAILURE(FltAddRc.rc))
+ {
+ AssertFailed();
+ LogRelFunc(("Adding a USB filter failed with rc=%d!\n", FltAddRc.rc));
+ return NULL;
+ }
+
+ LogRel(("Added USB filter (ID=%u, type=%d) for device %04X:%04X rev %04X, c/s/p %02X/%02X/%02X, Manufacturer=`%s' Product=`%s' Serial=`%s'\n", FltAddRc.uId, USBFilterGetFilterType(pFilter),
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID), USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ 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>"));
+
+ 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))
+ {
+ DWORD dwErr = GetLastError();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_REMOVE_FILTER failed (dwErr=%u)!\n", dwErr));
+ }
+ else
+ LogRel(("Removed USB filter ID=%u\n", uId));
+}
+
+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();
+ AssertFailed();
+ LogRelFunc(("SUPUSBFLT_IOCTL_RUN_FILTERS failed (dwErr=%u)!\n", dwErr));
+ return RTErrConvertFromWin32(dwErr);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static VOID CALLBACK usbLibTimerCallback(__in PVOID lpParameter, __in BOOLEAN TimerOrWaitFired) RT_NOTHROW_DEF
+{
+ 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 (%u)\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 (%u)\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) RT_NOTHROW_DEF
+{
+ 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;
+}
+
+
+/**
+ * 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 */
+ FALSE, /* set to false since it will be initially used for notification thread startup sync */
+ 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=%u) for `%s'\n", dwErr, USBMON_DEVICE_NAME));
+ 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
+ )
+ {
+ /*
+ * 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)
+ {
+/** @todo r=bird: Which lunatic used CreateThread here?!?
+ * Only the CRT uses CreateThread. */
+ 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=%u)\n", dwErr));
+ g_VBoxUsbGlobal.hThread = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ DWORD dwErr = GetLastError(); NOREF(dwErr);
+ AssertMsgFailed(("CreateThread failed, (dwErr=%u)\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=%u)\n", dwErr));
+ }
+ }
+ 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);
+ LogRelFunc(("SUPUSBFLT_IOCTL_GET_VERSION failed (dwErr=%u)\n", dwErr));
+ AssertFailed();
+ 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
+ {
+ AssertMsgFailed(("CreateEvent for InterruptEvent failed (dwErr=%u)\n", GetLastError()));
+ rc = VERR_GENERAL_FAILURE;
+ }
+
+ CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+ }
+ else
+ {
+ AssertMsgFailed(("CreateEvent for NotifyEvent failed (dwErr=%u)\n", GetLastError()));
+ 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;
+ fRc = PostMessage(g_VBoxUsbGlobal.hWnd, WM_CLOSE, 0, 0);
+ AssertMsg(fRc, ("PostMessage for hWnd failed (dwErr=%u)\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=%u)\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=%u)\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=%u)\n", GetLastError()));
+ }
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hMonitor);
+ AssertMsg(fRc, ("CloseHandle for hMonitor failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hMonitor = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hInterruptEvent);
+ AssertMsg(fRc, ("CloseHandle for hInterruptEvent failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hInterruptEvent = NULL;
+
+ fRc = CloseHandle(g_VBoxUsbGlobal.hNotifyEvent);
+ AssertMsg(fRc, ("CloseHandle for hNotifyEvent failed (dwErr=%u)\n", GetLastError()));
+ g_VBoxUsbGlobal.hNotifyEvent = NULL;
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
new file mode 100644
index 00000000..d54bb101
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUSBMon.inf
@@ -0,0 +1,98 @@
+; $Id: VBoxUSBMon.inf $
+;; @file
+; VBox USB Monitor driver - Installation file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox 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.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=System
+ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%ORACLE%
+;edit-DriverVer=08/26/2008,2.00.0000
+DriverPackageType=KernelService
+;cat CatalogFile=VBoxUSBMon.cat
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[DefaultInstall@DOT-NT-ARCH@]
+CopyFiles=VBoxUSBMon_CopyFiles
+
+[DefaultInstall@DOT-NT-ARCH@.Services]
+AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+;; Cannot get this to work (same works fine for VBoxDrv):
+;; [Manufacturer]
+;; %ORACLE%=VBoxUSBMon@COMMA-NT-ARCH@
+;;
+;; ; Models section (referenced by [Manufacturer]).
+;; [VBoxUSBMon@DOT-NT-ARCH@]
+;; %VBoxUSBMon.DRVDESC%=VBoxUSBMon_Install,root\VBoxUSBMon
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@]
+;; CopyFiles=VBoxUSBMon_CopyFiles
+;;
+;; [VBoxUSBMon_Install@DOT-NT-ARCH@.Services]
+;; AddService=VBoxUSBMon,0x00000002,VBoxUSBMon_Service,VBoxUSBMon_AddEventLog
+
+[SourceDisksFiles]
+VBoxUSBMon.sys=1
+
+[SourceDisksNames]
+1=%VBoxUSBMon.DSKDESC%,
+
+[VBoxUSBMon_CopyFiles]
+VBoxUSBMon.sys
+
+[VBoxUSBMon_Service]
+DisplayName = %VBoxUSBMon.SVCDESC%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+;StartType = 3 ; SERVICE_DEMAND_START
+StartType = 1 ; autostart to fix Vista problem
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxUSBMon.sys
+
+[VBoxUSBMon_AddEventLog]
+AddReg = VBoxUSBMon_AddEventLogRegistry
+
+[VBoxUSBMon_AddEventLogRegistry]
+HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+HKR,,TypesSupported,0x00010001,7
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxUSBMon.SVCDESC = "VirtualBox USB Monitor Service"
+VBoxUSBMon.DRVDESC = "VirtualBox USB Monitor Driver"
+VBoxUSBMon.DSKDESC = "VirtualBox USB Monitor Driver Installation Disk"
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
new file mode 100644
index 00000000..23d4d088
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp
@@ -0,0 +1,1763 @@
+/* $Id: VBoxUsbFlt.cpp $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <iprt/assert.h>
+
+#pragma warning(disable : 4200)
+#include "usbdi.h"
+#pragma warning(default : 4200)
+#include "usbdlib.h"
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/usblib.h>
+#include <devguid.h>
+#include <devpkey.h>
+
+
+/* We should be including ntifs.h but that's not as easy as it sounds. */
+extern "C" {
+NTKERNELAPI PDEVICE_OBJECT IoGetDeviceAttachmentBaseRef(__in PDEVICE_OBJECT DeviceObject);
+}
+
+/*
+ * state transitions:
+ *
+ * (we are not filtering this device )
+ * ADDED --> UNCAPTURED ------------------------------->-
+ * | |
+ * | (we are filtering this device, | (the device is being
+ * | waiting for our device driver | re-plugged to perform
+ * | to pick it up) | capture-uncapture transition)
+ * |-> CAPTURING -------------------------------->|---> REPLUGGING -----
+ * ^ | (device driver picked | |
+ * | | up the device) | (remove cased | (device is removed
+ * | ->---> CAPTURED ---------------------->| by "real" removal | the device info is removed form the list)
+ * | | |------------------->->--> REMOVED
+ * | | |
+ * |-----------<->---> USED_BY_GUEST ------->|
+ * | |
+ * |------------------------<-
+ *
+ * NOTE: the order of enums DOES MATTER!!
+ * Do not blindly modify!! as the code assumes the state is ordered this way.
+ */
+typedef enum
+{
+ VBOXUSBFLT_DEVSTATE_UNKNOWN = 0,
+ VBOXUSBFLT_DEVSTATE_REMOVED,
+ VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ VBOXUSBFLT_DEVSTATE_ADDED,
+ VBOXUSBFLT_DEVSTATE_UNCAPTURED,
+ VBOXUSBFLT_DEVSTATE_CAPTURING,
+ VBOXUSBFLT_DEVSTATE_CAPTURED,
+ VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ VBOXUSBFLT_DEVSTATE_32BIT_HACK = 0x7fffffff
+} VBOXUSBFLT_DEVSTATE;
+
+typedef struct VBOXUSBFLT_DEVICE
+{
+ LIST_ENTRY GlobalLe;
+ /* auxiliary list to be used for gathering devices to be re-plugged
+ * only thread that puts the device to the REPLUGGING state can use this list */
+ LIST_ENTRY RepluggingLe;
+ /* Owning session. Each matched device has an owning session. */
+ struct VBOXUSBFLTCTX *pOwner;
+ /* filter id - if NULL AND device has an owner - the filter is destroyed */
+ uintptr_t uFltId;
+ /* true iff device is filtered with a one-shot filter */
+ bool fIsFilterOneShot;
+ /* true if descriptors could not be read and only inferred from PnP Manager data */
+ bool fInferredDesc;
+ /* The device state. If the non-owner session is requesting the state while the device is grabbed,
+ * the USBDEVICESTATE_USED_BY_HOST is returned. */
+ VBOXUSBFLT_DEVSTATE enmState;
+ volatile uint32_t cRefs;
+ PDEVICE_OBJECT Pdo;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint16_t bPort;
+ uint8_t bClass;
+ uint8_t bSubClass;
+ uint8_t bProtocol;
+ char szSerial[MAX_USB_SERIAL_STRING];
+ char szMfgName[MAX_USB_SERIAL_STRING];
+ char szProduct[MAX_USB_SERIAL_STRING];
+ WCHAR szLocationPath[768];
+#if 0
+ char szDrvKeyName[512];
+ BOOLEAN fHighSpeed;
+#endif
+} VBOXUSBFLT_DEVICE, *PVBOXUSBFLT_DEVICE;
+
+#define PVBOXUSBFLT_DEVICE_FROM_LE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, GlobalLe) ) )
+#define PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, RepluggingLe) ) )
+#define PVBOXUSBFLTCTX_FROM_LE(_pLe) ( (PVBOXUSBFLTCTX)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLTCTX, ListEntry) ) )
+
+typedef struct VBOXUSBFLT_LOCK
+{
+ KSPIN_LOCK Lock;
+ KIRQL OldIrql;
+} VBOXUSBFLT_LOCK, *PVBOXUSBFLT_LOCK;
+
+#define VBOXUSBFLT_LOCK_INIT() \
+ KeInitializeSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock)
+#define VBOXUSBFLT_LOCK_TERM() do { } while (0)
+#define VBOXUSBFLT_LOCK_ACQUIRE() \
+ KeAcquireSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, &g_VBoxUsbFltGlobals.Lock.OldIrql);
+#define VBOXUSBFLT_LOCK_RELEASE() \
+ KeReleaseSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, g_VBoxUsbFltGlobals.Lock.OldIrql);
+
+
+typedef struct VBOXUSBFLT_BLDEV
+{
+ LIST_ENTRY ListEntry;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+} VBOXUSBFLT_BLDEV, *PVBOXUSBFLT_BLDEV;
+
+#define PVBOXUSBFLT_BLDEV_FROM_LE(_pLe) ( (PVBOXUSBFLT_BLDEV)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_BLDEV, ListEntry) ) )
+
+typedef struct VBOXUSBFLTGLOBALS
+{
+ LIST_ENTRY DeviceList;
+ LIST_ENTRY ContextList;
+ /* devices known to misbehave */
+ LIST_ENTRY BlackDeviceList;
+ VBOXUSBFLT_LOCK Lock;
+ /** Flag whether to force replugging a device we can't query descirptors from.
+ * Short term workaround for @bugref{9479}. */
+ ULONG dwForceReplugWhenDevPopulateFails;
+} VBOXUSBFLTGLOBALS, *PVBOXUSBFLTGLOBALS;
+static VBOXUSBFLTGLOBALS g_VBoxUsbFltGlobals;
+
+static bool vboxUsbFltBlDevMatchLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_BLDEV pDev = PVBOXUSBFLT_BLDEV_FROM_LE(pEntry);
+ if (pDev->idVendor != idVendor)
+ continue;
+ if (pDev->idProduct != idProduct)
+ continue;
+ if (pDev->bcdDevice != bcdDevice)
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
+static NTSTATUS vboxUsbFltBlDevAddLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
+{
+ if (vboxUsbFltBlDevMatchLocked(idVendor, idProduct, bcdDevice))
+ return STATUS_SUCCESS;
+ PVBOXUSBFLT_BLDEV pDev = (PVBOXUSBFLT_BLDEV)VBoxUsbMonMemAllocZ(sizeof (*pDev));
+ if (!pDev)
+ {
+ AssertFailed();
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pDev->idVendor = idVendor;
+ pDev->idProduct = idProduct;
+ pDev->bcdDevice = bcdDevice;
+ InsertHeadList(&g_VBoxUsbFltGlobals.BlackDeviceList, &pDev->ListEntry);
+ return STATUS_SUCCESS;
+}
+
+static void vboxUsbFltBlDevClearLocked()
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ VBoxUsbMonMemFree(pEntry);
+ }
+}
+
+static void vboxUsbFltBlDevPopulateWithKnownLocked()
+{
+ /* this one halts when trying to get string descriptors from it */
+ vboxUsbFltBlDevAddLocked(0x5ac, 0x921c, 0x115);
+}
+
+
+DECLINLINE(void) vboxUsbFltDevRetain(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(pDevice->cRefs);
+ ASMAtomicIncU32(&pDevice->cRefs);
+}
+
+static void vboxUsbFltDevDestroy(PVBOXUSBFLT_DEVICE pDevice)
+{
+ Assert(!pDevice->cRefs);
+ Assert(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED);
+ VBoxUsbMonMemFree(pDevice);
+}
+
+DECLINLINE(void) vboxUsbFltDevRelease(PVBOXUSBFLT_DEVICE pDevice)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pDevice->cRefs);
+ Assert(cRefs < UINT32_MAX/2);
+ if (!cRefs)
+ {
+ vboxUsbFltDevDestroy(pDevice);
+ }
+}
+
+static void vboxUsbFltDevOwnerSetLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ ASSERT_WARN(!pDevice->pOwner, ("device 0x%p has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ++pContext->cActiveFilters;
+ pDevice->pOwner = pContext;
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+}
+
+static void vboxUsbFltDevOwnerClearLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ ASSERT_WARN(pDevice->pOwner, ("no owner for device 0x%p", pDevice));
+ --pDevice->pOwner->cActiveFilters;
+ ASSERT_WARN(pDevice->pOwner->cActiveFilters < UINT32_MAX/2, ("cActiveFilters (%d)", pDevice->pOwner->cActiveFilters));
+ pDevice->pOwner = NULL;
+ pDevice->uFltId = 0;
+}
+
+static void vboxUsbFltDevOwnerUpdateLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
+{
+ if (pDevice->pOwner != pContext)
+ {
+ if (pDevice->pOwner)
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+ if (pContext)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pContext, uFltId, fIsOneShot);
+ }
+ else if (pContext)
+ {
+ pDevice->uFltId = uFltId;
+ pDevice->fIsFilterOneShot = fIsOneShot;
+ }
+}
+
+static PVBOXUSBFLT_DEVICE vboxUsbFltDevGetLocked(PDEVICE_OBJECT pPdo)
+{
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ for (PLIST_ENTRY pEntry2 = pEntry->Flink;
+ pEntry2 != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry2 = pEntry2->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice2 = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry2);
+ ASSERT_WARN( pDevice->idVendor != pDevice2->idVendor
+ || pDevice->idProduct != pDevice2->idProduct
+ || pDevice->bcdDevice != pDevice2->bcdDevice, ("duplicate devices in a list!!"));
+ }
+ }
+#endif
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ ASSERT_WARN( pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
+ ("Invalid device state(%d) for device(0x%p) PDO(0x%p)", pDevice->enmState, pDevice, pDevice->Pdo));
+ if (pDevice->Pdo == pPdo)
+ return pDevice;
+ }
+ return NULL;
+}
+
+static NTSTATUS vboxUsbFltPdoReplug(PDEVICE_OBJECT pDo)
+{
+ LOG(("Replugging PDO(0x%p)", pDo));
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_CYCLE_PORT, NULL, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("replugging PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Replugging PDO(0x%p) done with Status(0x%x)", pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCanBeCaptured(PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (pDevice->bClass == USB_DEVICE_CLASS_HUB)
+ {
+ LOG(("device (0x%p), pdo (0x%p) is a hub, can not be captured", pDevice, pDevice->Pdo));
+ return false;
+ }
+ return true;
+}
+
+static PVBOXUSBFLTCTX vboxUsbFltDevMatchLocked(PVBOXUSBFLT_DEVICE pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
+{
+ *puId = 0;
+ *pfFilter = false;
+ *pfIsOneShot = false;
+ if (!vboxUsbFltDevCanBeCaptured(pDevice))
+ {
+ LOG(("vboxUsbFltDevCanBeCaptured returned false"));
+ return NULL;
+ }
+
+ USBFILTER DevFlt;
+ USBFilterInit(&DevFlt, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_VENDOR_ID, pDevice->idVendor, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PRODUCT_ID, pDevice->idProduct, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_REV, pDevice->bcdDevice, true);
+
+ /* If we could not read a string descriptor, don't set the filter item at all. */
+ if (pDevice->szMfgName[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_MANUFACTURER_STR, pDevice->szMfgName, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szProduct[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_PRODUCT_STR, pDevice->szProduct, true /*fMustBePresent*/, true /*fPurge*/);
+ if (pDevice->szSerial[0])
+ USBFilterSetStringExact(&DevFlt, USBFILTERIDX_SERIAL_NUMBER_STR, pDevice->szSerial, true /*fMustBePresent*/, true /*fPurge*/);
+
+ /* If device descriptor had to be inferred from PnP Manager data, the class/subclass/protocol may be wrong.
+ * When Windows reports CompatibleIDs 'USB\Class_03&SubClass_00&Prot_00', the device descriptor might be
+ * reporting class 3 (HID), *or* the device descriptor might be reporting class 0 (specified by interface)
+ * and the device's interface reporting class 3. Ignore the class/subclass/protocol in such case, since
+ * we are more or less guaranteed to rely on VID/PID anyway.
+ * See @bugref{9479}.
+ */
+ if (pDevice->fInferredDesc)
+ {
+ LOG(("Device descriptor was not read, only inferred; ignoring class/subclass/protocol!"));
+ }
+ else
+ {
+ LOG(("Setting filter class/subclass/protocol %02X/%02X/%02X\n", pDevice->bClass, pDevice->bSubClass, pDevice->bProtocol));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_CLASS, pDevice->bClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_SUB_CLASS, pDevice->bSubClass, true);
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_PROTOCOL, pDevice->bProtocol, true);
+ }
+
+ /* If the port number looks valid, add it to the filter. */
+ if (pDevice->bPort)
+ {
+ LOG(("Setting filter port %04X\n", pDevice->bPort));
+ USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PORT, pDevice->bPort, true);
+ }
+ else
+ LOG(("Port number not known, ignoring!"));
+
+ /* Run filters on the thing. */
+ PVBOXUSBFLTCTX pOwner = VBoxUSBFilterMatchEx(&DevFlt, puId, fRemoveFltIfOneShot, pfFilter, pfIsOneShot);
+ USBFilterDelete(&DevFlt);
+ return pOwner;
+}
+
+static void vboxUsbFltDevStateMarkReplugLocked(PVBOXUSBFLT_DEVICE pDevice)
+{
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, NULL, 0, false);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REPLUGGING;
+}
+
+static bool vboxUsbFltDevStateIsNotFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+}
+
+static bool vboxUsbFltDevStateIsFiltered(PVBOXUSBFLT_DEVICE pDevice)
+{
+ return pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+static uint16_t vboxUsbParseHexNumU16(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (12 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static uint8_t vboxUsbParseHexNumU8(WCHAR **ppStr)
+{
+ WCHAR *pStr = *ppStr;
+ WCHAR wc;
+ uint16_t num = 0;
+ unsigned u;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ if (!*pStr) /* Just in case the string is too short. */
+ break;
+
+ wc = *pStr;
+ u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
+ num |= u << (4 - 4 * i);
+ pStr++;
+ }
+ *ppStr = pStr;
+
+ return num;
+}
+
+static bool vboxUsbParseHardwareID(WCHAR *pchIdStr, uint16_t *pVid, uint16_t *pPid, uint16_t *pRev)
+{
+#define VID_PREFIX L"USB\\VID_"
+#define PID_PREFIX L"&PID_"
+#define REV_PREFIX L"&REV_"
+
+ *pVid = *pPid = *pRev = 0xFFFF;
+
+ /* The Hardware ID is in the format USB\VID_xxxx&PID_xxxx&REV_xxxx, with 'xxxx'
+ * being 16-bit hexadecimal numbers. The string is coming from the
+ * Windows PnP manager so OEMs should have no opportunity to mess it up.
+ */
+
+ if (wcsncmp(pchIdStr, VID_PREFIX, wcslen(VID_PREFIX)))
+ return false;
+ /* Point to the start of the vendor ID number and parse it. */
+ pchIdStr += wcslen(VID_PREFIX);
+ *pVid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PID_PREFIX, wcslen(PID_PREFIX)))
+ return false;
+ /* Point to the start of the product ID number and parse it. */
+ pchIdStr += wcslen(PID_PREFIX);
+ *pPid = vboxUsbParseHexNumU16(&pchIdStr);
+
+ /* The revision might not be there; the Windows documentation is not
+ * entirely clear if it will be always present for USB devices or not.
+ * If it's not there, still consider this a success. */
+ if (wcsncmp(pchIdStr, REV_PREFIX, wcslen(REV_PREFIX)))
+ return true;
+
+ /* Point to the start of the revision number and parse it. */
+ pchIdStr += wcslen(REV_PREFIX);
+ *pRev = vboxUsbParseHexNumU16(&pchIdStr);
+
+ return true;
+#undef VID_PREFIX
+#undef PID_PREFIX
+#undef REV_PREFIX
+}
+
+static bool vboxUsbParseCompatibleIDs(WCHAR *pchIdStr, uint8_t *pClass, uint8_t *pSubClass, uint8_t *pProt)
+{
+#define CLS_PREFIX L"USB\\Class_"
+#define SUB_PREFIX L"&SubClass_"
+#define PRO_PREFIX L"&Prot_"
+
+ *pClass = *pSubClass = *pProt = 0xFF;
+
+ /* The Compatible IDs string is in the format USB\Class_xx&SubClass_xx&Prot_xx,
+ * with 'xx' being 8-bit hexadecimal numbers. Since this string is provided by the
+ * PnP manager and USB devices always report these as part of the basic USB device
+ * descriptor, we assume all three must be present.
+ */
+
+ if (wcsncmp(pchIdStr, CLS_PREFIX, wcslen(CLS_PREFIX)))
+ return false;
+ /* Point to the start of the device class and parse it. */
+ pchIdStr += wcslen(CLS_PREFIX);
+ *pClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, SUB_PREFIX, wcslen(SUB_PREFIX)))
+ return false;
+
+ /* Point to the start of the subclass and parse it. */
+ pchIdStr += wcslen(SUB_PREFIX);
+ *pSubClass = vboxUsbParseHexNumU8(&pchIdStr);
+
+ if (wcsncmp(pchIdStr, PRO_PREFIX, wcslen(PRO_PREFIX)))
+ return false;
+
+ /* Point to the start of the protocol and parse it. */
+ pchIdStr += wcslen(PRO_PREFIX);
+ *pProt = vboxUsbParseHexNumU8(&pchIdStr);
+
+ return true;
+#undef CLS_PREFIX
+#undef SUB_PREFIX
+#undef PRO_PREFIX
+}
+
+#define VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS 10000
+
+static NTSTATUS vboxUsbFltDevPopulate(PVBOXUSBFLT_DEVICE pDevice, PDEVICE_OBJECT pDo /*, BOOLEAN bPopulateNonFilterProps*/)
+{
+ NTSTATUS Status;
+ USB_TOPOLOGY_ADDRESS TopoAddr;
+ PUSB_DEVICE_DESCRIPTOR pDevDr = 0;
+ ULONG ulResultLen;
+ DEVPROPTYPE type;
+ WCHAR wchPropBuf[256];
+ uint16_t port;
+ bool rc;
+
+ pDevice->Pdo = pDo;
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p)", pDevice, pDo));
+
+ pDevDr = (PUSB_DEVICE_DESCRIPTOR)VBoxUsbMonMemAllocZ(sizeof(*pDevDr));
+ if (pDevDr == NULL)
+ {
+ WARN(("Failed to alloc mem for urb"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ do
+ {
+ pDevice->fInferredDesc = false;
+ Status = VBoxUsbToolGetDescriptor(pDo, pDevDr, sizeof(*pDevDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ WARN(("getting device descriptor failed, Status (0x%x); falling back to IoGetDeviceProperty", Status));
+
+ /* Try falling back to IoGetDevicePropertyData. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_HardwareIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This just isn't our day. We have no idea what the device is. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_HardwareIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseHardwareID(wchPropBuf, &vid, &pid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ /* Now grab the Compatible IDs to get the class/subclass/protocol. */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_CompatibleIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We really kind of need these. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_CompatibleIds, Status (0x%x)", Status));
+ break;
+ }
+ rc = vboxUsbParseCompatibleIDs(wchPropBuf, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ break;
+ }
+
+ LOG(("Parsed HardwareID: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+ if (vid == 0xFFFF || pid == 0xFFFF)
+ break;
+
+ LOG(("Successfully fell back to IoGetDeviceProperty result"));
+ pDevDr->idVendor = vid;
+ pDevDr->idProduct = pid;
+ pDevDr->bcdDevice = rev;
+ pDevDr->bDeviceClass = cls;
+ pDevDr->bDeviceSubClass = sub;
+ pDevDr->bDeviceProtocol = prt;
+
+ /* The USB device class/subclass/protocol may not be accurate. We have to be careful when comparing
+ * and not take mismatches too seriously.
+ */
+ pDevice->fInferredDesc = true;
+ }
+
+ /* Query the location path. The path is purely a function of the physical device location
+ * and does not change if the device changes, and also does not change depending on
+ * whether the device is captured or not.
+ * NB: We ignore any additional strings and only look at the first one.
+ */
+ Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_LocationPaths, LOCALE_NEUTRAL, 0, sizeof(pDevice->szLocationPath), pDevice->szLocationPath, &ulResultLen, &type);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We do need this, but not critically. On Windows 7, we may get STATUS_OBJECT_NAME_NOT_FOUND. */
+ WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_LocationPaths, Status (0x%x)", Status));
+ }
+ else
+ {
+ LOG_STRW(pDevice->szLocationPath);
+ }
+
+ // Disabled, but could be used as a fallback instead of IoGetDevicePropertyData; it should work even
+ // when this code is entered from the PnP IRP processing path.
+#if 0
+ {
+ HUB_DEVICE_CONFIG_INFO HubInfo;
+
+ memset(&HubInfo, 0, sizeof(HubInfo));
+ HubInfo.Version = 1;
+ HubInfo.Length = sizeof(HubInfo);
+
+ NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_DEVICE_CONFIG_INFO, &HubInfo, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_DEVICE_CONFIG_INFO for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying hub device config info for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t vid, pid, rev;
+ uint8_t cls, sub, prt;
+
+ LOG(("Hub flags: %X\n", HubInfo.HubFlags));
+ LOG_STRW(HubInfo.HardwareIds.Buffer);
+ LOG_STRW(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ LOG_STRW(HubInfo.DeviceDescription.Buffer);
+
+ rc = vboxUsbParseHardwareID(HubInfo.HardwareIds.Buffer, &pid, &vid, &rev);
+ if (!rc)
+ {
+ /* This *really* should not happen. */
+ WARN(("Failed to parse Hardware ID"));
+ }
+
+ /* The CompatibleID the IOCTL gives is not always the same as what the PnP Manager uses
+ * (thanks, Microsoft). It might look like "USB\DevClass_00&SubClass_00&Prot_00" or like
+ * "USB\USB30_HUB". In such cases, we must consider the class/subclass/protocol
+ * information simply unavailable.
+ */
+ rc = vboxUsbParseCompatibleIDs(HubInfo.CompatibleIds.Buffer, &cls, &sub, &prt);
+ if (!rc)
+ {
+ /* This is unfortunate but not fatal. */
+ WARN(("Failed to parse Compatible ID"));
+ }
+ LOG(("Parsed HardwareID from IOCTL: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
+
+ ExFreePool(HubInfo.HardwareIds.Buffer);
+ ExFreePool(HubInfo.CompatibleIds.Buffer);
+ if (HubInfo.DeviceDescription.Buffer)
+ ExFreePool(HubInfo.DeviceDescription.Buffer);
+ }
+ }
+#endif
+
+ /* Query the topology address from the hub driver. This is not trivial to translate to the location
+ * path, but at least we can get the port number this way.
+ */
+ memset(&TopoAddr, 0, sizeof(TopoAddr));
+ Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_TOPOLOGY_ADDRESS, &TopoAddr, NULL);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_TOPOLOGY_ADDRESS for PDO(0x%p) failed Status(0x%x)", pDo, Status));
+ LOG(("Querying topology address for PDO(0x%p) done with Status(0x%x)", pDo, Status));
+
+ port = 0;
+ if (Status == STATUS_SUCCESS)
+ {
+ uint16_t *pPort = &TopoAddr.RootHubPortNumber;
+
+ /* The last non-zero port number is the one we're looking for. It might be on the
+ * root hub directly, or on some downstream hub.
+ */
+ for (int i = 0; i < RT_ELEMENTS(TopoAddr.HubPortNumber) + 1; ++i) {
+ if (*pPort)
+ port = *pPort;
+ pPort++;
+ }
+ LOG(("PCI bus/dev/fn: %02X:%02X:%02X, parsed port: %u\n", TopoAddr.PciBusNumber, TopoAddr.PciDeviceNumber, TopoAddr.PciFunctionNumber, port));
+ LOG(("RH port: %u, hub ports: %u/%u/%u/%u/%u/%u\n", TopoAddr.RootHubPortNumber, TopoAddr.HubPortNumber[0],
+ TopoAddr.HubPortNumber[1], TopoAddr.HubPortNumber[2], TopoAddr.HubPortNumber[3], TopoAddr.HubPortNumber[4], TopoAddr.HubPortNumber[5]));
+
+ /* In the extremely unlikely case that the port number does not fit into 8 bits, force
+ * it to zero to indicate that we can't use it.
+ */
+ if (port > 255)
+ port = 0;
+ }
+
+ if (vboxUsbFltBlDevMatchLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice))
+ {
+ WARN(("found a known black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ LOG(("Device pid=%x vid=%x rev=%x port=%x", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice, port));
+ pDevice->bPort = port;
+ pDevice->idVendor = pDevDr->idVendor;
+ pDevice->idProduct = pDevDr->idProduct;
+ pDevice->bcdDevice = pDevDr->bcdDevice;
+ pDevice->bClass = pDevDr->bDeviceClass;
+ pDevice->bSubClass = pDevDr->bDeviceSubClass;
+ pDevice->bProtocol = pDevDr->bDeviceProtocol;
+ pDevice->szSerial[0] = 0;
+ pDevice->szMfgName[0] = 0;
+ pDevice->szProduct[0] = 0;
+
+ /* If there are no strings, don't even try to get any string descriptors. */
+ if (pDevDr->iSerialNumber || pDevDr->iManufacturer || pDevDr->iProduct)
+ {
+ int langId;
+
+ Status = VBoxUsbToolGetLangID(pDo, &langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading language ID failed"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+
+ if (pDevDr->iSerialNumber)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szSerial, sizeof (pDevice->szSerial), pDevDr->iSerialNumber, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading serial number failed"));
+ ASSERT_WARN(pDevice->szSerial[0] == '\0', ("serial is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iManufacturer)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szMfgName, sizeof (pDevice->szMfgName), pDevDr->iManufacturer, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading manufacturer name failed"));
+ ASSERT_WARN(pDevice->szMfgName[0] == '\0', ("szMfgName is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (pDevDr->iProduct)
+ {
+ Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szProduct, sizeof (pDevice->szProduct), pDevDr->iProduct, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("reading product name failed"));
+ ASSERT_WARN(pDevice->szProduct[0] == '\0', ("szProduct is not zero!!"));
+ if (Status == STATUS_CANCELLED)
+ {
+ WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
+ vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ LOG(("pretending success.."));
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ LOG((": strings: '%s':'%s':'%s' (lang ID %x)",
+ pDevice->szMfgName, pDevice->szProduct, pDevice->szSerial, langId));
+ }
+
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Succeeded", pDevice, pDo));
+ Status = STATUS_SUCCESS;
+ } while (0);
+
+ VBoxUsbMonMemFree(pDevDr);
+ LOG(("Populating Device(0x%p) for PDO(0x%p) Done, Status (0x%x)", pDevice, pDo, Status));
+ return Status;
+}
+
+static bool vboxUsbFltDevCheckReplugLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext)
+{
+ ASSERT_WARN(pContext, ("context is NULL!"));
+
+ LOG(("Current context is (0x%p)", pContext));
+ LOG(("Current Device owner is (0x%p)", pDevice->pOwner));
+
+ /* check if device is already replugging */
+ if (pDevice->enmState <= VBOXUSBFLT_DEVSTATE_ADDED)
+ {
+ LOG(("Device (0x%p) is already replugging, return..", pDevice));
+ /* it is, do nothing */
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING,
+ ("Device (0x%p) state is NOT REPLUGGING (%d)", pDevice, pDevice->enmState));
+ return false;
+ }
+
+ if (pDevice->pOwner && pContext != pDevice->pOwner)
+ {
+ LOG(("Device (0x%p) is owned by another context(0x%p), current is(0x%p)", pDevice, pDevice->pOwner, pContext));
+ /* this device is owned by another context, we're not allowed to do anything */
+ return false;
+ }
+
+ uintptr_t uId = 0;
+ bool bNeedReplug = false;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pNewOwner = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), NewOwner(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pNewOwner, (int)fFilter, (int)fIsOneShot));
+ if (pDevice->pOwner && pNewOwner && pDevice->pOwner != pNewOwner)
+ {
+ LOG(("Matching: Device (0x%p) is requested another owner(0x%p), current is(0x%p)", pDevice, pNewOwner, pDevice->pOwner));
+ /* the device is owned by another owner, we can not change the owner here */
+ return false;
+ }
+
+ if (!fFilter)
+ {
+ LOG(("Matching: Device (0x%p) should NOT be filtered", pDevice));
+ /* the device should NOT be filtered, check the current state */
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) is NOT filtered", pDevice));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ ASSERT_WARN(pNewOwner, ("no new owner"));
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ if (!pDevice->pOwner)
+ {
+ LOG(("Matching: updating the one-shot owner to (0x%p), fltId(0x%p)", pNewOwner, uId));
+ /* update owner for one-shot if the owner is changed (i.e. assigned) */
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, true);
+ }
+ else
+ {
+ LOG(("Matching: device already has owner (0x%p) assigned", pDevice->pOwner));
+ }
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), newOwner(0x%p)", uId, pNewOwner));
+ if (pNewOwner)
+ {
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, false);
+ }
+ }
+ }
+ else
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is currently filtered, we should release it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be released bue to a one-shot filter
+ * or
+ * 3. it is NOT grabbed by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ LOG(("Matching: Need replug"));
+ bNeedReplug = true;
+ }
+ }
+ }
+ else
+ {
+ LOG(("Matching: Device (0x%p) SHOULD be filtered", pDevice));
+ /* the device should be filtered, check the current state */
+ ASSERT_WARN(uId, ("zero uid"));
+ ASSERT_WARN(pNewOwner, ("zero pNewOwner"));
+ if (vboxUsbFltDevStateIsFiltered(pDevice))
+ {
+ LOG(("Device (0x%p) IS filtered", pDevice));
+ /* the device is filtered */
+ if (pNewOwner == pDevice->pOwner)
+ {
+ LOG(("Device owner match"));
+ /* no changes */
+ if (fIsOneShot)
+ {
+ LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
+ /* remove a one-shot filter and keep the original filter data */
+ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
+ ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
+ }
+ else
+ {
+ LOG(("Matching: This is NOT a one-shot filter (0x%p), Owner(0x%p)", uId, pDevice->pOwner));
+ vboxUsbFltDevOwnerUpdateLocked(pDevice, pDevice->pOwner, uId, false);
+ }
+ }
+ else
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("device should NOT have owner"));
+ LOG(("Matching: Need replug"));
+ /* the device needs to be filtered, but the owner changes, replug needed */
+ bNeedReplug = true;
+ }
+ }
+ else
+ {
+ /* the device is currently NOT filtered,
+ * we should replug it only if
+ * 1. device does not have an owner
+ * or
+ * 2. it should be captured due to a one-shot filter
+ * or
+ * 3. it is NOT released by a one-shot filter */
+ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
+ {
+ bNeedReplug = true;
+ LOG(("Matching: Need replug"));
+ }
+ }
+ }
+
+ if (bNeedReplug)
+ {
+ LOG(("Matching: Device needs replugging, marking as such"));
+ vboxUsbFltDevStateMarkReplugLocked(pDevice);
+ }
+ else
+ {
+ LOG(("Matching: Device does NOT need replugging"));
+ }
+
+ return bNeedReplug;
+}
+
+static void vboxUsbFltReplugList(PLIST_ENTRY pList)
+{
+ PLIST_ENTRY pNext;
+ for (PLIST_ENTRY pEntry = pList->Flink;
+ pEntry != pList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(pEntry);
+ LOG(("replugging matched PDO(0x%p), pDevice(0x%p)", pDevice->Pdo, pDevice));
+ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED,
+ ("invalid state(0x%x) for device(0x%p)", pDevice->enmState, pDevice));
+
+ vboxUsbFltPdoReplug(pDevice->Pdo);
+ ObDereferenceObject(pDevice->Pdo);
+ vboxUsbFltDevRelease(pDevice);
+ }
+}
+
+typedef struct VBOXUSBFLTCHECKWALKER
+{
+ PVBOXUSBFLTCTX pContext;
+} VBOXUSBFLTCHECKWALKER, *PVBOXUSBFLTCHECKWALKER;
+
+static DECLCALLBACK(BOOLEAN) vboxUsbFltFilterCheckWalker(PFILE_OBJECT pHubFile,
+ PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ PVBOXUSBFLTCHECKWALKER pData = (PVBOXUSBFLTCHECKWALKER)pvContext;
+ PVBOXUSBFLTCTX pContext = pData->pContext;
+
+ LOG(("Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ PDEVICE_RELATIONS pDevRelations = NULL;
+
+ NTSTATUS Status = VBoxUsbMonQueryBusRelations(pHubDo, pHubFile, &pDevRelations);
+ if (Status == STATUS_SUCCESS && pDevRelations)
+ {
+ ULONG cReplugPdos = pDevRelations->Count;
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ PDEVICE_OBJECT pDevObj;
+
+ /* Grab the PDO+reference. We won't need the upper layer device object
+ * anymore, so dereference that right here, and drop the PDO ref later.
+ */
+ pDevObj = IoGetDeviceAttachmentBaseRef(pDevRelations->Objects[k]);
+ LOG(("DevObj=%p, PDO=%p\n", pDevRelations->Objects[k], pDevObj));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ pDevRelations->Objects[k] = pDevObj;
+
+ LOG(("Found existing USB PDO 0x%p", pDevObj));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pDevObj);
+ if (pDevice)
+ {
+ LOG(("Found existing device info (0x%p) for PDO 0x%p", pDevice, pDevObj));
+ bool bReplug = vboxUsbFltDevCheckReplugLocked(pDevice, pContext);
+ if (bReplug)
+ {
+ LOG(("Replug needed for device (0x%p)", pDevice));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ vboxUsbFltDevRetain(pDevice);
+ /* do not dereference object since we will use it later */
+ }
+ else
+ {
+ LOG(("Replug NOT needed for device (0x%p)", pDevice));
+ ObDereferenceObject(pDevObj);
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos(%d) state broken", cReplugPdos));
+ continue;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("NO device info found for PDO 0x%p", pDevObj));
+ VBOXUSBFLT_DEVICE Device;
+ Status = vboxUsbFltDevPopulate(&Device, pDevObj /*, FALSE /* only need filter properties */);
+ if (NT_SUCCESS(Status))
+ {
+ uintptr_t uId = 0;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ PVBOXUSBFLTCTX pCtx = vboxUsbFltDevMatchLocked(&Device, &uId,
+ false, /* do not remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ VBOXUSBFLT_LOCK_RELEASE();
+ NOREF(pCtx);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Matching: This device SHOULD be filtered"));
+ /* this device needs to be filtered, but it's not,
+ * leave the PDO in array to issue a replug request for it
+ * later on */
+ continue;
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbFltDevPopulate for PDO 0x%p failed with Status 0x%x", pDevObj, Status));
+ if ( Status == STATUS_CANCELLED
+ && g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ {
+ /*
+ * This can happen if the device got suspended and is in D3 state where we can't query any strings.
+ * There is no known way to set the power state of the device, especially if there is no driver attached yet.
+ * The sledgehammer approach is to just replug the device to force it out of suspend, see bugref @{9479}.
+ */
+ continue;
+ }
+ }
+
+ LOG(("Matching: This device should NOT be filtered"));
+ /* this device should not be filtered, and it's not */
+ ObDereferenceObject(pDevObj);
+ pDevRelations->Objects[k] = NULL;
+ --cReplugPdos;
+ ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos is %d", cReplugPdos));
+ }
+
+ LOG(("(%d) non-matched PDOs to be replugged", cReplugPdos));
+
+ if (cReplugPdos)
+ {
+ for (ULONG k = 0; k < pDevRelations->Count; ++k)
+ {
+ if (!pDevRelations->Objects[k])
+ continue;
+
+ Status = vboxUsbFltPdoReplug(pDevRelations->Objects[k]);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("vboxUsbFltPdoReplug failed! Status(0x%x)", Status));
+ ObDereferenceObject(pDevRelations->Objects[k]);
+ if (!--cReplugPdos)
+ break;
+ }
+
+ ASSERT_WARN(!cReplugPdos, ("cReplugPdos reached zero!"));
+ }
+
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ ExFreePool(pDevRelations);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonQueryBusRelations failed for hub DO(0x%p), Status(0x%x), pDevRelations(0x%p)",
+ pHubDo, Status, pDevRelations));
+ }
+
+ LOG(("Done Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
+
+ return TRUE;
+}
+
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext)
+{
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
+
+ LOG(("Running filters, Context (0x%p)..", pContext));
+
+ VBOXUSBFLTCHECKWALKER Data;
+ Data.pContext = pContext;
+ vboxUsbMonHubDevWalk(vboxUsbFltFilterCheckWalker, &Data);
+
+ LOG(("DONE Running filters, Context (0x%p)", pContext));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Closing context(0x%p)", pContext));
+ LIST_ENTRY ReplugDevList;
+ InitializeListHead(&ReplugDevList);
+
+ ASSERT_WARN(pContext, ("null context"));
+
+ KIRQL Irql = KeGetCurrentIrql();
+ ASSERT_WARN(Irql == PASSIVE_LEVEL, ("irql==(%d)", Irql));
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ pContext->bRemoved = TRUE;
+ RemoveEntryList(&pContext->ListEntry);
+
+ LOG(("removing owner filters"));
+ /* now re-arrange the filters */
+ /* 1. remove filters */
+ VBoxUSBFilterRemoveOwner(pContext);
+
+ LOG(("enumerating devices.."));
+ /* 2. check if there are devices owned */
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+
+ vboxUsbFltDevOwnerClearLocked(pDevice);
+
+ if (vboxUsbFltDevCheckReplugLocked(pDevice, pContext))
+ {
+ LOG(("device needs replug"));
+ InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
+ /* retain to ensure the device is not removed before we issue a replug */
+ vboxUsbFltDevRetain(pDevice);
+ /* keep the PDO alive */
+ ObReferenceObject(pDevice->Pdo);
+ }
+ else
+ {
+ LOG(("device does NOT need replug"));
+ }
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* this should replug all devices that were either skipped or grabbed due to the context's */
+ vboxUsbFltReplugList(&ReplugDevList);
+
+ LOG(("SUCCESS done context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext)
+{
+ LOG(("Creating context(0x%p)", pContext));
+ memset(pContext, 0, sizeof (*pContext));
+ pContext->Process = RTProcSelf();
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ InsertHeadList(&g_VBoxUsbFltGlobals.ContextList, &pContext->ListEntry);
+ VBOXUSBFLT_LOCK_RELEASE();
+ LOG(("SUCCESS context(0x%p)", pContext));
+ return STATUS_SUCCESS;
+}
+
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+ LOG(("adding filter, Context (0x%p)..", pContext));
+ *pId = 0;
+ /* LOG the filter details. */
+ LOG((__FUNCTION__": %s %s %s",
+ 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>"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x Type%#x",
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PORT),
+ USBFilterGetFilterType(pFilter)));
+#endif
+
+ /* We can't get the bus/port numbers. Ignore them while matching. */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PORT, false);
+
+ /* We may not be able to reconstruct the class/subclass/protocol if we aren't able to
+ * read the device descriptor. Don't require these to be present. See also the fInferredDesc flag.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_PROTOCOL, false);
+
+ /* We may also be unable to read string descriptors. Often the userland can't read the
+ * string descriptors either because the device is in a low-power state, but it can happen
+ * that the userland gets lucky and reads the strings, but by the time we get to read them
+ * they're inaccessible due to power management. So, don't require the strings to be present.
+ */
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_MANUFACTURER_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PRODUCT_STR, false);
+ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR, false);
+
+ uintptr_t uId = 0;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* Add the filter. */
+ int rc = VBoxUSBFilterAdd(pFilter, pContext, &uId);
+ VBOXUSBFLT_LOCK_RELEASE();
+ if (RT_SUCCESS(rc))
+ {
+ LOG(("ADDED filter id 0x%p", uId));
+ ASSERT_WARN(uId, ("uid is NULL"));
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ else
+ {
+ WARN(("VBoxUSBFilterAdd failed rc (%d)", rc));
+ ASSERT_WARN(!uId, ("uid is not NULL"));
+ }
+
+ *pId = uId;
+ return rc;
+}
+
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId)
+{
+ LOG(("removing filter id(0x%p), Context (0x%p)..", pContext, uId));
+ Assert(uId);
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ int rc = VBoxUSBFilterRemove(pContext, uId);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("VBoxUSBFilterRemove failed rc (%d)", rc));
+ VBOXUSBFLT_LOCK_RELEASE();
+ return rc;
+ }
+
+ LOG(("enumerating devices.."));
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ if (pDevice->fIsFilterOneShot)
+ {
+ ASSERT_WARN(!pDevice->uFltId, ("oneshot filter on device(0x%p): unexpected uFltId(%d)", pDevice, pDevice->uFltId));
+ }
+
+ if (pDevice->uFltId != uId)
+ continue;
+
+ ASSERT_WARN(pDevice->pOwner == pContext, ("Device(0x%p) owner(0x%p) not match to (0x%p)", pDevice, pDevice->pOwner, pContext));
+ if (pDevice->pOwner != pContext)
+ continue;
+
+ LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
+ pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
+ ASSERT_WARN(!pDevice->fIsFilterOneShot, ("device(0x%p) is filtered with a oneshot filter", pDevice));
+ pDevice->uFltId = 0;
+ /* clear the fIsFilterOneShot flag to ensure the device is replugged on the next VBoxUsbFltFilterCheck call */
+ pDevice->fIsFilterOneShot = false;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ LOG(("done enumerating devices"));
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
+ VBoxUsbFltFilterCheck();
+#endif
+ }
+ return rc;
+}
+
+static USBDEVICESTATE vboxUsbDevGetUserState(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice)
+{
+ if (vboxUsbFltDevStateIsNotFiltered(pDevice))
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+
+ /* the device is filtered, or replugging */
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ ASSERT_WARN(!pDevice->pOwner, ("replugging device(0x%p) still has an owner(0x%p)", pDevice, pDevice->pOwner));
+ ASSERT_WARN(!pDevice->uFltId, ("replugging device(0x%p) still has filter(0x%p)", pDevice, pDevice->uFltId));
+ /* no user state for this, we should not return it tu the user */
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ /* the device is filtered, if owner differs from the context, return as USED_BY_HOST */
+ ASSERT_WARN(pDevice->pOwner, ("device(0x%p) has noowner", pDevice));
+ /* the id can be null if a filter is removed */
+// Assert(pDevice->uFltId);
+
+ if (pDevice->pOwner != pContext)
+ {
+ LOG(("Device owner differs from the current context, returning used by host"));
+ return USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ switch (pDevice->enmState)
+ {
+ case VBOXUSBFLT_DEVSTATE_UNCAPTURED:
+ case VBOXUSBFLT_DEVSTATE_CAPTURING:
+ return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+ case VBOXUSBFLT_DEVSTATE_CAPTURED:
+ return USBDEVICESTATE_HELD_BY_PROXY;
+ case VBOXUSBFLT_DEVSTATE_USED_BY_GUEST:
+ return USBDEVICESTATE_USED_BY_GUEST;
+ default:
+ WARN(("unexpected device state(%d) for device(0x%p)", pDevice->enmState, pDevice));
+ return USBDEVICESTATE_UNSUPPORTED;
+ }
+}
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ if (!hDevice)
+ return STATUS_INVALID_PARAMETER;
+
+ memset (pInfo, 0, sizeof (*pInfo));
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pEntry->Flink)
+ {
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED);
+ Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED);
+
+ if (pDevice != hDevice)
+ continue;
+
+ USBDEVICESTATE enmUsrState = vboxUsbDevGetUserState(pContext, pDevice);
+ pInfo->enmState = enmUsrState;
+ VBOXUSBFLT_LOCK_RELEASE();
+ return STATUS_SUCCESS;
+ }
+
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ /* We should not get this far with valid input. */
+ return STATUS_INVALID_PARAMETER;
+}
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered)
+{
+ *pbFiltered = FALSE;
+ PVBOXUSBFLT_DEVICE pDevice;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ /* first check if device is in the a already */
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ return STATUS_SUCCESS;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ pDevice = (PVBOXUSBFLT_DEVICE)VBoxUsbMonMemAllocZ(sizeof (*pDevice));
+ if (!pDevice)
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ ObDereferenceObject(pPdo);
+ return STATUS_NO_MEMORY;
+ }
+
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_ADDED;
+ pDevice->cRefs = 1;
+ NTSTATUS Status = vboxUsbFltDevPopulate(pDevice, pPdo /* , TRUE /* need all props */);
+ if (!NT_SUCCESS(Status))
+ {
+ WARN(("vboxUsbFltDevPopulate failed, Status 0x%x", Status));
+ ObDereferenceObject(pPdo);
+ VBoxUsbMonMemFree(pDevice);
+ return Status;
+ }
+
+ uintptr_t uId;
+ bool fFilter = false;
+ bool fIsOneShot = false;
+ PVBOXUSBFLTCTX pCtx;
+ PVBOXUSBFLT_DEVICE pTmpDev;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ /* (paranoia) re-check the device is still not here */
+ pTmpDev = vboxUsbFltDevGetLocked(pPdo);
+
+ /* Drop the PDO ref, now we won't need it anymore. */
+ ObDereferenceObject(pPdo);
+
+ if (pTmpDev)
+ {
+ LOG(("second try: found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("second try: VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
+ ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("second try: VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
+ *pbFiltered = pTmpDev->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+ VBOXUSBFLT_LOCK_RELEASE();
+ VBoxUsbMonMemFree(pDevice);
+ return STATUS_SUCCESS;
+ }
+
+ LOG(("Created Device 0x%p for PDO 0x%p", pDevice, pPdo));
+
+ pCtx = vboxUsbFltDevMatchLocked(pDevice, &uId,
+ true, /* remove a one-shot filter */
+ &fFilter, &fIsOneShot);
+ LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
+ if (fFilter)
+ {
+ LOG(("Created Device 0x%p should be filtered", pDevice));
+ ASSERT_WARN(pCtx, ("zero ctx"));
+ ASSERT_WARN(uId, ("zero uId"));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ LOG(("Created Device 0x%p should NOT be filtered", pDevice));
+ ASSERT_WARN(!uId == !pCtx, ("invalid uid(0x%p) - ctx(0x%p) pair", uId, pCtx)); /* either both zero or both not */
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_UNCAPTURED;
+ }
+
+ if (pCtx)
+ vboxUsbFltDevOwnerSetLocked(pDevice, pCtx, fIsOneShot ? 0 : uId, fIsOneShot);
+
+ InsertHeadList(&g_VBoxUsbFltGlobals.DeviceList, &pDevice->GlobalLe);
+
+ /* do not need to signal anything here -
+ * going to do that once the proxy device object starts */
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ *pbFiltered = fFilter;
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo)
+{
+ VBOXUSBFLT_DEVSTATE enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ enmState = pDevice->enmState;
+
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+
+ return enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
+}
+
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_DEVSTATE enmOldState;
+
+ /* Find the real PDO+reference. Dereference when we're done with it. Note that
+ * the input pPdo was not explicitly referenced so we're not dropping its ref.
+ */
+ PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
+ LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
+ pPdo = pDevObj;
+
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ if (pDevice)
+ {
+ RemoveEntryList(&pDevice->GlobalLe);
+ enmOldState = pDevice->enmState;
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ ObDereferenceObject(pPdo);
+ if (pDevice)
+ vboxUsbFltDevRelease(pDevice);
+ return STATUS_SUCCESS;
+}
+
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo)
+{
+ PVBOXUSBFLT_DEVICE pDevice;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+
+ /* NB: The USB proxy (VBoxUSB.sys) passes us the real PDO, not anything above that. */
+ pDevice = vboxUsbFltDevGetLocked(pPdo);
+ /*
+ * Prevent a host crash when vboxUsbFltDevGetLocked fails to locate the matching PDO
+ * in g_VBoxUsbFltGlobals.DeviceList (see @bugref{6509}).
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("failed to get device for PDO(0x%p)", pPdo));
+ }
+ else if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING)
+ {
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURED;
+ LOG(("The proxy notified proxy start for the captured device 0x%p", pDevice));
+ vboxUsbFltDevRetain(pDevice);
+ }
+ else
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ pDevice = NULL;
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+ return pDevice;
+}
+
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev)
+{
+ PVBOXUSBFLT_DEVICE pDevice = (PVBOXUSBFLT_DEVICE)hDev;
+ /*
+ * Prevent a host crash when VBoxUsbFltProxyStarted fails, returning NULL.
+ * See @bugref{6509}.
+ */
+ if (pDevice == NULL)
+ {
+ WARN(("VBoxUsbFltProxyStopped called with NULL device pointer"));
+ return;
+ }
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
+ || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST)
+ {
+ /* this is due to devie was physically removed */
+ LOG(("The proxy notified proxy stop for the captured device 0x%p, current state %d", pDevice, pDevice->enmState));
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
+ }
+ else
+ {
+ if (pDevice->enmState != VBOXUSBFLT_DEVSTATE_REPLUGGING)
+ {
+ WARN(("invalid state, %d", pDevice->enmState));
+ }
+ }
+ VBOXUSBFLT_LOCK_RELEASE();
+
+ vboxUsbFltDevRelease(pDevice);
+}
+
+
+static NTSTATUS vboxUsbFltRegKeyQuery(PWSTR ValueName, ULONG ValueType, PVOID ValueData, ULONG ValueLength, PVOID Context, PVOID EntryContext)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ RT_NOREF(ValueName, Context);
+ if ( ValueType == REG_DWORD
+ && ValueLength == sizeof(ULONG))
+ *(ULONG *)EntryContext = *(ULONG *)ValueData;
+ else
+ Status = STATUS_OBJECT_TYPE_MISMATCH;
+
+ return Status;
+}
+
+
+NTSTATUS VBoxUsbFltInit()
+{
+ int rc = VBoxUSBFilterInit();
+ if (RT_FAILURE(rc))
+ {
+ WARN(("VBoxUSBFilterInit failed, rc (%d)", rc));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ memset(&g_VBoxUsbFltGlobals, 0, sizeof (g_VBoxUsbFltGlobals));
+ InitializeListHead(&g_VBoxUsbFltGlobals.DeviceList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.ContextList);
+ InitializeListHead(&g_VBoxUsbFltGlobals.BlackDeviceList);
+ vboxUsbFltBlDevPopulateWithKnownLocked();
+ VBOXUSBFLT_LOCK_INIT();
+
+ /*
+ * Check whether the setting to force replugging USB devices when
+ * querying string descriptors fail is set in the registry,
+ * see @bugref{9479}.
+ */
+ RTL_QUERY_REGISTRY_TABLE aParams[] =
+ {
+ {vboxUsbFltRegKeyQuery, 0, L"ForceReplugWhenDevPopulateFails", &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, REG_DWORD, &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, sizeof(ULONG) },
+ { NULL, 0, NULL, NULL, 0, 0, 0 }
+ };
+ UNICODE_STRING UnicodePath = RTL_CONSTANT_STRING(L"\\VBoxUSB");
+
+ NTSTATUS Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, UnicodePath.Buffer, &aParams[0], NULL, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
+ LOG(("Forcing replug of USB devices where querying the descriptors fail\n"));
+ }
+ else
+ LOG(("RtlQueryRegistryValues() -> %#x, assuming defaults\n", Status));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS VBoxUsbFltTerm()
+{
+ bool bBusy = false;
+ VBOXUSBFLT_LOCK_ACQUIRE();
+ do
+ {
+ if (!IsListEmpty(&g_VBoxUsbFltGlobals.ContextList))
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+
+ PLIST_ENTRY pNext = NULL;
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = pNext)
+ {
+ pNext = pEntry->Flink;
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ Assert(!pDevice->uFltId);
+ Assert(!pDevice->pOwner);
+ if (pDevice->cRefs != 1)
+ {
+ AssertFailed();
+ bBusy = true;
+ break;
+ }
+ }
+ } while (0);
+
+ VBOXUSBFLT_LOCK_RELEASE()
+
+ if (bBusy)
+ {
+ return STATUS_DEVICE_BUSY;
+ }
+
+ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
+ pEntry != &g_VBoxUsbFltGlobals.DeviceList;
+ pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink)
+ {
+ RemoveEntryList(pEntry);
+ PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
+ pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
+ vboxUsbFltDevRelease(pDevice);
+ }
+
+ vboxUsbFltBlDevClearLocked();
+
+ VBOXUSBFLT_LOCK_TERM();
+
+ VBoxUSBFilterTerm();
+
+ return STATUS_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
new file mode 100644
index 00000000..2e8c7501
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.h
@@ -0,0 +1,75 @@
+/* $Id: VBoxUsbFlt.h $ */
+/** @file
+ * VBox USB Monitor Device Filtering functionality
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+#include <VBoxUSBFilterMgr.h>
+
+#include <VBox/usblib-win.h>
+
+typedef struct VBOXUSBFLTCTX
+{
+ LIST_ENTRY ListEntry;
+ RTPROCESS Process; // Purely informational, no function?
+ uint32_t cActiveFilters;
+ BOOLEAN bRemoved; // For debugging only?
+} VBOXUSBFLTCTX, *PVBOXUSBFLTCTX;
+
+NTSTATUS VBoxUsbFltInit();
+NTSTATUS VBoxUsbFltTerm();
+NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext);
+NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext);
+int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId);
+int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId);
+NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext);
+
+NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo);
+
+typedef void* HVBOXUSBFLTDEV;
+HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo);
+void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev);
+
+NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered);
+NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo);
+BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbFlt_h */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
new file mode 100644
index 00000000..83dc8de9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.cpp
@@ -0,0 +1,218 @@
+/* $Id: VBoxUsbHook.cpp $ */
+/** @file
+ * Driver Dispatch Table Hooking API
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBHOOK_MEMTAG 'HUBV'
+
+
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (pHook->fIsInstalled)
+ {
+ WARN(("hook is marked installed, returning failure"));
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->pfnOldHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnHook);
+ Assert(pHook->pfnOldHandler);
+ Assert(pHook->pfnHook != pHook->pfnOldHandler);
+ pHook->fIsInstalled = TRUE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+
+}
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_SUCCESS;
+ }
+
+ PDRIVER_DISPATCH pfnOldVal = (PDRIVER_DISPATCH)InterlockedCompareExchangePointer((PVOID*)&pHook->pDrvObj->MajorFunction[pHook->iMjFunction], pHook->pfnOldHandler, pHook->pfnHook);
+ Assert(pfnOldVal == pHook->pfnHook);
+ if (pfnOldVal != pHook->pfnHook)
+ {
+ AssertMsgFailed(("unhook failed!!!\n"));
+ /* this is bad! this could happen if someone else has chained another hook,
+ * or (which is even worse) restored the "initial" entry value it saved when doing a hooking before us
+ * return the failure and don't do anything else
+ * the best thing to do if this happens is to leave everything as is
+ * and to prevent the driver from being unloaded to ensure no one references our unloaded hook routine */
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pHook->fIsInstalled = FALSE;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+
+ /* wait for the current handlers to exit */
+ VBoxDrvToolRefWaitEqual(&pHook->HookRef, 1);
+
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ BOOLEAN fIsInstalled;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ fIsInstalled = pHook->fIsInstalled;
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return fIsInstalled;
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook)
+{
+ Assert(pDrvObj);
+ Assert(iMjFunction <= IRP_MJ_MAXIMUM_FUNCTION);
+ Assert(pfnHook);
+ memset(pHook, 0, sizeof (*pHook));
+ InitializeListHead(&pHook->RequestList);
+ KeInitializeSpinLock(&pHook->Lock);
+ VBoxDrvToolRefInit(&pHook->HookRef);
+ pHook->pDrvObj = pDrvObj;
+ pHook->iMjFunction = iMjFunction;
+ pHook->pfnHook = pfnHook;
+ Assert(!pHook->pfnOldHandler);
+ Assert(!pHook->fIsInstalled);
+
+}
+
+static void vboxUsbHookRequestRegisterCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ Assert(pRequest);
+ Assert(pDevObj);
+ Assert(pIrp);
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ memset(pRequest, 0, sizeof (*pRequest));
+ pRequest->pHook = pHook;
+ pRequest->OldLocation = *pSl;
+ pRequest->pDevObj = pDevObj;
+ pRequest->pIrp = pIrp;
+ pRequest->bCompletionStopped = FALSE;
+ pSl->CompletionRoutine = pfnCompletion;
+ pSl->Context = pRequest;
+ pSl->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ InsertTailList(&pHook->RequestList, &pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ Assert(pfnCompletion);
+ vboxUsbHookRequestRegisterCompletion(pHook, pDevObj, pIrp, pfnCompletion, pRequest);
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ return pHook->pfnOldHandler(pDevObj, pIrp);
+}
+
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp,
+ PVBOXUSBHOOK_REQUEST pRequest)
+{
+ RT_NOREF3(pHook, pDevObj, pIrp);
+ Assert(!pRequest->bCompletionStopped);
+ pRequest->bCompletionStopped = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ if (pRequest->OldLocation.CompletionRoutine && pRequest->OldLocation.Control)
+ {
+ Status = pRequest->OldLocation.CompletionRoutine(pDevObj, pIrp, pRequest->OldLocation.Context);
+ }
+
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ if (pRequest->bCompletionStopped)
+ {
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ }
+ }
+ /*
+ * else - in case driver returned STATUS_MORE_PROCESSING_REQUIRED,
+ * it will call IoCompleteRequest itself
+ */
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ RemoveEntryList(&pRequest->ListEntry);
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+ return Status;
+}
+
+#define PVBOXUSBHOOK_REQUEST_FROM_LE(_pLe) ( (PVBOXUSBHOOK_REQUEST)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBHOOK_REQUEST, ListEntry) ) )
+
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp)
+{
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&pHook->Lock, &oldIrql);
+ for (PLIST_ENTRY pLe = pHook->RequestList.Flink; pLe != &pHook->RequestList; pLe = pLe->Flink)
+ {
+ PVBOXUSBHOOK_REQUEST pCur = PVBOXUSBHOOK_REQUEST_FROM_LE(pLe);
+ if (pCur != pRequest)
+ continue;
+ if (pCur->pIrp != pIrp)
+ continue;
+ WARN(("found pending IRP(0x%p) when it should not be", pIrp));
+ }
+ KeReleaseSpinLock(&pHook->Lock, oldIrql);
+
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
new file mode 100644
index 00000000..f9207297
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbHook.h
@@ -0,0 +1,96 @@
+/* $Id: VBoxUsbHook.h $ */
+/** @file
+ * Driver Dispatch Table Hooking API impl
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBoxUsbMon.h"
+
+typedef struct VBOXUSBHOOK_ENTRY
+{
+ LIST_ENTRY RequestList;
+ KSPIN_LOCK Lock;
+ BOOLEAN fIsInstalled;
+ PDRIVER_DISPATCH pfnOldHandler;
+ VBOXDRVTOOL_REF HookRef;
+ PDRIVER_OBJECT pDrvObj;
+ UCHAR iMjFunction;
+ PDRIVER_DISPATCH pfnHook;
+} VBOXUSBHOOK_ENTRY, *PVBOXUSBHOOK_ENTRY;
+
+typedef struct VBOXUSBHOOK_REQUEST
+{
+ LIST_ENTRY ListEntry;
+ PVBOXUSBHOOK_ENTRY pHook;
+ IO_STACK_LOCATION OldLocation;
+ PDEVICE_OBJECT pDevObj;
+ PIRP pIrp;
+ BOOLEAN bCompletionStopped;
+} VBOXUSBHOOK_REQUEST, *PVBOXUSBHOOK_REQUEST;
+
+DECLINLINE(BOOLEAN) VBoxUsbHookRetain(PVBOXUSBHOOK_ENTRY pHook)
+{
+ KIRQL Irql;
+ KeAcquireSpinLock(&pHook->Lock, &Irql);
+ if (!pHook->fIsInstalled)
+ {
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return FALSE;
+ }
+
+ VBoxDrvToolRefRetain(&pHook->HookRef);
+ KeReleaseSpinLock(&pHook->Lock, Irql);
+ return TRUE;
+}
+
+DECLINLINE(VOID) VBoxUsbHookRelease(PVBOXUSBHOOK_ENTRY pHook)
+{
+ VBoxDrvToolRefRelease(&pHook->HookRef);
+}
+
+VOID VBoxUsbHookInit(PVBOXUSBHOOK_ENTRY pHook, PDRIVER_OBJECT pDrvObj, UCHAR iMjFunction, PDRIVER_DISPATCH pfnHook);
+NTSTATUS VBoxUsbHookInstall(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookUninstall(PVBOXUSBHOOK_ENTRY pHook);
+BOOLEAN VBoxUsbHookIsInstalled(PVBOXUSBHOOK_ENTRY pHook);
+NTSTATUS VBoxUsbHookRequestPassDownHookCompletion(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PIO_COMPLETION_ROUTINE pfnCompletion, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestPassDownHookSkip(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp);
+NTSTATUS VBoxUsbHookRequestMoreProcessingRequired(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+NTSTATUS VBoxUsbHookRequestComplete(PVBOXUSBHOOK_ENTRY pHook, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVBOXUSBHOOK_REQUEST pRequest);
+VOID VBoxUsbHookVerifyCompletion(PVBOXUSBHOOK_ENTRY pHook, PVBOXUSBHOOK_REQUEST pRequest, PIRP pIrp);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbHook_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
new file mode 100644
index 00000000..349893bd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.cpp
@@ -0,0 +1,1568 @@
+/* $Id: VBoxUsbMon.cpp $ */
+/** @file
+ * VBox USB Monitor
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ *
+ * Theory of Operation
+ * - or -
+ * The Document I Wish The Original Author Had Written
+ *
+ *
+ * The USB Monitor (VBoxUSBMon.sys) serves to capture and uncapture USB
+ * devices. Its job is to ensure that the USB proxy (VBoxUSB.sys) gets installed
+ * for captured devices and removed again when not needed, restoring the regular
+ * driver (if any).
+ *
+ * The USB Monitor does not handle any actual USB traffic; that is the role of
+ * VBoxUSB.sys, the USB proxy. A typical solution for installing such USB proxy
+ * is using a filter driver, but that approach was rejected because filter drivers
+ * cannot be dynamically added and removed. What VBoxUSBMon does instead is hook
+ * into the dispatch routine of the bus driver, i.e. USB hub driver, and alter
+ * the PnP information returned by the bus driver.
+ *
+ * The key functionality for capturing is cycling a USB port (which causes a USB
+ * device reset and triggers re-enumeration in the Windows USB driver stack), and
+ * then modifying IRP_MN_QUERY_ID / BusQueryHardwareIDs and related requests so
+ * that they return the synthetic USB VID/PID that VBoxUSB.sys handles rather than
+ * the true hardware VID/PID. That causes Windows to install VBoxUSB.sys for the
+ * device.
+ *
+ * Uncapturing again cycles the USB port but returns unmodified hardware IDs,
+ * causing Windows to load the normal driver for the device.
+ *
+ * Identifying devices to capture or release (uncapture) is done through USB filters,
+ * a cross-platform concept which matches USB device based on their VID/PID, class,
+ * and other criteria.
+ *
+ * There is an IOCTL interface for adding/removing USB filters and applying them.
+ * The IOCTLs are normally issued by VBoxSVC.
+ *
+ * USB devices are enumerated by finding all USB hubs (GUID_DEVINTERFACE_USB_HUB)
+ * and querying their child devices (i.e. USB devices or other hubs) by sending
+ * IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations. This is done when
+ * applying existing filters.
+ *
+ * Newly arrived USB devices are intercepted early in their PnP enumeration
+ * through the hooked bus driver dispatch routine. Devices which satisty the
+ * filter matching criteria are morphed (see above) such that VBoxUSB.sys loads
+ * for them before any default driver does.
+ *
+ * There is an IDC interface to VBoxUSB.sys which allows the USB proxy to report
+ * that it's installed for a given USB device, and also report when the USB proxy
+ * is unloaded (typically caused by either unplugging the device or uncapturing
+ * and cycling the port). VBoxUSBMon.sys relies on these IDC calls to track
+ * captured devices and be informed when VBoxUSB.sys unloads.
+ *
+ * Windows 8+ complicates the USB Monitor's life by automatically putting some
+ * USB devices to a low-power state where they are unable to respond to any USB
+ * requests and VBoxUSBMon can't read any of their descriptors (note that in
+ * userland, the device descriptor can always be read, but string descriptors
+ * can't). Such devices' USB VID/PID/revision is recovered using the Windows
+ * PnP Manager from their DevicePropertyHardwareID, but their USB class/subclass
+ * and protocol unfortunately cannot be unambiguously recovered from their
+ * DevicePropertyCompatibleIDs.
+ *
+ * Filter drivers add another complication. With filter drivers in place, the
+ * device objects returned by the BusRelations query (or passing through the PnP
+ * hooks) may not be PDOs but rather filter DOs higher in the stack. To avoid
+ * confusion, we flatten the references to their base, i.e. the real PDO, which
+ * should remain the same for the lifetime of a device. Note that VBoxUSB.sys
+ * always passes its own PDO in the proxy startup IOCTL.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxUsbMon.h"
+#include "../cmn/VBoxUsbIdc.h"
+#include <iprt/errcore.h>
+#include <VBox/usblib.h>
+#include <excpt.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXUSBMON_MEMTAG 'MUBV'
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXUSBMONINS
+{
+ void * pvDummy;
+} VBOXUSBMONINS, *PVBOXUSBMONINS;
+
+typedef struct VBOXUSBMONCTX
+{
+ VBOXUSBFLTCTX FltCtx;
+} VBOXUSBMONCTX, *PVBOXUSBMONCTX;
+
+typedef struct VBOXUSBHUB_PNPHOOK
+{
+ VBOXUSBHOOK_ENTRY Hook;
+ bool fUninitFailed;
+} VBOXUSBHUB_PNPHOOK, *PVBOXUSBHUB_PNPHOOK;
+
+typedef struct VBOXUSBHUB_PNPHOOK_COMPLETION
+{
+ VBOXUSBHOOK_REQUEST Rq;
+} VBOXUSBHUB_PNPHOOK_COMPLETION, *PVBOXUSBHUB_PNPHOOK_COMPLETION;
+
+#define VBOXUSBMON_MAXDRIVERS 5
+typedef struct VBOXUSB_PNPDRIVER
+{
+ PDRIVER_OBJECT DriverObject;
+ VBOXUSBHUB_PNPHOOK UsbHubPnPHook;
+ PDRIVER_DISPATCH pfnHookStub;
+} VBOXUSB_PNPDRIVER, *PVBOXUSB_PNPDRIVER;
+
+typedef struct VBOXUSBMONGLOBALS
+{
+ PDEVICE_OBJECT pDevObj;
+ VBOXUSB_PNPDRIVER pDrivers[VBOXUSBMON_MAXDRIVERS];
+ KEVENT OpenSynchEvent;
+ IO_REMOVE_LOCK RmLock;
+ uint32_t cOpens;
+ volatile LONG ulPreventUnloadOn;
+ PFILE_OBJECT pPreventUnloadFileObj;
+} VBOXUSBMONGLOBALS, *PVBOXUSBMONGLOBALS;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static VBOXUSBMONGLOBALS g_VBoxUsbMonGlobals;
+
+/*
+ * Note: Must match the VID & PID in the USB driver .inf file!!
+ */
+/*
+ BusQueryDeviceID USB\Vid_80EE&Pid_CAFE
+ BusQueryInstanceID 2
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE&Rev_0100
+ BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00&Prot_00
+ BusQueryCompatibleIDs USB\Class_ff&SubClass_00
+ BusQueryCompatibleIDs USB\Class_ff
+*/
+
+static WCHAR const g_szBusQueryDeviceId[] = L"USB\\Vid_80EE&Pid_CAFE";
+static WCHAR const g_szBusQueryHardwareIDs[] = L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0";
+static WCHAR const g_szBusQueryCompatibleIDs[] = L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0";
+static WCHAR const g_szDeviceTextDescription[] = L"VirtualBox USB";
+
+
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes)
+{
+ PVOID pvMem = ExAllocatePoolWithTag(NonPagedPool, cbBytes, VBOXUSBMON_MEMTAG);
+ Assert(pvMem);
+ return pvMem;
+}
+
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes)
+{
+ PVOID pvMem = VBoxUsbMonMemAlloc(cbBytes);
+ if (pvMem)
+ {
+ RtlZeroMemory(pvMem, cbBytes);
+ }
+ return pvMem;
+}
+
+VOID VBoxUsbMonMemFree(PVOID pvMem)
+{
+ ExFreePoolWithTag(pvMem, VBOXUSBMON_MEMTAG);
+}
+
+#define VBOXUSBDBG_STRCASE(_t) \
+ case _t: return #_t
+#define VBOXUSBDBG_STRCASE_UNKNOWN(_v) \
+ default: LOG((__FUNCTION__": Unknown Value (0n%d), (0x%x)", _v, _v)); return "Unknown"
+
+/* These minor code are semi-undocumented. */
+#ifndef IRP_MN_QUERY_LEGACY_BUS_INFORMATION
+#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18
+#endif
+#ifndef IRP_MN_DEVICE_ENUMERATED
+#define IRP_MN_DEVICE_ENUMERATED 0x19
+#endif
+
+static const char* vboxUsbDbgStrPnPMn(UCHAR uMn)
+{
+ switch (uMn)
+ {
+ VBOXUSBDBG_STRCASE(IRP_MN_START_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_REMOVE_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_CANCEL_STOP_DEVICE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_RELATIONS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_INTERFACE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_CAPABILITIES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCES);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_DEVICE_TEXT);
+ VBOXUSBDBG_STRCASE(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
+ VBOXUSBDBG_STRCASE(IRP_MN_READ_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_WRITE_CONFIG);
+ VBOXUSBDBG_STRCASE(IRP_MN_EJECT);
+ VBOXUSBDBG_STRCASE(IRP_MN_SET_LOCK);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_ID);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_PNP_DEVICE_STATE);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_USAGE_NOTIFICATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_SURPRISE_REMOVAL);
+ VBOXUSBDBG_STRCASE(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
+ VBOXUSBDBG_STRCASE(IRP_MN_DEVICE_ENUMERATED);
+ VBOXUSBDBG_STRCASE_UNKNOWN(uMn);
+ }
+}
+
+/**
+ * Send IRP_MN_QUERY_DEVICE_RELATIONS
+ *
+ * @returns NT Status
+ * @param pDevObj USB device pointer
+ * @param pFileObj Valid file object pointer
+ * @param pDevRelations Pointer to DEVICE_RELATIONS pointer (out)
+ */
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations)
+{
+ IO_STATUS_BLOCK IoStatus;
+ KEVENT Event;
+ NTSTATUS Status;
+ PIRP pIrp;
+ PIO_STACK_LOCATION pSl;
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ Assert(pDevRelations);
+ *pDevRelations = NULL;
+
+ pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pDevObj, NULL, 0, NULL, &Event, &IoStatus);
+ if (!pIrp)
+ {
+ WARN(("IoBuildDeviceIoControlRequest failed!!"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ pSl = IoGetNextIrpStackLocation(pIrp);
+ pSl->MajorFunction = IRP_MJ_PNP;
+ pSl->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
+ pSl->Parameters.QueryDeviceRelations.Type = BusRelations;
+ pSl->FileObject = pFileObj;
+
+ Status = IoCallDriver(pDevObj, pIrp);
+ if (Status == STATUS_PENDING)
+ {
+ LOG(("IoCallDriver returned STATUS_PENDING!!"));
+ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+ Status = IoStatus.Status;
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)IoStatus.Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ *pDevRelations = pRel;
+ else
+ {
+ WARN(("Invalid pointer %p", pRel));
+ }
+ }
+ else
+ {
+ WARN(("IRP_MN_QUERY_DEVICE_RELATIONS failed Status(0x%x)", Status));
+ }
+
+ LOG(("IoCallDriver returned %x", Status));
+ return Status;
+}
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker)
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PWSTR szwHubList;
+ Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_USB_HUB, NULL, 0, &szwHubList);
+ if (Status != STATUS_SUCCESS)
+ {
+ LOG(("IoGetDeviceInterfaces failed with %d\n", Status));
+ return;
+ }
+ if (szwHubList)
+ {
+ UNICODE_STRING UnicodeName;
+ PDEVICE_OBJECT pHubDevObj;
+ PFILE_OBJECT pHubFileObj;
+ PWSTR szwHubName = szwHubList;
+ while (*szwHubName != UNICODE_NULL)
+ {
+ RtlInitUnicodeString(&UnicodeName, szwHubName);
+ Status = IoGetDeviceObjectPointer(&UnicodeName, FILE_READ_DATA, &pHubFileObj, &pHubDevObj);
+ if (Status == STATUS_SUCCESS)
+ {
+ /* We could not log hub name here.
+ * It is the paged memory and we cannot use it in logger cause it increases the IRQL
+ */
+ LOG(("IoGetDeviceObjectPointer returned %p %p", pHubDevObj, pHubFileObj));
+ if (!pfnWalker(pHubFileObj, pHubDevObj, pvWalker))
+ {
+ LOG(("the walker said to stop"));
+ ObDereferenceObject(pHubFileObj);
+ break;
+ }
+
+ LOG(("going forward.."));
+ ObDereferenceObject(pHubFileObj);
+ }
+ szwHubName += wcslen(szwHubName) + 1;
+ }
+ ExFreePool(szwHubList);
+ }
+}
+
+/* NOTE: the stack location data is not the "actual" IRP stack location,
+ * but a copy being preserved on the IRP way down.
+ * See the note in VBoxUsbPnPCompletion for detail */
+static NTSTATUS vboxUsbMonHandlePnPIoctl(PDEVICE_OBJECT pDevObj, PIO_STACK_LOCATION pSl, PIO_STATUS_BLOCK pIoStatus)
+{
+ LOG(("IRQL = %d", KeGetCurrentIrql()));
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ {
+ LOG(("IRP_MN_QUERY_DEVICE_TEXT: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+ switch (pSl->Parameters.QueryDeviceText.DeviceTextType)
+ {
+ case DeviceTextLocationInformation:
+ LOG(("DeviceTextLocationInformation"));
+ LOG_STRW(pId);
+ break;
+
+ case DeviceTextDescription:
+ LOG(("DeviceTextDescription"));
+ LOG_STRW(pId);
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ WCHAR *pId2 = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szDeviceTextDescription));
+ AssertBreak(pId2);
+ memcpy(pId2, g_szDeviceTextDescription, sizeof(g_szDeviceTextDescription));
+ LOG(("NEW szDeviceTextDescription"));
+ LOG_STRW(pId2);
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId2;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+ default:
+ LOG(("DeviceText %d", pSl->Parameters.QueryDeviceText.DeviceTextType));
+ break;
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pId));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_ID:
+ {
+ LOG(("IRP_MN_QUERY_ID: Irp->pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS && pDevObj)
+ {
+ WCHAR *pId = (WCHAR *)pIoStatus->Information;
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ WCHAR *pTmp;
+#endif
+ if (RT_VALID_PTR(pId))
+ {
+ KIRQL Iqrl = KeGetCurrentIrql();
+ /* IRQL should be always passive here */
+ ASSERT_WARN(Iqrl == PASSIVE_LEVEL, ("irql is not PASSIVE"));
+
+ switch (pSl->Parameters.QueryId.IdType)
+ {
+ case BusQueryInstanceID:
+ LOG(("BusQueryInstanceID"));
+ LOG_STRW(pId);
+ break;
+
+ case BusQueryDeviceID:
+ {
+ LOG(("BusQueryDeviceID"));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryDeviceId));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ ExFreePool((PVOID)pIoStatus->Information);
+ memcpy(pId, g_szBusQueryDeviceId, sizeof(g_szBusQueryDeviceId));
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryHardwareIDs:
+ {
+ LOG(("BusQueryHardwareIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryHardwareIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+
+ BOOLEAN bFiltered = FALSE;
+ NTSTATUS Status = VBoxUsbFltPdoAdd(pDevObj, &bFiltered);
+ if (Status != STATUS_SUCCESS || !bFiltered)
+ {
+ if (Status == STATUS_SUCCESS)
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ else
+ {
+ WARN(("VBoxUsbFltPdoAdd for PDO (0x%p) failed Status 0x%x", pDevObj, Status));
+ }
+ ExFreePool(pId);
+ break;
+ }
+
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+
+ memcpy(pId, g_szBusQueryHardwareIDs, sizeof(g_szBusQueryHardwareIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryHardwareIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ break;
+ }
+ case BusQueryCompatibleIDs:
+ LOG(("BusQueryCompatibleIDs"));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ while (*pId) //MULTI_SZ
+ {
+ LOG_STRW(pId);
+ while (*pId) pId++;
+ pId++;
+ }
+#endif
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ {
+ LOG(("PDO (0x%p) is filtered", pDevObj));
+ pId = (WCHAR *)ExAllocatePool(PagedPool, sizeof(g_szBusQueryCompatibleIDs));
+ if (!pId)
+ {
+ WARN(("ExAllocatePool failed"));
+ break;
+ }
+ memcpy(pId, g_szBusQueryCompatibleIDs, sizeof(g_szBusQueryCompatibleIDs));
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ LOG(("NEW BusQueryCompatibleIDs"));
+ pTmp = pId;
+ while (*pTmp) //MULTI_SZ
+ {
+ LOG_STRW(pTmp);
+ while (*pTmp) pTmp++;
+ pTmp++;
+ }
+#endif
+ ExFreePool((PVOID)pIoStatus->Information);
+ pIoStatus->Information = (ULONG_PTR)pId;
+ }
+ else
+ {
+ LOG(("PDO (0x%p) is NOT filtered", pDevObj));
+ }
+ break;
+
+ default:
+ /** @todo r=bird: handle BusQueryContainerID and whatever else we might see */
+ break;
+ }
+ }
+ else
+ {
+ LOG(("Invalid pointer %p", pId));
+ }
+ }
+ break;
+ }
+
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ {
+ switch(pSl->Parameters.QueryDeviceRelations.Type)
+ {
+ case BusRelations:
+ LOG(("BusRelations"));
+
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_RELATIONS pRel = (PDEVICE_RELATIONS)pIoStatus->Information;
+ LOG(("pRel = %p", pRel));
+ if (RT_VALID_PTR(pRel))
+ {
+ for (unsigned i=0;i<pRel->Count;i++)
+ {
+ if (VBoxUsbFltPdoIsFiltered(pDevObj))
+ LOG(("New PDO %p", pRel->Objects[i]));
+ }
+ }
+ else
+ LOG(("Invalid pointer %p", pRel));
+ }
+ break;
+ case TargetDeviceRelation:
+ LOG(("TargetDeviceRelation"));
+ break;
+ case RemovalRelations:
+ LOG(("RemovalRelations"));
+ break;
+ case EjectionRelations:
+ LOG(("EjectionRelations"));
+ break;
+ default:
+ LOG(("QueryDeviceRelations.Type=%d", pSl->Parameters.QueryDeviceRelations.Type));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ {
+ LOG(("IRP_MN_QUERY_CAPABILITIES: pIoStatus->Status = %x", pIoStatus->Status));
+ if (pIoStatus->Status == STATUS_SUCCESS)
+ {
+ PDEVICE_CAPABILITIES pCaps = pSl->Parameters.DeviceCapabilities.Capabilities;
+ if (RT_VALID_PTR(pCaps))
+ {
+ LOG(("Caps.SilentInstall = %d", pCaps->SilentInstall));
+ LOG(("Caps.UniqueID = %d", pCaps->UniqueID ));
+ LOG(("Caps.Address = %d", pCaps->Address ));
+ LOG(("Caps.UINumber = %d", pCaps->UINumber ));
+ }
+ else
+ LOG(("Invalid pointer %p", pCaps));
+ }
+ break;
+ }
+
+ default:
+ break;
+#endif
+ } /*switch */
+
+ LOG(("Done returns %x (IRQL = %d)", pIoStatus->Status, KeGetCurrentIrql()));
+ return pIoStatus->Status;
+}
+
+NTSTATUS _stdcall VBoxUsbPnPCompletion(DEVICE_OBJECT *pDevObj, IRP *pIrp, void *pvContext)
+{
+ LOG(("Completion PDO(0x%p), IRP(0x%p), Status(0x%x)", pDevObj, pIrp, pIrp->IoStatus.Status));
+ ASSERT_WARN(pvContext, ("zero context"));
+
+ PVBOXUSBHOOK_REQUEST pRequest = (PVBOXUSBHOOK_REQUEST)pvContext;
+ /* NOTE: despite a regular IRP processing the stack location in our completion
+ * differs from those of the PnP hook since the hook is invoked in the "context" of the calle,
+ * while the completion is in the "coller" context in terms of IRP,
+ * so the completion stack location is one level "up" here.
+ *
+ * Moreover we CAN NOT access irp stack location in the completion because we might not have one at all
+ * in case the hooked driver is at the top of the irp call stack
+ *
+ * This is why we use the stack location we saved on IRP way down.
+ * */
+ PIO_STACK_LOCATION pSl = &pRequest->OldLocation;
+ ASSERT_WARN(pIrp == pRequest->pIrp, ("completed IRP(0x%x) not match request IRP(0x%x)", pIrp, pRequest->pIrp));
+ /* NOTE: we can not rely on pDevObj passed in IoCompletion since it may be zero
+ * in case IRP was created with extra stack locations and the caller did not initialize
+ * the IO_STACK_LOCATION::DeviceObject */
+ DEVICE_OBJECT *pRealDevObj = pRequest->pDevObj;
+// Assert(!pDevObj || pDevObj == pRealDevObj);
+// Assert(pSl->DeviceObject == pDevObj);
+
+ switch(pSl->MinorFunction)
+ {
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ case IRP_MN_QUERY_ID:
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ case IRP_MN_QUERY_CAPABILITIES:
+#endif
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ vboxUsbMonHandlePnPIoctl(pRealDevObj, pSl, &pIrp->IoStatus);
+ }
+ else
+ {
+ ASSERT_WARN(pIrp->IoStatus.Status == STATUS_NOT_SUPPORTED, ("Irp failed with status(0x%x)", pIrp->IoStatus.Status));
+ }
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ case IRP_MN_REMOVE_DEVICE:
+ if (NT_SUCCESS(pIrp->IoStatus.Status))
+ {
+ VBoxUsbFltPdoRemove(pRealDevObj);
+ }
+ else
+ {
+ AssertFailed();
+ }
+ break;
+
+ /* These two IRPs are received when the PnP subsystem has determined the id of the newly arrived device */
+ /* IRP_MN_START_DEVICE only arrives if it's a USB device of a known class or with a present host driver */
+ case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+ case IRP_MN_QUERY_RESOURCES:
+ /* There used to be code to support SUPUSBFLT_IOCTL_SET_NOTIFY_EVENT but it was not reliable. */
+
+ default:
+ break;
+ }
+
+ LOG(("<==PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x), Sl PDO(0x%p), Compl PDO(0x%p)",
+ vboxUsbDbgStrPnPMn(pSl->MinorFunction),
+ pRealDevObj, pIrp, pIrp->IoStatus.Status,
+ pSl->DeviceObject, pDevObj));
+#ifdef DEBUG_misha
+ NTSTATUS tmpStatus = pIrp->IoStatus.Status;
+#endif
+ PVBOXUSBHOOK_ENTRY pHook = pRequest->pHook;
+ NTSTATUS Status = VBoxUsbHookRequestComplete(pHook, pDevObj, pIrp, pRequest);
+ VBoxUsbMonMemFree(pRequest);
+#ifdef DEBUG_misha
+ if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+ {
+ Assert(pIrp->IoStatus.Status == tmpStatus);
+ }
+#endif
+ VBoxUsbHookRelease(pHook);
+ return Status;
+}
+
+/**
+ * Device PnP hook
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+static NTSTATUS vboxUsbMonPnPHook(IN PVBOXUSBHOOK_ENTRY pHook, IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ LOG(("==>PnP: Mn(%s), PDO(0x%p), IRP(0x%p), Status(0x%x)", vboxUsbDbgStrPnPMn(IoGetCurrentIrpStackLocation(pIrp)->MinorFunction), pDevObj, pIrp, pIrp->IoStatus.Status));
+
+ if (!VBoxUsbHookRetain(pHook))
+ {
+ WARN(("VBoxUsbHookRetain failed"));
+ return VBoxUsbHookRequestPassDownHookSkip(pHook, pDevObj, pIrp);
+ }
+
+ PVBOXUSBHUB_PNPHOOK_COMPLETION pCompletion = (PVBOXUSBHUB_PNPHOOK_COMPLETION)VBoxUsbMonMemAlloc(sizeof (*pCompletion));
+ if (!pCompletion)
+ {
+ WARN(("VBoxUsbMonMemAlloc failed"));
+ VBoxUsbHookRelease(pHook);
+ pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NTSTATUS Status = VBoxUsbHookRequestPassDownHookCompletion(pHook, pDevObj, pIrp, VBoxUsbPnPCompletion, &pCompletion->Rq);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ if (Status != STATUS_PENDING)
+ {
+ LOG(("Request completed, Status(0x%x)", Status));
+ VBoxUsbHookVerifyCompletion(pHook, &pCompletion->Rq, pIrp);
+ }
+ else
+ {
+ LOG(("Request pending"));
+ }
+#endif
+ return Status;
+}
+
+/**
+ * Device PnP hook stubs.
+ *
+ * @param pDevObj Device object.
+ * @param pIrp Request packet.
+ */
+#define VBOX_PNPHOOKSTUB(n) NTSTATUS _stdcall VBoxUsbMonPnPHook##n(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) \
+{ \
+ return vboxUsbMonPnPHook(&g_VBoxUsbMonGlobals.pDrivers[n].UsbHubPnPHook.Hook, pDevObj, pIrp); \
+}
+
+#define VBOX_PNPHOOKSTUB_INIT(n) g_VBoxUsbMonGlobals.pDrivers[n].pfnHookStub = VBoxUsbMonPnPHook##n
+
+VBOX_PNPHOOKSTUB(0)
+VBOX_PNPHOOKSTUB(1)
+VBOX_PNPHOOKSTUB(2)
+VBOX_PNPHOOKSTUB(3)
+VBOX_PNPHOOKSTUB(4)
+AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+typedef struct VBOXUSBMONHOOKDRIVERWALKER
+{
+ PDRIVER_OBJECT pDrvObj;
+} VBOXUSBMONHOOKDRIVERWALKER, *PVBOXUSBMONHOOKDRIVERWALKER;
+
+/**
+ * Logs an error to the system event log.
+ *
+ * @param ErrCode Error to report to event log.
+ * @param ReturnedStatus Error that was reported by the driver to the caller.
+ * @param uErrId Unique error id representing the location in the driver.
+ * @param cbDumpData Number of bytes at pDumpData.
+ * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
+ *
+ * NB: We only use IoLogMsg.dll as the message file, limiting
+ * ErrCode to status codes and messages defined in ntiologc.h
+ */
+static void vboxUsbMonLogError(NTSTATUS ErrCode, NTSTATUS ReturnedStatus, ULONG uErrId, USHORT cbDumpData, PVOID pDumpData)
+{
+ PIO_ERROR_LOG_PACKET pErrEntry;
+
+
+ /* Truncate dumps that do not fit into IO_ERROR_LOG_PACKET. */
+ if (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData > ERROR_LOG_MAXIMUM_SIZE)
+ cbDumpData = ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
+
+ pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxUsbMonGlobals.pDevObj,
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData);
+ if (pErrEntry)
+ {
+ uint8_t *pDump = (uint8_t *)pErrEntry->DumpData;
+ if (cbDumpData)
+ memcpy(pDump, pDumpData, cbDumpData);
+ pErrEntry->MajorFunctionCode = 0;
+ pErrEntry->RetryCount = 0;
+ pErrEntry->DumpDataSize = cbDumpData;
+ pErrEntry->NumberOfStrings = 0;
+ pErrEntry->StringOffset = 0;
+ pErrEntry->ErrorCode = ErrCode;
+ pErrEntry->UniqueErrorValue = uErrId;
+ pErrEntry->FinalStatus = ReturnedStatus;
+ pErrEntry->IoControlCode = 0;
+ IoWriteErrorLogEntry(pErrEntry);
+ }
+ else
+ {
+ LOG(("Failed to allocate error log entry (cb=%d)\n", FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData));
+ }
+}
+
+static DECLCALLBACK(BOOLEAN) vboxUsbMonHookDrvObjWalker(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext)
+{
+ RT_NOREF2(pHubFile, pvContext);
+ PDRIVER_OBJECT pDrvObj = pHubDo->DriverObject;
+
+ /* First we try to figure out if we are already hooked to this driver. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ /* We are not hooked yet, find an empty slot. */
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (!g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ /* Found an emtpy slot, use it. */
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = pDrvObj;
+ ObReferenceObject(pDrvObj);
+ LOG(("pDrivers[%d] = %p, installing the hook...\n", i, pDrvObj));
+ VBoxUsbHookInit(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook,
+ pDrvObj,
+ IRP_MJ_PNP,
+ g_VBoxUsbMonGlobals.pDrivers[i].pfnHookStub);
+ VBoxUsbHookInstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ return TRUE; /* Must continue to find all drivers. */
+ }
+ if (pDrvObj == g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ LOG(("Found %p at pDrivers[%d]\n", pDrvObj, i));
+ /* We've already hooked to this one -- nothing to do. */
+ return TRUE;
+ }
+ }
+ /* No empty slots! No reason to continue. */
+ LOG(("No empty slots!\n"));
+ ANSI_STRING ansiDrvName;
+ NTSTATUS Status = RtlUnicodeStringToAnsiString(&ansiDrvName, &pDrvObj->DriverName, true);
+ if (Status != STATUS_SUCCESS)
+ {
+ ansiDrvName.Length = 0;
+ LOG(("RtlUnicodeStringToAnsiString failed with 0x%x", Status));
+ }
+ vboxUsbMonLogError(IO_ERR_INSUFFICIENT_RESOURCES, STATUS_SUCCESS, 1, ansiDrvName.Length, ansiDrvName.Buffer);
+ if (Status == STATUS_SUCCESS)
+ RtlFreeAnsiString(&ansiDrvName);
+ return FALSE;
+}
+
+/**
+ * Finds all USB drivers in the system and installs hooks if haven't done already.
+ */
+static NTSTATUS vboxUsbMonInstallAllHooks()
+{
+ vboxUsbMonHubDevWalk(vboxUsbMonHookDrvObjWalker, NULL);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookCheckInit()
+{
+ static bool fIsHookInited = false;
+ if (fIsHookInited)
+ {
+ LOG(("hook inited already, success"));
+ return STATUS_SUCCESS;
+ }
+ return vboxUsbMonInstallAllHooks();
+}
+
+static NTSTATUS vboxUsbMonHookInstall()
+{
+ /* Nothing to do here as we have already installed all hooks in vboxUsbMonHookCheckInit(). */
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS vboxUsbMonHookUninstall()
+{
+#ifdef VBOXUSBMON_DBG_NO_PNPHOOK
+ return STATUS_SUCCESS;
+#else
+ NTSTATUS Status = STATUS_SUCCESS;
+ for (int i = 0; i < VBOXUSBMON_MAXDRIVERS; i++)
+ {
+ if (g_VBoxUsbMonGlobals.pDrivers[i].DriverObject)
+ {
+ Assert(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject == g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook.pDrvObj);
+ LOG(("Unhooking from %p...\n", g_VBoxUsbMonGlobals.pDrivers[i].DriverObject));
+ Status = VBoxUsbHookUninstall(&g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.Hook);
+ if (!NT_SUCCESS(Status))
+ {
+ /*
+ * We failed to uninstall the hook, so we keep the reference to the driver
+ * in order to prevent another driver re-using this slot because we are
+ * going to mark this hook as fUninitFailed.
+ */
+ //AssertMsgFailed(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.UsbHubPnPHook.fUninitFailed));
+ LOG(("usbhub pnp unhook failed, setting the fUninitFailed flag, the current value of fUninitFailed (%d)", g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed));
+ g_VBoxUsbMonGlobals.pDrivers[i].UsbHubPnPHook.fUninitFailed = true;
+ }
+ else
+ {
+ /* The hook was removed successfully, now we can forget about this driver. */
+ ObDereferenceObject(g_VBoxUsbMonGlobals.pDrivers[i].DriverObject);
+ g_VBoxUsbMonGlobals.pDrivers[i].DriverObject = NULL;
+ }
+ }
+ }
+ return Status;
+#endif
+}
+
+
+static NTSTATUS vboxUsbMonCheckTermStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ AssertRelease(Status == STATUS_SUCCESS);
+
+ do
+ {
+ if (--g_VBoxUsbMonGlobals.cOpens)
+ break;
+
+ Status = vboxUsbMonHookUninstall();
+
+ NTSTATUS tmpStatus = VBoxUsbFltTerm();
+ if (!NT_SUCCESS(tmpStatus))
+ {
+ /* this means a driver state is screwed up, KeBugCheckEx here ? */
+ AssertReleaseFailed();
+ }
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonCheckInitStuff()
+{
+ NTSTATUS Status = KeWaitForSingleObject(&g_VBoxUsbMonGlobals.OpenSynchEvent,
+ Executive, KernelMode,
+ FALSE, /* BOOLEAN Alertable */
+ NULL /* IN PLARGE_INTEGER Timeout */
+ );
+ if (Status == STATUS_SUCCESS)
+ {
+ do
+ {
+ if (g_VBoxUsbMonGlobals.cOpens++)
+ {
+ LOG(("opens: %d, success", g_VBoxUsbMonGlobals.cOpens));
+ break;
+ }
+
+ Status = VBoxUsbFltInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookCheckInit();
+ if (NT_SUCCESS(Status))
+ {
+ Status = vboxUsbMonHookInstall();
+ if (NT_SUCCESS(Status))
+ {
+ Status = STATUS_SUCCESS;
+ LOG(("succeded!!"));
+ break;
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookInstall failed, Status (0x%x)", Status));
+ }
+ }
+ else
+ {
+ WARN(("vboxUsbMonHookCheckInit failed, Status (0x%x)", Status));
+ }
+ VBoxUsbFltTerm();
+ }
+ else
+ {
+ WARN(("VBoxUsbFltInit failed, Status (0x%x)", Status));
+ }
+
+ --g_VBoxUsbMonGlobals.cOpens;
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+ } while (0);
+
+ KeSetEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, 0, FALSE);
+ }
+ else
+ {
+ WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
+ }
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextCreate(PVBOXUSBMONCTX *ppCtx)
+{
+ NTSTATUS Status;
+ *ppCtx = NULL;
+ PVBOXUSBMONCTX pFileCtx = (PVBOXUSBMONCTX)VBoxUsbMonMemAllocZ(sizeof (*pFileCtx));
+ if (pFileCtx)
+ {
+ Status = vboxUsbMonCheckInitStuff();
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = VBoxUsbFltCreate(&pFileCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ *ppCtx = pFileCtx;
+ LOG(("succeeded!!"));
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ WARN(("VBoxUsbFltCreate failed"));
+ }
+ vboxUsbMonCheckTermStuff();
+ }
+ else
+ {
+ WARN(("vboxUsbMonCheckInitStuff failed"));
+ }
+ VBoxUsbMonMemFree(pFileCtx);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonMemAllocZ failed"));
+ Status = STATUS_NO_MEMORY;
+ }
+
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonContextClose(PVBOXUSBMONCTX pCtx)
+{
+ NTSTATUS Status = VBoxUsbFltClose(&pCtx->FltCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = vboxUsbMonCheckTermStuff();
+ Assert(Status == STATUS_SUCCESS);
+ /* ignore the failure */
+ VBoxUsbMonMemFree(pCtx);
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+
+ LOG(("VBoxUsbMonClose"));
+
+ NTSTATUS Status = vboxUsbMonContextClose(pCtx);
+ if (Status != STATUS_SUCCESS)
+ {
+ WARN(("vboxUsbMonContextClose failed, Status (0x%x), prevent unload", Status));
+ if (!InterlockedExchange(&g_VBoxUsbMonGlobals.ulPreventUnloadOn, 1))
+ {
+ LOGREL(("ulPreventUnloadOn not set, preventing unload"));
+ UNICODE_STRING UniName;
+ PDEVICE_OBJECT pTmpDevObj;
+ RtlInitUnicodeString(&UniName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS tmpStatus = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbMonGlobals.pPreventUnloadFileObj, &pTmpDevObj);
+ AssertRelease(NT_SUCCESS(tmpStatus));
+ AssertRelease(pTmpDevObj == pDevObj);
+ }
+ else
+ {
+ WARN(("ulPreventUnloadOn already set"));
+ }
+ LOG(("success!!"));
+ Status = STATUS_SUCCESS;
+ }
+ pFileObj->FsContext = NULL;
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+
+static NTSTATUS _stdcall VBoxUsbMonCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pStack->FileObject;
+ NTSTATUS Status;
+
+ LOG(("VBoxUSBMonCreate"));
+
+ if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+ {
+ WARN(("trying to open as a directory"));
+ pIrp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return STATUS_NOT_A_DIRECTORY;
+ }
+
+ pFileObj->FsContext = NULL;
+ PVBOXUSBMONCTX pCtx = NULL;
+ Status = vboxUsbMonContextCreate(&pCtx);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pCtx);
+ pFileObj->FsContext = pCtx;
+ }
+ else
+ {
+ WARN(("vboxUsbMonContextCreate failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Status = Status;
+ pIrp->IoStatus.Information = 0;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static int VBoxUsbMonFltAdd(PVBOXUSBMONCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ static uintptr_t idDummy = 1;
+ *pId = idDummy;
+ ++idDummy;
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltAdd(&pContext->FltCtx, pFilter, pId);
+ return rc;
+#endif
+}
+
+static int VBoxUsbMonFltRemove(PVBOXUSBMONCTX pContext, uintptr_t uId)
+{
+#ifdef VBOXUSBMON_DBG_NO_FILTERS
+ return VINF_SUCCESS;
+#else
+ int rc = VBoxUsbFltRemove(&pContext->FltCtx, uId);
+ return rc;
+#endif
+}
+
+static NTSTATUS VBoxUsbMonRunFilters(PVBOXUSBMONCTX pContext)
+{
+ NTSTATUS Status = VBoxUsbFltFilterCheck(&pContext->FltCtx);
+ return Status;
+}
+
+static NTSTATUS VBoxUsbMonGetDevice(PVBOXUSBMONCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
+{
+ NTSTATUS Status = VBoxUsbFltGetDevice(&pContext->FltCtx, hDevice, pInfo);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonIoctlDispatch(PVBOXUSBMONCTX pContext, ULONG Ctl, PVOID pvBuffer, ULONG cbInBuffer,
+ ULONG cbOutBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG_PTR Info = 0;
+ switch (Ctl)
+ {
+ case SUPUSBFLT_IOCTL_GET_VERSION:
+ {
+ PUSBSUP_VERSION pOut = (PUSBSUP_VERSION)pvBuffer;
+
+ LOG(("SUPUSBFLT_IOCTL_GET_VERSION"));
+ if (!pvBuffer || cbOutBuffer != sizeof(*pOut) || cbInBuffer != 0)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = USBMON_MAJOR_VERSION;
+ pOut->u32Minor = USBMON_MINOR_VERSION;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_ADD_FILTER:
+ {
+ PUSBFILTER pFilter = (PUSBFILTER)pvBuffer;
+ PUSBSUP_FLTADDOUT pOut = (PUSBSUP_FLTADDOUT)pvBuffer;
+ uintptr_t uId = 0;
+ int rc;
+ if (RT_UNLIKELY(!pvBuffer || cbInBuffer != sizeof (*pFilter) || cbOutBuffer != sizeof (*pOut)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pFilter), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = VBoxUsbMonFltAdd(pContext, pFilter, &uId);
+ pOut->rc = rc;
+ pOut->uId = uId;
+ Info = sizeof (*pOut);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_REMOVE_FILTER:
+ {
+ uintptr_t *pIn = (uintptr_t *)pvBuffer;
+ int *pRc = (int *)pvBuffer;
+
+ if (!pvBuffer || cbInBuffer != sizeof (*pIn) || (cbOutBuffer && cbOutBuffer != sizeof (*pRc)))
+ {
+ WARN(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, sizeof (*pIn), cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_REMOVE_FILTER %x", *pIn));
+ int rc = VBoxUsbMonFltRemove(pContext, *pIn);
+ if (cbOutBuffer)
+ {
+ /* we've validated that already */
+ Assert(cbOutBuffer == (ULONG)*pRc);
+ *pRc = rc;
+ Info = sizeof (*pRc);
+ }
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_RUN_FILTERS:
+ {
+ if (pvBuffer || cbInBuffer || cbOutBuffer)
+ {
+ WARN(("SUPUSBFLT_IOCTL_RUN_FILTERS: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.",
+ cbInBuffer, 0, cbOutBuffer, 0));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ LOG(("SUPUSBFLT_IOCTL_RUN_FILTERS "));
+ Status = VBoxUsbMonRunFilters(pContext);
+ ASSERT_WARN(Status != STATUS_PENDING, ("status pending!"));
+ break;
+ }
+
+ case SUPUSBFLT_IOCTL_GET_DEVICE:
+ {
+ HVBOXUSBDEVUSR hDevice;
+ PUSBSUP_GETDEV_MON pOut = (PUSBSUP_GETDEV_MON)pvBuffer;
+ if (!pvBuffer || cbInBuffer != sizeof (hDevice) || cbOutBuffer < sizeof (*pOut))
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: Invalid input/output sizes! cbIn=%d expected %d. cbOut=%d expected >= %d.",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ hDevice = *(HVBOXUSBDEVUSR*)pvBuffer;
+ if (!hDevice)
+ {
+ WARN(("SUPUSBFLT_IOCTL_GET_DEVICE: hDevice is NULL!",
+ cbInBuffer, sizeof (hDevice), cbOutBuffer, sizeof (*pOut)));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = VBoxUsbMonGetDevice(pContext, hDevice, pOut);
+
+ if (NT_SUCCESS(Status))
+ {
+ Info = sizeof (*pOut);
+ }
+ else
+ {
+ WARN(("VBoxUsbMonGetDevice fail 0x%x", Status));
+ }
+ break;
+ }
+
+ default:
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending!"));
+
+ *pInfo = Info;
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ PFILE_OBJECT pFileObj = pSl->FileObject;
+ Assert(pFileObj);
+ Assert(pFileObj->FsContext);
+ PVBOXUSBMONCTX pCtx = (PVBOXUSBMONCTX)pFileObj->FsContext;
+ Assert(pCtx);
+ Status = vboxUsbMonIoctlDispatch(pCtx,
+ pSl->Parameters.DeviceIoControl.IoControlCode,
+ pIrp->AssociatedIrp.SystemBuffer,
+ pSl->Parameters.DeviceIoControl.InputBufferLength,
+ pSl->Parameters.DeviceIoControl.OutputBufferLength,
+ &Info);
+ ASSERT_WARN(Status != STATUS_PENDING, ("Status pending"));
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+ else
+ {
+ WARN(("IoAcquireRemoveLock failed Status (0x%x)", Status));
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static NTSTATUS vboxUsbMonInternalIoctlDispatch(ULONG Ctl, PVOID pvBuffer, ULONG_PTR *pInfo)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ *pInfo = 0;
+ switch (Ctl)
+ {
+ case VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION:
+ {
+ PVBOXUSBIDC_VERSION pOut = (PVBOXUSBIDC_VERSION)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_GET_VERSION: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ pOut->u32Major = VBOXUSBIDC_VERSION_MAJOR;
+ pOut->u32Minor = VBOXUSBIDC_VERSION_MINOR;
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP:
+ {
+ PVBOXUSBIDC_PROXY_STARTUP pOut = (PVBOXUSBIDC_PROXY_STARTUP)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_STARTUP: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ PDEVICE_OBJECT pDevObj = pOut->u.pPDO;
+ pOut->u.hDev = VBoxUsbFltProxyStarted(pDevObj);
+
+ /* If we couldn't find the PDO in our list, that's a real problem and
+ * the capturing will not really work. Log an error.
+ */
+ if (!pOut->u.hDev)
+ vboxUsbMonLogError(IO_ERR_DRIVER_ERROR, STATUS_SUCCESS, 2, sizeof("INTERNAL_IOCTL_PROXY_STARTUP"), "INTERNAL_IOCTL_PROXY_STARTUP");
+
+ ASSERT_WARN(pOut->u.hDev, ("zero hDev"));
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ case VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN:
+ {
+ PVBOXUSBIDC_PROXY_TEARDOWN pOut = (PVBOXUSBIDC_PROXY_TEARDOWN)pvBuffer;
+
+ LOG(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN"));
+ if (!pvBuffer)
+ {
+ WARN(("VBOXUSBIDC_INTERNAL_IOCTL_PROXY_TEARDOWN: Buffer is NULL"));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ASSERT_WARN(pOut->hDev, ("zero hDev"));
+ VBoxUsbFltProxyStopped(pOut->hDev);
+ ASSERT_WARN(Status == STATUS_SUCCESS, ("unexpected status, 0x%x", Status));
+ break;
+ }
+
+ default:
+ {
+ WARN(("Unknown code 0x%x", Ctl));
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ return Status;
+}
+
+static NTSTATUS _stdcall VBoxUsbMonInternalDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
+{
+ ULONG_PTR Info = 0;
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ PIO_STACK_LOCATION pSl = IoGetCurrentIrpStackLocation(pIrp);
+ Status = vboxUsbMonInternalIoctlDispatch(pSl->Parameters.DeviceIoControl.IoControlCode,
+ pSl->Parameters.Others.Argument1,
+ &Info);
+ Assert(Status != STATUS_PENDING);
+
+ IoReleaseRemoveLock(&g_VBoxUsbMonGlobals.RmLock, pDevObj);
+ }
+
+ pIrp->IoStatus.Information = Info;
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest (pIrp, IO_NO_INCREMENT);
+ return Status;
+}
+
+/**
+ * Unload the driver.
+ *
+ * @param pDrvObj Driver object.
+ */
+static void _stdcall VBoxUsbMonUnload(PDRIVER_OBJECT pDrvObj)
+{
+ RT_NOREF1(pDrvObj);
+ LOG(("VBoxUSBMonUnload pDrvObj (0x%p)", pDrvObj));
+
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+
+ Assert(!g_VBoxUsbMonGlobals.cOpens);
+
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ IoDeleteSymbolicLink(&DosName);
+
+ IoDeleteDevice(g_VBoxUsbMonGlobals.pDevObj);
+
+ /* cleanup the logger */
+ PRTLOGGER pLogger = RTLogRelSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+ pLogger = RTLogSetDefaultInstance(NULL);
+ if (pLogger)
+ RTLogDestroy(pLogger);
+}
+
+RT_C_DECLS_BEGIN
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
+RT_C_DECLS_END
+
+/**
+ * Driver entry point.
+ *
+ * @returns appropriate status code.
+ * @param pDrvObj Pointer to driver object.
+ * @param pRegPath Registry base path.
+ */
+NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
+{
+ RT_NOREF1(pRegPath);
+#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
+ RTLogGroupSettings(0, "+default.e.l.f.l2.l3");
+ RTLogDestinations(0, "debugger");
+#endif
+
+ LOGREL(("Built %s %s", __DATE__, __TIME__));
+
+ memset (&g_VBoxUsbMonGlobals, 0, sizeof (g_VBoxUsbMonGlobals));
+
+ VBOX_PNPHOOKSTUB_INIT(0);
+ VBOX_PNPHOOKSTUB_INIT(1);
+ VBOX_PNPHOOKSTUB_INIT(2);
+ VBOX_PNPHOOKSTUB_INIT(3);
+ VBOX_PNPHOOKSTUB_INIT(4);
+ AssertCompile(VBOXUSBMON_MAXDRIVERS == 5);
+
+ KeInitializeEvent(&g_VBoxUsbMonGlobals.OpenSynchEvent, SynchronizationEvent, TRUE /* signaled */);
+ IoInitializeRemoveLock(&g_VBoxUsbMonGlobals.RmLock, VBOXUSBMON_MEMTAG, 1, 100);
+ UNICODE_STRING DevName;
+ PDEVICE_OBJECT pDevObj;
+ /* create the device */
+ RtlInitUnicodeString(&DevName, USBMON_DEVICE_NAME_NT);
+ NTSTATUS Status = IoAcquireRemoveLock(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ if (NT_SUCCESS(Status))
+ {
+ Status = IoCreateDevice(pDrvObj, sizeof (VBOXUSBMONINS), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
+ if (NT_SUCCESS(Status))
+ {
+ UNICODE_STRING DosName;
+ RtlInitUnicodeString(&DosName, USBMON_DEVICE_NAME_DOS);
+ Status = IoCreateSymbolicLink(&DosName, &DevName);
+ if (NT_SUCCESS(Status))
+ {
+ PVBOXUSBMONINS pDevExt = (PVBOXUSBMONINS)pDevObj->DeviceExtension;
+ memset(pDevExt, 0, sizeof(*pDevExt));
+
+ pDrvObj->DriverUnload = VBoxUsbMonUnload;
+ pDrvObj->MajorFunction[IRP_MJ_CREATE] = VBoxUsbMonCreate;
+ pDrvObj->MajorFunction[IRP_MJ_CLOSE] = VBoxUsbMonClose;
+ pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VBoxUsbMonDeviceControl;
+ pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = VBoxUsbMonInternalDeviceControl;
+
+ g_VBoxUsbMonGlobals.pDevObj = pDevObj;
+ LOG(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS"));
+ return STATUS_SUCCESS;
+ }
+ IoDeleteDevice(pDevObj);
+ }
+ IoReleaseRemoveLockAndWait(&g_VBoxUsbMonGlobals.RmLock, &g_VBoxUsbMonGlobals);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
new file mode 100644
index 00000000..e67c6856
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.h
@@ -0,0 +1,77 @@
+/* $Id: VBoxUsbMon.h $ */
+/** @file
+ * VBox USB Monitor
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <iprt/assert.h>
+#include <VBox/sup.h>
+#include <iprt/asm.h>
+#include <VBox/log.h>
+
+#ifdef DEBUG
+/* disables filters */
+//#define VBOXUSBMON_DBG_NO_FILTERS
+/* disables pnp hooking */
+//#define VBOXUSBMON_DBG_NO_PNPHOOK
+#endif
+
+#include "../../../win/VBoxDbgLog.h"
+#include "../cmn/VBoxDrvTool.h"
+#include "../cmn/VBoxUsbTool.h"
+
+#include "VBoxUsbHook.h"
+#include "VBoxUsbFlt.h"
+
+PVOID VBoxUsbMonMemAlloc(SIZE_T cbBytes);
+PVOID VBoxUsbMonMemAllocZ(SIZE_T cbBytes);
+VOID VBoxUsbMonMemFree(PVOID pvMem);
+
+NTSTATUS VBoxUsbMonGetDescriptor(PDEVICE_OBJECT pDevObj, void *buffer, int size, int type, int index, int language_id);
+NTSTATUS VBoxUsbMonQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations);
+
+void vboxUsbDbgPrintUnicodeString(PUNICODE_STRING pUnicodeString);
+
+typedef DECLCALLBACKTYPE(BOOLEAN, FNVBOXUSBMONDEVWALKER,(PFILE_OBJECT pHubFile, PDEVICE_OBJECT pHubDo, PVOID pvContext));
+typedef FNVBOXUSBMONDEVWALKER *PFNVBOXUSBMONDEVWALKER;
+
+VOID vboxUsbMonHubDevWalk(PFNVBOXUSBMONDEVWALKER pfnWalker, PVOID pvWalker);
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_win_mon_VBoxUsbMon_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
new file mode 100644
index 00000000..f22b3dd8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbMon.rc
@@ -0,0 +1,70 @@
+/* $Id: VBoxUsbMon.rc $ */
+/** @file
+ * VBoxUSBMon - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DRV
+ FILESUBTYPE VFT2_DRV_SYSTEM
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox USB Monitor Driver\0"
+ VALUE "InternalName", "VBoxUSBMon\0"
+ VALUE "OriginalFilename", "VBoxUSBMon.sys\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/testcase/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp b/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp
new file mode 100644
index 00000000..6b75c07f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/testcase/USBTest.cpp
@@ -0,0 +1,384 @@
+/* $Id: USBTest.cpp $ */
+/** @file
+ * VBox host drivers - USB drivers - Filter & driver installation
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/setupapi.h>
+#include <newdev.h>
+
+#include <iprt/assert.h>
+#include <VBox/err.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <VBox/usblib.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Handle to the open device. */
+static HANDLE g_hUSBMonitor = INVALID_HANDLE_VALUE;
+/** Flags whether or not we started the service. */
+static bool g_fStartedService = false;
+
+
+/**
+ * Attempts to start the service, creating it if necessary.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ * @param fRetry Indicates retry call.
+ */
+int usbMonStartService(void)
+{
+ HRESULT hr = VBoxDrvCfgSvcStart(USBMON_SERVICE_NAME_W);
+ if (hr != S_OK)
+ {
+ AssertMsgFailed(("couldn't start service, hr (0x%x)\n", hr));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Stops a possibly running service.
+ *
+ * @returns 0 on success.
+ * @returns -1 on failure.
+ */
+int usbMonStopService(void)
+{
+ RTPrintf("usbMonStopService\n");
+
+ /*
+ * Assume it didn't exist, so we'll create the service.
+ */
+ int rc = -1;
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ AssertMsg(hSMgr, ("OpenSCManager(,,delete) failed rc=%d\n", GetLastError()));
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenServiceW(hSMgr, USBMON_SERVICE_NAME_W, SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Stop the service.
+ */
+ SERVICE_STATUS Status;
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
+ {
+ int iWait = 100;
+ while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ rc = 0;
+ else
+ AssertMsgFailed(("Failed to stop service. status=%d\n", Status.dwCurrentState));
+ }
+ else
+ AssertMsgFailed(("ControlService failed with LastError=%Rwa. status=%d\n", GetLastError(), Status.dwCurrentState));
+ CloseServiceHandle(hService);
+ }
+ else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
+ rc = 0;
+ else
+ AssertMsgFailed(("OpenService failed LastError=%Rwa\n", GetLastError()));
+ CloseServiceHandle(hSMgr);
+ }
+ return rc;
+}
+
+/**
+ * Release specified USB device to the host.
+ *
+ * @returns VBox status code
+ * @param usVendorId Vendor id
+ * @param usProductId Product id
+ * @param usRevision Revision
+ */
+int usbMonReleaseDevice(USHORT usVendorId, USHORT usProductId, USHORT usRevision)
+{
+ USBSUP_RELEASE release;
+ DWORD cbReturned = 0;
+
+ RTPrintf("usbLibReleaseDevice %x %x %x\n", usVendorId, usProductId, usRevision);
+
+ release.usVendorId = usVendorId;
+ release.usProductId = usProductId;
+ release.usRevision = usRevision;
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_RELEASE_DEVICE, &release, sizeof(release), NULL, 0, &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Add USB device filter
+ *
+ * @returns VBox status code.
+ * @param usVendorId Vendor id
+ * @param usProductId Product id
+ * @param usRevision Revision
+ * @param ppID Pointer to filter id
+ */
+int usbMonInsertFilter(USHORT usVendorId, USHORT usProductId, USHORT usRevision, void **ppID)
+{
+ USBFILTER filter;
+ USBSUP_FLTADDOUT flt_add;
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ RTPrintf("usblibInsertFilter %04X %04X %04X\n", usVendorId, usProductId, usRevision);
+
+ USBFilterInit(&filter, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_VENDOR_ID, usVendorId, true);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_PRODUCT_ID, usProductId, true);
+ USBFilterSetNumExact(&filter, USBFILTERIDX_DEVICE_REV, usRevision, true);
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_ADD_FILTER, &filter, sizeof(filter), &flt_add, sizeof(flt_add), &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ *ppID = (void *)flt_add.uId;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Applies existing filters to currently plugged-in USB devices
+ *
+ * @returns VBox status code.
+ */
+int usbMonRunFilters(void)
+{
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_RUN_FILTERS, NULL, 0, NULL, 0, &cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Remove USB device filter
+ *
+ * @returns VBox status code.
+ * @param aID Filter id
+ */
+int usbMonRemoveFilter (void *aID)
+{
+ uintptr_t uId;
+ DWORD cbReturned = 0;
+
+ Assert(g_hUSBMonitor);
+
+ RTPrintf("usblibRemoveFilter %p\n", aID);
+
+ uId = (uintptr_t)aID;
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
+ {
+ AssertMsgFailed(("DeviceIoControl failed with %d\n", GetLastError()));
+ return RTErrConvertFromWin32(GetLastError());
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialize the USB monitor
+ *
+ * @returns VBox status code.
+ */
+int usbMonitorInit()
+{
+ int rc;
+ USBSUP_VERSION version = {0};
+ DWORD cbReturned;
+
+ RTPrintf("usbproxy: usbLibInit\n");
+
+ g_hUSBMonitor = CreateFile (USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // no SECURITY_ATTRIBUTES structure
+ OPEN_EXISTING, // No special create flags
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL); // No template file
+
+ if (g_hUSBMonitor == INVALID_HANDLE_VALUE)
+ {
+ usbMonStartService();
+
+ g_hUSBMonitor = CreateFile (USBMON_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // no SECURITY_ATTRIBUTES structure
+ OPEN_EXISTING, // No special create flags
+ FILE_ATTRIBUTE_SYSTEM,
+ NULL); // No template file
+
+ if (g_hUSBMonitor == INVALID_HANDLE_VALUE)
+ {
+ /* AssertFailed(); */
+ RTPrintf("usbproxy: Unable to open filter driver!! (rc=%lu)\n", GetLastError());
+ rc = VERR_FILE_NOT_FOUND;
+ goto failure;
+ }
+ }
+
+ /*
+ * Check the version
+ */
+ cbReturned = 0;
+ if (!DeviceIoControl(g_hUSBMonitor, SUPUSBFLT_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
+ {
+ RTPrintf("usbproxy: Unable to query filter version!! (rc=%lu)\n", GetLastError());
+ rc = VERR_VERSION_MISMATCH;
+ goto failure;
+ }
+
+ if ( version.u32Major != USBMON_MAJOR_VERSION
+#if USBMON_MINOR_VERSION != 0
+ || version.u32Minor < USBMON_MINOR_VERSION
+#endif
+ )
+ {
+ RTPrintf("usbproxy: Filter driver version mismatch!!\n");
+ rc = VERR_VERSION_MISMATCH;
+ goto failure;
+ }
+
+ return VINF_SUCCESS;
+
+failure:
+ if (g_hUSBMonitor != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(g_hUSBMonitor);
+ g_hUSBMonitor = INVALID_HANDLE_VALUE;
+ }
+ return rc;
+}
+
+
+
+/**
+ * Terminate the USB monitor
+ *
+ * @returns VBox status code.
+ */
+int usbMonitorTerm()
+{
+ if (g_hUSBMonitor != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(g_hUSBMonitor);
+ g_hUSBMonitor = INVALID_HANDLE_VALUE;
+ }
+ /*
+ * If we started the service we might consider stopping it too.
+ *
+ * Since this won't work unless the process starting it is the
+ * last user we might wanna skip this...
+ */
+ if (g_fStartedService)
+ {
+ usbMonStopService();
+ g_fStartedService = false;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int __cdecl main(int argc, char **argv)
+{
+ int rc;
+ int c;
+ RT_NOREF2(argc, argv);
+
+ RTPrintf("USB test\n");
+
+ rc = usbMonitorInit();
+ AssertRC(rc);
+
+ void *pId1, *pId2, *pId3;
+
+ usbMonInsertFilter(0x0529, 0x0514, 0x0100, &pId1);
+ usbMonInsertFilter(0x0A16, 0x2499, 0x0100, &pId2);
+ usbMonInsertFilter(0x80EE, 0x0030, 0x0110, &pId3);
+
+ RTPrintf("Waiting to capture devices... enter 'r' to run filters\n");
+ c = RTStrmGetCh(g_pStdIn);
+ if (c == 'r')
+ {
+ usbMonRunFilters();
+ RTPrintf("Waiting to capture devices...\n");
+ RTStrmGetCh(g_pStdIn); /* eat the '\n' */
+ RTStrmGetCh(g_pStdIn); /* wait for more input */
+ }
+
+ RTPrintf("Releasing device\n");
+ usbMonReleaseDevice(0xA16, 0x2499, 0x100);
+
+ usbMonRemoveFilter(pId1);
+ usbMonRemoveFilter(pId2);
+ usbMonRemoveFilter(pId3);
+
+ rc = usbMonitorTerm();
+
+ return 0;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/usbd/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def b/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def
new file mode 100644
index 00000000..d3a618ab
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/win/usbd/usbd.def
@@ -0,0 +1,74 @@
+; $Id: usbd.def $
+;; @file
+; Export definitions
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox 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.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+LIBRARY USBD.SYS
+
+EXPORTS
+USBD_AllocateDeviceName
+USBD_CalculateUsbBandwidth
+USBD_CompleteRequest
+USBD_CreateConfigurationRequest
+USBD_CreateDevice
+USBD_Debug_GetHeap
+USBD_Debug_LogEntry
+USBD_Debug_RetHeap
+USBD_Dispatch
+USBD_FreeDeviceMutex
+USBD_FreeDeviceName
+USBD_GetDeviceInformation
+USBD_GetInterfaceLength
+USBD_GetPdoRegistryParameter
+USBD_GetSuspendPowerState
+USBD_GetUSBDIVersion
+USBD_InitializeDevice
+USBD_MakePdoName
+USBD_ParseConfigurationDescriptor
+USBD_QueryBusTime
+USBD_RegisterHcDeviceCapabilities
+USBD_RegisterHcFilter
+USBD_RegisterHostController
+USBD_RemoveDevice
+USBD_RestoreDevice
+USBD_SetSuspendPowerState
+USBD_WaitDeviceMutex
+_USBD_CreateConfigurationRequestEx@8
+_USBD_ParseDescriptors@16
+_USBD_ParseConfigurationDescriptorEx@28
+
+; win64 strips the _stdcall postfix
+USBD_CreateConfigurationRequestEx
+USBD_ParseConfigurationDescriptorEx
+USBD_ParseDescriptors