summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/HostDrivers/VBoxUSB
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB')
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/Makefile.kmk85
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp1860
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/USBLib.cpp71
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp571
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h68
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c1278
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h242
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk67
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp279
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c4054
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c1064
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h161
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf37
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf37
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp391
-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
54 files changed, 21880 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk
new file mode 100644
index 00000000..8936ba55
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/Makefile.kmk
@@ -0,0 +1,85 @@
+# $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
+
+# Include sub-makefiles.
+if1of ($(KBUILD_TARGET), solaris win)
+ include $(PATH_SUB_CURRENT)/$(KBUILD_TARGET)/Makefile.kmk
+endif
+
+#
+# USBLib - The static USB Library for use in VBoxSVC and VBoxDD.
+#
+# Note! Drivers using USBFilter.cpp should just add ../USBFilter.cpp
+# to their source list.
+#
+LIBRARIES += USBLib
+USBLib_TEMPLATE = VBoxR3Dll
+USBLib_SDKS.win = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+USBLib_DEFS = IN_USBLIB
+USBLib_DEFS.os2 = STATIC_USBCALLS
+USBLib_DEFS.win = _WIN32_WINNT=0x0501
+USBLib_SOURCES = \
+ USBLib.cpp \
+ USBFilter.cpp
+
+# OS specific bits if applicable.
+USBLib_SOURCES.os2 = \
+ os2/usbcalls.c
+USBLib_SOURCES.solaris = \
+ solaris/USBLib-solaris.cpp
+USBLib_SOURCES.win = \
+ win/lib/VBoxUsbLib-win.cpp
+
+ifdef VBOX_WITH_TESTCASES
+ #
+ # USBFilter testcase.
+ #
+ PROGRAMS += tstUSBFilter
+ tstUSBFilter_TEMPLATE = VBoxR3TstExe
+ tstUSBFilter_DEFS = IN_USBLIB
+ tstUSBFilter_SOURCES = \
+ testcase/tstUSBFilter.cpp
+ tstUSBFilter_LIBS = \
+ $(USBLib_1_TARGET)
+endif # VBOX_WITH_TESTCASES
+
+
+# generate rules
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp b/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp
new file mode 100644
index 00000000..e2e6e199
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp
@@ -0,0 +1,1860 @@
+/* $Id: USBFilter.cpp $ */
+/** @file
+ * VirtualBox USB filter abstraction.
+ */
+
+/*
+ * Copyright (C) 2007-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 <VBox/usbfilter.h>
+#include <VBox/usblib.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+
+
+/** @todo split this up for the sake of device drivers and such. */
+
+
+/**
+ * Initializes an USBFILTER structure.
+ *
+ * @param pFilter The filter to initialize.
+ * @param enmType The filter type. If not valid, the filter will not
+ * be properly initialized and all other calls will fail.
+ */
+USBLIB_DECL(void) USBFilterInit(PUSBFILTER pFilter, USBFILTERTYPE enmType)
+{
+ memset(pFilter, 0, sizeof(*pFilter));
+ AssertReturnVoid(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END);
+ pFilter->u32Magic = USBFILTER_MAGIC;
+ pFilter->enmType = enmType;
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ pFilter->aFields[i].enmMatch = USBFILTERMATCH_IGNORE;
+}
+
+
+/**
+ * Make a clone of the specified filter.
+ *
+ * @param pFilter The target filter.
+ * @param pToClone The source filter.
+ */
+USBLIB_DECL(void) USBFilterClone(PUSBFILTER pFilter, PCUSBFILTER pToClone)
+{
+ memcpy(pFilter, pToClone, sizeof(*pToClone));
+}
+
+
+/**
+ * Deletes (invalidates) an USBFILTER structure.
+ *
+ * @param pFilter The filter to delete.
+ */
+USBLIB_DECL(void) USBFilterDelete(PUSBFILTER pFilter)
+{
+ pFilter->u32Magic = ~USBFILTER_MAGIC;
+ pFilter->enmType = USBFILTERTYPE_INVALID;
+ pFilter->offCurEnd = 0xfffff;
+}
+
+
+/**
+ * Skips blanks.
+ *
+ * @returns Next non-blank char in the string.
+ * @param psz The string.
+ */
+DECLINLINE(const char *) usbfilterSkipBlanks(const char *psz)
+{
+ while (RT_C_IS_BLANK(*psz))
+ psz++;
+ return psz;
+}
+
+
+/**
+ * Worker for usbfilterReadNumber that parses a hexadecimal number.
+ *
+ * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
+ * @param pszExpr Where to start converting, first char is a valid digit.
+ * @param ppszExpr See usbfilterReadNumber.
+ * @param pu16Val See usbfilterReadNumber.
+ */
+static int usbfilterReadNumberHex(const char *pszExpr, const char **ppszExpr, uint16_t *pu16Val)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t u32 = 0;
+ do
+ {
+ unsigned uDigit = *pszExpr >= 'a' && *pszExpr <= 'f'
+ ? *pszExpr - 'a' + 10
+ : *pszExpr >= 'A' && *pszExpr <= 'F'
+ ? *pszExpr - 'A' + 10
+ : *pszExpr - '0';
+ if (uDigit >= 16)
+ break;
+ u32 *= 16;
+ u32 += uDigit;
+ if (u32 > UINT16_MAX)
+ rc = VWRN_NUMBER_TOO_BIG;
+ } while (*++pszExpr);
+
+ *ppszExpr = usbfilterSkipBlanks(pszExpr);
+ *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for usbfilterReadNumber that parses a decimal number.
+ *
+ * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
+ * @param pszExpr Where to start converting, first char is a valid digit.
+ * @param uBase The base - 8 or 16.
+ * @param ppszExpr See usbfilterReadNumber.
+ * @param pu16Val See usbfilterReadNumber.
+ */
+static int usbfilterReadNumberDecimal(const char *pszExpr, unsigned uBase, const char **ppszExpr, uint16_t *pu16Val)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t u32 = 0;
+ do
+ {
+ unsigned uDigit = *pszExpr - '0';
+ if (uDigit >= uBase)
+ break;
+ u32 *= uBase;
+ u32 += uDigit;
+ if (u32 > UINT16_MAX)
+ rc = VWRN_NUMBER_TOO_BIG;
+ } while (*++pszExpr);
+
+ *ppszExpr = usbfilterSkipBlanks(pszExpr);
+ *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
+ return rc;
+}
+
+
+/**
+ * Reads a number from a numeric expression.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if all is fine. *ppszExpr and *pu16Val are updated.
+ * @retval VWRN_NUMBER_TOO_BIG if the number exceeds unsigned 16-bit, both *ppszExpr and *pu16Val are updated.
+ * @retval VERR_NO_DIGITS if there aren't any digits.
+ *
+ * @param ppszExpr Pointer to the current expression pointer.
+ * This is advanced past the expression and trailing blanks on success.
+ * @param pu16Val Where to store the value on success.
+ */
+static int usbfilterReadNumber(const char **ppszExpr, uint16_t *pu16Val)
+{
+ const char *pszExpr = usbfilterSkipBlanks(*ppszExpr);
+ if (!RT_C_IS_DIGIT(*pszExpr))
+ return VERR_NO_DIGITS;
+
+ if (*pszExpr == '0')
+ {
+ if (pszExpr[1] == 'x' || pszExpr[1] == 'X')
+ {
+ if (!RT_C_IS_XDIGIT(pszExpr[2]))
+ return VERR_NO_DIGITS;
+ return usbfilterReadNumberHex(pszExpr + 2, ppszExpr, pu16Val);
+ }
+ if (RT_C_IS_ODIGIT(pszExpr[1]))
+ return usbfilterReadNumberDecimal(pszExpr + 1, 8, ppszExpr, pu16Val);
+ /* Solitary 0! */
+ if (RT_C_IS_DIGIT(pszExpr[1]))
+ return VERR_NO_DIGITS;
+ }
+ return usbfilterReadNumberDecimal(pszExpr, 10, ppszExpr, pu16Val);
+}
+
+
+/**
+ * Validates a numeric expression.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if valid.
+ * @retval VERR_INVALID_PARAMETER if invalid.
+ * @retval VERR_NO_DIGITS if some expression is short of digits.
+ *
+ * @param pszExpr The numeric expression.
+ */
+static int usbfilterValidateNumExpression(const char *pszExpr)
+{
+ /*
+ * An empty expression is fine.
+ */
+ if (!*pszExpr)
+ return VINF_SUCCESS;
+
+ /*
+ * The string format is: "int:((<m>)|([<m>]-[<n>]))(,(<m>)|([<m>]-[<n>]))*"
+ * where <m> and <n> are numbers in decimal, hex (0xNNN) or octal (0NNN).
+ * Spaces are allowed around <m> and <n>.
+ */
+ unsigned cSubExpressions = 0;
+ while (*pszExpr)
+ {
+ if (!strncmp(pszExpr, RT_STR_TUPLE("int:")))
+ pszExpr += strlen("int:");
+
+ /*
+ * Skip remnants of the previous expression and any empty expressions.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr) || *pszExpr == '(' || *pszExpr == ')')
+ pszExpr++;
+ if (!*pszExpr)
+ break;
+
+ /*
+ * Parse the expression.
+ */
+ int rc;
+ uint16_t u16First = 0;
+ uint16_t u16Last = 0;
+ if (*pszExpr == '-')
+ {
+ /* -N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M or M,N or M-N or M- */
+ rc = usbfilterReadNumber(&pszExpr, &u16First);
+ if (RT_SUCCESS(rc))
+ {
+ pszExpr = usbfilterSkipBlanks(pszExpr);
+ if (*pszExpr == '-')
+ {
+ pszExpr++;
+ if (*pszExpr) /* M-N */
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ else /* M- */
+ u16Last = UINT16_MAX;
+ }
+ else if (*pszExpr == ',')
+ {
+ /* M,N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M */
+ u16Last = u16First;
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * We should either be at the end of the string, at an expression separator (|),
+ * or at the end of an interval filter (')').
+ */
+ if (*pszExpr && *pszExpr != '|' && *pszExpr != ')')
+ return VERR_INVALID_PARAMETER;
+
+ cSubExpressions++;
+ }
+
+ return cSubExpressions ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Validates a string pattern.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if valid.
+ * @retval VERR_INVALID_PARAMETER if invalid.
+ *
+ * @param psz The string pattern.
+ */
+static int usbfilterValidateStringPattern(const char *psz)
+{
+ /*
+ * This is only becomes important if we start doing
+ * sets ([0-9]) and such like.
+ */
+ RT_NOREF1(psz);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Thoroughly validates the USB Filter.
+ *
+ * @returns Appropriate VBox status code.
+ * @param pFilter The filter to validate.
+ */
+USBLIB_DECL(int) USBFilterValidate(PCUSBFILTER pFilter)
+{
+ if (!RT_VALID_PTR(pFilter))
+ return VERR_INVALID_POINTER;
+
+ if (pFilter->u32Magic != USBFILTER_MAGIC)
+ return VERR_INVALID_MAGIC;
+
+ if ( pFilter->enmType <= USBFILTERTYPE_INVALID
+ || pFilter->enmType >= USBFILTERTYPE_END)
+ {
+ Log(("USBFilter: %p - enmType=%d!\n", pFilter, pFilter->enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (pFilter->offCurEnd >= sizeof(pFilter->achStrTab))
+ {
+ Log(("USBFilter: %p - offCurEnd=%#x!\n", pFilter, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Validate that string value offsets are inside the string table. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
+ && pFilter->aFields[i].u16Value > pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - bad offset=%#x\n", pFilter, pFilter->aFields[i].u16Value));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Validate the string table.
+ */
+ if (pFilter->achStrTab[0])
+ {
+ Log(("USBFilter: %p - bad null string\n", pFilter));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ const char *psz = &pFilter->achStrTab[1];
+ while (psz < &pFilter->achStrTab[pFilter->offCurEnd])
+ {
+ const char *pszEnd = RTStrEnd(psz, &pFilter->achStrTab[sizeof(pFilter->achStrTab)] - psz);
+ if (!pszEnd)
+ {
+ Log(("USBFilter: %p - string at %#x isn't terminated!\n",
+ pFilter, psz - &pFilter->achStrTab[0]));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ uint16_t off = (uint16_t)(uintptr_t)(psz - &pFilter->achStrTab[0]);
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
+ && pFilter->aFields[i].u16Value == off)
+ break;
+ if (i >= RT_ELEMENTS(pFilter->aFields))
+ {
+ Log(("USBFilter: %p - string at %#x isn't used by anyone! (%s)\n",
+ pFilter, psz - &pFilter->achStrTab[0], psz));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ psz = pszEnd + 1;
+ }
+
+ if ((uintptr_t)(psz - &pFilter->achStrTab[0] - 1) != pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - offCurEnd=%#x currently at %#x\n",
+ pFilter, pFilter->offCurEnd, psz - &pFilter->achStrTab[0] - 1));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ while (psz < &pFilter->achStrTab[sizeof(pFilter->achStrTab)])
+ {
+ if (*psz)
+ {
+ Log(("USBFilter: %p - str tab isn't zero padded! %#x: %c\n",
+ pFilter, psz - &pFilter->achStrTab[0], *psz));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz++;
+ }
+
+
+ /*
+ * Validate the fields.
+ */
+ int rc;
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ if (pFilter->aFields[i].u16Value)
+ {
+ Log(("USBFilter: %p - #%d/%d u16Value=%d expected 0!\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if (!USBFilterIsNumericField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not numeric field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if (!USBFilterIsNumericField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not numeric field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
+ rc = usbfilterValidateNumExpression(psz);
+ if (RT_FAILURE(rc))
+ {
+ Log(("USBFilter: %p - #%d / %d - bad num expr: %s (rc=%Rrc)\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
+ return rc;
+ }
+ break;
+
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if (!USBFilterIsStringField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not string field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if (!USBFilterIsStringField((USBFILTERIDX)i))
+ {
+ Log(("USBFilter: %p - #%d / %d - not string field\n",
+ pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
+ && pFilter->offCurEnd)
+ {
+ Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
+ return VERR_INVALID_PARAMETER;
+ }
+ psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
+ rc = usbfilterValidateStringPattern(psz);
+ if (RT_FAILURE(rc))
+ {
+ Log(("USBFilter: %p - #%d / %d - bad string pattern: %s (rc=%Rrc)\n",
+ pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
+ return rc;
+ }
+ break;
+
+ default:
+ Log(("USBFilter: %p - #%d enmMatch=%d!\n", pFilter, i, pFilter->aFields[i].enmMatch));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Find the specified field in the string table.
+ *
+ * @returns Pointer to the string in the string table on success.
+ * NULL if the field is invalid or it doesn't have a string value.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+DECLINLINE(const char *) usbfilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ {
+ switch (pFilter->aFields[enmFieldIdx].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ Assert(pFilter->aFields[enmFieldIdx].u16Value < sizeof(pFilter->achStrTab));
+ return &pFilter->achStrTab[pFilter->aFields[enmFieldIdx].u16Value];
+
+ default:
+ AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ break;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Gets a number value of a field.
+ *
+ * The field must contain a numeric value.
+ *
+ * @returns The field value on success, -1 on failure (invalid input / not numeric).
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+DECLINLINE(int) usbfilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ {
+ switch (pFilter->aFields[enmFieldIdx].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return pFilter->aFields[enmFieldIdx].u16Value;
+
+ default:
+ AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ break;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * Performs simple pattern matching.
+ *
+ * @returns true on match and false on mismatch.
+ * @param pszExpr The numeric expression.
+ * @param u16Value The value to match.
+ */
+static bool usbfilterMatchNumExpression(const char *pszExpr, uint16_t u16Value)
+{
+ /*
+ * The string format is: "int:((<m>)|([<m>]-[<n>]))(,(<m>)|([<m>]-[<n>]))*"
+ * where <m> and <n> are numbers in decimal, hex (0xNNN) or octal (0NNN).
+ * Spaces are allowed around <m> and <n>.
+ */
+ while (*pszExpr)
+ {
+ if (!strncmp(pszExpr, RT_STR_TUPLE("int:")))
+ pszExpr += strlen("int:");
+
+ /*
+ * Skip remnants of the previous expression and any empty expressions.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr) || *pszExpr == '(' || *pszExpr == ')')
+ pszExpr++;
+ if (!*pszExpr)
+ break;
+
+ /*
+ * Parse the expression.
+ */
+ int rc;
+ uint16_t u16First = 0;
+ uint16_t u16Last = 0;
+ if (*pszExpr == '-')
+ {
+ /* -N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M or M,N or M-N or M- */
+ rc = usbfilterReadNumber(&pszExpr, &u16First);
+ if (RT_SUCCESS(rc))
+ {
+ pszExpr = usbfilterSkipBlanks(pszExpr);
+ if (*pszExpr == '-')
+ {
+ pszExpr++;
+ if (*pszExpr) /* M-N */
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ else /* M- */
+ u16Last = UINT16_MAX;
+ }
+ else if (*pszExpr == ',')
+ {
+ /* M,N */
+ pszExpr++;
+ rc = usbfilterReadNumber(&pszExpr, &u16Last);
+ }
+ else
+ {
+ /* M */
+ u16Last = u16First;
+ }
+ }
+ }
+
+ /* On success, we should either be at the end of the string, at an expression
+ * separator (|), or at the end of an interval filter (')').
+ */
+ if (RT_SUCCESS(rc) && *pszExpr && *pszExpr != '|' && *pszExpr != ')')
+ rc = VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Swap the values if the order is mixed up.
+ */
+ if (u16First > u16Last)
+ {
+ uint16_t u16Tmp = u16First;
+ u16First = u16Last;
+ u16Last = u16Tmp;
+ }
+
+ /*
+ * Perform the compare.
+ */
+ if ( u16Value >= u16First
+ && u16Value <= u16Last)
+ return true;
+ }
+ else
+ {
+ /*
+ * Skip the bad expression.
+ * ('|' is the expression separator.)
+ */
+ while (*pszExpr && *pszExpr != '|')
+ pszExpr++;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Performs simple pattern matching.
+ *
+ * @returns true on match and false on mismatch.
+ * @param pszPattern The pattern to match against.
+ * @param psz The string to match.
+ */
+static bool usbfilterMatchStringPattern(const char *pszPattern, const char *psz)
+{
+ char ch;
+ while ((ch = *pszPattern++))
+ {
+ if (ch == '?')
+ {
+ /*
+ * Matches one char or end of string.
+ */
+ if (*psz)
+ psz++;
+ }
+ else if (ch == '*')
+ {
+ /*
+ * Matches zero or more characters.
+ */
+ /* skip subsequent wildcards */
+ while ( (ch = *pszPattern) == '*'
+ || ch == '?')
+ pszPattern++;
+ if (!ch)
+ /* Pattern ends with a '*' and thus matches the rest of psz. */
+ return true;
+
+ /* Find the length of the following exact pattern sequence. */
+ ssize_t cchMatch = 1;
+ while ( (ch = pszPattern[cchMatch]) != '\0'
+ && ch != '*'
+ && ch != '?')
+ cchMatch++;
+
+ /* Check if the exact pattern sequence is too long. */
+ ssize_t cch = strlen(psz);
+ cch -= cchMatch;
+ if (cch < 0)
+ return false;
+
+ /* Is the rest an exact match? */
+ if (!ch)
+ return memcmp(psz + cch, pszPattern, cchMatch) == 0;
+
+ /*
+ * This is where things normally starts to get recursive or ugly.
+ *
+ * Just to make life simple, we'll skip the nasty stuff and say
+ * that we will do a maximal wildcard match and forget about any
+ * alternative matches.
+ *
+ * If somebody is bored out of their mind one day, feel free to
+ * implement correct matching without using recursion.
+ */
+ ch = *pszPattern;
+ const char *pszMatch = NULL;
+ while ( cch-- >= 0
+ && *psz)
+ {
+ if ( *psz == ch
+ && !strncmp(psz, pszPattern, cchMatch))
+ pszMatch = psz;
+ psz++;
+ }
+ if (!pszMatch)
+ return false;
+
+ /* advance */
+ psz = pszMatch + cchMatch;
+ pszPattern += cchMatch;
+ }
+ else
+ {
+ /* exact match */
+ if (ch != *psz)
+ return false;
+ psz++;
+ }
+ }
+
+ return *psz == '\0';
+}
+
+
+/**
+ * Match a filter against a device.
+ *
+ * @returns true if they match, false if not.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device data. This is a filter (type ignored) that
+ * contains 'exact' values for all present fields and 'ignore'
+ * values for the non-present fields.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(bool) USBFilterMatch(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
+{
+ return USBFilterMatchRated(pFilter, pDevice) > 0;
+}
+
+
+#if 0 /*def IN_RING0*/ /** @todo convert to proper logging. */
+extern "C" int printf(const char *format, ...);
+# define dprintf(a) printf a
+#else
+# define dprintf(a) do {} while (0)
+#endif
+
+/**
+ * Match a filter against a device and rate the result.
+ *
+ * @returns -1 if no match, matching rate between 1 and 100 (inclusive) if matched.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device data. This is a filter (type ignored) that
+ * contains 'exact' values for all present fields and 'ignore'
+ * values for the non-present fields.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(int) USBFilterMatchRated(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
+{
+ unsigned iRate = 0;
+dprintf(("USBFilterMatchRated: %p %p\n", pFilter, pDevice));
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
+ {
+dprintf(("filter match[%d]: !present\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
+ {
+if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
+ dprintf(("filter match[%d]: !num_exact device=ignore\n", i));
+else
+ dprintf(("filter match[%d]: !num_exact %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
+ {
+dprintf(("filter match[%d]: !num_exact_np %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ pDevice->aFields[i].u16Value))
+ {
+dprintf(("filter match[%d]: !num_expression\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ pDevice->aFields[i].u16Value))
+ {
+dprintf(("filter match[%d]: !num_expression_no\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_exact\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_exact_np\n", i));
+ return -1;
+ }
+ iRate += 2;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
+ || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_pattern\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
+ && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
+ usbfilterGetString(pDevice, (USBFILTERIDX)i)))
+ {
+dprintf(("filter match[%d]: !str_pattern_np\n", i));
+ return -1;
+ }
+ iRate += 1;
+ break;
+
+ default:
+ AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
+ return -1;
+ }
+ }
+
+ /* iRate is the range 0..2*cFields - recalc to percent. */
+dprintf(("filter match: iRate=%d", iRate));
+ return iRate == 2 * RT_ELEMENTS(pFilter->aFields)
+ ? 100
+ : (iRate * 100) / (2 * RT_ELEMENTS(pFilter->aFields));
+}
+
+
+/**
+ * Match a filter against a USBDEVICE.
+ *
+ * @returns true if they match, false if not.
+ *
+ * @param pFilter The filter to match with.
+ * @param pDevice The device to match.
+ *
+ * @remark Both the filter and the device are ASSUMED to be valid because
+ * we don't wish to waste any time in this function.
+ */
+USBLIB_DECL(bool) USBFilterMatchDevice(PCUSBFILTER pFilter, PUSBDEVICE pDevice)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ {
+ const char *psz;
+ switch (i)
+ {
+ case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
+ case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
+ case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
+ default: psz = ""; break;
+ }
+ if (!psz)
+ return false;
+ break;
+ }
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ {
+ uint16_t u16Value;
+ switch (i)
+ {
+ case USBFILTERIDX_VENDOR_ID: u16Value = pDevice->idVendor; break;
+ case USBFILTERIDX_PRODUCT_ID: u16Value = pDevice->idProduct; break;
+ case USBFILTERIDX_DEVICE: u16Value = pDevice->bcdDevice; break;
+ case USBFILTERIDX_DEVICE_CLASS: u16Value = pDevice->bDeviceClass; break;
+ case USBFILTERIDX_DEVICE_SUB_CLASS: u16Value = pDevice->bDeviceSubClass; break;
+ case USBFILTERIDX_DEVICE_PROTOCOL: u16Value = pDevice->bDeviceProtocol; break;
+ case USBFILTERIDX_BUS: u16Value = pDevice->bBus; break;
+ case USBFILTERIDX_PORT: u16Value = pDevice->bPort; break;
+ default: u16Value = UINT16_MAX; break;
+
+ }
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ if (pFilter->aFields[i].u16Value != u16Value)
+ return false;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ if (!usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i), u16Value))
+ return false;
+ break;
+ }
+ break;
+ }
+
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ {
+ const char *psz;
+ switch (i)
+ {
+ case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
+ case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
+ case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
+ default: psz = NULL; break;
+ }
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_STR_EXACT:
+ if ( !psz
+ || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_EXACT_NP:
+ if ( psz
+ && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN:
+ if ( !psz
+ || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ if ( psz
+ && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
+ return false;
+ break;
+ }
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks if the two filters are identical.
+ *
+ * @returns true if the are identical, false if they aren't.
+ * @param pFilter The first filter.
+ * @param pFilter2 The second filter.
+ */
+USBLIB_DECL(bool) USBFilterIsIdentical(PCUSBFILTER pFilter, PCUSBFILTER pFilter2)
+{
+ /* Lazy works here because we're darn strict with zero padding and such elsewhere. */
+ return memcmp(pFilter, pFilter2, sizeof(*pFilter)) == 0;
+}
+
+
+
+/**
+ * Sets the filter type.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INVALID_PARAMETER if the filter type is invalid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmType The new filter type.
+ */
+USBLIB_DECL(int) USBFilterSetFilterType(PUSBFILTER pFilter, USBFILTERTYPE enmType)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END, VERR_INVALID_PARAMETER);
+
+ pFilter->enmType = enmType;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Replaces the string value of a field.
+ *
+ * This will remove any existing string value current held by the field from the
+ * string table and then attempt to add the new value. This function can be used
+ * to delete any assigned string before changing the type to numeric by passing
+ * in an empty string. This works because the first byte in the string table is
+ * reserved for the empty (NULL) string.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_POINTER if pszString isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pszString The string to add.
+ * @param fPurge Purge invalid UTF-8 encoding and control characters
+ * before setting it.
+ */
+static int usbfilterSetString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszString, bool fPurge)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+
+ Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
+ Assert(pFilter->achStrTab[pFilter->offCurEnd] == '\0');
+
+ /*
+ * Remove old string value if any.
+ */
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
+ && pFilter->aFields[enmFieldIdx].u16Value != 0)
+ {
+ uint32_t off = pFilter->aFields[enmFieldIdx].u16Value;
+ pFilter->aFields[enmFieldIdx].u16Value = 0; /* Assign it to the NULL string. */
+
+ unsigned cchShift = (unsigned)strlen(&pFilter->achStrTab[off]) + 1;
+ ssize_t cchToMove = (pFilter->offCurEnd + 1) - (off + cchShift);
+ Assert(cchToMove >= 0);
+ if (cchToMove > 0)
+ {
+ /* We're not last - must shift the strings. */
+ memmove(&pFilter->achStrTab[off], &pFilter->achStrTab[off + cchShift], cchToMove);
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ if ( pFilter->aFields[i].u16Value >= off
+ && USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch))
+ pFilter->aFields[i].u16Value -= cchShift;
+ }
+ pFilter->offCurEnd -= cchShift;
+ Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
+ Assert(pFilter->offCurEnd + cchShift <= sizeof(pFilter->achStrTab));
+
+ /* zero the unused string table (to allow lazyness/strictness elsewhere). */
+ memset(&pFilter->achStrTab[pFilter->offCurEnd], '\0', cchShift);
+ }
+
+ /*
+ * Make a special case for the empty string.
+ * (This also makes the delete logical above work correctly for the last string.)
+ */
+ if (!*pszString)
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ else
+ {
+ size_t cch = strlen(pszString);
+ if (pFilter->offCurEnd + cch + 2 > sizeof(pFilter->achStrTab))
+ return VERR_BUFFER_OVERFLOW;
+
+ pFilter->aFields[enmFieldIdx].u16Value = pFilter->offCurEnd + 1;
+ memcpy(&pFilter->achStrTab[pFilter->offCurEnd + 1], pszString, cch + 1);
+ if (fPurge)
+ cch = USBLibPurgeEncoding(&pFilter->achStrTab[pFilter->offCurEnd + 1]);
+ pFilter->offCurEnd += (uint32_t)cch + 1;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wrapper around usbfilterSetString() that deletes any string value
+ * currently assigned to a field.
+ *
+ * Upon successful return the field contains a null string, nothing or a number.
+ *
+ * This function will validate the field index if there isn't any string
+ * value to delete, thus preventing any extra validating of the index.
+ *
+ * @returns VBox status code. See usbfilterSetString.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The index of the field which string value should be deleted.
+ */
+static int usbfilterDeleteAnyStringValue(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = VINF_SUCCESS;
+ if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
+ && pFilter->aFields[enmFieldIdx].u16Value != 0)
+ rc = usbfilterSetString(pFilter, enmFieldIdx, "", false /*fPurge*/);
+ else if ((unsigned)enmFieldIdx >= (unsigned)USBFILTERIDX_END)
+ rc = VERR_INVALID_PARAMETER;
+ return rc;
+}
+
+
+/**
+ * Sets a field to always match (ignore whatever is thrown at it).
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ */
+USBLIB_DECL(int) USBFilterSetIgnore(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_IGNORE;
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to match on device field present only.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ */
+USBLIB_DECL(int) USBFilterSetPresentOnly(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_PRESENT;
+ pFilter->aFields[enmFieldIdx].u16Value = 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to exactly match a number.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param u16Value The string pattern.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetNumExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t u16Value, bool fMustBePresent)
+{
+ int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
+ if (RT_SUCCESS(rc))
+ {
+ pFilter->aFields[enmFieldIdx].u16Value = u16Value;
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXACT : USBFILTERMATCH_NUM_EXACT_NP;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Sets a field to match a numeric expression.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or the numeric expression aren't valid.
+ * @retval VERR_INVALID_POINTER if pszExpression isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszExpression The numeric expression.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetNumExpression(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszExpression, bool fMustBePresent)
+{
+ int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ /* Strip leading spaces and empty sub expressions (||). */
+ while (*pszExpression && (RT_C_IS_BLANK(*pszExpression) || *pszExpression == '|'))
+ pszExpression++;
+
+ rc = usbfilterValidateNumExpression(pszExpression);
+ if (RT_SUCCESS(rc))
+ {
+ /* We could optimize the expression further (stripping spaces, convert numbers),
+ but it's more work than what it's worth and it could upset some users. */
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszExpression, false /*fPurge*/);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXPRESSION : USBFILTERMATCH_NUM_EXPRESSION_NP;
+ else if (rc == VERR_NO_DIGITS)
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to exactly match a string.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszValue The string value.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ * @param fPurge Purge invalid UTF-8 encoding and control
+ * characters before setting it.
+ */
+USBLIB_DECL(int) USBFilterSetStringExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszValue,
+ bool fMustBePresent, bool fPurge)
+{
+ int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszValue, fPurge);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_EXACT : USBFILTERMATCH_STR_EXACT_NP;
+ }
+ return rc;
+}
+
+
+/**
+ * Sets a field to match a string pattern.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the string table is full.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index. This must be a string field.
+ * @param pszPattern The string pattern.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetStringPattern(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszPattern, bool fMustBePresent)
+{
+ int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterValidateStringPattern(pszPattern);
+ if (RT_SUCCESS(rc))
+ {
+ rc = usbfilterSetString(pFilter, enmFieldIdx, pszPattern, false /*fPurge*/);
+ if (RT_SUCCESS(rc))
+ pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_PATTERN : USBFILTERMATCH_STR_PATTERN_NP;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Sets the must-be-present part of a field.
+ *
+ * This only works on field which already has matching criteria. This means
+ * that field marked 'ignore' will not be processed and will result in a
+ * warning status code.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VWRN_INVALID_PARAMETER if the field is marked 'ignore'. No assertions.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
+ * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
+ * If clear, a non-present field on the device will match.
+ */
+USBLIB_DECL(int) USBFilterSetMustBePresent(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, bool fMustBePresent)
+{
+ AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
+
+ USBFILTERMATCH enmMatch = (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
+ if (fMustBePresent)
+ {
+ switch (enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ return VWRN_INVALID_PARAMETER;
+
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_PATTERN:
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ enmMatch = USBFILTERMATCH_NUM_EXACT;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ enmMatch = USBFILTERMATCH_NUM_EXPRESSION;
+ break;
+ case USBFILTERMATCH_STR_EXACT_NP:
+ enmMatch = USBFILTERMATCH_STR_EXACT;
+ break;
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ enmMatch = USBFILTERMATCH_STR_PATTERN;
+ break;
+ default:
+ AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
+ }
+ }
+ else
+ {
+ switch (enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ return VWRN_INVALID_PARAMETER;
+
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ break;
+
+ case USBFILTERMATCH_PRESENT:
+ enmMatch = USBFILTERMATCH_IGNORE;
+ break;
+ case USBFILTERMATCH_NUM_EXACT:
+ enmMatch = USBFILTERMATCH_NUM_EXACT_NP;
+ break;
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ enmMatch = USBFILTERMATCH_NUM_EXPRESSION_NP;
+ break;
+ case USBFILTERMATCH_STR_EXACT:
+ enmMatch = USBFILTERMATCH_STR_EXACT_NP;
+ break;
+ case USBFILTERMATCH_STR_PATTERN:
+ enmMatch = USBFILTERMATCH_STR_PATTERN_NP;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
+ }
+ }
+
+ pFilter->aFields[enmFieldIdx].enmMatch = enmMatch;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the filter type.
+ *
+ * @returns The filter type.
+ * USBFILTERTYPE_INVALID if the filter is invalid.
+ * @param pFilter The filter.
+ */
+USBLIB_DECL(USBFILTERTYPE) USBFilterGetFilterType(PCUSBFILTER pFilter)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, USBFILTERTYPE_INVALID);
+ return pFilter->enmType;
+}
+
+
+/**
+ * Gets the matching method for a field.
+ *
+ * @returns The matching method on success, UBFILTERMATCH_INVALID on invalid field index.
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(USBFILTERMATCH) USBFilterGetMatchingMethod(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if ( pFilter->u32Magic == USBFILTER_MAGIC
+ && (unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
+ return (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
+ return USBFILTERMATCH_INVALID;
+}
+
+
+/**
+ * Gets the numeric value of a field.
+ *
+ * The field must contain a number, we're not doing any conversions for you.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a number.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pu16Value Where to store the value.
+ */
+USBLIB_DECL(int) USBFilterQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t *pu16Value)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+ int iValue = usbfilterGetNum(pFilter, enmFieldIdx);
+ if (iValue == -1)
+ return VERR_INVALID_PARAMETER;
+ *pu16Value = (uint16_t)iValue;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the numeric value of a field.
+ *
+ * The field must contain a number, we're not doing any conversions for you.
+ *
+ * @returns The field value on success, -1 on failure (invalid input / not numeric).
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(int) USBFilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, -1);
+ return usbfilterGetNum(pFilter, enmFieldIdx);
+}
+
+
+/**
+ * Gets the string value of a field.
+ *
+ * The field must contain a string, we're not doing any conversions for you.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_BUFFER_OVERFLOW if the buffer isn't sufficient to hold the string. The buffer
+ * will be filled with as much of the string that'll fit.
+ * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a string.
+ * @retval VERR_INVALID_MAGIC if pFilter is invalid.
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ * @param pszBuf Where to store the string.
+ * @param cchBuf The size of the buffer.
+ */
+USBLIB_DECL(int) USBFilterQueryString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, char *pszBuf, size_t cchBuf)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
+
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_UNLIKELY(!psz))
+ return VERR_INVALID_PARAMETER;
+
+ int rc = VINF_SUCCESS;
+ size_t cch = strlen(psz);
+ if (cch < cchBuf)
+ memcpy(pszBuf, psz, cch + 1);
+ else
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ if (cchBuf)
+ {
+ memcpy(pszBuf, psz, cchBuf - 1);
+ pszBuf[cchBuf - 1] = '\0';
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Gets the string table entry for a field.
+ *
+ * @returns Pointer to the string. (readonly!)
+ *
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(const char *) USBFilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, NULL);
+
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_UNLIKELY(!psz))
+ return NULL;
+ return psz;
+}
+
+
+/**
+ * Gets the string length of a field containing a string.
+ *
+ * @returns String length on success, -1 on failure (not a string, bad filter).
+ * @param pFilter The filter.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(ssize_t) USBFilterGetStringLen(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
+{
+ if (RT_LIKELY(pFilter->u32Magic == USBFILTER_MAGIC))
+ {
+ const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
+ if (RT_LIKELY(psz))
+ return strlen(psz);
+ }
+ return -1;
+}
+
+
+/**
+ * Check if any of the fields are set to something substatial.
+ *
+ * Consider the fileter a wildcard if this returns false.
+ *
+ * @returns true / false.
+ * @param pFilter The filter.
+ */
+USBLIB_DECL(bool) USBFilterHasAnySubstatialCriteria(PCUSBFILTER pFilter)
+{
+ AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, false);
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
+ {
+ switch (pFilter->aFields[i].enmMatch)
+ {
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ break;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ return true;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ {
+ const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
+ if (psz)
+ {
+ while (*psz && (*psz == '|' || RT_C_IS_BLANK(*psz)))
+ psz++;
+ if (*psz)
+ return true;
+ }
+ break;
+ }
+
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ {
+ const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
+ if (psz)
+ {
+ while (*psz && (*psz == '*' || *psz == '?'))
+ psz++;
+ if (*psz)
+ return true;
+ }
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
+/**
+ * Checks whether the specified field is a numeric field or not.
+ *
+ * @returns true / false.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(bool) USBFilterIsNumericField(USBFILTERIDX enmFieldIdx)
+{
+ switch (enmFieldIdx)
+ {
+ case USBFILTERIDX_VENDOR_ID:
+ case USBFILTERIDX_PRODUCT_ID:
+ case USBFILTERIDX_DEVICE:
+ case USBFILTERIDX_DEVICE_CLASS:
+ case USBFILTERIDX_DEVICE_SUB_CLASS:
+ case USBFILTERIDX_DEVICE_PROTOCOL:
+ case USBFILTERIDX_BUS:
+ case USBFILTERIDX_PORT:
+ return true;
+
+ default:
+ AssertMsgFailed(("%d\n", enmFieldIdx));
+ RT_FALL_THRU();
+ case USBFILTERIDX_MANUFACTURER_STR:
+ case USBFILTERIDX_PRODUCT_STR:
+ case USBFILTERIDX_SERIAL_NUMBER_STR:
+ return false;
+ }
+}
+
+
+/**
+ * Checks whether the specified field is a string field or not.
+ *
+ * @returns true / false.
+ * @param enmFieldIdx The field index.
+ */
+USBLIB_DECL(bool) USBFilterIsStringField(USBFILTERIDX enmFieldIdx)
+{
+ switch (enmFieldIdx)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmFieldIdx));
+ RT_FALL_THRU();
+ case USBFILTERIDX_VENDOR_ID:
+ case USBFILTERIDX_PRODUCT_ID:
+ case USBFILTERIDX_DEVICE:
+ case USBFILTERIDX_DEVICE_CLASS:
+ case USBFILTERIDX_DEVICE_SUB_CLASS:
+ case USBFILTERIDX_DEVICE_PROTOCOL:
+ case USBFILTERIDX_BUS:
+ case USBFILTERIDX_PORT:
+ return false;
+
+ case USBFILTERIDX_MANUFACTURER_STR:
+ case USBFILTERIDX_PRODUCT_STR:
+ case USBFILTERIDX_SERIAL_NUMBER_STR:
+ return true;
+ }
+}
+
+
+/**
+ * Checks whether the specified matching method uses a numeric value or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodUsingNumericValue(USBFILTERMATCH enmMatchingMethod)
+{
+ switch (enmMatchingMethod)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmMatchingMethod));
+ RT_FALL_THRU();
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ return false;
+
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return true;
+ }
+}
+
+
+/**
+ * Checks whether the specified matching method uses a string value or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodUsingStringValue(USBFILTERMATCH enmMatchingMethod)
+{
+ switch (enmMatchingMethod)
+ {
+ default:
+ AssertMsgFailed(("%d\n", enmMatchingMethod));
+ RT_FALL_THRU();
+ case USBFILTERMATCH_IGNORE:
+ case USBFILTERMATCH_PRESENT:
+ case USBFILTERMATCH_NUM_EXACT:
+ case USBFILTERMATCH_NUM_EXACT_NP:
+ return false;
+
+ case USBFILTERMATCH_NUM_EXPRESSION:
+ case USBFILTERMATCH_NUM_EXPRESSION_NP:
+ case USBFILTERMATCH_STR_EXACT:
+ case USBFILTERMATCH_STR_EXACT_NP:
+ case USBFILTERMATCH_STR_PATTERN:
+ case USBFILTERMATCH_STR_PATTERN_NP:
+ return true;
+ }
+}
+
+
+/**
+ * Checks if a matching method is for numeric fields or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodNumeric(USBFILTERMATCH enmMatchingMethod)
+{
+ return enmMatchingMethod >= USBFILTERMATCH_NUM_FIRST
+ && enmMatchingMethod <= USBFILTERMATCH_NUM_LAST;
+}
+
+/**
+ * Checks if a matching method is for string fields or not.
+ *
+ * @returns true / false.
+ * @param enmMatchingMethod The matching method.
+ */
+USBLIB_DECL(bool) USBFilterIsMethodString(USBFILTERMATCH enmMatchingMethod)
+{
+ return enmMatchingMethod >= USBFILTERMATCH_STR_FIRST
+ && enmMatchingMethod <= USBFILTERMATCH_STR_LAST;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp b/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp
new file mode 100644
index 00000000..b3f9ec6b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/USBLib.cpp
@@ -0,0 +1,71 @@
+/* $Id: USBLib.cpp $ */
+/** @file
+ * VirtualBox USB Library, Common Bits.
+ */
+
+/*
+ * 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 <VBox/usblib.h>
+
+
+/**
+ * Calculate the hash of the serial string.
+ *
+ * 64bit FNV1a, chosen because it is designed to hash in to a power of two
+ * space, and is much quicker and simpler than, say, a half MD4.
+ *
+ * @returns the hash.
+ * @param pszSerial The serial string.
+ */
+USBLIB_DECL(uint64_t) USBLibHashSerial(const char *pszSerial)
+{
+ if (!pszSerial)
+ pszSerial = "";
+
+ const uint8_t *pu8 = (const uint8_t *)pszSerial;
+ uint64_t u64 = UINT64_C(14695981039346656037);
+ for (;;)
+ {
+ uint8_t u8 = *pu8;
+ if (!u8)
+ break;
+ u64 = (u64 * UINT64_C(1099511628211)) ^ u8;
+ pu8++;
+ }
+
+ return u64;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp
new file mode 100644
index 00000000..7533e653
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.cpp
@@ -0,0 +1,571 @@
+/* $Id: VBoxUSBFilterMgr.cpp $ */
+/** @file
+ * VirtualBox Ring-0 USB Filter Manager.
+ */
+
+/*
+ * Copyright (C) 2007-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 <VBox/usbfilter.h>
+#include "VBoxUSBFilterMgr.h"
+
+#include <iprt/err.h>
+#include <iprt/handletable.h>
+#include <iprt/mem.h>
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+# include <iprt/spinlock.h>
+#else
+# include <iprt/semaphore.h>
+#endif
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** @def VBOXUSBFILTERMGR_LOCK
+ * Locks the filter list. Careful with scoping since this may
+ * create a temporary variable. Don't call twice in the same function.
+ */
+
+/** @def VBOXUSBFILTERMGR_UNLOCK
+ * Unlocks the filter list.
+ */
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+
+# define VBOXUSBFILTERMGR_LOCK() \
+ RTSpinlockAcquire(g_Spinlock)
+
+# define VBOXUSBFILTERMGR_UNLOCK() \
+ RTSpinlockRelease(g_Spinlock)
+
+#else
+
+# define VBOXUSBFILTERMGR_LOCK() \
+ do { int rc2 = RTSemFastMutexRequest(g_Mtx); AssertRC(rc2); } while (0)
+
+# define VBOXUSBFILTERMGR_UNLOCK() \
+ do { int rc2 = RTSemFastMutexRelease(g_Mtx); AssertRC(rc2); } while (0)
+
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to an VBoxUSB filter. */
+typedef struct VBOXUSBFILTER *PVBOXUSBFILTER;
+/** Pointer to PVBOXUSBFILTER. */
+typedef PVBOXUSBFILTER *PPVBOXUSBFILTER;
+
+/**
+ * VBoxUSB internal filter representation.
+ */
+typedef struct VBOXUSBFILTER
+{
+ /** The core filter. */
+ USBFILTER Core;
+ /** The filter owner. */
+ VBOXUSBFILTER_CONTEXT Owner;
+ /** The filter Id. */
+ uint32_t uHnd;
+ /** Pointer to the next filter in the list. */
+ PVBOXUSBFILTER pNext;
+} VBOXUSBFILTER;
+
+/**
+ * VBoxUSB filter list.
+ */
+typedef struct VBOXUSBFILTERLIST
+{
+ /** The head pointer. */
+ PVBOXUSBFILTER pHead;
+ /** The tail pointer. */
+ PVBOXUSBFILTER pTail;
+} VBOXUSBFILTERLIST;
+/** Pointer to a VBOXUSBFILTERLIST. */
+typedef VBOXUSBFILTERLIST *PVBOXUSBFILTERLIST;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+/** Spinlock protecting the filter lists. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+#else
+/** Mutex protecting the filter lists. */
+static RTSEMFASTMUTEX g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+/** The per-type filter lists.
+ * @remark The first entry is empty (USBFILTERTYPE_INVALID). */
+static VBOXUSBFILTERLIST g_aLists[USBFILTERTYPE_END];
+/** The handle table to match handles to the right filter. */
+static RTHANDLETABLE g_hHndTableFilters = NIL_RTHANDLETABLE;
+
+
+
+/**
+ * Initializes the VBoxUSB filter manager.
+ *
+ * @returns IPRT status code.
+ */
+int VBoxUSBFilterInit(void)
+{
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxUSBFilter");
+#else
+ int rc = RTSemFastMutexCreate(&g_Mtx);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t fFlags;
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ fFlags = RTHANDLETABLE_FLAGS_LOCKED_IRQ_SAFE;
+#else
+ fFlags = RTHANDLETABLE_FLAGS_LOCKED;
+#endif
+ rc = RTHandleTableCreateEx(&g_hHndTableFilters, fFlags, 1 /* uBase */, 8192 /* cMax */,
+ NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* not really required, but anyway... */
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ g_aLists[i].pHead = g_aLists[i].pTail = NULL;
+ }
+ else
+ {
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+#else
+ RTSemFastMutexDestroy(g_Mtx);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Internal worker that frees a filter.
+ *
+ * @param pFilter The filter to free.
+ */
+static void vboxUSBFilterFree(PVBOXUSBFILTER pFilter)
+{
+ USBFilterDelete(&pFilter->Core);
+ pFilter->Owner = VBOXUSBFILTER_CONTEXT_NIL;
+ pFilter->pNext = NULL;
+ RTMemFree(pFilter);
+}
+
+
+/**
+ * Terminates the VBoxUSB filter manager.
+ */
+void VBoxUSBFilterTerm(void)
+{
+#ifdef VBOXUSBFILTERMGR_USB_SPINLOCK
+ RTSpinlockDestroy(g_Spinlock);
+ g_Spinlock = NIL_RTSPINLOCK;
+#else
+ RTSemFastMutexDestroy(g_Mtx);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+#endif
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ g_aLists[i].pHead = g_aLists[i].pTail = NULL;
+ while (pCur)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ vboxUSBFilterFree(pCur);
+ pCur = pNext;
+ }
+ }
+
+ RTHandleTableDestroy(g_hHndTableFilters, NULL, NULL);
+}
+
+
+/**
+ * Adds a new filter.
+ *
+ * The filter will be validate, duplicated and added.
+ *
+ * @returns IPRT status code.
+ * @param pFilter The filter.
+ * @param Owner The filter owner. Must be non-zero.
+ * @param puId Where to store the filter ID.
+ */
+int VBoxUSBFilterAdd(PCUSBFILTER pFilter, VBOXUSBFILTER_CONTEXT Owner, uintptr_t *puId)
+{
+ /*
+ * Validate input.
+ */
+ int rc = USBFilterValidate(pFilter);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
+ return VERR_INVALID_PARAMETER;
+ if (!RT_VALID_PTR(puId))
+ return VERR_INVALID_POINTER;
+
+ /*
+ * Allocate a new filter.
+ */
+ PVBOXUSBFILTER pNew = (PVBOXUSBFILTER)RTMemAlloc(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+ memcpy(&pNew->Core, pFilter, sizeof(pNew->Core));
+ pNew->Owner = Owner;
+ pNew->pNext = NULL;
+
+ rc = RTHandleTableAlloc(g_hHndTableFilters, pNew, &pNew->uHnd);
+ if (RT_SUCCESS(rc))
+ {
+ *puId = pNew->uHnd;
+
+ /*
+ * Insert it.
+ */
+ PVBOXUSBFILTERLIST pList = &g_aLists[pFilter->enmType];
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ if (pList->pTail)
+ pList->pTail->pNext = pNew;
+ else
+ pList->pHead = pNew;
+ pList->pTail = pNew;
+
+ VBOXUSBFILTERMGR_UNLOCK();
+ }
+ else
+ RTMemFree(pNew);
+
+ return rc;
+}
+
+
+/**
+ * Removes an existing filter.
+ *
+ * The filter will be validate, duplicated and added.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if successfully removed.
+ * @retval VERR_FILE_NOT_FOUND if the specified filter/owner cannot be found.
+ *
+ * @param Owner The filter owner.
+ * @param uId The ID of the filter that's to be removed.
+ * Returned by VBoxUSBFilterAdd().
+ */
+int VBoxUSBFilterRemove(VBOXUSBFILTER_CONTEXT Owner, uintptr_t uId)
+{
+ /*
+ * Validate input.
+ */
+ if (!uId || uId != (uint32_t)uId)
+ return VERR_INVALID_PARAMETER;
+ if (!Owner || Owner == VBOXUSBFILTER_CONTEXT_NIL)
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Locate and unlink it.
+ */
+ uint32_t uHnd = (uint32_t)uId;
+ PVBOXUSBFILTER pCur = NULL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; !pCur && i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if ( pCur->uHnd == uHnd
+ && pCur->Owner == Owner)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+ break;
+ }
+
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ /*
+ * Free it (if found).
+ */
+ if (pCur)
+ {
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ Assert(pv == pCur); NOREF(pv);
+ vboxUSBFilterFree(pCur);
+ return VINF_SUCCESS;
+ }
+
+ return VERR_FILE_NOT_FOUND;
+}
+
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterGetOwner(uintptr_t uId)
+{
+ Assert(uId);
+ /*
+ * Validate input.
+ */
+ if (!uId || uId != (uint32_t)uId)
+ return VBOXUSBFILTER_CONTEXT_NIL;
+
+ /*
+ * Result.
+ */
+ VBOXUSBFILTER_CONTEXT Owner = VBOXUSBFILTER_CONTEXT_NIL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ PVBOXUSBFILTER pCur = (PVBOXUSBFILTER)RTHandleTableLookup(g_hHndTableFilters, (uint32_t)uId);
+ if (pCur)
+ Owner = pCur->Owner;
+
+ Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ return Owner;
+}
+
+/**
+ * Removes all filters belonging to the specified owner.
+ *
+ * This is typically called when an owner disconnects or
+ * terminates unexpectedly.
+ *
+ * @param Owner The owner
+ */
+void VBoxUSBFilterRemoveOwner(VBOXUSBFILTER_CONTEXT Owner)
+{
+ /*
+ * Collect the filters that should be freed.
+ */
+ PVBOXUSBFILTER pToFree = NULL;
+
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if (pCur->Owner == Owner)
+ {
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+
+ pCur->pNext = pToFree;
+ pToFree = pCur;
+
+ pCur = pNext;
+ }
+ else
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ /*
+ * Free any filters we've found.
+ */
+ while (pToFree)
+ {
+ PVBOXUSBFILTER pNext = pToFree->pNext;
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pToFree->uHnd);
+ Assert(pv == pToFree); NOREF(pv);
+ vboxUSBFilterFree(pToFree);
+ pToFree = pNext;
+ }
+}
+
+/**
+ * Match the specified device against the filters.
+ * Unlike the VBoxUSBFilterMatch, returns Owner also if exclude filter is matched
+ *
+ * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
+ * @param pDevice The device data as a filter structure.
+ * See USBFilterMatch for how to construct this.
+ * @param puId Where to store the filter id (optional).
+ * @param fRemoveFltIfOneShot Whether or not to remove one-shot filters on
+ * match.
+ * @param pfFilter Where to store whether the device must be filtered or not
+ * @param pfIsOneShot Where to return whetehr the match was a one-shot
+ * filter or not. Optional.
+ *
+ */
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatchEx(PCUSBFILTER pDevice, uintptr_t *puId,
+ bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
+{
+ /*
+ * Validate input.
+ */
+ int rc = USBFilterValidate(pDevice);
+ AssertRCReturn(rc, VBOXUSBFILTER_CONTEXT_NIL);
+
+ *pfFilter = false;
+ if (puId)
+ *puId = 0;
+
+ /*
+ * Search the lists for a match.
+ * (The lists are ordered by priority.)
+ */
+ VBOXUSBFILTERMGR_LOCK();
+
+ for (unsigned i = USBFILTERTYPE_FIRST; i < RT_ELEMENTS(g_aLists); i++)
+ {
+ PVBOXUSBFILTER pPrev = NULL;
+ PVBOXUSBFILTER pCur = g_aLists[i].pHead;
+ while (pCur)
+ {
+ if (USBFilterMatch(&pCur->Core, pDevice))
+ {
+ /*
+ * Take list specific actions and return.
+ *
+ * The code does NOT implement the case where there are two or more
+ * filter clients, and one of them is releasing a device that's
+ * requested by some of the others. It's just too much work for a
+ * situation that noone will encounter.
+ */
+ if (puId)
+ *puId = pCur->uHnd;
+ VBOXUSBFILTER_CONTEXT Owner = pCur->Owner;
+ *pfFilter = !!(i != USBFILTERTYPE_IGNORE
+ && i != USBFILTERTYPE_ONESHOT_IGNORE);
+
+ if ( i == USBFILTERTYPE_ONESHOT_IGNORE
+ || i == USBFILTERTYPE_ONESHOT_CAPTURE)
+ {
+ if (fRemoveFltIfOneShot)
+ {
+ /* unlink */
+ PVBOXUSBFILTER pNext = pCur->pNext;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ g_aLists[i].pHead = pNext;
+ if (!pNext)
+ g_aLists[i].pTail = pPrev;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+
+ if ( i == USBFILTERTYPE_ONESHOT_IGNORE
+ || i == USBFILTERTYPE_ONESHOT_CAPTURE)
+ {
+ if (fRemoveFltIfOneShot)
+ {
+ void *pv = RTHandleTableFree(g_hHndTableFilters, pCur->uHnd);
+ Assert(pv == pCur); NOREF(pv);
+ vboxUSBFilterFree(pCur);
+ }
+ if (pfIsOneShot)
+ *pfIsOneShot = true;
+ }
+ else
+ {
+ if (pfIsOneShot)
+ *pfIsOneShot = false;
+ }
+ return Owner;
+ }
+
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+ }
+
+ VBOXUSBFILTERMGR_UNLOCK();
+ return VBOXUSBFILTER_CONTEXT_NIL;
+}
+
+/**
+ * Match the specified device against the filters.
+ *
+ * @returns Owner on if matched, VBOXUSBFILTER_CONTEXT_NIL it not matched.
+ * @param pDevice The device data as a filter structure.
+ * See USBFilterMatch for how to construct this.
+ * @param puId Where to store the filter id (optional).
+ */
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatch(PCUSBFILTER pDevice, uintptr_t *puId)
+{
+ bool fFilter = false;
+ VBOXUSBFILTER_CONTEXT Owner = VBoxUSBFilterMatchEx(pDevice, puId,
+ true, /* remove filter is it's a one-shot*/
+ &fFilter, NULL /* bool * fIsOneShot */);
+ if (fFilter)
+ {
+ Assert(Owner != VBOXUSBFILTER_CONTEXT_NIL);
+ return Owner;
+ }
+ return VBOXUSBFILTER_CONTEXT_NIL;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h
new file mode 100644
index 00000000..170c750a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/VBoxUSBFilterMgr.h
@@ -0,0 +1,68 @@
+/* $Id: VBoxUSBFilterMgr.h $ */
+/** @file
+ * VirtualBox Ring-0 USB Filter Manager.
+ */
+
+/*
+ * Copyright (C) 2007-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_VBoxUSBFilterMgr_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_VBoxUSBFilterMgr_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/usbfilter.h>
+
+RT_C_DECLS_BEGIN
+
+/** @todo r=bird: VBOXUSBFILTER_CONTEXT isn't following the coding
+ * guildlines. Don't know which clueless dude did this... */
+#if defined(RT_OS_WINDOWS)
+typedef struct VBOXUSBFLTCTX *VBOXUSBFILTER_CONTEXT;
+#define VBOXUSBFILTER_CONTEXT_NIL NULL
+#else
+typedef RTPROCESS VBOXUSBFILTER_CONTEXT;
+#define VBOXUSBFILTER_CONTEXT_NIL NIL_RTPROCESS
+#endif
+
+int VBoxUSBFilterInit(void);
+void VBoxUSBFilterTerm(void);
+void VBoxUSBFilterRemoveOwner(VBOXUSBFILTER_CONTEXT Owner);
+int VBoxUSBFilterAdd(PCUSBFILTER pFilter, VBOXUSBFILTER_CONTEXT Owner, uintptr_t *puId);
+int VBoxUSBFilterRemove(VBOXUSBFILTER_CONTEXT Owner, uintptr_t uId);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatch(PCUSBFILTER pDevice, uintptr_t *puId);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterMatchEx(PCUSBFILTER pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot);
+VBOXUSBFILTER_CONTEXT VBoxUSBFilterGetOwner(uintptr_t uId);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_VBoxUSBFilterMgr_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c
new file mode 100644
index 00000000..5950cc6f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.c
@@ -0,0 +1,1278 @@
+#define INCL_DOSERRORS
+#define INCL_DOSMEMMGR
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSDEVICES
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSMODULEMGR
+#include <os2.h>
+
+#if !defined(__GNUC__) || defined(STATIC_USBCALLS)
+#include <string.h>
+#else
+#define memcpy __builtin_memcpy
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
+#include <VBox/log.h>
+
+#ifdef __GNUC__
+# define APIEXPORT __declspec(dllexport)
+#else
+# define APIEXPORT
+#endif
+
+#ifndef ERROR_USER_DEFINED_BASE
+/*#define ERROR_USER_DEFINED_BASE 0xFF00 */
+
+#define ERROR_I24_WRITE_PROTECT 0
+#define ERROR_I24_BAD_UNIT 1
+#define ERROR_I24_NOT_READY 2
+#define ERROR_I24_BAD_COMMAND 3
+#define ERROR_I24_CRC 4
+#define ERROR_I24_BAD_LENGTH 5
+#define ERROR_I24_SEEK 6
+#define ERROR_I24_NOT_DOS_DISK 7
+#define ERROR_I24_SECTOR_NOT_FOUND 8
+#define ERROR_I24_OUT_OF_PAPER 9
+#define ERROR_I24_WRITE_FAULT 10
+#define ERROR_I24_READ_FAULT 11
+#define ERROR_I24_GEN_FAILURE 12
+#define ERROR_I24_DISK_CHANGE 13
+#define ERROR_I24_WRONG_DISK 15
+#define ERROR_I24_UNCERTAIN_MEDIA 16
+#define ERROR_I24_CHAR_CALL_INTERRUPTED 17
+#define ERROR_I24_NO_MONITOR_SUPPORT 18
+#define ERROR_I24_INVALID_PARAMETER 19
+#define ERROR_I24_DEVICE_IN_USE 20
+#define ERROR_I24_QUIET_INIT_FAIL 21
+#endif
+
+#include "usbcalls.h"
+
+#define IOCAT_USBRES 0x000000A0 /* USB Resource device control */
+#define IOCTLF_NUMDEVICE 0x00000031 /* Get Number of plugged in Devices */
+#define IOCTLF_GETINFO 0x00000032 /* Get Info About a device */
+#define IOCTLF_AQUIREDEVICE 0x00000033
+#define IOCTLF_RELEASEDEVICE 0x00000034
+#define IOCTLF_GETSTRING 0x00000035
+#define IOCTLF_SENDCONTROLURB 0x00000036
+#define IOCTLF_SENDBULKURB 0x00000037 /* Send */
+#define IOCTLF_START_IRQ_PROC 0x00000038 /* Start IRQ polling in a buffer */
+#define IOCTLF_GETDEVINFO 0x00000039 /* Get information about device */
+#define IOCTLF_STOP_IRQ_PROC 0x0000003A /* Stop IRQ Polling */
+#define IOCTLF_START_ISO_PROC 0x0000003B /* Start ISO buffering in a Ringbuffer */
+#define IOCTLF_STOP_ISO_PROC 0x0000003C /* Stop ISO buffering */
+#define IOCTLF_CANCEL_IORB 0x0000003D /* Abort an IORB; */
+#define IOCTLF_SELECT_BULKPIPE 0x0000003E /* Select which Bulk endpoints can be used via Read/Write */
+#define IOCTLF_SENDIRQURB 0x0000003F /* Start IRQ polling in a buffer */
+#define IOCTLF_FIXUPDEVUCE 0x00000040 /* Fixup USB device configuration data */
+#define IOCTLF_REG_STATUSSEM 0x00000041 /* Register Semaphore for general Statuschange */
+#define IOCTLF_DEREG_STATUSSEM 0x00000042 /* Deregister Semaphore */
+#define IOCTLF_REG_DEVICESEM 0x00000043 /* Register Semaphore for a vendor&deviceID */
+#define IOCTLF_DEREG_DEVICESEM 0x00000044 /* Deregister Semaphore */
+
+
+#define NOTIFY_FREE 0
+#define NOTIFY_CHANGE 1
+#define NOTIFY_DEVICE 2
+#define MAX_NOTIFICATIONS 256
+
+#pragma pack(1)
+
+typedef struct
+{
+ HEV hDeviceAdded;
+ HEV hDeviceRemoved;
+ USHORT usFlags;
+ USHORT usVendor;
+ USHORT usProduct;
+ USHORT usBCDDevice;
+} NOTIFYENTRY, *PNOTIFYENTRY;
+
+#define DEV_SEM_ADD 0x00000001
+#define DEV_SEM_REMOVE 0x00000002
+#define DEV_SEM_MASK 0x00000003
+#define DEV_SEM_VENDORID 0x00000004
+#define DEV_SEM_PRODUCTID 0x00000008
+#define DEV_SEM_BCDDEVICE 0x00000010
+
+typedef struct{
+ ULONG ulSize;
+ ULONG ulCaps;
+ ULONG ulSemDeviceAdd;
+ ULONG ulSemDeviceRemove;
+} STATUSEVENTSET, * PSTATUSEVENTSET;
+
+
+typedef struct{
+ ULONG ulSize;
+ ULONG ulCaps;
+ ULONG ulSemDeviceAdd;
+ ULONG ulSemDeviceRemove;
+ USHORT usVendorID;
+ USHORT usProductID;
+ USHORT usBCDDevice;
+ USHORT usStatus;
+} DEVEVENTSET, * PDEVEVENTSET;
+
+typedef struct
+{
+ USHORT usVendorID;
+ USHORT usProductID;
+ USHORT usBCDDevice;
+ USHORT usDeviceNumber; /* Get the usDeviceNumber device in the system fi. if 2 acquire the 2nd device
+ 0 means first not acquired device. */
+} AQUIREDEV, *PAQUIREDEV;
+
+typedef struct
+{
+ UCHAR bRequestType;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ ULONG ulTimeout; /* in milliseconds */
+} SETUPPACKET, *PSETUPPACKET;
+
+typedef struct
+{
+ ULONG ulHandle;
+ UCHAR bRequestType;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ ULONG ulTimeout; /* in milliseconds */
+ USHORT usStatus;
+} USBCALLS_CTRL_REQ, *PUSBCALLS_CTRL_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+ ULONG ulID;
+} USBCALLS_ISO_START, *NPUSBCALLS_ISO_START, FAR *PUSBCALLS_ISO_START,
+ USBCALLS_IRQ_START, *NPUSBCALLS_IRQ_START, FAR *PUSBCALLS_IRQ_START,
+ USBCALLS_CANCEL_REQ, *NPUSBCALLS_CANCEL_REQ, FAR *PUSBCALLS_CANCEL_REQ;
+
+#define ISO_DIRMASK 0x80
+typedef struct
+{
+ ULONG hSemAccess; /* Synchronise access to the Pos values */
+ ULONG hDevice;
+ USHORT usPosWrite;
+ USHORT usPosRead;
+ USHORT usBufSize;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ UCHAR ucBuffer[16*1023];
+} ISORINGBUFFER, *PISORINGBUFFER;
+
+typedef USBCALLS_ISO_START USBCALLS_ISO_STOP, * NPUSBCALLS_ISO_STOP, FAR *PUSBCALLS_ISO_STOP;
+typedef USBCALLS_ISO_START USBCALLS_IRQ_STOP, * NPUSBCALLS_IRQ_STOP, FAR *PUSBCALLS_IRQ_STOP;
+
+#define USB_TRANSFER_FULL_SIZE 0x01
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+/* ULONG ulID; - yeah, right */
+ ULONG ulTimeout;
+ USHORT usDataProcessed;
+ USHORT usDataRemain;
+ USHORT usFlags;
+} USBCALLS_BULK_REQ, *PUSBCALLS_BULK_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucEndpoint;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+ ULONG ulEvent;
+ ULONG ulID;
+ ULONG ulTimeout;
+ USHORT usDataLen;
+} LIBUSB_IRQ_REQ, *NPLIBUSB_IRQ_REQ, FAR *PLIBUSB_IRQ_REQ;
+
+typedef struct
+{
+ ULONG ulDevHandle;
+ UCHAR ucConfiguration;
+ UCHAR ucAltInterface;
+ USHORT usStatus;
+} LIBUSB_FIXUP, *NPLIBUSB_FIXUP, FAR *PLIBUSB_FIXUP;
+
+#pragma pack()
+
+/******************************************************************************/
+
+HFILE g_hUSBDrv;
+ULONG g_cInit;
+ULONG g_ulFreeNotifys;
+HMTX g_hSemNotifytable;
+NOTIFYENTRY g_Notifications[MAX_NOTIFICATIONS];
+
+HMTX g_hSemRingBuffers;
+PISORINGBUFFER g_pIsoRingBuffers;
+ULONG g_ulNumIsoRingBuffers;
+
+APIEXPORT APIRET APIENTRY
+InitUsbCalls(void)
+{
+ int i;
+ ULONG ulAction;
+ APIRET rc;
+
+ if (++g_cInit > 1)
+ return NO_ERROR;
+
+ rc = DosOpen( (PCSZ)"USBRESM$",
+ &g_hUSBDrv,
+ &ulAction,
+ 0,
+ FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_ACCESS_READWRITE |
+ OPEN_FLAGS_NOINHERIT |
+ OPEN_SHARE_DENYNONE,
+ 0 );
+ if(rc)
+ {
+ g_hUSBDrv = 0;
+ g_cInit = 0;
+
+ }
+ else
+ {
+ /* @@ToDO Add EnvVar or INI for dynamically setting the number */
+ g_ulNumIsoRingBuffers = 8;
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ g_Notifications[i].hDeviceAdded = 0;
+ g_Notifications[i].hDeviceRemoved = 0;
+ g_Notifications[i].usVendor = 0;
+ g_Notifications[i].usProduct = 0;
+ g_Notifications[i].usBCDDevice = 0;
+ }
+ rc = DosAllocMem( (PPVOID)&g_pIsoRingBuffers,
+ g_ulNumIsoRingBuffers * sizeof(ISORINGBUFFER),
+ PAG_WRITE | PAG_COMMIT | OBJ_TILE);
+ if(!rc)
+ {
+ PISORINGBUFFER pIter = g_pIsoRingBuffers;
+ for(i=0;i< g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ pIter->hDevice = 0;
+ pIter->hSemAccess = 0; /* Synchronise access to the Pos values */
+ pIter->usPosWrite = 0;
+ pIter->usPosRead = 0;
+ pIter->usBufSize = 16*1023;
+ pIter->ucEndpoint = 0;
+ pIter->ucAltInterface = 0;
+ /*pIter->ucBuffer */
+ }
+ rc=DosCreateMutexSem(NULL,&g_hSemRingBuffers,DC_SEM_SHARED,FALSE);
+ if(!rc)
+ {
+ rc=DosCreateMutexSem(NULL,&g_hSemNotifytable,DC_SEM_SHARED,FALSE);
+ if(rc)
+ {
+ DosCloseMutexSem(g_hSemRingBuffers);
+ DosFreeMem(g_pIsoRingBuffers);
+ }
+ }
+ else
+ {
+ DosFreeMem(g_pIsoRingBuffers);
+ }
+ }
+
+ if(rc)
+ {
+ DosClose(g_hUSBDrv);
+ g_hUSBDrv = 0;
+ g_cInit = 0;
+ }
+ }
+ return g_cInit ? NO_ERROR : rc ? rc : ERROR_GEN_FAILURE;
+}
+
+APIEXPORT APIRET APIENTRY
+TermUsbCalls(void)
+{
+ if (!g_cInit)
+ return ERROR_GEN_FAILURE;
+ if(!--g_cInit)
+ {
+ int i;
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ if( g_Notifications[i].usFlags != NOTIFY_FREE);
+ UsbDeregisterNotification((USBNOTIFY)(&g_Notifications[i]));
+
+ DosClose(g_hUSBDrv);
+ g_hUSBDrv = NULLHANDLE;
+
+ if (g_pIsoRingBuffers)
+ DosFreeMem(g_pIsoRingBuffers);
+
+ DosCloseMutexSem(g_hSemRingBuffers);
+ g_hSemRingBuffers = NULLHANDLE;
+ DosCloseMutexSem(g_hSemNotifytable);
+ g_hSemNotifytable = NULLHANDLE;
+ }
+ return NO_ERROR;
+}
+
+
+#ifdef VBOX /* complete wast of time */
+# define IsBadReadPointer(pBase, ulSize) (FALSE)
+# define IsBadWritePointer(pBase, ulSize) (FALSE)
+#else
+static BOOL IsBadReadPointer(PVOID pBase, ULONG ulSize)
+{
+ APIRET rc;
+ ULONG ulFlags;
+ rc = DosQueryMem(pBase, &ulSize, &ulFlags);
+
+ return rc!=0?TRUE:(ulFlags&PAG_READ)&&(ulFlags&PAG_COMMIT)?FALSE:TRUE;
+}
+
+static BOOL IsBadWritePointer(PVOID pBase, ULONG ulSize)
+{
+ APIRET rc;
+ ULONG ulFlags;
+ rc = DosQueryMem(pBase, &ulSize, &ulFlags);
+
+ return rc!=0?TRUE:((ulFlags&PAG_WRITE)==PAG_WRITE&&(ulFlags&PAG_COMMIT)==PAG_COMMIT)?FALSE:TRUE;
+}
+#endif
+
+APIEXPORT APIRET APIENTRY
+UsbQueryNumberDevices(ULONG *pulNumDev)
+{
+ APIRET rc;
+ ULONG ulLength;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pulNumDev,sizeof(ULONG)) )
+ return ERROR_INVALID_PARAMETER;
+ ulLength=sizeof(ULONG);
+ *pulNumDev = 0;
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_NUMDEVICE,
+ NULL, 0, NULL,
+ pulNumDev, ulLength, &ulLength);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbQueryDeviceReport(ULONG ulDevNumber, ULONG *pulBufLen, PVOID pData)
+{
+ APIRET rc;
+ ULONG ulParmLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pulBufLen, sizeof(ULONG)) )
+ return ERROR_INVALID_PARAMETER;
+
+ if( pData!=NULL && IsBadWritePointer(pData,*pulBufLen) )
+ return ERROR_INVALID_PARAMETER;
+ if(pData==NULL)
+ *pulBufLen = 0;
+ ulParmLen = sizeof(ulDevNumber);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_GETINFO,
+ (PVOID)&ulDevNumber, ulParmLen, &ulParmLen,
+ pData, *pulBufLen, pulBufLen);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbRegisterChangeNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved)
+{
+ APIRET rc;
+ int i;
+ STATUSEVENTSET EventSet;
+ ULONG ulSize;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pNotifyID, sizeof(ULONG)) ||
+ (hDeviceAdded==0 && hDeviceRemoved==0) )
+ return ERROR_INVALID_PARAMETER;
+
+ ulSize = sizeof(EventSet);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = 0;
+
+ if(hDeviceAdded!=0)
+ {
+ ULONG ulCnt;
+ rc = DosQueryEventSem(hDeviceAdded,&ulCnt);
+ if(rc)
+ return rc;
+ EventSet.ulCaps |= DEV_SEM_ADD;
+ EventSet.ulSemDeviceAdd = hDeviceAdded;
+ }
+
+ if(hDeviceRemoved!=0)
+ {
+ ULONG ulCnt;
+ rc = DosQueryEventSem(hDeviceRemoved,&ulCnt);
+ if(rc)
+ return rc;
+ EventSet.ulCaps |= DEV_SEM_REMOVE;
+ EventSet.ulSemDeviceRemove = hDeviceRemoved;
+ }
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+ if(rc)
+ return rc;
+
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ if( g_Notifications[i].usFlags == NOTIFY_FREE)
+ {
+ g_Notifications[i].usFlags = NOTIFY_CHANGE;
+ g_Notifications[i].hDeviceAdded = hDeviceAdded;
+ g_Notifications[i].hDeviceRemoved = hDeviceRemoved;
+ g_Notifications[i].usVendor = 0;
+ g_Notifications[i].usProduct = 0;
+ g_Notifications[i].usBCDDevice = 0;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+ if(i==MAX_NOTIFICATIONS)
+ return USB_ERROR_NO_MORE_NOTIFICATIONS;
+
+ /* @@ToDo come up with a better way to generate IDs */
+ *pNotifyID = (USBNOTIFY) (&g_Notifications[i]);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_REG_STATUSSEM,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(rc)
+ {
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ *pNotifyID = 0;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbRegisterDeviceNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion)
+{
+ DEVEVENTSET EventSet;
+ ULONG ulCnt,ulSize;
+ int i;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if( IsBadWritePointer(pNotifyID, sizeof(ULONG)) ||
+ hDeviceAdded==0 || hDeviceRemoved==0 ||
+ usVendor == 0 || usVendor == 0xFFFF ||
+ usProduct == 0 || usProduct == 0xFFFF )
+ return ERROR_INVALID_PARAMETER;
+
+
+ rc = DosQueryEventSem(hDeviceAdded,&ulCnt);
+ if(rc)
+ return rc;
+ rc = DosQueryEventSem(hDeviceRemoved,&ulCnt);
+ if(rc)
+ return rc;
+
+ ulSize = sizeof(EventSet);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE |
+ DEV_SEM_VENDORID | DEV_SEM_PRODUCTID |
+ DEV_SEM_BCDDEVICE ;
+ EventSet.ulSemDeviceAdd = hDeviceAdded;
+ EventSet.ulSemDeviceRemove = hDeviceRemoved;
+ EventSet.usVendorID = usVendor;
+ EventSet.usProductID = usProduct;
+ EventSet.usBCDDevice = usBCDVersion;
+ EventSet.usStatus = 0;
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+
+ if(rc)
+ return rc;
+
+ for(i=0;i<MAX_NOTIFICATIONS;i++)
+ {
+ if( g_Notifications[i].usFlags == NOTIFY_FREE)
+ {
+ g_Notifications[i].usFlags = NOTIFY_DEVICE;
+ g_Notifications[i].hDeviceAdded = hDeviceAdded;
+ g_Notifications[i].hDeviceRemoved = hDeviceRemoved;
+ g_Notifications[i].usVendor = usVendor;
+ g_Notifications[i].usProduct = usProduct;
+ g_Notifications[i].usBCDDevice = usBCDVersion;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+ if(i==MAX_NOTIFICATIONS)
+ return USB_ERROR_NO_MORE_NOTIFICATIONS;
+
+ /* @@ToDo come up with a better way to generate IDs */
+ *pNotifyID = (USBNOTIFY) (&g_Notifications[i]);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_REG_DEVICESEM,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(rc)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= EventSet.usStatus;
+
+ g_Notifications[i].usFlags = NOTIFY_FREE;
+ *pNotifyID = 0;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbDeregisterNotification( USBNOTIFY NotifyID )
+{
+ DEVEVENTSET EventSet;
+ USBNOTIFY MinID,MaxID;
+ ULONG Index, ulFunction, ulSize;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ MinID = (USBNOTIFY) (&g_Notifications[0]);
+ MaxID = (USBNOTIFY) (&g_Notifications[MAX_NOTIFICATIONS-1]);
+
+ if(NotifyID<MinID || NotifyID>MaxID)
+ return ERROR_INVALID_PARAMETER;
+
+ Index = NotifyID - MinID;
+
+ if(Index % sizeof(NOTIFYENTRY))
+ return ERROR_INVALID_PARAMETER;
+
+ Index /= sizeof(NOTIFYENTRY);
+
+ rc = DosRequestMutexSem(g_hSemNotifytable,SEM_INDEFINITE_WAIT);
+
+ switch(g_Notifications[Index].usFlags)
+ {
+ case NOTIFY_FREE:
+ DosReleaseMutexSem(g_hSemNotifytable);
+ return ERROR_INVALID_PARAMETER;
+ case NOTIFY_CHANGE:
+ ulFunction = IOCTLF_DEREG_STATUSSEM;
+ ulSize = sizeof(STATUSEVENTSET);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE;
+ EventSet.ulSemDeviceAdd = g_Notifications[Index].hDeviceAdded;
+ EventSet.ulSemDeviceRemove = g_Notifications[Index].hDeviceRemoved;
+ break;
+ case NOTIFY_DEVICE:
+ ulFunction = IOCTLF_DEREG_DEVICESEM;
+ ulSize = sizeof(DEVEVENTSET);
+ EventSet.ulSize = ulSize;
+ EventSet.ulCaps = DEV_SEM_ADD | DEV_SEM_REMOVE |
+ DEV_SEM_VENDORID | DEV_SEM_PRODUCTID |
+ DEV_SEM_BCDDEVICE ;
+ EventSet.ulSemDeviceAdd = g_Notifications[Index].hDeviceAdded;
+ EventSet.ulSemDeviceRemove = g_Notifications[Index].hDeviceRemoved;
+ EventSet.usVendorID = g_Notifications[Index].usVendor;
+ EventSet.usProductID = g_Notifications[Index].usProduct;
+ EventSet.usBCDDevice = g_Notifications[Index].usBCDDevice;
+ EventSet.usStatus = 0;
+ break;
+ default:
+ DosReleaseMutexSem(g_hSemNotifytable);
+ return ERROR_GEN_FAILURE;
+ }
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, ulFunction,
+ NULL, 0, NULL,
+ &EventSet,ulSize, &ulSize);
+ if(0==rc)
+ {
+ g_Notifications[Index].usFlags = NOTIFY_FREE;
+ g_Notifications[Index].hDeviceAdded = 0;
+ g_Notifications[Index].hDeviceRemoved = 0;
+ g_Notifications[Index].usVendor = 0;
+ g_Notifications[Index].usProduct = 0;
+ g_Notifications[Index].usBCDDevice = 0;
+ } else
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= EventSet.usStatus;
+ }
+ DosReleaseMutexSem(g_hSemNotifytable);
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbOpen( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice)
+{
+ ULONG ulCat, ulFunc;
+ ULONG ulParmLen, ulDataLen;
+ AQUIREDEV Aquire;
+ APIRET rc;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+ if(IsBadWritePointer(pHandle,sizeof(USBHANDLE)) )
+ return ERROR_INVALID_PARAMETER;
+
+ Aquire.usVendorID = usVendor;
+ Aquire.usProductID = usProduct;
+ Aquire.usBCDDevice = usBCDDevice;
+ Aquire.usDeviceNumber = usEnumDevice;
+ ulCat = 0xA0;
+ ulFunc = 0x33;
+ ulParmLen = sizeof(Aquire);
+ ulDataLen = sizeof(USBHANDLE);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ ulCat,ulFunc, /*IOCAT_USBRES, IOCTLF_AQUIREDEVICE, */
+ &Aquire, ulParmLen, &ulParmLen,
+ pHandle, ulDataLen, &ulDataLen);
+
+ /* @@ ToDO maybe gether some info about device here (endpoints etc for safety checks) */
+ return rc;
+
+}
+
+APIEXPORT APIRET APIENTRY
+UsbClose( USBHANDLE Handle)
+{
+ APIRET rc;
+ ULONG ulDataLen,ulParmLen;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(USBHANDLE);
+ ulDataLen = 0;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_RELEASEDEVICE,
+ (PVOID)&Handle, ulParmLen, &ulParmLen,
+ NULL, ulDataLen, &ulDataLen);
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbCtrlMessage( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ USBCALLS_CTRL_REQ CtrlRequest;
+ ULONG ulParmLen, ulDataLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(USBCALLS_CTRL_REQ);
+ CtrlRequest.ulHandle = Handle;
+ CtrlRequest.bRequestType = ucRequestType;
+ CtrlRequest.bRequest = ucRequest;
+ CtrlRequest.wValue = usValue;
+ CtrlRequest.wIndex = usIndex;
+ CtrlRequest.wLength = usLength;
+ CtrlRequest.ulTimeout = ulTimeout;
+ ulDataLen = usLength;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDCONTROLURB,
+ (PVOID)&CtrlRequest, ulParmLen, &ulParmLen,
+ ulDataLen>0?(PVOID)pData:NULL,
+ ulDataLen,
+ ulDataLen>0?&ulDataLen:NULL);
+ if( rc != NO_ERROR )
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= CtrlRequest.usStatus;
+ }
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ return UsbBulkRead2(Handle, Endpoint, AltInterface, TRUE /* fShortOk */, ulNumBytes, pvData, ulTimeout);
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkRead2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen, ulToProcess, ulTotalProcessed;
+ USBCALLS_BULK_REQ BulkRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if(*ulNumBytes==0)
+ return 0;
+
+ /* just require this */
+ if ((ULONG)pvData & 0xfff)
+ return ERROR_INVALID_ADDRESS;
+ if ((ULONG)pvData >= 0x20000000)
+ return ERROR_INVALID_ADDRESS;
+
+ ulToProcess = *ulNumBytes;
+ ulTotalProcessed = 0;
+
+ do
+ {
+ /* Process up to 64k, making sure we're working on segments. */
+ ulDataLen = 0x10000 - ((ULONG)pvData & 0xffff);
+ if (ulDataLen > ulToProcess)
+ ulDataLen = ulToProcess;
+
+ ulParmLen = sizeof(USBCALLS_BULK_REQ);
+
+ memset(&BulkRequest, 0, sizeof(BulkRequest));
+ BulkRequest.ulDevHandle = Handle;
+ BulkRequest.ucEndpoint = Endpoint;
+ BulkRequest.ucAltInterface = AltInterface;
+ BulkRequest.usStatus = 0;
+ BulkRequest.ulEvent = 0;
+ //BulkRequest.ulID = (ULONG)pvData;
+ BulkRequest.ulTimeout = ulTimeout;
+ BulkRequest.usDataProcessed = 0;
+ BulkRequest.usDataRemain = ulDataLen;
+ BulkRequest.usFlags = fShortOk && ulDataLen == ulToProcess ? 0 : USB_TRANSFER_FULL_SIZE;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDBULKURB,
+ (PVOID)&BulkRequest, ulParmLen, &ulParmLen,
+ pvData, ulDataLen, &ulDataLen);
+ Log(("BulkRead: usStatus=%d rc=%ld usDataProcessed=%d usDataRemain=%d ulDataLen=%ld\n",
+ BulkRequest.usStatus, rc, BulkRequest.usDataProcessed, BulkRequest.usDataRemain, ulDataLen));
+
+ if (rc != NO_ERROR)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= BulkRequest.usStatus;
+ break;
+ }
+
+ /* Adjust count and source pointer */
+ ulToProcess -= ulDataLen;
+ pvData = (PBYTE)pvData + ulDataLen;
+ ulTotalProcessed += BulkRequest.usDataProcessed;
+
+ if (BulkRequest.usDataProcessed != ulDataLen)
+ {
+ /* Transferred less than we wanted? so something is wrong,
+ or device doesn't wish to send more, exit loop */
+ rc = USB_ERROR_LESSTRANSFERED;
+ break;
+ }
+ } while( ulToProcess>0 );
+
+ *ulNumBytes = ulTotalProcessed;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkWrite( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ return UsbBulkWrite2(Handle, Endpoint, AltInterface, FALSE /* fShortOk */, ulNumBytes, pvData, ulTimeout);
+}
+
+APIEXPORT APIRET APIENTRY
+UsbBulkWrite2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ USBCALLS_BULK_REQ BulkRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ /* just require this */
+ if ((ULONG)pvData & 0xfff)
+ return ERROR_INVALID_ADDRESS;
+ if ((ULONG)pvData >= 0x20000000)
+ return ERROR_INVALID_ADDRESS;
+
+ do
+ {
+ /* Process up to 64k, making sure we're working on segments. */
+ ulDataLen = 0x10000 - ((ULONG)pvData & 0xffff);
+ if (ulDataLen > ulNumBytes)
+ ulDataLen = ulNumBytes;
+
+ ulParmLen = sizeof(USBCALLS_BULK_REQ);
+
+ memset(&BulkRequest, 0, sizeof(BulkRequest));
+ BulkRequest.ulDevHandle = Handle;
+ BulkRequest.ucEndpoint = Endpoint;
+ BulkRequest.ucAltInterface = AltInterface;
+ BulkRequest.usStatus = 0;
+ BulkRequest.ulEvent = 0;
+ //BulkRequest.ulID = (ULONG)pvData;
+ BulkRequest.ulTimeout = ulTimeout;
+ BulkRequest.usDataProcessed = 0;
+ BulkRequest.usDataRemain = ulDataLen;
+ BulkRequest.usFlags = fShortOk && ulDataLen == ulNumBytes ? 0 : USB_TRANSFER_FULL_SIZE;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDBULKURB,
+ &BulkRequest, ulParmLen, &ulParmLen,
+ pvData, ulDataLen, &ulDataLen );
+ Log(("BulkWrite: usStatus=%d rc=%ld usDataProcessed=%d usDataRemain=%d ulDataLen=%ld\n",
+ BulkRequest.usStatus, rc, BulkRequest.usDataProcessed, BulkRequest.usDataRemain, ulDataLen));
+ if (rc != NO_ERROR)
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= BulkRequest.usStatus;
+ break;
+ }
+ /* Adjust count and source pointer */
+ ulNumBytes -= ulDataLen;
+ pvData = (PBYTE)pvData + ulDataLen;
+ } while( ulNumBytes > 0 );
+
+ return rc;
+}
+
+APIRET APIENTRY
+UsbIrqStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ USHORT ulNumBytes,
+ PVOID pData,
+ PHEV pHevModified)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ USBCALLS_IRQ_START IrqStart;
+ HEV hEvent;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ if(0==ulNumBytes || IsBadWritePointer(pData, ulNumBytes))
+ return ERROR_INVALID_PARAMETER;
+
+ rc = DosCreateEventSem( NULL,
+ &hEvent,
+ DC_SEM_SHARED,
+ FALSE);
+ if(rc)
+ return rc;
+
+ IrqStart.ulDevHandle = Handle;
+ IrqStart.ucEndpoint = Endpoint;
+ IrqStart.ucAltInterface = AltInterface;
+ IrqStart.usStatus = 0;
+ IrqStart.ulEvent = hEvent;
+ ulParmLen = sizeof(IrqStart);
+ ulDataLen = ulNumBytes;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_START_IRQ_PROC,
+ (PVOID)&IrqStart, ulParmLen, &ulParmLen,
+ pData, ulDataLen,&ulDataLen);
+ if(rc)
+ DosCloseEventSem(hEvent);
+ else
+ *pHevModified = hEvent;
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIrqStop( USBHANDLE Handle,
+ HEV HevModified)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ ulParmLen = sizeof(Handle);
+ ulDataLen = sizeof(HevModified);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_STOP_IRQ_PROC,
+ (PVOID)&Handle, ulParmLen, &ulParmLen,
+ &HevModified, ulDataLen, &ulDataLen);
+ if(!rc)
+ DosCloseEventSem(HevModified);
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ISOHANDLE *phIso)
+{
+ APIRET rc;
+ PISORINGBUFFER pIter = g_pIsoRingBuffers;
+ USBCALLS_ISO_START IsoStart;
+ ULONG ulParmLen, ulDataLen;
+ int i;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ rc = DosRequestMutexSem(g_hSemRingBuffers,SEM_INDEFINITE_WAIT);
+ if(rc)
+ return rc;
+
+ for(i=0;i< g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ if (pIter->hDevice==0)
+ {
+ pIter->hDevice = Handle;
+ break;
+ }
+ }
+ DosReleaseMutexSem(g_hSemRingBuffers);
+
+ if(i==g_ulNumIsoRingBuffers)
+ return USB_ERROR_OUTOF_RESOURCES;
+
+ IsoStart.ulDevHandle = Handle;
+ IsoStart.ucEndpoint = Endpoint;
+ IsoStart.ucAltInterface = AltInterface;
+ ulParmLen = sizeof(IsoStart);
+ ulDataLen = sizeof(ISORINGBUFFER);
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_STOP_IRQ_PROC,
+ (PVOID)&IsoStart, ulParmLen, &ulParmLen,
+ pIter, ulDataLen, &ulDataLen);
+ if(rc)
+ {
+ pIter->hDevice = 0;
+ *phIso = 0;
+ }
+ else
+ {
+ pIter->ucEndpoint = Endpoint;
+ pIter->ucAltInterface = AltInterface;
+ }
+ return rc;
+}
+
+static APIRET IsInvalidIsoHandle(const ISOHANDLE hIso)
+{
+ PISORINGBUFFER pIter;
+ ULONG i;
+ pIter = g_pIsoRingBuffers;
+
+ for(i=0;i<g_ulNumIsoRingBuffers;i++,pIter++)
+ {
+ if(pIter==(PISORINGBUFFER)hIso && pIter->hDevice)
+ return 0;
+ }
+ return ERROR_INVALID_PARAMETER;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoStop( ISOHANDLE hIso)
+{
+
+ APIRET rc = NO_ERROR;
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+/* rc = DosDevIOCtl( g_hUSBDrv, */
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoDequeue( ISOHANDLE hIso,
+ PVOID pBuffer,
+ ULONG ulNumBytes)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(!(pRB->ucEndpoint & ISO_DIRMASK))
+ return ERROR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoPeekQueue( ISOHANDLE hIso,
+ UCHAR * pByte,
+ ULONG ulOffset)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(!(pRB->ucEndpoint & ISO_DIRMASK))
+ return ERROR_INVALID_PARAMETER;
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoEnqueue( ISOHANDLE hIso,
+ const UCHAR * pBuffer,
+ ULONG ulNumBytes)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER)hIso;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ if(pRB->ucEndpoint & ISO_DIRMASK)
+ return ERROR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIsoGetLength( ISOHANDLE hIso,
+ ULONG *pulLength)
+{
+ APIRET rc;
+ PISORINGBUFFER pRB = (PISORINGBUFFER) hIso;
+ USHORT ri,wi;
+
+ rc = IsInvalidIsoHandle(hIso);
+ if(rc)
+ return rc;
+ wi = pRB->usPosWrite;
+ ri = pRB->usPosRead;
+
+ if (ri == wi)
+ *pulLength = 0;
+ else if (ri < wi)
+ *pulLength = wi - ri;
+ else
+ *pulLength = wi + (pRB->usBufSize - ri);
+
+ return 0;
+}
+
+APIEXPORT APIRET APIENTRY
+UsbIrqRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ ULONG *ulNumBytes,
+ PVOID pData,
+ ULONG ulTimeout)
+{
+ APIRET rc;
+ ULONG ulParmLen, ulDataLen;
+ LIBUSB_IRQ_REQ IrqRequest;
+
+ if(!g_cInit)
+ return USB_NOT_INIT;
+
+ /* 10 01 2003 - KIEWITZ -> Still @@ToDo Add Endpoint check based on descriptors
+ We currently only allow Endpoint-addresses 80h->8Fh here */
+ if ((Endpoint<0x80) || (Endpoint>0x8F))
+ return USB_ERROR_INVALID_ENDPOINT;
+
+ if(*ulNumBytes==0)
+ return 0;
+
+ IrqRequest.ulDevHandle = Handle;
+ IrqRequest.ucEndpoint = Endpoint;
+ IrqRequest.ucAltInterface = AltInterface;
+ IrqRequest.usStatus = 0;
+ IrqRequest.ulEvent = 0;
+ IrqRequest.ulTimeout = ulTimeout;
+ ulParmLen = sizeof(LIBUSB_IRQ_REQ);
+ ulDataLen = *ulNumBytes;
+
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_SENDIRQURB,
+ (PVOID)&IrqRequest, ulParmLen, &ulParmLen,
+ pData, ulDataLen, &ulDataLen);
+
+ if( rc == NO_ERROR )
+ {
+ *ulNumBytes = IrqRequest.usDataLen;
+ } else
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= IrqRequest.usStatus;
+ }
+ return rc;
+}
+
+
+APIEXPORT APIRET APIENTRY
+UsbFixupDevice( USBHANDLE Handle,
+ UCHAR ucConfiguration,
+ UCHAR *pucConfigurationData,
+ ULONG ulConfigurationLen )
+{
+ LIBUSB_FIXUP request;
+ ULONG ulParmLen;
+ APIRET rc;
+
+ request.ulDevHandle= Handle;
+ request.ucConfiguration= ucConfiguration;
+ request.usStatus= 0;
+ ulParmLen= sizeof(LIBUSB_FIXUP);
+ rc = DosDevIOCtl( g_hUSBDrv,
+ IOCAT_USBRES, IOCTLF_FIXUPDEVUCE,
+ (PVOID)&request, ulParmLen, &ulParmLen,
+ pucConfigurationData, ulConfigurationLen, &ulConfigurationLen);
+ if( rc != NO_ERROR )
+ {
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_INVALID_PARAMETER) )
+ rc= ERROR_INVALID_PARAMETER;
+ if( rc == (ERROR_USER_DEFINED_BASE|ERROR_I24_GEN_FAILURE) )
+ rc= request.usStatus;
+ }
+ return rc;
+}
+
+#ifndef STATIC_USBCALLS
+ /*+-------------------------------------------------------------------+*/
+ /*| _CRT_init is the C run-time environment initialization function. |*/
+ /*|It will return 0 to indicate success and -1 to indicate failure. |*/
+ /*+-------------------------------------------------------------------+*/
+
+/* int _CRT_init (void); */
+
+ /*+-------------------------------------------------------------------+*/
+ /*| _CRT_term is the C run-time environment termination function. |*/
+ /*+-------------------------------------------------------------------+*/
+
+/* void _CRT_term (unsigned long);*/
+
+ /*+-------------------------------------------------------------------+*/
+ /*| _DLL_InitTerm is the function that gets called by the operating |*/
+ /*| system loader when it loads and frees this DLL for each process |*/
+ /*| that accesses this DLL. However, it only gets called the first |*/
+ /*| time the DLL is loaded and the last time it is freed for a |*/
+ /*| particular process. The system linkage convention must be used |*/
+ /*| because the operating system loader is calling this function. |*/
+ /*+-------------------------------------------------------------------+*/
+
+#ifdef STATIC_LINK
+int _CRT_init (void);
+void _CRT_term(0UL);
+#endif
+
+unsigned long _System _DLL_InitTerm (unsigned long modhandle, unsigned long flag)
+{
+
+ /* If flag is zero then the DLL is being loaded so initialization */
+ /* should be performed. If flag is 1 then the DLL is being freed */
+ /* so termination should be performed. */
+
+ switch (flag)
+ {
+ case 0:
+ /* The C run-time environment initialization function must */
+ /* be called before any calls to C run-time functions that */
+ /* are not inlined. */
+
+#ifdef STATIC_LINK
+ if (_CRT_init () == -1)
+ return 0UL;
+#endif
+ InitUsbCalls();
+ break;
+
+ case 1:
+ TermUsbCalls();
+#ifdef STATIC_LINK
+ _CRT_term(0UL);
+#endif
+ break;
+
+ default:
+ return 0UL;
+
+ }
+
+ /* A nonzero value must be returned to indicate success. */
+ return 1UL;
+}
+#endif /* !STATIC_USBCALLS */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h
new file mode 100644
index 00000000..151a5bc1
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/os2/usbcalls.h
@@ -0,0 +1,242 @@
+
+#ifndef VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h
+#define VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef ULONG USBHANDLE, *PUSBHANDLE;
+typedef ULONG USBNOTIFY, *PUSBNOTIFY;
+typedef ULONG ISOHANDLE, *PISOHANDLE;
+
+#define USB_NOT_INIT 7000
+#define USB_ERROR_NO_MORE_NOTIFICATIONS 7001
+#define USB_ERROR_OUTOF_RESOURCES 7002
+#define USB_ERROR_INVALID_ENDPOINT 7003
+#define USB_ERROR_LESSTRANSFERED 7004
+
+/* IORB status codes */
+#define USB_IORB_DONE 0x0000
+#define USB_IORB_FAILED 0x8000
+
+#define USB_ANY_PRODUCTVERSION 0xFFFF
+#define USB_OPEN_FIRST_UNUSED 0
+
+#ifdef USB_BIND_DYNAMIC
+ typedef APIRET APIENTRY USBREGISTERDEVICENOTIFICATION( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion);
+ typedef USBREGISTERDEVICENOTIFICATION *PUSBREGISTERDEVICENOTIFICATION;
+
+ typedef APIRET APIENTRY USBDEREGISTERNOTIFICATION( USBNOTIFY NotifyID);
+
+ typedef USBDEREGISTERNOTIFICATION *PUSBDEREGISTERNOTIFICATION;
+
+ typedef APIRET APIENTRY USBOPEN( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice);
+ typedef USBOPEN *PUSBOPEN;
+ typedef APIRET APIENTRY USBCLOSE( USBHANDLE Handle);
+ typedef USBCLOSE *PUSBCLOSE;
+
+ typedef APIRET APIENTRY USBCTRLMESSAGE( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pvData,
+ ULONG ulTimeout);
+ typedef USBCTRLMESSAGE *PUSBCTRLMESSAGE;
+#else
+ APIRET APIENTRY UsbQueryNumberDevices( ULONG *pulNumDev);
+
+ APIRET APIENTRY UsbQueryDeviceReport( ULONG ulDevNumber,
+ ULONG *ulBufLen,
+ PVOID pvData);
+ APIRET APIENTRY UsbRegisterChangeNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved);
+
+ APIRET APIENTRY UsbRegisterDeviceNotification( PUSBNOTIFY pNotifyID,
+ HEV hDeviceAdded,
+ HEV hDeviceRemoved,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDVersion);
+
+ APIRET APIENTRY UsbDeregisterNotification( USBNOTIFY NotifyID);
+
+ APIRET APIENTRY UsbOpen( PUSBHANDLE pHandle,
+ USHORT usVendor,
+ USHORT usProduct,
+ USHORT usBCDDevice,
+ USHORT usEnumDevice);
+
+ APIRET APIENTRY UsbClose( USBHANDLE Handle);
+
+ APIRET APIENTRY UsbCtrlMessage( USBHANDLE Handle,
+ UCHAR ucRequestType,
+ UCHAR ucRequest,
+ USHORT usValue,
+ USHORT usIndex,
+ USHORT usLength,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkRead2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ BOOL fShortOk,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkWrite( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbBulkWrite2( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR AltInterface,
+ BOOL fShortOk,
+ ULONG ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbIrqStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ USHORT usNumBytes,
+ PVOID pvData,
+ PHEV pHevModified);
+ APIRET APIENTRY UsbIrqStop( USBHANDLE Handle,
+ HEV HevModified);
+
+ APIRET APIENTRY UsbIsoStart( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ISOHANDLE *phIso);
+ APIRET APIENTRY UsbIsoStop( ISOHANDLE hIso);
+
+ APIRET APIENTRY UsbIsoDequeue( ISOHANDLE hIso,
+ PVOID pBuffer,
+ ULONG ulNumBytes);
+ APIRET APIENTRY UsbIsoEnqueue( ISOHANDLE hIso,
+ const UCHAR * pBuffer,
+ ULONG ulNumBytes);
+ APIRET APIENTRY UsbIsoPeekQueue( ISOHANDLE hIso,
+ UCHAR * pByte,
+ ULONG ulOffset);
+ APIRET APIENTRY UsbIsoGetLength( ISOHANDLE hIso,
+ ULONG *pulLength);
+
+ APIRET APIENTRY UsbIrqRead( USBHANDLE Handle,
+ UCHAR Endpoint,
+ UCHAR Interface,
+ ULONG *ulNumBytes,
+ PVOID pvData,
+ ULONG ulTimeout);
+
+ APIRET APIENTRY UsbFixupDevice( USBHANDLE Handle,
+ UCHAR ucConfiguration,
+ UCHAR *pucConfigurationData,
+ ULONG ulConfigurationLen );
+
+ APIRET APIENTRY InitUsbCalls(void);
+ APIRET APIENTRY TermUsbCalls(void);
+
+ /* Standard USB Requests See 9.4. in USB 1.1. spec. */
+
+ /* 09 01 2003 - KIEWITZ */
+ #define UsbDeviceClearFeature(HANDLE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x00, 0x01, FEAT, 0, 0, NULL, 0)
+ #define UsbDeviceSetFeature(HANDLE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, 0, 0, NULL, 0)
+ #define UsbInterfaceClearFeature(HANDLE, IFACE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x01, 0x01, FEAT, IFACE, 0, NULL, 0)
+ #define UsbInterfaceSetFeature(HANDLE, IFACE, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, IFACE, 0, NULL, 0)
+ #define UsbEndpointClearFeature(HANDLE, ENDPOINT, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x02, 0x01, FEAT, ENDPOINT, 0, NULL, 0)
+ #define UsbEndpointSetFeature(HANDLE, ENDPOINT, FEAT) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x03, FEAT, ENDPOINT, 0, NULL, 0)
+ #define FEATURE_DEVICE_REMOTE_WAKEUP 1
+ #define FEATURE_ENDPOINT_HALT 0
+ #define UsbEndpointClearHalt(HANDLE, ENDPOINT) \
+ UsbEndpointClearFeature(HANDLE, ENDPOINT, FEATURE_ENDPOINT_HALT)
+
+ #define UsbDeviceGetConfiguration(HANDLE, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x08, 0, 0, 1, DATA, 0)
+ #define UsbDeviceSetConfiguration(HANDLE, CONFIG) \
+ UsbCtrlMessage(HANDLE, 0x00, 0x09, CONFIG, 0, 0, NULL, 0)
+
+ #define UsbDeviceGetStatus(HANDLE, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, 0, 2, STATUS, 0)
+ #define UsbInterfaceGetStatus(HANDLE, IFACE, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, IFACE, 2, STATUS, 0)
+ #define UsbEndpointGetStatus(HANDLE, ENDPOINT, STATUS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x00, 0, ENDPOINT, 2, STATUS, 0)
+
+ #define STATUS_ENDPOINT_HALT 0x0001
+ #define STATUS_DEVICE_SELFPOWERD 0x0001
+ #define STATUS_DEVICE_REMOTEWAKEUP 0x0002
+
+ #define UsbDeviceSetAddress(HANDLE, ADDRESS) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x05, ADDRESS, 0, 0, NULL, 0)
+
+ #define UsbDeviceGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0100|INDEX), LID, LEN, DATA, 0)
+ #define UsbDeviceSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0100|INDEX), LID, LEN, DATA, 0)
+ #define UsbConfigurationGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0200|INDEX), LID, LEN, DATA, 0)
+ #define UsbConfigurationSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0200|INDEX), LID, LEN, DATA, 0)
+ #define UsbStringGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0300|INDEX), LID, LEN, DATA, 0)
+ #define UsbStringSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0300|INDEX), LID, LEN, DATA, 0)
+ #define UsbInterfaceGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0400|INDEX), LID, LEN, DATA, 0)
+ #define UsbInterfaceSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0400|INDEX), LID, LEN, DATA, 0)
+ #define UsbEndpointGetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x06, (0x0500|INDEX), LID, LEN, DATA, 0)
+ #define UsbEndpointSetDescriptor(HANDLE, INDEX, LID, LEN, DATA) \
+ UsbCtrlMessage(HANDLE, 0x80, 0x07, (0x0500|INDEX), LID, LEN, DATA, 0)
+
+ #define UsbInterfaceGetAltSetting(HANDLE, IFACE, SETTING) \
+ UsbCtrlMessage(HANDLE, 0x81, 0x0A, 0, IFACE, 1, SETTING, 0)
+ #define UsbInterfaceSetAltSetting(HANDLE, IFACE, ALTSET) \
+ UsbCtrlMessage(HANDLE, 0x01, 0x0B, ALTSET, IFACE, 0, NULL, 0)
+
+ #define UsbEndpointSynchFrame(HANDLE, ENDPOINT, FRAMENUM) \
+ UsbCtrlMessage(HANDLE, 0x82, 0x0B, 0, ENDPOINT, 2, FRAMENUM, 0)
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxUSB_os2_usbcalls_h */
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk b/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk
new file mode 100644
index 00000000..2ba0f963
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/Makefile.kmk
@@ -0,0 +1,67 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Solaris VBoxUSB kernel extension.
+#
+
+#
+# Copyright (C) 2008-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
+
+#
+# vboxusbmon - The Solaris USB Monitor Kernel module.
+#
+SYSMODS.solaris += vboxusbmon
+vboxusbmon_TEMPLATE = VBoxR0Drv
+vboxusbmon_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxusbmon_DEPS += $(VBOX_SVN_REV_KMK)
+vboxusbmon_INCS := . .. include
+vboxusbmon_LDFLAGS += -N drv/vboxdrv -N misc/usba
+vboxusbmon_SOURCES = \
+ VBoxUSBMon-solaris.c \
+ ../USBFilter.cpp \
+ ../VBoxUSBFilterMgr.cpp
+
+#
+# vboxusb - The Solaris Generic USB Client Kernel module.
+#
+SYSMODS.solaris += vboxusb
+vboxusb_TEMPLATE = VBoxR0Drv
+vboxusb_DEFS = IN_RT_R0 IN_SUP_R0 VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxusb_DEPS += $(VBOX_SVN_REV_KMK)
+vboxusb_INCS := . include/
+vboxusb_LDFLAGS += -N drv/vboxdrv -N drv/vboxusbmon -N misc/usba
+vboxusb_SOURCES = \
+ VBoxUSB-solaris.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp b/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp
new file mode 100644
index 00000000..10eaf026
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/USBLib-solaris.cpp
@@ -0,0 +1,279 @@
+/** $Id: USBLib-solaris.cpp $ */
+/** @file
+ * USBLib - Library for wrapping up the VBoxUSB functionality, Solaris flavor.
+ */
+
+/*
+ * Copyright (C) 2008-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 <VBox/usblib.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/env.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <errno.h>
+# include <unistd.h>
+# include <string.h>
+# include <limits.h>
+# include <strings.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Logging class. */
+#define USBLIBR3 "USBLibR3"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Reference counter. */
+static uint32_t volatile g_cUsers = 0;
+/** VBoxUSB Device handle. */
+static RTFILE g_hFile = NIL_RTFILE;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int usblibDoIOCtl(unsigned iFunction, void *pvData, size_t cbData);
+
+
+USBLIB_DECL(int) USBLibInit(void)
+{
+ LogFlow((USBLIBR3 ":USBLibInit\n"));
+
+ /*
+ * Already open?
+ * This isn't properly serialized, but we'll be fine with the current usage.
+ */
+ if (g_cUsers)
+ {
+ ASMAtomicIncU32(&g_cUsers);
+ return VINF_SUCCESS;
+ }
+
+ RTFILE File;
+ int rc = RTFileOpen(&File, VBOXUSB_DEVICE_NAME, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((USBLIBR3 ":failed to open the VBoxUSB monitor device node '%s' rc=%Rrc\n", VBOXUSB_DEVICE_NAME, rc));
+ return rc;
+ }
+ g_hFile = File;
+
+ ASMAtomicIncU32(&g_cUsers);
+ /*
+ * Check the USBMonitor version.
+ */
+ VBOXUSBREQ_GET_VERSION Req;
+ bzero(&Req, sizeof(Req));
+ rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_GET_VERSION, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ {
+ if ( Req.u32Major != VBOXUSBMON_VERSION_MAJOR
+ || Req.u32Minor < VBOXUSBMON_VERSION_MINOR)
+ {
+ rc = VERR_VERSION_MISMATCH;
+ LogRel((USBLIBR3 ":USBMonitor version mismatch! driver v%d.%d, expecting ~v%d.%d\n",
+ Req.u32Major, Req.u32Minor, VBOXUSBMON_VERSION_MAJOR, VBOXUSBMON_VERSION_MINOR));
+
+ RTFileClose(File);
+ g_hFile = NIL_RTFILE;
+ ASMAtomicDecU32(&g_cUsers);
+ return rc;
+ }
+ }
+ else
+ {
+ LogRel((USBLIBR3 ":USBMonitor driver version query failed. rc=%Rrc\n", rc));
+ RTFileClose(File);
+ g_hFile = NIL_RTFILE;
+ ASMAtomicDecU32(&g_cUsers);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+USBLIB_DECL(int) USBLibTerm(void)
+{
+ LogFlow((USBLIBR3 ":USBLibTerm\n"));
+
+ if (!g_cUsers)
+ return VERR_WRONG_ORDER;
+ if (ASMAtomicDecU32(&g_cUsers) != 0)
+ return VINF_SUCCESS;
+
+ /*
+ * We're the last guy, close down the connection.
+ */
+ RTFILE File = g_hFile;
+ g_hFile = NIL_RTFILE;
+ if (File == NIL_RTFILE)
+ return VERR_INTERNAL_ERROR;
+
+ int rc = RTFileClose(File);
+ AssertRC(rc);
+ return rc;
+}
+
+
+USBLIB_DECL(void *) USBLibAddFilter(PCUSBFILTER pFilter)
+{
+ LogFlow((USBLIBR3 ":USBLibAddFilter pFilter=%p\n", pFilter));
+
+ VBOXUSBREQ_ADD_FILTER Req;
+ Req.Filter = *pFilter;
+ Req.uId = 0;
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_ADD_FILTER, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ return (void *)Req.uId;
+
+ AssertMsgFailed((USBLIBR3 ":VBOXUSBMON_IOCTL_ADD_FILTER failed! rc=%Rrc\n", rc));
+ return NULL;
+}
+
+
+USBLIB_DECL(void) USBLibRemoveFilter(void *pvId)
+{
+ LogFlow((USBLIBR3 ":USBLibRemoveFilter pvId=%p\n", pvId));
+
+ VBOXUSBREQ_REMOVE_FILTER Req;
+ Req.uId = (uintptr_t)pvId;
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_REMOVE_FILTER, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ return;
+
+ AssertMsgFailed((USBLIBR3 ":VBOXUSBMON_IOCTL_REMOVE_FILTER failed! rc=%Rrc\n", rc));
+}
+
+
+USBLIB_DECL(int) USBLibGetClientInfo(char *pszDeviceIdent, char **ppszClientPath, int *pInstance)
+{
+ LogFlow((USBLIBR3 ":USBLibGetClientInfo pszDeviceIdent=%s ppszClientPath=%p pInstance=%p\n",
+ pszDeviceIdent, ppszClientPath, pInstance));
+
+ AssertPtrReturn(pInstance, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppszClientPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszDeviceIdent, VERR_INVALID_PARAMETER);
+
+ VBOXUSBREQ_CLIENT_INFO Req;
+ bzero(&Req, sizeof(Req));
+ RTStrPrintf(Req.szDeviceIdent, sizeof(Req.szDeviceIdent), "%s", pszDeviceIdent);
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_CLIENT_INFO, &Req, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ {
+ *pInstance = Req.Instance;
+ rc = RTStrDupEx(ppszClientPath, Req.szClientPath);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((USBLIBR3 ":USBLibGetClientInfo RTStrDupEx failed! rc=%Rrc szClientPath=%s\n", rc, Req.szClientPath));
+ }
+ else
+ LogRel((USBLIBR3 ":USBLibGetClientInfo VBOXUSBMON_IOCTL_CLIENTPATH failed! rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+USBLIB_DECL(int) USBLibResetDevice(char *pszDevicePath, bool fReattach)
+{
+ LogFlow((USBLIBR3 ":USBLibResetDevice pszDevicePath=%s\n", pszDevicePath));
+
+ size_t cbPath = strlen(pszDevicePath) + 1;
+ size_t cbReq = sizeof(VBOXUSBREQ_RESET_DEVICE) + cbPath;
+ VBOXUSBREQ_RESET_DEVICE *pReq = (VBOXUSBREQ_RESET_DEVICE *)RTMemTmpAllocZ(cbReq);
+ if (RT_UNLIKELY(!pReq))
+ return VERR_NO_MEMORY;
+
+ pReq->fReattach = fReattach;
+ if (strlcpy(pReq->szDevicePath, pszDevicePath, cbPath) >= cbPath)
+ {
+ LogRel((USBLIBR3 ":USBLibResetDevice buffer overflow. cbPath=%u pszDevicePath=%s\n", cbPath, pszDevicePath));
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ int rc = usblibDoIOCtl(VBOXUSBMON_IOCTL_RESET_DEVICE, pReq, cbReq);
+ if (RT_FAILURE(rc))
+ LogRel((USBLIBR3 ":VBOXUSBMON_IOCTL_RESET_DEVICE failed! rc=%Rrc\n", rc));
+
+ RTMemFree(pReq);
+ return rc;
+}
+
+
+static int usblibDoIOCtl(unsigned iFunction, void *pvData, size_t cbData)
+{
+ if (g_hFile == NIL_RTFILE)
+ {
+ LogRel((USBLIBR3 ":IOCtl failed, device not open.\n"));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ VBOXUSBREQ Hdr;
+ Hdr.u32Magic = VBOXUSBMON_MAGIC;
+ Hdr.cbData = cbData; /* Don't include full size because the header size is fixed. */
+ Hdr.pvDataR3 = pvData;
+
+ int rc = ioctl(RTFileToNative(g_hFile), iFunction, &Hdr);
+ if (rc < 0)
+ {
+ rc = errno;
+ LogRel((USBLIBR3 ":IOCtl failed iFunction=%x errno=%d g_file=%d\n", iFunction, rc, RTFileToNative(g_hFile)));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ rc = Hdr.rc;
+ if (RT_UNLIKELY(RT_FAILURE(rc)))
+ LogRel((USBLIBR3 ":Function (%x) failed. rc=%Rrc\n", iFunction, rc));
+
+ return rc;
+}
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c
new file mode 100644
index 00000000..0568bf66
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c
@@ -0,0 +1,4054 @@
+/* $Id: VBoxUSB-solaris.c $ */
+/** @file
+ * VirtualBox USB Client Driver, Solaris Hosts.
+ */
+
+/*
+ * Copyright (C) 2008-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_USB_DRV
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/cdefs.h>
+#include <VBox/sup.h>
+#include <VBox/usblib-solaris.h>
+
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/semaphore.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/path.h>
+#include <iprt/thread.h>
+#include <iprt/dbg.h>
+
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+#include <sys/usb/usba.h>
+#include <sys/strsun.h>
+#include "usbai_private.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxusb"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox USB"
+
+/** -=-=-=-=-=-=- Standard Specifics -=-=-=-=-=-=- */
+/** Max. supported endpoints. */
+#define VBOXUSB_MAX_ENDPOINTS 32
+/** Size of USB Ctrl Xfer Header in bytes. */
+#define VBOXUSB_CTRL_XFER_SIZE 8
+/**
+ * USB2.0 (Sec. 9-13) Bits 10..0 is the max packet size; for high speed Isoc/Intr, bits 12..11 is
+ * number of additional transaction opportunities per microframe.
+ */
+#define VBOXUSB_PKT_SIZE(pkt) (pkt & 0x07FF) * (1 + ((pkt >> 11) & 3))
+/** Endpoint Xfer Type. */
+#define VBOXUSB_XFER_TYPE(endp) ((endp)->EpDesc.bmAttributes & USB_EP_ATTR_MASK)
+/** Endpoint Xfer Direction. */
+#define VBOXUSB_XFER_DIR(endp) ((endp)->EpDesc.bEndpointAddress & USB_EP_DIR_IN)
+/** Create an Endpoint index from an Endpoint address. */
+#define VBOXUSB_GET_EP_INDEX(epaddr) (((epaddr) & USB_EP_NUM_MASK) + \
+ (((epaddr) & USB_EP_DIR_MASK) ? 16 : 0))
+
+
+/** -=-=-=-=-=-=- Tunable Parameters -=-=-=-=-=-=- */
+/** Time to wait while draining inflight UBRs on suspend, in seconds. */
+#define VBOXUSB_DRAIN_TIME 20
+/** Ctrl Xfer timeout in seconds. */
+#define VBOXUSB_CTRL_XFER_TIMEOUT 15
+/** Maximum URB queue length. */
+#define VBOXUSB_URB_QUEUE_SIZE 512
+/** Maximum asynchronous requests per pipe. */
+#define VBOXUSB_MAX_PIPE_ASYNC_REQS 2
+
+/** For enabling global symbols while debugging. **/
+#if defined(DEBUG_ramshankar)
+# define LOCAL
+#else
+# define LOCAL static
+#endif
+
+
+/*********************************************************************************************************************************
+* Kernel Entry Hooks *
+*********************************************************************************************************************************/
+int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
+int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
+int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
+int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
+int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
+int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
+int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
+int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxUSBSolarisCbOps =
+{
+ VBoxUSBSolarisOpen,
+ VBoxUSBSolarisClose,
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ VBoxUSBSolarisRead,
+ VBoxUSBSolarisWrite,
+ VBoxUSBSolarisIOCtl,
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ VBoxUSBSolarisPoll,
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV, /* revision */
+ nodev, /* c aread */
+ nodev /* c awrite */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxUSBSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxUSBSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxUSBSolarisAttach,
+ VBoxUSBSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxUSBSolarisCbOps,
+ NULL, /* bus ops */
+ VBoxUSBSolarisPower,
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxUSBSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxUSBSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxUSBSolarisModLinkage =
+{
+ MODREV_1,
+ &g_VBoxUSBSolarisModule,
+ NULL,
+};
+
+/**
+ * vboxusb_ep_t: Endpoint structure with info. for managing an endpoint.
+ */
+typedef struct vboxusb_ep_t
+{
+ bool fInitialized; /* Whether this Endpoint is initialized */
+ usb_ep_descr_t EpDesc; /* Endpoint descriptor */
+ usb_pipe_handle_t pPipe; /* Endpoint pipe handle */
+ usb_pipe_policy_t PipePolicy; /* Endpoint policy */
+ bool fIsocPolling; /* Whether Isoc. IN polling is enabled */
+ list_t hIsocInUrbs; /* Isoc. IN inflight URBs */
+ uint16_t cIsocInUrbs; /* Number of Isoc. IN inflight URBs */
+ list_t hIsocInLandedReqs; /* Isoc. IN landed requests */
+ uint16_t cbIsocInLandedReqs; /* Cumulative size of landed Isoc. IN requests */
+ size_t cbMaxIsocData; /* Maximum size of Isoc. IN landed buffer */
+} vboxusb_ep_t;
+
+/**
+ * vboxusb_isoc_req_t: Isoc IN. requests queued from device till they are reaped.
+ */
+typedef struct vboxusb_isoc_req_t
+{
+ mblk_t *pMsg; /* Pointer to the data buffer */
+ uint32_t cIsocPkts; /* Number of Isoc pkts */
+ VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
+ list_node_t hListLink;
+} vboxusb_isoc_req_t;
+
+/**
+ * VBOXUSB_URB_STATE: Internal USB URB state.
+ */
+typedef enum VBOXUSB_URB_STATE
+{
+ VBOXUSB_URB_STATE_FREE = 0x00,
+ VBOXUSB_URB_STATE_INFLIGHT = 0x04,
+ VBOXUSB_URB_STATE_LANDED = 0x08
+} VBOXUSB_URB_STATE;
+
+/**
+ * vboxusb_urb_t: kernel URB representation.
+ */
+typedef struct vboxusb_urb_t
+{
+ void *pvUrbR3; /* Userspace URB address (untouched, returned while reaping) */
+ uint8_t bEndpoint; /* Endpoint address */
+ VUSBXFERTYPE enmType; /* Xfer type */
+ VUSBDIRECTION enmDir; /* Xfer direction */
+ VUSBSTATUS enmStatus; /* URB status */
+ bool fShortOk; /* Whether receiving less data than requested is acceptable */
+ RTR3PTR pvDataR3; /* Userspace address of the original data buffer */
+ size_t cbDataR3; /* Size of the data buffer */
+ mblk_t *pMsg; /* Pointer to the data buffer */
+ uint32_t cIsocPkts; /* Number of Isoc pkts */
+ VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
+ VBOXUSB_URB_STATE enmState; /* URB state (free/in-flight/landed). */
+ struct vboxusb_state_t *pState; /* Pointer to the device instance */
+ list_node_t hListLink; /* List node link handle */
+} vboxusb_urb_t;
+
+/**
+ * vboxusb_power_t: Per Device Power Management info.
+ */
+typedef struct vboxusb_power_t
+{
+ uint_t PowerStates; /* Bit mask of the power states */
+ int PowerBusy; /* Busy reference counter */
+ bool fPowerWakeup; /* Whether remote power wakeup is enabled */
+ bool fPowerRaise; /* Whether to raise the power level */
+ uint8_t PowerLevel; /* Current power level */
+} vboxusb_power_t;
+
+/**
+ * vboxusb_state_t: Per Device instance state info.
+ */
+typedef struct vboxusb_state_t
+{
+ dev_info_t *pDip; /* Per instance device info. */
+ usb_client_dev_data_t *pDevDesc; /* Parsed & complete device descriptor */
+ uint8_t DevState; /* Current USB Device state */
+ bool fDefaultPipeOpen; /* Whether the device (default control pipe) is closed */
+ bool fPollPending; /* Whether the userland process' poll is pending */
+ kmutex_t Mtx; /* Mutex state protection */
+ usb_serialization_t StateMulti; /* State serialization */
+ size_t cbMaxBulkXfer; /* Maximum bulk xfer size */
+ vboxusb_ep_t aEps[VBOXUSB_MAX_ENDPOINTS]; /* Array of all endpoints structures */
+ list_t hFreeUrbs; /* List of free URBs */
+ list_t hInflightUrbs; /* List of inflight URBs */
+ list_t hLandedUrbs; /* List of landed URBs */
+ uint32_t cFreeUrbs; /* Number of free URBs */
+ uint32_t cInflightUrbs; /* Number of inflight URBs */
+ uint32_t cLandedUrbs; /* Number of landed URBs */
+ pollhead_t PollHead; /* Handle to pollhead for waking polling processes */
+ RTPROCESS Process; /* The process (pid) of the user session */
+ VBOXUSBREQ_CLIENT_INFO ClientInfo; /* Registration data */
+ vboxusb_power_t *pPower; /* Power Management */
+ char szMfg[255]; /* Parsed manufacturer string */
+ char szProduct[255]; /* Parsed product string */
+} vboxusb_state_t;
+AssertCompileMemberSize(vboxusb_state_t, szMfg, USB_MAXSTRINGLEN);
+AssertCompileMemberSize(vboxusb_state_t, szProduct, USB_MAXSTRINGLEN);
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData);
+LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
+LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fControlPipe);
+LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
+LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq);
+LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *purb);
+LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq);
+LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq);
+LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
+LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq);
+LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg);
+LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status);
+LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb);
+LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus);
+LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
+ size_t *pcbDataOut);
+LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip);
+
+/** @name Device Operation Hooks
+ * @{ */
+LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
+LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
+LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint);
+LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bCfgValue);
+LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pCfgValue);
+LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
+LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset);
+LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint);
+LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bCfgValue);
+/** @} */
+
+/** @name Hotplug & Power Management Hooks
+ * @{ */
+LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip);
+LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip);
+
+LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState);
+LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState);
+LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState);
+/** @} */
+
+/** @name Monitor Hooks
+ * @{ */
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip);
+/** @} */
+
+/** @name Callbacks from Monitor
+ * @{ */
+LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved);
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global list of all device instances. */
+static void *g_pVBoxUSBSolarisState;
+
+/** The default endpoint descriptor */
+static usb_ep_descr_t g_VBoxUSBSolarisDefaultEpDesc = { 7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0 };
+
+/** Size of the usb_ep_data_t struct (used to index into data). */
+static size_t g_cbUsbEpData = ~0UL;
+
+/** The offset of usb_ep_data_t::ep_desc. */
+static size_t g_offUsbEpDataDescr = ~0UL;
+
+
+#ifdef LOG_ENABLED
+/**
+ * Gets the description of an Endpoint's transfer type.
+ *
+ * @param pEp The Endpoint.
+ * @returns The type of the Endpoint.
+ */
+static const char *vboxUsbSolarisEpType(vboxusb_ep_t *pEp)
+{
+ uint8_t uType = VBOXUSB_XFER_TYPE(pEp);
+ switch (uType)
+ {
+ case 0: return "CTRL";
+ case 1: return "ISOC";
+ case 2: return "BULK";
+ default: return "INTR";
+ }
+}
+
+
+/**
+ * Gets the description of an Endpoint's direction.
+ *
+ * @param pEp The Endpoint.
+ * @returns The direction of the Endpoint.
+ */
+static const char *vboxUsbSolarisEpDir(vboxusb_ep_t *pEp)
+{
+ return VBOXUSB_XFER_DIR(pEp) == USB_EP_DIR_IN ? "IN " : "OUT";
+}
+#endif
+
+
+/**
+ * Caches device strings from the parsed device descriptors.
+ *
+ * @param pState The USB device instance.
+ *
+ * @remarks Must only be called after usb_get_dev_data().
+ */
+static void vboxUsbSolarisGetDeviceStrings(vboxusb_state_t *pState)
+{
+ AssertReturnVoid(pState);
+ AssertReturnVoid(pState->pDevDesc);
+
+ if (pState->pDevDesc->dev_product)
+ strlcpy(&pState->szMfg[0], pState->pDevDesc->dev_mfg, sizeof(pState->szMfg));
+ else
+ strlcpy(&pState->szMfg[0], "<Unknown Manufacturer>", sizeof(pState->szMfg));
+
+ if (pState->pDevDesc->dev_product)
+ strlcpy(&pState->szProduct[0], pState->pDevDesc->dev_product, sizeof(pState->szProduct));
+ else
+ strlcpy(&pState->szProduct[0], "<Unnamed USB device>", sizeof(pState->szProduct));
+}
+
+
+/**
+ * Queries the necessary symbols at runtime.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisQuerySymbols(void)
+{
+ RTDBGKRNLINFO hKrnlDbgInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlDbgInfo, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Query and sanitize the size of usb_ep_data_t struct.
+ */
+ size_t cbPrevUsbEpData = g_cbUsbEpData;
+ rc = RTR0DbgKrnlInfoQuerySize(hKrnlDbgInfo, "usba", "usb_ep_data_t", &g_cbUsbEpData);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("Failed to query size of \"usb_ep_data_t\" in the \"usba\" module, rc=%Rrc\n", rc));
+ return rc;
+ }
+ if (g_cbUsbEpData > _4K)
+ {
+ LogRel(("Size of \"usb_ep_data_t\" (%u bytes) seems implausible, too paranoid to continue\n", g_cbUsbEpData));
+ return VERR_MISMATCH;
+ }
+
+ /*
+ * Query and sanitizie the offset of usb_ep_data_t::ep_descr.
+ */
+ size_t offPrevUsbEpDataDescr = g_offUsbEpDataDescr;
+ rc = RTR0DbgKrnlInfoQueryMember(hKrnlDbgInfo, "usba", "usb_ep_data_t", "ep_descr", &g_offUsbEpDataDescr);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("Failed to query offset of usb_ep_data_t::ep_descr, rc=%Rrc\n", rc));
+ return rc;
+ }
+ if (g_offUsbEpDataDescr > _4K - sizeof(usb_ep_descr_t))
+ {
+ LogRel(("Offset of \"ep_desrc\" (%u) seems implausible, too paranoid to continue\n", g_offUsbEpDataDescr));
+ return VERR_MISMATCH;
+ }
+
+ /*
+ * Log only when it changes / first time, since _init() seems to be called often (e.g. on failed attaches).
+ * cmn_err, CE_CONT and '!' is used to not show the message on console during boot each time.
+ */
+ if ( cbPrevUsbEpData != g_cbUsbEpData
+ || offPrevUsbEpDataDescr != g_offUsbEpDataDescr)
+ {
+ cmn_err(CE_CONT, "!usba_ep_data_t is %lu bytes\n", g_cbUsbEpData);
+ cmn_err(CE_CONT, "!usba_ep_data_t::ep_descr @ 0x%lx (%ld)\n", g_offUsbEpDataDescr, g_offUsbEpDataDescr);
+ }
+
+ RTR0DbgKrnlInfoRelease(hKrnlDbgInfo);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((DEVICE_NAME ": _init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxUSBSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ": _init: failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxUsbSolarisQuerySymbols();
+ if (RT_FAILURE(rc))
+ {
+ RTR0Term();
+ return EINVAL;
+ }
+
+ rc = ddi_soft_state_init(&g_pVBoxUSBSolarisState, sizeof(vboxusb_state_t), 4 /* pre-alloc */);
+ if (!rc)
+ {
+ rc = mod_install(&g_VBoxUSBSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc));
+ ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: failed to initialize soft state\n"));
+
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _fini\n"));
+
+ rc = mod_remove(&g_VBoxUSBSolarisModLinkage);
+ if (!rc)
+ {
+ ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
+ RTR0Term();
+ }
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ": _info\n"));
+
+ return mod_info(&g_VBoxUSBSolarisModLinkage, pModInfo);
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ int rc;
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = NULL;
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ rc = ddi_soft_state_zalloc(g_pVBoxUSBSolarisState, instance);
+ if (rc == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_LIKELY(pState))
+ {
+ pState->pDip = pDip;
+ pState->pDevDesc = NULL;
+ pState->fPollPending = false;
+ pState->cInflightUrbs = 0;
+ pState->cFreeUrbs = 0;
+ pState->cLandedUrbs = 0;
+ pState->Process = NIL_RTPROCESS;
+ pState->pPower = NULL;
+ bzero(pState->aEps, sizeof(pState->aEps));
+ list_create(&pState->hFreeUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ list_create(&pState->hInflightUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ list_create(&pState->hLandedUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+
+ /*
+ * There is a bug in usb_client_attach() as of Nevada 120 which panics when we bind to
+ * a non-USB device. So check if we are really binding to a USB device or not.
+ */
+ if (vboxUsbSolarisIsUSBDevice(pState->pDip))
+ {
+ /*
+ * Here starts the USB specifics.
+ */
+ rc = usb_client_attach(pState->pDip, USBDRV_VERSION, 0);
+ if (rc == USB_SUCCESS)
+ {
+ pState->fDefaultPipeOpen = true;
+
+ /*
+ * Parse out the entire descriptor.
+ */
+ rc = usb_get_dev_data(pState->pDip, &pState->pDevDesc, USB_PARSE_LVL_ALL, 0 /* Unused */);
+ if (rc == USB_SUCCESS)
+ {
+ /*
+ * Cache some device descriptor strings.
+ */
+ vboxUsbSolarisGetDeviceStrings(pState);
+#ifdef DEBUG_ramshankar
+ usb_print_descr_tree(pState->pDip, pState->pDevDesc);
+#endif
+
+ /*
+ * Initialize state locks.
+ */
+ mutex_init(&pState->Mtx, NULL, MUTEX_DRIVER, pState->pDevDesc->dev_iblock_cookie);
+ pState->StateMulti = usb_init_serialization(pState->pDip, USB_INIT_SER_CHECK_SAME_THREAD);
+
+ /*
+ * Get maximum bulk transfer size supported by the HCD.
+ */
+ rc = usb_pipe_get_max_bulk_transfer_size(pState->pDip, &pState->cbMaxBulkXfer);
+ if (rc == USB_SUCCESS)
+ {
+ Log((DEVICE_NAME ": VBoxUSBSolarisAttach: cbMaxBulkXfer=%d\n", pState->cbMaxBulkXfer));
+
+ /*
+ * Initialize the default endpoint.
+ */
+ rc = vboxUsbSolarisInitEp(pState, NULL /* pEp */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the device state.
+ */
+ pState->DevState = USB_DEV_ONLINE;
+
+ /*
+ * Initialize power management for the device.
+ */
+ rc = vboxUsbSolarisInitPower(pState);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize endpoints for the current config.
+ */
+ rc = vboxUsbSolarisInitEpsForCfg(pState);
+ AssertRC(rc);
+
+ /*
+ * Publish the minor node.
+ */
+ rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0,
+ "none", "none", 0666);
+ if (RT_LIKELY(rc == DDI_SUCCESS))
+ {
+ /*
+ * Register hotplug callbacks.
+ */
+ rc = usb_register_hotplug_cbs(pState->pDip, &vboxUsbSolarisDeviceDisconnected,
+ &vboxUsbSolarisDeviceReconnected);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ /*
+ * Register with our monitor driver.
+ */
+ bzero(&pState->ClientInfo, sizeof(pState->ClientInfo));
+ char szDevicePath[MAXPATHLEN];
+ ddi_pathname(pState->pDip, szDevicePath);
+ RTStrPrintf(pState->ClientInfo.szClientPath,
+ sizeof(pState->ClientInfo.szClientPath),
+ "/devices%s:%s", szDevicePath, DEVICE_NAME);
+ RTStrPrintf(pState->ClientInfo.szDeviceIdent,
+ sizeof(pState->ClientInfo.szDeviceIdent),
+ "%#x:%#x:%d:%s",
+ pState->pDevDesc->dev_descr->idVendor,
+ pState->pDevDesc->dev_descr->idProduct,
+ pState->pDevDesc->dev_descr->bcdDevice, szDevicePath);
+ pState->ClientInfo.Instance = instance;
+ pState->ClientInfo.pfnSetConsumerCredentials = &vboxUsbSolarisSetConsumerCredentials;
+ rc = VBoxUSBMonSolarisRegisterClient(pState->pDip, &pState->ClientInfo);
+ if (RT_SUCCESS(rc))
+ {
+#if 0
+ LogRel((DEVICE_NAME ": Captured %s %s (Ident=%s)\n", pState->szMfg,
+ pState->szProduct, pState->ClientInfo.szDeviceIdent));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Captured %s %s (Ident=%s)\n", pState->szMfg,
+ pState->szProduct, pState->ClientInfo.szDeviceIdent);
+#endif
+ return DDI_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient failed! rc=%d "
+ "path=%s instance=%d\n", rc, pState->ClientInfo.szClientPath,
+ instance));
+
+ usb_unregister_hotplug_cbs(pState->pDip);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to register hotplug callbacks! rc=%d\n", rc));
+
+ ddi_remove_minor_node(pState->pDip, NULL);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc));
+
+ mutex_enter(&pState->Mtx);
+ vboxUsbSolarisDestroyPower(pState);
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to init power management! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: vboxUsbSolarisInitEp failed! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_pipe_get_max_bulk_transfer_size failed! rc=%d\n", rc));
+
+ usb_fini_serialization(pState->StateMulti);
+ mutex_destroy(&pState->Mtx);
+ usb_free_dev_data(pState->pDip, pState->pDevDesc);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get device descriptor. rc=%d\n", rc));
+
+ usb_client_detach(pState->pDip, NULL);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_client_attach failed! rc=%d\n", rc));
+ }
+ else
+ {
+ /* This would appear on every boot if it were LogRel() */
+ Log((DEVICE_NAME ": VBoxUSBSolarisAttach: Not a USB device\n"));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get soft state\n", sizeof(*pState)));
+
+ ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to alloc soft state. rc=%d\n", rc));
+
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: DDI_RESUME failed to get soft state on detach\n"));
+ return DDI_FAILURE;
+ }
+
+ vboxUsbSolarisDeviceResume(pState);
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisDetach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisDetach: Failed to get soft state on detach\n"));
+ return DDI_FAILURE;
+ }
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ /*
+ * At this point it must be assumed that the default control pipe has
+ * already been closed by userland (via VBoxUSBSolarisClose() entry point).
+ * Once it's closed we can no longer open or reference the device here.
+ */
+
+ /*
+ * Notify userland if any that we're gone (while resetting device held by us).
+ */
+ mutex_enter(&pState->Mtx);
+ pState->DevState = USB_DEV_DISCONNECTED;
+ vboxUsbSolarisNotifyUnplug(pState);
+ mutex_exit(&pState->Mtx);
+
+
+ /*
+ * Unregister hotplug callback events first without holding the mutex as the callbacks
+ * would otherwise block on the mutex.
+ */
+ usb_unregister_hotplug_cbs(pDip);
+
+ /*
+ * Serialize: paranoid; drain other driver activity.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0 /* timeout */);
+ usb_release_access(pState->StateMulti);
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Close all pipes.
+ */
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ Assert(!pState->fDefaultPipeOpen);
+
+ /*
+ * Deinitialize power, destroy all endpoints.
+ */
+ vboxUsbSolarisDestroyPower(pState);
+ vboxUsbSolarisDestroyAllEps(pState);
+
+ /*
+ * Free up all URB lists.
+ */
+ vboxusb_urb_t *pUrb = NULL;
+ while ((pUrb = list_remove_head(&pState->hFreeUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ while ((pUrb = list_remove_head(&pState->hInflightUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ while ((pUrb = list_remove_head(&pState->hLandedUrbs)) != NULL)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ }
+ pState->cFreeUrbs = 0;
+ pState->cLandedUrbs = 0;
+ pState->cInflightUrbs = 0;
+ list_destroy(&pState->hFreeUrbs);
+ list_destroy(&pState->hInflightUrbs);
+ list_destroy(&pState->hLandedUrbs);
+
+ /*
+ * Destroy locks, free up descriptor and detach from USBA.
+ */
+ mutex_exit(&pState->Mtx);
+ usb_fini_serialization(pState->StateMulti);
+ mutex_destroy(&pState->Mtx);
+
+ usb_free_dev_data(pState->pDip, pState->pDevDesc);
+ usb_client_detach(pState->pDip, NULL);
+
+ /*
+ * Deregister with our Monitor driver.
+ */
+ VBoxUSBMonSolarisUnregisterClient(pState->pDip);
+
+ ddi_remove_minor_node(pState->pDip, NULL);
+
+#if 0
+ LogRel((DEVICE_NAME ": Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct, pState->ClientInfo.szDeviceIdent);
+#endif
+
+ ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
+ pState = NULL;
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+ int rc = vboxUsbSolarisDeviceSuspend(pState);
+ if (RT_SUCCESS(rc))
+ return DDI_SUCCESS;
+
+ return DDI_FAILURE;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns Solaris error code.
+ */
+int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisGetInfo\n"));
+
+ vboxusb_state_t *pState = NULL;
+ int instance = getminor((dev_t)pvArg);
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ /*
+ * One is to one mapping of instance & minor number as we publish only one minor node per device.
+ */
+ pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (pState)
+ {
+ *ppvResult = (void *)pState->pDip;
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisGetInfo: Failed to get device state\n"));
+ return DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ *ppvResult = (void *)(uintptr_t)instance;
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Callback invoked from the VirtualBox USB Monitor driver when a VM process
+ * tries to access this USB client instance.
+ *
+ * This determines which VM process will be allowed to open and access this USB
+ * device.
+ *
+ * @returns VBox status code.
+ *
+ * @param Process The VM process performing the client info. query.
+ * @param Instance This client instance (the one set while we register
+ * ourselves to the Monitor driver)
+ * @param pvReserved Reserved for future, unused.
+ */
+LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Process=%u Instance=%d\n", Process, Instance));
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, Instance);
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed to get device state for instance %d\n", Instance));
+ return VERR_INVALID_STATE;
+ }
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pState->Mtx);
+
+ if (pState->Process == NIL_RTPROCESS)
+ pState->Process = Process;
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed! Process %u already has client open\n",
+ pState->Process));
+ rc = VERR_RESOURCE_BUSY;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisOpen: pDev=%p fFlag=%d fType=%d pCred=%p\n", pDev, fFlag, fType, pCred));
+
+ /*
+ * Verify we are being opened as a character device
+ */
+ if (fType != OTYP_CHR)
+ return EINVAL;
+
+ /*
+ * One is to one mapping. (Minor<=>Instance).
+ */
+ int instance = getminor((dev_t)*pDev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Failed to get device state for instance %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Only one user process can open a device instance at a time.
+ */
+ if (pState->Process != RTProcSelf())
+ {
+ if (pState->Process == NIL_RTPROCESS)
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: No prior information about authorized process\n"));
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Process %u is already using this device instance\n", pState->Process));
+
+ mutex_exit(&pState->Mtx);
+ return EPERM;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ NOREF(fFlag);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisClose: Dev=%d fFlag=%d fType=%d pCred=%p\n", Dev, fFlag, fType, pCred));
+
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisClose: Failed to get device state for instance %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+ pState->fPollPending = false;
+ pState->Process = NIL_RTPROCESS;
+ mutex_exit(&pState->Mtx);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisRead\n"));
+ return ENOTSUP;
+}
+
+
+int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisWrite\n"));
+ return ENOTSUP;
+}
+
+
+int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisPoll: Dev=%d fEvents=%d fAnyYet=%d pReqEvents=%p\n", Dev, fEvents, fAnyYet, pReqEvents));
+
+ /*
+ * Get the device state (one to one mapping).
+ */
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisPoll: No state data for %d\n", instance));
+ return ENXIO;
+ }
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Disconnect event (POLLHUP) is invalid in "fEvents".
+ */
+ if (pState->DevState == USB_DEV_DISCONNECTED)
+ *pReqEvents |= POLLHUP;
+ else if (pState->cLandedUrbs)
+ *pReqEvents |= POLLIN;
+ else
+ {
+ *pReqEvents = 0;
+ if (!fAnyYet)
+ {
+ *ppPollHead = &pState->PollHead;
+ pState->fPollPending = true;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return 0;
+}
+
+
+int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBSolarisPower: pDip=%p Component=%d Level=%d\n", pDip, Component, Level));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisPower: Failed! State Gone\n"));
+ return DDI_FAILURE;
+ }
+
+ if (!pState->pPower)
+ return DDI_SUCCESS;
+
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ int rc = USB_FAILURE;
+ if (pState->DevState == USB_DEV_ONLINE)
+ {
+ /*
+ * Check if we are transitioning to a valid power state.
+ */
+ if (!USB_DEV_PWRSTATE_OK(pState->pPower->PowerStates, Level))
+ {
+ switch (Level)
+ {
+ case USB_DEV_OS_PWR_OFF:
+ {
+ if (pState->pPower->PowerBusy)
+ break;
+
+ /*
+ * USB D3 command.
+ */
+ pState->pPower->PowerLevel = USB_DEV_OS_PWR_OFF;
+ mutex_exit(&pState->Mtx);
+ rc = USB_SUCCESS; /* usb_set_device_pwrlvl3(pDip); */
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+
+ case USB_DEV_OS_FULL_PWR:
+ {
+ /*
+ * Can happen during shutdown of the OS.
+ */
+ pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
+ mutex_exit(&pState->Mtx);
+ rc = USB_SUCCESS; /* usb_set_device_pwrlvl0(pDip); */
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+
+ default: /* Power levels 1, 2 not implemented */
+ break;
+ }
+ }
+ else
+ Log((DEVICE_NAME ": VBoxUSBSolarisPower: USB_DEV_PWRSTATE_OK failed\n"));
+ }
+ else
+ rc = USB_SUCCESS;
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+ return rc == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE;
+}
+
+
+/** @def IOCPARM_LEN
+ * Gets the length from the ioctl number.
+ * This is normally defined by sys/ioccom.h on BSD systems...
+ */
+#ifndef IOCPARM_LEN
+# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK)
+#endif
+
+int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
+{
+ /* LogFunc((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); */
+
+ /*
+ * Get the device state (one to one mapping).
+ */
+ int instance = getminor((dev_t)Dev);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+ if (RT_UNLIKELY(!pState))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: No state data for %d\n", instance));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request wrapper.
+ */
+ VBOXUSBREQ ReqWrap;
+ if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd),
+ sizeof(ReqWrap)));
+ return ENOTTY;
+ }
+
+ int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc));
+ return EINVAL;
+ }
+
+ if (ReqWrap.u32Magic != VBOXUSB_MAGIC)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || ReqWrap.cbData > _1M*16))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request.
+ */
+ void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
+ if (RT_UNLIKELY(!pvBuf))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData));
+ return ENOMEM;
+ }
+
+ rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
+ return EFAULT;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || pvBuf == NULL))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Invalid request! pvBuf=%p cbData=%d\n", pvBuf, ReqWrap.cbData));
+ return EINVAL;
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ size_t cbDataOut = 0;
+ rc = vboxUsbSolarisProcessIOCtl(Cmd, pState, Mode, &ReqWrap, pvBuf, &cbDataOut);
+ ReqWrap.rc = rc;
+ rc = 0;
+
+ if (RT_UNLIKELY(cbDataOut > ReqWrap.cbData))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Too much output data %d expected %d Truncating!\n", cbDataOut,
+ ReqWrap.cbData));
+ cbDataOut = ReqWrap.cbData;
+ }
+
+ ReqWrap.cbData = cbDataOut;
+
+ /*
+ * Copy VBOXUSBREQ back to userspace (which contains rc for USB operation).
+ */
+ rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Copy payload (if any) back to userspace.
+ */
+ if (cbDataOut > 0)
+ {
+ rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataOut, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg,
+ Cmd, rc));
+ rc = EFAULT;
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout(1)failed! pReqWrap=%p pArg=%p Cmd=%d. rc=%d\n", &ReqWrap, pArg,
+ Cmd, rc));
+ rc = EFAULT;
+ }
+
+ *pVal = rc;
+ RTMemTmpFree(pvBuf);
+ return rc;
+}
+
+
+/**
+ * IOCtl processor for user to kernel and kernel to kernel communication.
+ *
+ * @returns VBox status code.
+ *
+ * @param iFunction The requested function.
+ * @param pvState The USB device instance.
+ * @param Mode The IOCtl mode.
+ * @param pUSBReq Pointer to the VBOXUSB request.
+ * @param pvBuf Pointer to the ring-3 URB.
+ * @param pcbDataOut Where to store the IOCtl OUT data size.
+ */
+LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
+ size_t *pcbDataOut)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: iFunction=%d pvState=%p pUSBReq=%p\n", iFunction, pvState, pUSBReq)); */
+
+ AssertPtrReturn(pvState, VERR_INVALID_PARAMETER);
+ vboxusb_state_t *pState = (vboxusb_state_t *)pvState;
+ size_t cbData = pUSBReq->cbData;
+ int rc;
+
+#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
+ do { \
+ if (RT_UNLIKELY(cbData < (cbMin))) \
+ { \
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
+ cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
+ return VERR_BUFFER_OVERFLOW; \
+ } \
+ if (RT_UNLIKELY((cbMin) != 0 && !RT_VALID_PTR(pvBuf))) \
+ { \
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvBuf)); \
+ return VERR_INVALID_PARAMETER; \
+ } \
+ } while (0)
+
+ switch (iFunction)
+ {
+ case VBOXUSB_IOCTL_SEND_URB:
+ {
+ CHECKRET_MIN_SIZE("SEND_URB", sizeof(VBOXUSBREQ_URB));
+
+ PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
+ rc = vboxUsbSolarisSendUrb(pState, pUrbReq, Mode);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SEND_URB returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_REAP_URB:
+ {
+ CHECKRET_MIN_SIZE("REAP_URB", sizeof(VBOXUSBREQ_URB));
+
+ PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
+ rc = vboxUsbSolarisReapUrb(pState, pUrbReq, Mode);
+ *pcbDataOut = sizeof(VBOXUSBREQ_URB);
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: REAP_URB returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_CLEAR_EP:
+ {
+ CHECKRET_MIN_SIZE("CLEAR_EP", sizeof(VBOXUSBREQ_CLEAR_EP));
+
+ PVBOXUSBREQ_CLEAR_EP pClearEpReq = (PVBOXUSBREQ_CLEAR_EP)pvBuf;
+ rc = vboxUsbSolarisClearEndPoint(pState, pClearEpReq->bEndpoint);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLEAR_EP returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_SET_CONFIG:
+ {
+ CHECKRET_MIN_SIZE("SET_CONFIG", sizeof(VBOXUSBREQ_SET_CONFIG));
+
+ PVBOXUSBREQ_SET_CONFIG pSetCfgReq = (PVBOXUSBREQ_SET_CONFIG)pvBuf;
+ rc = vboxUsbSolarisSetConfig(pState, pSetCfgReq->bConfigValue);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_CONFIG returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_SET_INTERFACE:
+ {
+ CHECKRET_MIN_SIZE("SET_INTERFACE", sizeof(VBOXUSBREQ_SET_INTERFACE));
+
+ PVBOXUSBREQ_SET_INTERFACE pSetInterfaceReq = (PVBOXUSBREQ_SET_INTERFACE)pvBuf;
+ rc = vboxUsbSolarisSetInterface(pState, pSetInterfaceReq->bInterface, pSetInterfaceReq->bAlternate);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_INTERFACE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_CLOSE_DEVICE:
+ {
+ CHECKRET_MIN_SIZE("CLOSE_DEVICE", sizeof(VBOXUSBREQ_CLOSE_DEVICE));
+
+ PVBOXUSBREQ_CLOSE_DEVICE pCloseDeviceReq = (PVBOXUSBREQ_CLOSE_DEVICE)pvBuf;
+ if ( pCloseDeviceReq->ResetLevel != VBOXUSB_RESET_LEVEL_REATTACH
+ || (Mode & FKIOCTL))
+ {
+ rc = vboxUsbSolarisCloseDevice(pState, pCloseDeviceReq->ResetLevel);
+ }
+ else
+ {
+ /* Userland IOCtls are not allowed to perform a reattach of the device. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLOSE_DEVICE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_ABORT_PIPE:
+ {
+ CHECKRET_MIN_SIZE("ABORT_PIPE", sizeof(VBOXUSBREQ_ABORT_PIPE));
+
+ PVBOXUSBREQ_ABORT_PIPE pAbortPipeReq = (PVBOXUSBREQ_ABORT_PIPE)pvBuf;
+ rc = vboxUsbSolarisAbortPipe(pState, pAbortPipeReq->bEndpoint);
+ *pcbDataOut = 0;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: ABORT_PIPE returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_GET_CONFIG:
+ {
+ CHECKRET_MIN_SIZE("GET_CONFIG", sizeof(VBOXUSBREQ_GET_CONFIG));
+
+ PVBOXUSBREQ_GET_CONFIG pGetCfgReq = (PVBOXUSBREQ_GET_CONFIG)pvBuf;
+ rc = vboxUsbSolarisGetConfig(pState, &pGetCfgReq->bConfigValue);
+ *pcbDataOut = sizeof(VBOXUSBREQ_GET_CONFIG);
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_CONFIG returned %d\n", rc));
+ break;
+ }
+
+ case VBOXUSB_IOCTL_GET_VERSION:
+ {
+ CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION));
+
+ PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvBuf;
+ pGetVersionReq->u32Major = VBOXUSB_VERSION_MAJOR;
+ pGetVersionReq->u32Minor = VBOXUSB_VERSION_MINOR;
+ *pcbDataOut = sizeof(VBOXUSBREQ_GET_VERSION);
+ rc = VINF_SUCCESS;
+ Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_VERSION returned %d\n", rc));
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: Unknown request %#x\n", iFunction));
+ rc = VERR_NOT_SUPPORTED;
+ *pcbDataOut = 0;
+ break;
+ }
+ }
+
+ pUSBReq->cbData = *pcbDataOut;
+ return rc;
+}
+
+
+/**
+ * Initializes device power management.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitPower: pState=%p\n", pState));
+
+ int rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_ENABLE);
+ if (rc == USB_SUCCESS)
+ {
+ vboxusb_power_t *pPower = RTMemAllocZ(sizeof(vboxusb_power_t));
+ if (RT_LIKELY(pPower))
+ {
+ mutex_enter(&pState->Mtx);
+ pState->pPower = pPower;
+ pState->pPower->fPowerWakeup = false;
+ mutex_exit(&pState->Mtx);
+
+ uint_t PowerStates;
+ rc = usb_create_pm_components(pState->pDip, &PowerStates);
+ if (rc == USB_SUCCESS)
+ {
+ pState->pPower->fPowerWakeup = true;
+ pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
+ pState->pPower->PowerStates = PowerStates;
+
+ rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+
+ if (rc != DDI_SUCCESS)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to raise power level usb(%#x,%#x)\n",
+ pState->pDevDesc->dev_descr->idVendor, pState->pDevDesc->dev_descr->idProduct));
+ }
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to create power components\n"));
+
+ return VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to enable remote wakeup, No PM!\n"));
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Destroys device power management.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyPower: pState=%p\n", pState));
+
+ if (pState->pPower)
+ {
+ mutex_exit(&pState->Mtx);
+ vboxUsbSolarisPowerBusy(pState);
+ mutex_enter(&pState->Mtx);
+
+ int rc = -1;
+ if ( pState->pPower->fPowerWakeup
+ && pState->DevState != USB_DEV_DISCONNECTED)
+ {
+ mutex_exit(&pState->Mtx);
+ rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Raising power failed! rc=%d\n", rc));
+
+ rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_DISABLE);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Failed to disable remote wakeup\n"));
+ }
+ else
+ mutex_exit(&pState->Mtx);
+
+ rc = pm_lower_power(pState->pDip, 0 /* component */, USB_DEV_OS_PWR_OFF);
+ if (rc != DDI_SUCCESS)
+ Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Lowering power failed! rc=%d\n", rc));
+
+ vboxUsbSolarisPowerIdle(pState);
+ mutex_enter(&pState->Mtx);
+ RTMemFree(pState->pPower);
+ pState->pPower = NULL;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA URB status to VBox's USB URB status.
+ *
+ * @param Status Solaris USBA USB URB status.
+ *
+ * @returns VBox USB URB status.
+ */
+LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status)
+{
+ switch (Status)
+ {
+ case USB_CR_OK: return VUSBSTATUS_OK;
+ case USB_CR_CRC: return VUSBSTATUS_CRC;
+ case USB_CR_DEV_NOT_RESP: return VUSBSTATUS_DNR;
+ case USB_CR_DATA_UNDERRUN: return VUSBSTATUS_DATA_UNDERRUN;
+ case USB_CR_DATA_OVERRUN: return VUSBSTATUS_DATA_OVERRUN;
+ case USB_CR_STALL: return VUSBSTATUS_STALL;
+ /*
+ case USB_CR_BITSTUFFING:
+ case USB_CR_DATA_TOGGLE_MM:
+ case USB_CR_PID_CHECKFAILURE:
+ case USB_CR_UNEXP_PID:
+ case USB_CR_BUFFER_OVERRUN:
+ case USB_CR_BUFFER_UNDERRUN:
+ case USB_CR_TIMEOUT:
+ case USB_CR_NOT_ACCESSED:
+ case USB_CR_NO_RESOURCES:
+ case USB_CR_UNSPECIFIED_ERR:
+ case USB_CR_STOPPED_POLLING:
+ case USB_CR_PIPE_CLOSING:
+ case USB_CR_PIPE_RESET:
+ case USB_CR_NOT_SUPPORTED:
+ case USB_CR_FLUSHED:
+ case USB_CR_HC_HARDWARE_ERR:
+ */
+ default: return VUSBSTATUS_INVALID;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA error code to VBox's error code.
+ *
+ * @param UsbRc Solaris USBA error code.
+ *
+ * @returns VBox error code.
+ */
+static int vboxUsbSolarisToVBoxRC(int UsbRc)
+{
+ switch (UsbRc)
+ {
+ case USB_SUCCESS: return VINF_SUCCESS;
+ case USB_INVALID_ARGS: return VERR_INVALID_PARAMETER;
+ case USB_INVALID_PIPE: return VERR_BAD_PIPE;
+ case USB_INVALID_CONTEXT: return VERR_INVALID_CONTEXT;
+ case USB_BUSY: return VERR_PIPE_BUSY;
+ case USB_PIPE_ERROR: return VERR_PIPE_IO_ERROR;
+ /*
+ case USB_FAILURE:
+ case USB_NO_RESOURCES:
+ case USB_NO_BANDWIDTH:
+ case USB_NOT_SUPPORTED:
+ case USB_PIPE_ERROR:
+ case USB_NO_FRAME_NUMBER:
+ case USB_INVALID_START_FRAME:
+ case USB_HC_HARDWARE_ERROR:
+ case USB_INVALID_REQUEST:
+ case USB_INVALID_VERSION:
+ case USB_INVALID_PERM:
+ */
+ default: return VERR_GENERAL_FAILURE;
+ }
+}
+
+
+/**
+ * Converts Solaris' USBA device state to VBox's error code.
+ *
+ * @param uDeviceState The USB device state to convert.
+ *
+ * @returns VBox error code.
+ */
+static int vboxUsbSolarisDeviceState(uint8_t uDeviceState)
+{
+ switch (uDeviceState)
+ {
+ case USB_DEV_ONLINE: return VINF_SUCCESS;
+ case USB_DEV_SUSPENDED: return VERR_VUSB_DEVICE_IS_SUSPENDED;
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_PWRED_DOWN: return VERR_VUSB_DEVICE_NOT_ATTACHED;
+ default: return VERR_GENERAL_FAILURE;
+ }
+}
+
+
+/**
+ * Checks if the device is a USB device.
+ *
+ * @param pDip Pointer to this device info. structure.
+ *
+ * @returns If this is really a USB device returns true, otherwise false.
+ */
+LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip)
+{
+ int rc = DDI_FAILURE;
+
+ /*
+ * Check device for "usb" compatible property, root hubs->device would likely mean parent has no "usb" property.
+ */
+ char **ppszCompatible = NULL;
+ uint_t cCompatible;
+ rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible, &cCompatible);
+ if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
+ {
+ while (cCompatible--)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Compatible[%d]=%s\n", cCompatible, ppszCompatible[cCompatible]));
+ if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. pszCompatible=%s\n",
+ ppszCompatible[cCompatible]));
+ ddi_prop_free(ppszCompatible);
+ return true;
+ }
+ }
+
+ ddi_prop_free(ppszCompatible);
+ ppszCompatible = NULL;
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB property lookup failed, rc=%d\n", rc));
+
+ /*
+ * Check parent for "usb" compatible property.
+ */
+ dev_info_t *pParentDip = ddi_get_parent(pDip);
+ if (pParentDip)
+ {
+ rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pParentDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible,
+ &cCompatible);
+ if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
+ {
+ while (cCompatible--)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Parent compatible[%d]=%s\n", cCompatible,
+ ppszCompatible[cCompatible]));
+ if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. parent pszCompatible=%s\n",
+ ppszCompatible[cCompatible]));
+ ddi_prop_free(ppszCompatible);
+ return true;
+ }
+ }
+
+ ddi_prop_free(ppszCompatible);
+ ppszCompatible = NULL;
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB parent property lookup failed. rc=%d\n", rc));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Failed to obtain parent device for property lookup\n"));
+
+ return false;
+}
+
+
+/**
+ * Submits a URB.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Pointer to the VBox USB URB.
+ * @param Mode The IOCtl mode.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
+{
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(pUrbReq->bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ AssertPtrReturn(pEp, VERR_INVALID_POINTER);
+ Assert(pUrbReq);
+
+#if 0
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSendUrb: pState=%p pUrbReq=%p bEndpoint=%#x[%d] enmDir=%#x enmType=%#x "
+ "cbData=%d pvData=%p\n", pState, pUrbReq, pUrbReq->bEndpoint, iEpIndex, pUrbReq->enmDir,
+ pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData));
+#endif
+
+ if (RT_UNLIKELY(!pUrbReq->pvData))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Invalid request - No data\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Allocate message block & copy userspace buffer for host to device Xfers and for
+ * Control Xfers (since input has Setup header that needs copying).
+ */
+ mblk_t *pMsg = NULL;
+ int rc = VINF_SUCCESS;
+ if ( pUrbReq->enmDir == VUSBDIRECTION_OUT
+ || pUrbReq->enmType == VUSBXFERTYPE_MSG)
+ {
+ pMsg = allocb(pUrbReq->cbData, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to allocate %u bytes\n", pUrbReq->cbData));
+ return VERR_NO_MEMORY;
+ }
+
+ rc = ddi_copyin(pUrbReq->pvData, pMsg->b_wptr, pUrbReq->cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: ddi_copyin failed! rc=%d\n", rc));
+ freemsg(pMsg);
+ return VERR_NO_MEMORY;
+ }
+
+ pMsg->b_wptr += pUrbReq->cbData;
+ }
+
+ mutex_enter(&pState->Mtx);
+ rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (!pState->fDefaultPipeOpen) /* Required for Isoc. IN Xfers which don't Xfer through the pipe after polling starts */
+ rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the pipe if needed.
+ */
+ rc = vboxUsbSolarisOpenPipe(pState, pEp);
+ if (RT_UNLIKELY(RT_FAILURE(rc)))
+ {
+ mutex_exit(&pState->Mtx);
+ freemsg(pMsg);
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: OpenPipe failed! pState=%p pUrbReq=%p bEndpoint=%#x enmDir=%#x "
+ "enmType=%#x cbData=%d pvData=%p rc=%d\n", pState, pUrbReq, pUrbReq->bEndpoint, pUrbReq->enmDir,
+ pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData, rc));
+ return VERR_BAD_PIPE;
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ vboxusb_urb_t *pUrb = NULL;
+ if ( pUrbReq->enmType == VUSBXFERTYPE_ISOC
+ && pUrbReq->enmDir == VUSBDIRECTION_IN)
+ pUrb = vboxUsbSolarisGetIsocInUrb(pState, pUrbReq);
+ else
+ pUrb = vboxUsbSolarisQueueUrb(pState, pUrbReq, pMsg);
+
+ if (RT_LIKELY(pUrb))
+ {
+ switch (pUrb->enmType)
+ {
+ case VUSBXFERTYPE_MSG:
+ {
+ rc = vboxUsbSolarisCtrlXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_BULK:
+ {
+ rc = vboxUsbSolarisBulkXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_INTR:
+ {
+ rc = vboxUsbSolarisIntrXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ case VUSBXFERTYPE_ISOC:
+ {
+ rc = vboxUsbSolarisIsocXfer(pState, pEp, pUrb);
+ break;
+ }
+
+ default:
+ {
+ LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisSendUrb: URB type unsupported %d\n", pUrb->enmType));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ mutex_enter(&pState->Mtx);
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ pMsg = NULL;
+
+ if ( pUrb->enmType == VUSBXFERTYPE_ISOC
+ && pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ RTMemFree(pUrb);
+ pUrb = NULL;
+ }
+ else
+ {
+ /*
+ * Xfer failed, move URB back to the free list.
+ */
+ list_remove(&pState->hInflightUrbs, pUrb);
+ Assert(pState->cInflightUrbs > 0);
+ --pState->cInflightUrbs;
+
+ pUrb->enmState = VBOXUSB_URB_STATE_FREE;
+ Assert(!pUrb->pMsg);
+ list_insert_head(&pState->hFreeUrbs, pUrb);
+ ++pState->cFreeUrbs;
+ }
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to queue URB\n"));
+ rc = VERR_NO_MEMORY;
+ freemsg(pMsg);
+ }
+ }
+ else
+ {
+ mutex_exit(&pState->Mtx);
+ freemsg(pMsg);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Reaps a completed URB.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Pointer to the VBox USB URB.
+ * @param Mode The IOCtl mode.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisReapUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq)); */
+
+ AssertPtrReturn(pUrbReq, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pState->Mtx);
+ rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (!pState->fDefaultPipeOpen)
+ rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
+ if (RT_SUCCESS(rc))
+ {
+ vboxusb_urb_t *pUrb = list_remove_head(&pState->hLandedUrbs);
+ if (pUrb)
+ {
+ Assert(pState->cLandedUrbs > 0);
+ --pState->cLandedUrbs;
+ }
+
+ /*
+ * It is safe to access pUrb->pMsg outside the state mutex because this is from the landed URB list
+ * and not the inflight URB list.
+ */
+ mutex_exit(&pState->Mtx);
+ if (pUrb)
+ {
+ /*
+ * Copy the URB which will then be copied to user-space.
+ */
+ pUrbReq->pvUrbR3 = pUrb->pvUrbR3;
+ pUrbReq->bEndpoint = pUrb->bEndpoint;
+ pUrbReq->enmType = pUrb->enmType;
+ pUrbReq->enmDir = pUrb->enmDir;
+ pUrbReq->enmStatus = pUrb->enmStatus;
+ pUrbReq->pvData = (void *)pUrb->pvDataR3;
+ pUrbReq->cbData = pUrb->cbDataR3;
+
+ if (RT_LIKELY(pUrb->pMsg))
+ {
+ /*
+ * Copy the message back into the user buffer.
+ */
+ if (RT_LIKELY(pUrb->pvDataR3 != NIL_RTR3PTR))
+ {
+ Assert(!pUrb->pMsg->b_cont); /* We really should have a single message block always. */
+ size_t cbData = RT_MIN(MBLKL(pUrb->pMsg), pUrb->cbDataR3);
+ pUrbReq->cbData = cbData;
+
+ if (RT_LIKELY(cbData))
+ {
+ rc = ddi_copyout(pUrb->pMsg->b_rptr, (void *)pUrbReq->pvData, cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: ddi_copyout failed! rc=%d\n", rc));
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ }
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisReapUrb: pvUrbR3=%p pvDataR3=%p cbData=%d\n", pUrbReq->pvUrbR3,
+ pUrbReq->pvData, pUrbReq->cbData));
+ }
+ else
+ {
+ pUrbReq->cbData = 0;
+ rc = VERR_INVALID_POINTER;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: Missing pvDataR3!!\n"));
+ }
+
+ /*
+ * Free buffer allocated in vboxUsbSolarisSendUrb or vboxUsbSolaris[Ctrl|Bulk|Intr]Xfer().
+ */
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ }
+ else
+ {
+ if ( pUrb->enmType == VUSBXFERTYPE_ISOC
+ && pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ pUrbReq->cbData = 0;
+ }
+ }
+
+ /*
+ * Copy Isoc packet descriptors.
+ */
+ if (pUrb->enmType == VUSBXFERTYPE_ISOC)
+ {
+ AssertCompile(sizeof(pUrbReq->aIsocPkts) == sizeof(pUrb->aIsocPkts));
+ pUrbReq->cIsocPkts = pUrb->cIsocPkts;
+
+ for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
+ {
+ pUrbReq->aIsocPkts[i].cbPkt = pUrb->aIsocPkts[i].cbPkt;
+ pUrbReq->aIsocPkts[i].cbActPkt = pUrb->aIsocPkts[i].cbActPkt;
+ pUrbReq->aIsocPkts[i].enmStatus = pUrb->aIsocPkts[i].enmStatus;
+ }
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ RTMemFree(pUrb);
+ pUrb = NULL;
+ }
+ }
+
+ if (pUrb)
+ {
+ /*
+ * Add URB back to the free list.
+ */
+ Assert(!pUrb->pMsg);
+ pUrb->cbDataR3 = 0;
+ pUrb->pvDataR3 = NIL_RTR3PTR;
+ pUrb->enmState = VBOXUSB_URB_STATE_FREE;
+ mutex_enter(&pState->Mtx);
+ list_insert_head(&pState->hFreeUrbs, pUrb);
+ ++pState->cFreeUrbs;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ {
+ pUrbReq->pvUrbR3 = NULL;
+ pUrbReq->cbData = 0;
+ pUrbReq->pvData = NULL;
+ pUrbReq->enmStatus = VUSBSTATUS_INVALID;
+ }
+ }
+ else
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Clears a pipe (CLEAR_FEATURE).
+ *
+ * @param pState The USB device instance.
+ * @param bEndpoint The Endpoint address.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ if (RT_LIKELY(pEp))
+ {
+ /*
+ * Check if the endpoint is open to be cleared.
+ */
+ if (pEp->pPipe)
+ {
+ mutex_exit(&pState->Mtx);
+
+ /*
+ * Synchronous reset pipe.
+ */
+ usb_pipe_reset(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ NULL, /* Completion callback */
+ NULL); /* Exception callback */
+
+ mutex_enter(&pState->Mtx);
+
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: bEndpoint=%#x[%d] returns %d\n", bEndpoint, iEpIndex, rc));
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Not opened to be cleared. Faking success. bEndpoint=%#x\n",
+ bEndpoint));
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Endpoint missing! bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
+ rc = VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Device not online, state=%d\n", pState->DevState));
+
+ mutex_exit(&pState->Mtx);
+ return rc;
+}
+
+
+/**
+ * Sets configuration (SET_CONFIGURATION)
+ *
+ * @param pState The USB device instance.
+ * @param bConfig The Configuration.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bConfig)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConfig: pState=%p bConfig=%u\n", pState, bConfig));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
+ int iCfgIndex = vboxUsbSolarisGetConfigIndex(pState, bConfig);
+
+ if ( iCfgIndex >= 0
+ && iCfgIndex < pState->pDevDesc->dev_n_cfg)
+ {
+ /*
+ * Switch Config synchronously.
+ */
+ mutex_exit(&pState->Mtx);
+ rc = usb_set_cfg(pState->pDip, (uint_t)iCfgIndex, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
+ mutex_enter(&pState->Mtx);
+
+ if (rc == USB_SUCCESS)
+ {
+ int rc2 = vboxUsbSolarisInitEpsForCfg(pState);
+ AssertRC(rc2); NOREF(rc2);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: usb_set_cfg failed for iCfgIndex=%#x bConfig=%u rc=%d\n",
+ iCfgIndex, bConfig, rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: Invalid iCfgIndex=%d bConfig=%u\n", iCfgIndex, bConfig));
+ rc = VERR_OUT_OF_RANGE;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Gets configuration (GET_CONFIGURATION)
+ *
+ * @param pState The USB device instance.
+ * @param pbConfig Where to store the Configuration.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pbConfig)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisGetConfig: pState=%p pbConfig=%p\n", pState, pbConfig));
+ AssertPtrReturn(pbConfig, VERR_INVALID_POINTER);
+
+ /*
+ * Get Config synchronously.
+ */
+ uint_t bConfig;
+ int rc = usb_get_cfg(pState->pDip, &bConfig, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ *pbConfig = bConfig;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisGetConfig: Failed, rc=%d\n", rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisGetConfig: Returns %d bConfig=%u\n", rc, *pbConfig));
+ return rc;
+}
+
+
+/**
+ * Sets interface (SET_INTERFACE) and alternate.
+ *
+ * @param pState The USB device instance.
+ * @param bIf The Interface.
+ * @param bAlt The Alternate setting.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisSetInterface: pState=%p bIf=%#x bAlt=%#x\n", pState, bIf, bAlt));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set Interface & Alt setting synchronously.
+ */
+ mutex_exit(&pState->Mtx);
+ rc = usb_set_alt_if(pState->pDip, bIf, bAlt, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
+ mutex_enter(&pState->Mtx);
+
+ if (rc == USB_SUCCESS)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisSetInterface: Success, bIf=%#x bAlt=%#x\n", bIf, bAlt, rc));
+ int rc2 = vboxUsbSolarisInitEpsForIfAlt(pState, bIf, bAlt);
+ AssertRC(rc2); NOREF(rc2);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisSetInterface: usb_set_alt_if failed for bIf=%#x bAlt=%#x rc=%d\n", bIf, bAlt, rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ return rc;
+}
+
+
+/**
+ * Closes the USB device and optionally resets it.
+ *
+ * @param pState The USB device instance.
+ * @param enmReset The reset level.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseDevice: pState=%p enmReset=%d\n", pState, enmReset));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+
+ if (enmReset == VBOXUSB_RESET_LEVEL_CLOSE)
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ else
+ vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
+
+ mutex_exit(&pState->Mtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmReset)
+ {
+ case VBOXUSB_RESET_LEVEL_REATTACH:
+ rc = usb_reset_device(pState->pDip, USB_RESET_LVL_REATTACH);
+ break;
+
+ case VBOXUSB_RESET_LEVEL_SOFT:
+ rc = usb_reset_device(pState->pDip, USB_RESET_LVL_DEFAULT);
+ break;
+
+ default:
+ rc = USB_SUCCESS;
+ break;
+ }
+
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseDevice: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Aborts pending requests and reset the pipe.
+ *
+ * @param pState The USB device instance.
+ * @param bEndpoint The Endpoint address.
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
+
+ mutex_enter(&pState->Mtx);
+ int rc = vboxUsbSolarisDeviceState(pState->DevState);
+ if (RT_SUCCESS(rc))
+ {
+ int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
+ if (RT_LIKELY(pEp))
+ {
+ if (pEp->pPipe)
+ {
+ /*
+ * Aborting requests not supported for the default control pipe.
+ */
+ if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
+ {
+ mutex_exit(&pState->Mtx);
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Cannot reset default control pipe\n"));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ mutex_exit(&pState->Mtx);
+ usb_pipe_reset(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ NULL, /* Completion callback */
+ NULL); /* Callback's parameter */
+
+ /*
+ * Allow pending async requests to complete.
+ */
+ /** @todo this is most likely not required. */
+ rc = usb_pipe_drain_reqs(pState->pDip, pEp->pPipe,
+ USB_FLAGS_SLEEP, /* Synchronous */
+ 5, /* Timeout (seconds) */
+ NULL, /* Completion callback */
+ NULL); /* Callback's parameter */
+
+ mutex_enter(&pState->Mtx);
+
+ Log((DEVICE_NAME ": vboxUsbSolarisAbortPipe: usb_pipe_drain_reqs returns %d\n", rc));
+ rc = vboxUsbSolarisToVBoxRC(rc);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pipe not open. bEndpoint=%#x\n", bEndpoint));
+ rc = VERR_PIPE_IO_ERROR;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Invalid pipe bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
+ rc = VERR_INVALID_HANDLE;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Initializes an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEpData The Endpoint data (NULL implies the default
+ * endpoint).
+ *
+ * @returns VBox error code.
+ */
+LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEp: pState=%p pEpData=%p", pState, pEpData));
+
+ /*
+ * Is this the default endpoint?
+ */
+ usb_ep_descr_t *pEpDesc = NULL;
+ vboxusb_ep_t *pEp = NULL;
+ int iEpIndex;
+ if (!pEpData)
+ {
+ iEpIndex = 0;
+ pEpDesc = &g_VBoxUSBSolarisDefaultEpDesc;
+ }
+ else
+ {
+ iEpIndex = VBOXUSB_GET_EP_INDEX(pEpData->ep_descr.bEndpointAddress);
+ pEpDesc = (usb_ep_descr_t *)((uint8_t *)pEpData + g_offUsbEpDataDescr);
+ }
+
+ Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
+ pEp = &pState->aEps[iEpIndex];
+
+ /*
+ * Initialize the endpoint.
+ */
+ pEp->EpDesc = *pEpDesc;
+ if (!pEp->fInitialized)
+ {
+ pEp->pPipe = NULL;
+ bzero(&pEp->PipePolicy, sizeof(pEp->PipePolicy));
+ pEp->PipePolicy.pp_max_async_reqs = VBOXUSB_MAX_PIPE_ASYNC_REQS;
+ pEp->fIsocPolling = false;
+ list_create(&pEp->hIsocInUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
+ pEp->cIsocInUrbs = 0;
+ list_create(&pEp->hIsocInLandedReqs, sizeof(vboxusb_isoc_req_t), offsetof(vboxusb_isoc_req_t, hListLink));
+ pEp->cbIsocInLandedReqs = 0;
+ pEp->cbMaxIsocData = 0;
+ pEp->fInitialized = true;
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisInitEp: Success, %s[%2d] %s %s bEndpoint=%#x\n", !pEpData ? "Default " : "Endpoint",
+ iEpIndex, vboxUsbSolarisEpType(pEp), vboxUsbSolarisEpDir(pEp), pEp->EpDesc.bEndpointAddress));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes Endpoints for the current configuration, all interfaces and
+ * alternate setting 0 for each interface.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState)
+{
+ uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
+ if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Invalid current config index %u\n", uCfgIndex));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
+ uchar_t bConfig = pConfig->cfg_descr.bConfigurationValue;
+
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: pState=%p bConfig=%u uCfgIndex=%u\n", pState, bConfig, uCfgIndex));
+
+ const uint_t cIfs = pConfig->cfg_n_if;
+ for (uchar_t uIf = 0; uIf < cIfs; uIf++)
+ {
+ usb_if_data_t *pIf = &pConfig->cfg_if[uIf];
+ const uint_t cAlts = pIf->if_n_alt;
+ for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
+ {
+ usb_alt_if_data_t *pAlt = &pIf->if_alt[uAlt];
+ if (pAlt->altif_descr.bAlternateSetting == 0) /* Refer USB 2.0 spec 9.6.5 "Interface" */
+ {
+ const uint_t cEps = pAlt->altif_n_ep;
+ for (uchar_t uEp = 0; uEp < cEps; uEp++)
+ {
+ uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
+ usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
+ int rc = vboxUsbSolarisInitEp(pState, pEpData);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Failed to init endpoint! "
+ "bConfig=%u bIf=%#x bAlt=%#x\n", bConfig, pAlt->altif_descr.bInterfaceNumber,
+ pAlt->altif_descr.bAlternateSetting));
+ return rc;
+ }
+ }
+ break; /* move on to next interface. */
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes Endpoints for the given Interface & Alternate setting.
+ *
+ * @param pState The USB device instance.
+ * @param bIf The Interface.
+ * @param bAlt The Alterate.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: pState=%p bIf=%d uAlt=%d\n", pState, bIf, bAlt));
+
+ /* Doesn't hurt to be paranoid */
+ uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
+ if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Invalid current config index %d\n", uCfgIndex));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
+ for (uchar_t uIf = 0; uIf < pConfig->cfg_n_if; uIf++)
+ {
+ usb_if_data_t *pInterface = &pConfig->cfg_if[uIf];
+ const uint_t cAlts = pInterface->if_n_alt;
+ for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
+ {
+ usb_alt_if_data_t *pAlt = &pInterface->if_alt[uAlt];
+ if ( pAlt->altif_descr.bInterfaceNumber == bIf
+ && pAlt->altif_descr.bAlternateSetting == bAlt)
+ {
+ const uint_t cEps = pAlt->altif_n_ep;
+ for (uchar_t uEp = 0; uEp < cEps; uEp++)
+ {
+ uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
+ usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
+ int rc = vboxUsbSolarisInitEp(pState, pEpData);
+ if (RT_FAILURE(rc))
+ {
+ uint8_t bCfgValue = pConfig->cfg_descr.bConfigurationValue;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Failed to init endpoint! "
+ "bCfgValue=%u bIf=%#x bAlt=%#x\n", bCfgValue, bIf, bAlt));
+ return rc;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Destroys all Endpoints.
+ *
+ * @param pState The USB device instance.
+ *
+ * @remarks Requires the state mutex to be held.
+ * Call only from Detach() or similar as callbacks
+ */
+LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyAllEps: pState=%p\n", pState));
+
+ Assert(mutex_owned(&pState->Mtx));
+ for (unsigned i = 0; i < VBOXUSB_MAX_ENDPOINTS; i++)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[i];
+ if (pEp->fInitialized)
+ vboxUsbSolarisDestroyEp(pState, pEp);
+ }
+}
+
+
+/**
+ * Destroys an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ *
+ * @remarks Requires the state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyEp: pState=%p pEp=%p\n", pState, pEp));
+
+ Assert(pEp->fInitialized);
+ Assert(mutex_owned(&pState->Mtx));
+ vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ while (pUrb)
+ {
+ if (pUrb->pMsg)
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ }
+ pEp->cIsocInUrbs = 0;
+ list_destroy(&pEp->hIsocInUrbs);
+
+ vboxusb_isoc_req_t *pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
+ while (pIsocReq)
+ {
+ kmem_free(pIsocReq, sizeof(vboxusb_isoc_req_t));
+ pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
+ }
+ pEp->cbIsocInLandedReqs = 0;
+ list_destroy(&pEp->hIsocInLandedReqs);
+
+ pEp->fInitialized = false;
+}
+
+
+/**
+ * Closes all non-default pipes and drains the default pipe.
+ *
+ * @param pState The USB device instance.
+ * @param fDefault Whether to close the default control pipe.
+ *
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fDefault)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: pState=%p\n", pState));
+
+ for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[i];
+ if ( pEp
+ && pEp->pPipe)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closing[%d]\n", i));
+ vboxUsbSolarisClosePipe(pState, pEp);
+ }
+ }
+
+ if (fDefault)
+ {
+ vboxusb_ep_t *pEp = &pState->aEps[0];
+ if ( pEp
+ && pEp->pPipe)
+ {
+ vboxUsbSolarisClosePipe(pState, pEp);
+ Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closed default pipe\n"));
+ }
+ }
+}
+
+
+/**
+ * Opens the pipe associated with an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ * @remarks Requires the device state mutex to be held.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ Assert(mutex_owned(&pState->Mtx));
+
+ /*
+ * Make sure the Endpoint isn't open already.
+ */
+ if (pEp->pPipe)
+ return VINF_SUCCESS;
+
+ /*
+ * Default Endpoint; already opened just copy the pipe handle.
+ */
+ if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
+ {
+ pEp->pPipe = pState->pDevDesc->dev_default_ph;
+ Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Default pipe opened\n"));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Open the non-default pipe for the Endpoint.
+ */
+ mutex_exit(&pState->Mtx);
+ int rc = usb_pipe_open(pState->pDip, &pEp->EpDesc, &pEp->PipePolicy, USB_FLAGS_NOSLEEP, &pEp->pPipe);
+ mutex_enter(&pState->Mtx);
+ if (rc == USB_SUCCESS)
+ {
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Opened pipe, pState=%p pEp=%p\n", pState, pEp));
+ usb_pipe_set_private(pEp->pPipe, (usb_opaque_t)pEp);
+
+ /*
+ * Determine input buffer size for Isoc. IN transfers.
+ */
+ if ( VBOXUSB_XFER_TYPE(pEp) == VUSBXFERTYPE_ISOC
+ && VBOXUSB_XFER_DIR(pEp) == VUSB_DIR_TO_HOST)
+ {
+ /*
+ * wMaxPacketSize bits 10..0 specifies maximum packet size which can hold 1024 bytes.
+ * If bits 12..11 is non-zero, cbMax will be more than 1024 and thus the Endpoint is a
+ * high-bandwidth Endpoint.
+ */
+ uint16_t cbMax = VBOXUSB_PKT_SIZE(pEp->EpDesc.wMaxPacketSize);
+ if (cbMax <= 1024)
+ {
+ /* Buffer 1 second for highspeed and 8 seconds for fullspeed Endpoints. */
+ pEp->cbMaxIsocData = 1000 * cbMax * 8;
+ }
+ else
+ {
+ /* Buffer about 400 milliseconds of data for highspeed high-bandwidth endpoints. */
+ pEp->cbMaxIsocData = 400 * cbMax * 8;
+ }
+ Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: bEndpoint=%#x cbMaxIsocData=%u\n", pEp->EpDesc.bEndpointAddress,
+ pEp->cbMaxIsocData));
+ }
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Failed! rc=%d pState=%p pEp=%p\n", rc, pState, pEp));
+ rc = VERR_BAD_PIPE;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Closes the pipe associated with an Endpoint.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint.
+ *
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisClosePipe: pState=%p pEp=%p\n", pState, pEp));
+ AssertPtr(pEp);
+
+ if (pEp->pPipe)
+ {
+ /*
+ * Default pipe: allow completion of pending requests.
+ */
+ if (pEp->pPipe == pState->pDevDesc->dev_default_ph)
+ {
+ mutex_exit(&pState->Mtx);
+ usb_pipe_drain_reqs(pState->pDip, pEp->pPipe, 0, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
+ mutex_enter(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Closed default pipe\n"));
+ pState->fDefaultPipeOpen = false;
+ }
+ else
+ {
+ /*
+ * Stop Isoc. IN polling if required.
+ */
+ if (pEp->fIsocPolling)
+ {
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pEp->pPipe, USB_FLAGS_NOSLEEP);
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Non-default pipe: close it.
+ */
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Pipe bmAttributes=%#x bEndpoint=%#x\n", pEp->EpDesc.bmAttributes,
+ pEp->EpDesc.bEndpointAddress));
+ mutex_exit(&pState->Mtx);
+ usb_pipe_close(pState->pDip, pEp->pPipe, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Free the Endpoint data message block and reset pipe handle.
+ */
+ pEp->pPipe = NULL;
+
+ Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Success, bEndpoint=%#x\n", pEp->EpDesc.bEndpointAddress));
+ }
+
+ Assert(pEp->pPipe == NULL);
+}
+
+
+/**
+ * Finds the Configuration index for the passed in Configuration value.
+ *
+ * @param pState The USB device instance.
+ * @param bConfig The Configuration.
+ *
+ * @returns The configuration index if found, otherwise -1.
+ */
+LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bConfig)
+{
+ for (int CfgIndex = 0; CfgIndex < pState->pDevDesc->dev_n_cfg; CfgIndex++)
+ {
+ usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[CfgIndex];
+ if (pConfig->cfg_descr.bConfigurationValue == bConfig)
+ return CfgIndex;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Allocates and initializes an Isoc. In URB from the ring-3 equivalent.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Opaque pointer to the complete request.
+ *
+ * @returns The allocated Isoc. In URB to be used.
+ */
+LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq)
+{
+ /*
+ * Isoc. In URBs are not queued into the Inflight list like every other URBs.
+ * For now we allocate each URB which gets queued into the respective Endpoint during Xfer.
+ */
+ vboxusb_urb_t *pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
+ if (RT_LIKELY(pUrb))
+ {
+ pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
+ pUrb->pState = pState;
+
+ if (RT_LIKELY(pUrbReq))
+ {
+ pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
+ pUrb->bEndpoint = pUrbReq->bEndpoint;
+ pUrb->enmType = pUrbReq->enmType;
+ pUrb->enmDir = pUrbReq->enmDir;
+ pUrb->enmStatus = pUrbReq->enmStatus;
+ pUrb->cbDataR3 = pUrbReq->cbData;
+ pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
+ pUrb->cIsocPkts = pUrbReq->cIsocPkts;
+
+ for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
+ pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
+
+ pUrb->pMsg = NULL;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisGetIsocInUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
+ return pUrb;
+}
+
+
+/**
+ * Queues a URB reusing previously allocated URBs as required.
+ *
+ * @param pState The USB device instance.
+ * @param pUrbReq Opaque pointer to the complete request.
+ * @param pMsg Pointer to the allocated request data.
+ *
+ * @returns The allocated URB to be used, or NULL upon failure.
+ */
+LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg)
+{
+ Assert(pUrbReq);
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisQueueUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq));
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * Grab a URB from the free list.
+ */
+ vboxusb_urb_t *pUrb = list_remove_head(&pState->hFreeUrbs);
+ if (pUrb)
+ {
+ Assert(pUrb->enmState == VBOXUSB_URB_STATE_FREE);
+ Assert(!pUrb->pMsg);
+ Assert(pState->cFreeUrbs > 0);
+ --pState->cFreeUrbs;
+ }
+ else
+ {
+ /*
+ * We can't discard "old" URBs. For instance, INTR IN URBs that don't complete as
+ * they don't have a timeout can essentially take arbitrarily long to complete depending
+ * on the device and it's not safe to discard them in case they -do- complete. However,
+ * we also have to reasonably assume a device doesn't have too many pending URBs always.
+ *
+ * Thus we just use a large queue and simply refuse further transfers. This is not
+ * a situation which normally ever happens as usually there are at most than 4 or 5 URBs
+ * in-flight until we reap them.
+ */
+ uint32_t const cTotalUrbs = pState->cInflightUrbs + pState->cFreeUrbs + pState->cLandedUrbs;
+ if (cTotalUrbs >= VBOXUSB_URB_QUEUE_SIZE)
+ {
+ mutex_exit(&pState->Mtx);
+ LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisQueueUrb: Max queue size %u reached, refusing further transfers",
+ cTotalUrbs));
+ return NULL;
+ }
+
+ /*
+ * Allocate a new URB as we have no free URBs.
+ */
+ mutex_exit(&pState->Mtx);
+ pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
+ if (RT_UNLIKELY(!pUrb))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisQueueUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
+ return NULL;
+ }
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Add the URB to the inflight list.
+ */
+ list_insert_tail(&pState->hInflightUrbs, pUrb);
+ ++pState->cInflightUrbs;
+
+ Assert(!pUrb->pMsg);
+ pUrb->pMsg = pMsg;
+ pUrb->pState = pState;
+ pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
+ pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
+ pUrb->bEndpoint = pUrbReq->bEndpoint;
+ pUrb->enmType = pUrbReq->enmType;
+ pUrb->enmDir = pUrbReq->enmDir;
+ pUrb->enmStatus = pUrbReq->enmStatus;
+ pUrb->fShortOk = pUrbReq->fShortOk;
+ pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
+ pUrb->cbDataR3 = pUrbReq->cbData;
+ pUrb->cIsocPkts = pUrbReq->cIsocPkts;
+ if (pUrbReq->enmType == VUSBXFERTYPE_ISOC)
+ {
+ for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
+ pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
+ }
+
+ mutex_exit(&pState->Mtx);
+ return pUrb;
+}
+
+
+/**
+ * Dequeues a completed URB into the landed list and informs user-land.
+ *
+ * @param pUrb The URB to move.
+ * @param URBStatus The Solaris URB completion code.
+ *
+ * @remarks All pipes could be closed at this point (e.g. Device disconnected during inflight URBs)
+ */
+LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeQueue: pUrb=%p\n", pUrb));
+ AssertPtrReturnVoid(pUrb);
+
+ pUrb->enmStatus = vboxUsbSolarisGetUrbStatus(URBStatus);
+ if (pUrb->enmStatus != VUSBSTATUS_OK)
+ Log((DEVICE_NAME ": vboxUsbSolarisDeQueueUrb: URB failed! URBStatus=%d bEndpoint=%#x\n", URBStatus, pUrb->bEndpoint));
+
+ vboxusb_state_t *pState = pUrb->pState;
+ if (RT_LIKELY(pState))
+ {
+ mutex_enter(&pState->Mtx);
+ pUrb->enmState = VBOXUSB_URB_STATE_LANDED;
+
+ /*
+ * Remove it from the inflight list & move it to the landed list.
+ */
+ list_remove(&pState->hInflightUrbs, pUrb);
+ Assert(pState->cInflightUrbs > 0);
+ --pState->cInflightUrbs;
+
+ list_insert_tail(&pState->hLandedUrbs, pUrb);
+ ++pState->cLandedUrbs;
+
+ vboxUsbSolarisNotifyComplete(pUrb->pState);
+ mutex_exit(&pState->Mtx);
+ return;
+ }
+
+ /* Well, let's at least not leak memory... */
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ pUrb->enmStatus = VUSBSTATUS_INVALID;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeQueue: State Gone\n"));
+}
+
+
+/**
+ * Concatenates a chain message block into a single message block if possible.
+ *
+ * @param pUrb The URB to move.
+ */
+LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb)
+{
+ /*
+ * Concatenate the whole message rather than doing a chained copy while reaping.
+ */
+ if ( pUrb->pMsg
+ && pUrb->pMsg->b_cont)
+ {
+ mblk_t *pFullMsg = msgpullup(pUrb->pMsg, -1 /* all data */);
+ if (RT_LIKELY(pFullMsg))
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = pFullMsg;
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisConcatMsg: Failed. Expect glitches due to truncated data!\n"));
+ }
+}
+
+
+/**
+ * Wakes up a user process signalling URB completion.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState)
+{
+ if (pState->fPollPending)
+ {
+ pollhead_t *pPollHead = &pState->PollHead;
+ pState->fPollPending = false;
+ mutex_exit(&pState->Mtx);
+ pollwakeup(pPollHead, POLLIN);
+ mutex_enter(&pState->Mtx);
+ }
+}
+
+
+/**
+ * Wakes up a user process signalling a device unplug events.
+ *
+ * @param pState The USB device instance.
+ * @remarks Requires the device state mutex to be held.
+ */
+LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState)
+{
+ if (pState->fPollPending)
+ {
+ pollhead_t *pPollHead = &pState->PollHead;
+ pState->fPollPending = false;
+ mutex_exit(&pState->Mtx);
+ pollwakeup(pPollHead, POLLHUP);
+ mutex_enter(&pState->Mtx);
+ }
+}
+
+
+/**
+ * Performs a Control Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ AssertPtrReturn(pUrb->pMsg, VERR_INVALID_PARAMETER);
+ const size_t cbData = pUrb->cbDataR3 > VBOXUSB_CTRL_XFER_SIZE ? pUrb->cbDataR3 - VBOXUSB_CTRL_XFER_SIZE : 0;
+
+ /*
+ * Allocate a wrapper request.
+ */
+ usb_ctrl_req_t *pReq = usb_alloc_ctrl_req(pState->pDip, cbData, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ uchar_t *pSetupData = pUrb->pMsg->b_rptr;
+
+ /*
+ * Initialize the Ctrl Xfer Header.
+ */
+ pReq->ctrl_bmRequestType = pSetupData[0];
+ pReq->ctrl_bRequest = pSetupData[1];
+ pReq->ctrl_wValue = (pSetupData[3] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[2];
+ pReq->ctrl_wIndex = (pSetupData[5] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[4];
+ pReq->ctrl_wLength = (pSetupData[7] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[6];
+
+ if ( pUrb->enmDir == VUSBDIRECTION_OUT
+ && cbData)
+ {
+ bcopy(pSetupData + VBOXUSB_CTRL_XFER_SIZE, pReq->ctrl_data->b_wptr, cbData);
+ pReq->ctrl_data->b_wptr += cbData;
+ }
+
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+
+ /*
+ * Initialize callbacks and timeouts.
+ */
+ pReq->ctrl_cb = vboxUsbSolarisCtrlXferCompleted;
+ pReq->ctrl_exc_cb = vboxUsbSolarisCtrlXferCompleted;
+ pReq->ctrl_timeout = VBOXUSB_CTRL_XFER_TIMEOUT;
+ pReq->ctrl_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_SHORT_XFER_OK;
+ pReq->ctrl_client_private = (usb_opaque_t)pUrb;
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_ctrl_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Request failed! bEndpoint=%#x rc=%d\n", pUrb->bEndpoint, rc));
+
+ usb_free_ctrl_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Failed to alloc request for %u bytes\n", cbData));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Control Xfers.
+ *
+ * @param pPipe The Ctrl pipe handle.
+ * @param pReq The Ctrl request.
+ */
+LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+ Assert(pReq);
+ Assert(!(pReq->ctrl_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->ctrl_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ /*
+ * Funky stuff: We need to reconstruct the header for control transfers.
+ * Let us chain along the data and concatenate the entire message.
+ */
+ mblk_t *pSetupMsg = allocb(sizeof(VUSBSETUP), BPRI_MED);
+ if (RT_LIKELY(pSetupMsg))
+ {
+ VUSBSETUP SetupData;
+ SetupData.bmRequestType = pReq->ctrl_bmRequestType;
+ SetupData.bRequest = pReq->ctrl_bRequest;
+ SetupData.wValue = pReq->ctrl_wValue;
+ SetupData.wIndex = pReq->ctrl_wIndex;
+ SetupData.wLength = pReq->ctrl_wLength;
+
+ bcopy(&SetupData, pSetupMsg->b_wptr, sizeof(VUSBSETUP));
+ pSetupMsg->b_wptr += sizeof(VUSBSETUP);
+
+ /*
+ * Should be safe to update pMsg here without the state mutex as typically nobody else
+ * touches this URB in the inflight list.
+ *
+ * The reason we choose to use vboxUsbSolarisConcatMsg here is that we don't assume the
+ * message returned by Solaris is one contiguous chunk in 'pMsg->b_rptr'.
+ */
+ Assert(!pUrb->pMsg);
+ pUrb->pMsg = pSetupMsg;
+ pUrb->pMsg->b_cont = pReq->ctrl_data;
+ pReq->ctrl_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Failed to alloc %u bytes for header\n", sizeof(VUSBSETUP)));
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->ctrl_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Extreme error! missing private data\n"));
+
+ usb_free_ctrl_req(pReq);
+}
+
+
+/**
+ * Performs a Bulk Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ /*
+ * Allocate a wrapper request.
+ */
+ size_t const cbAlloc = pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cbDataR3 : 0;
+ usb_bulk_req_t *pReq = usb_alloc_bulk_req(pState->pDip, cbAlloc, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Bulk Xfer, callbacks and timeouts.
+ */
+ usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->bulk_data = pUrb->pMsg;
+ pUrb->pMsg = NULL;
+ }
+ else if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pUrb->fShortOk)
+ {
+ fAttributes |= USB_ATTRS_SHORT_XFER_OK;
+ }
+
+ Assert(!pUrb->pMsg);
+ pReq->bulk_len = pUrb->cbDataR3;
+ pReq->bulk_cb = vboxUsbSolarisBulkXferCompleted;
+ pReq->bulk_exc_cb = vboxUsbSolarisBulkXferCompleted;
+ pReq->bulk_timeout = 0;
+ pReq->bulk_attributes = fAttributes;
+ pReq->bulk_client_private = (usb_opaque_t)pUrb;
+
+ /* Don't obtain state lock here, we're just reading unchanging data... */
+ if (RT_UNLIKELY(pUrb->cbDataR3 > pState->cbMaxBulkXfer))
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Requesting %d bytes when only %d bytes supported by device\n",
+ pUrb->cbDataR3, pState->cbMaxBulkXfer));
+ }
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_bulk_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Request failed! Ep=%#x rc=%d cbData=%u\n", pUrb->bEndpoint, rc,
+ pReq->bulk_len));
+
+ usb_free_bulk_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Failed to alloc bulk request\n"));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Bulk Xfers.
+ *
+ * @param pPipe The Bulk pipe handle.
+ * @param pReq The Bulk request.
+ */
+LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ Assert(pReq);
+ Assert(!(pReq->bulk_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_LIKELY(pEp))
+ {
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->bulk_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ Assert(!pUrb->pMsg);
+ if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pReq->bulk_data)
+ {
+ pUrb->pMsg = pReq->bulk_data;
+ pReq->bulk_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+
+ /*
+ * Update the URB and move to tail for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->bulk_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Extreme error! private request data missing!\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Pipe Gone!\n"));
+
+ usb_free_bulk_req(pReq);
+}
+
+
+/**
+ * Performs an Interrupt Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
+ pUrb->enmDir, pUrb->cbDataR3));
+
+ usb_intr_req_t *pReq = usb_alloc_intr_req(pState->pDip, 0 /* length */, USB_FLAGS_SLEEP);
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Intr Xfer, callbacks & timeouts.
+ */
+ usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->intr_data = pUrb->pMsg;
+ pUrb->pMsg = NULL;
+ }
+ else
+ {
+ Assert(pUrb->enmDir == VUSBDIRECTION_IN);
+ fAttributes |= USB_ATTRS_ONE_XFER;
+ if (pUrb->fShortOk)
+ fAttributes |= USB_ATTRS_SHORT_XFER_OK;
+ }
+
+ Assert(!pUrb->pMsg);
+ pReq->intr_len = pUrb->cbDataR3; /* Not pEp->EpDesc.wMaxPacketSize */
+ pReq->intr_cb = vboxUsbSolarisIntrXferCompleted;
+ pReq->intr_exc_cb = vboxUsbSolarisIntrXferCompleted;
+ pReq->intr_timeout = 0;
+ pReq->intr_attributes = fAttributes;
+ pReq->intr_client_private = (usb_opaque_t)pUrb;
+
+ /*
+ * Submit the request.
+ */
+ int rc = usb_pipe_intr_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: usb_pipe_intr_xfer failed! rc=%d bEndpoint=%#x\n", rc, pUrb->bEndpoint));
+
+ usb_free_intr_req(pReq);
+ return VERR_PIPE_IO_ERROR;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: Failed to alloc intr request\n"));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Completion/Exception callback for Intr Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ */
+LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ Assert(pReq);
+ Assert(!(pReq->intr_cb_flags & USB_CB_INTR_CONTEXT));
+
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->intr_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ if ( pUrb->enmDir == VUSBDIRECTION_IN
+ && pReq->intr_data)
+ {
+ pUrb->pMsg = pReq->intr_data;
+ pReq->intr_data = NULL;
+ vboxUsbSolarisConcatMsg(pUrb);
+ }
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->intr_completion_reason);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: Extreme error! private request data missing\n"));
+
+ usb_free_intr_req(pReq);
+}
+
+
+/**
+ * Performs an Isochronous Xfer.
+ *
+ * @param pState The USB device instance.
+ * @param pEp The Endpoint for the Xfer.
+ * @param pUrb The VBox USB URB.
+ *
+ * @returns VBox status code.
+ * @remarks Any errors, the caller should free pUrb->pMsg.
+ */
+LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocXfer: pState=%p pEp=%p pUrb=%p\n", pState, pEp, pUrb)); */
+
+ /*
+ * For Isoc. IN transfers we perform one request and USBA polls the device continuously
+ * and supplies our Xfer callback with input data. We cannot perform one-shot Isoc. In transfers.
+ */
+ size_t cbData = (pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cIsocPkts * pUrb->aIsocPkts[0].cbPkt : 0);
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Isoc. IN - Queueing\n"));
+
+ mutex_enter(&pState->Mtx);
+ if (pEp->fIsocPolling)
+ {
+ /*
+ * Queue a maximum of cbMaxIsocData bytes, else fail.
+ */
+ if (pEp->cbIsocInLandedReqs + cbData > pEp->cbMaxIsocData)
+ {
+ mutex_exit(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Max Isoc. data %d bytes queued\n", pEp->cbMaxIsocData));
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ list_insert_tail(&pEp->hIsocInUrbs, pUrb);
+ ++pEp->cIsocInUrbs;
+
+ mutex_exit(&pState->Mtx);
+ return VINF_SUCCESS;
+ }
+ mutex_exit(&pState->Mtx);
+ }
+
+ int rc = VINF_SUCCESS;
+ usb_isoc_req_t *pReq = usb_alloc_isoc_req(pState->pDip, pUrb->cIsocPkts, cbData, USB_FLAGS_NOSLEEP);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: enmDir=%#x cIsocPkts=%d aIsocPkts[0]=%d cbDataR3=%d\n", pUrb->enmDir,
+ pUrb->cIsocPkts, pUrb->aIsocPkts[0].cbPkt, pUrb->cbDataR3));
+ if (RT_LIKELY(pReq))
+ {
+ /*
+ * Initialize Isoc Xfer, callbacks & timeouts.
+ */
+ for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
+ pReq->isoc_pkt_descr[i].isoc_pkt_length = pUrb->aIsocPkts[i].cbPkt;
+
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ pReq->isoc_data = pUrb->pMsg;
+ pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP;
+ pReq->isoc_cb = vboxUsbSolarisIsocOutXferCompleted;
+ pReq->isoc_exc_cb = vboxUsbSolarisIsocOutXferCompleted;
+ pReq->isoc_client_private = (usb_opaque_t)pUrb;
+ }
+ else
+ {
+ pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_SHORT_XFER_OK;
+ pReq->isoc_cb = vboxUsbSolarisIsocInXferCompleted;
+ pReq->isoc_exc_cb = vboxUsbSolarisIsocInXferError;
+ pReq->isoc_client_private = (usb_opaque_t)pState;
+ }
+ pReq->isoc_pkts_count = pUrb->cIsocPkts;
+ pReq->isoc_pkts_length = 0; /* auto compute */
+
+ /*
+ * Submit the request.
+ */
+ rc = usb_pipe_isoc_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
+ if (RT_LIKELY(rc == USB_SUCCESS))
+ {
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ /*
+ * Add the first Isoc. IN URB to the queue as well.
+ */
+ mutex_enter(&pState->Mtx);
+ list_insert_tail(&pEp->hIsocInUrbs, pUrb);
+ ++pEp->cIsocInUrbs;
+ pEp->fIsocPolling = true;
+ mutex_exit(&pState->Mtx);
+ }
+
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: usb_pipe_isoc_xfer failed! rc=%d\n", rc));
+ rc = VERR_PIPE_IO_ERROR;
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ mutex_enter(&pState->Mtx);
+ vboxusb_urb_t *pIsocFailedUrb = list_remove_tail(&pEp->hIsocInUrbs);
+ if (pIsocFailedUrb)
+ {
+ RTMemFree(pIsocFailedUrb);
+ --pEp->cIsocInUrbs;
+ }
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+
+ if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = NULL;
+ }
+
+ usb_free_isoc_req(pReq);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Failed to alloc isoc req for %d packets\n", pUrb->cIsocPkts));
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Completion/Exception callback for Isoc IN Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ *
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq)); */
+
+ vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
+ if (RT_LIKELY(pState))
+ {
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if ( pEp
+ && pEp->pPipe)
+ {
+#if 0
+ /*
+ * Stop polling if all packets failed.
+ */
+ if (pReq->isoc_error_count == pReq->isoc_pkts_count)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Stopping polling! Too many errors\n"));
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
+ mutex_enter(&pState->Mtx);
+ pEp->fIsocPolling = false;
+ }
+#endif
+
+ /** @todo Query and verify this at runtime. */
+ AssertCompile(sizeof(VUSBISOC_PKT_DESC) == sizeof(usb_isoc_pkt_descr_t));
+ if (RT_LIKELY(pReq->isoc_data))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: cIsocInUrbs=%d cbIsocInLandedReqs=%d\n", pEp->cIsocInUrbs,
+ pEp->cbIsocInLandedReqs));
+
+ mutex_enter(&pState->Mtx);
+
+ /*
+ * If there are waiting URBs, satisfy the oldest one.
+ */
+ if ( pEp->cIsocInUrbs > 0
+ && pEp->cbIsocInLandedReqs == 0)
+ {
+ vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
+ if (RT_LIKELY(pUrb))
+ {
+ --pEp->cIsocInUrbs;
+ mutex_exit(&pState->Mtx);
+
+ for (unsigned i = 0; i < pReq->isoc_pkts_count; i++)
+ {
+ pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
+ }
+
+ pUrb->pMsg = pReq->isoc_data;
+ pReq->isoc_data = NULL;
+
+ /*
+ * Move to landed list
+ */
+ mutex_enter(&pState->Mtx);
+ list_insert_tail(&pState->hLandedUrbs, pUrb);
+ ++pState->cLandedUrbs;
+ vboxUsbSolarisNotifyComplete(pState);
+ }
+ else
+ {
+ /* Huh!? cIsocInUrbs is wrong then! Should never happen unless we decide to decrement cIsocInUrbs in
+ Reap time */
+ pEp->cIsocInUrbs = 0;
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Extreme error! Isoc. counter borked!\n"));
+ }
+
+ mutex_exit(&pState->Mtx);
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Data missing\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Pipe Gone\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: State Gone\n"));
+
+ usb_free_isoc_req(pReq);
+}
+
+
+/**
+ * Exception callback for Isoc IN Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
+ if (RT_UNLIKELY(!pState))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: State Gone\n"));
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ mutex_enter(&pState->Mtx);
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_UNLIKELY(!pEp))
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Pipe Gone\n"));
+ mutex_exit(&pState->Mtx);
+ usb_free_isoc_req(pReq);
+ return;
+ }
+
+ switch(pReq->isoc_completion_reason)
+ {
+ case USB_CR_NO_RESOURCES:
+ {
+ /*
+ * Resubmit the request in case the original request did not complete due to
+ * immediately unavailable requests
+ */
+ mutex_exit(&pState->Mtx);
+ usb_pipe_isoc_xfer(pPipe, pReq, USB_FLAGS_NOSLEEP);
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Resubmitted Isoc. IN request due to unavailable resources\n"));
+ return;
+ }
+
+ case USB_CR_PIPE_CLOSING:
+ case USB_CR_STOPPED_POLLING:
+ case USB_CR_PIPE_RESET:
+ {
+ pEp->fIsocPolling = false;
+ usb_free_isoc_req(pReq);
+ break;
+ }
+
+ default:
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Stopping Isoc. IN polling due to rc=%d\n",
+ pReq->isoc_completion_reason));
+ pEp->fIsocPolling = false;
+ mutex_exit(&pState->Mtx);
+ usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
+ usb_free_isoc_req(pReq);
+ mutex_enter(&pState->Mtx);
+ break;
+ }
+ }
+
+ /*
+ * Dequeue i.e. delete the last queued Isoc In. URB. as failed.
+ */
+ vboxusb_urb_t *pUrb = list_remove_tail(&pEp->hIsocInUrbs);
+ if (pUrb)
+ {
+ --pEp->cIsocInUrbs;
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Deleting last queued URB as it failed\n"));
+ freemsg(pUrb->pMsg);
+ RTMemFree(pUrb);
+ vboxUsbSolarisNotifyComplete(pState);
+ }
+
+ mutex_exit(&pState->Mtx);
+}
+
+
+/**
+ * Completion/Exception callback for Isoc OUT Xfers.
+ *
+ * @param pPipe The Intr pipe handle.
+ * @param pReq The Intr request.
+ * @remarks Completion callback executes in interrupt context!
+ */
+LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
+
+ vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
+ if (RT_LIKELY(pEp))
+ {
+ vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->isoc_client_private;
+ if (RT_LIKELY(pUrb))
+ {
+ size_t cbActPkt = 0;
+ for (int i = 0; i < pReq->isoc_pkts_count; i++)
+ {
+ cbActPkt += pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
+ pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
+ }
+
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: cIsocPkts=%d cbData=%d cbActPkt=%d\n", pUrb->cIsocPkts,
+ pUrb->cbDataR3, cbActPkt));
+
+ if (pReq->isoc_completion_reason == USB_CR_OK)
+ {
+ if (RT_UNLIKELY(pUrb->pMsg != pReq->isoc_data)) /* Paranoia */
+ {
+ freemsg(pUrb->pMsg);
+ pUrb->pMsg = pReq->isoc_data;
+ }
+ }
+ pReq->isoc_data = NULL;
+
+ pUrb->cIsocPkts = pReq->isoc_pkts_count;
+ pUrb->cbDataR3 = cbActPkt;
+
+ /*
+ * Update the URB and move to landed list for reaping.
+ */
+ vboxUsbSolarisDeQueueUrb(pUrb, pReq->isoc_completion_reason);
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Missing private data!?! Dropping OUT pUrb\n"));
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Pipe Gone\n"));
+
+ usb_free_isoc_req(pReq);
+}
+
+
+/**
+ * Callback when the device gets disconnected.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @returns Solaris USB error code.
+ */
+LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: pDip=%p\n", pDip));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+
+ if (RT_LIKELY(pState))
+ {
+ /*
+ * Serialize access: exclusive access to the state.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ pState->DevState = USB_DEV_DISCONNECTED;
+
+ vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
+ vboxUsbSolarisNotifyUnplug(pState);
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ return USB_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: Failed to get device state!\n"));
+ return USB_FAILURE;
+}
+
+
+/**
+ * Callback when the device gets reconnected.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @returns Solaris USB error code.
+ */
+LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: pDip=%p\n", pDip));
+
+ int instance = ddi_get_instance(pDip);
+ vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
+
+ if (RT_LIKELY(pState))
+ {
+ vboxUsbSolarisDeviceRestore(pState);
+ return USB_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: Failed to get device state!\n"));
+ return USB_FAILURE;
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ /*
+ * Raise device power.
+ */
+ vboxUsbSolarisPowerBusy(pState);
+ int rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
+
+ /*
+ * Check if the same device is resumed/reconnected.
+ */
+ rc = usb_check_same_device(pState->pDip,
+ NULL, /* log handle */
+ USB_LOG_L2, /* log level */
+ -1, /* log mask */
+ USB_CHK_ALL, /* check level */
+ NULL); /* device string */
+
+ if (rc != USB_SUCCESS)
+ {
+ mutex_enter(&pState->Mtx);
+ pState->DevState = USB_DEV_DISCONNECTED;
+ mutex_exit(&pState->Mtx);
+
+ /* Do we need to inform userland here? */
+ vboxUsbSolarisPowerIdle(pState);
+ Log((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: Not the same device\n"));
+ return;
+ }
+
+ /*
+ * Serialize access to not race with other PM functions.
+ */
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+
+ mutex_enter(&pState->Mtx);
+ if (pState->DevState == USB_DEV_DISCONNECTED)
+ pState->DevState = USB_DEV_ONLINE;
+ else if (pState->DevState == USB_DEV_SUSPENDED)
+ pState->DevState = USB_DEV_ONLINE;
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ vboxUsbSolarisPowerIdle(pState);
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: pState=%p\n", pState));
+
+ int rc = VERR_VUSB_DEVICE_IS_SUSPENDED;
+ mutex_enter(&pState->Mtx);
+
+ switch (pState->DevState)
+ {
+ case USB_DEV_SUSPENDED:
+ {
+ LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Invalid device state %d\n", pState->DevState));
+ break;
+ }
+
+ case USB_DEV_ONLINE:
+ case USB_DEV_DISCONNECTED:
+ case USB_DEV_PWRED_DOWN:
+ {
+ int PreviousState = pState->DevState;
+ pState->DevState = USB_DEV_DISCONNECTED;
+
+ /** @todo this doesn't make sense when for e.g. an INTR IN URB with infinite
+ * timeout is pending on the device. Fix suspend logic later. */
+ /*
+ * Drain pending URBs.
+ */
+ for (int i = 0; i < VBOXUSB_DRAIN_TIME; i++)
+ {
+ if (pState->cInflightUrbs < 1)
+ break;
+
+ mutex_exit(&pState->Mtx);
+ delay(drv_usectohz(100000));
+ mutex_enter(&pState->Mtx);
+ }
+
+ /*
+ * Deny suspend if we still have pending URBs.
+ */
+ if (pState->cInflightUrbs > 0)
+ {
+ pState->DevState = PreviousState;
+ LogRel((DEVICE_NAME ": Cannot suspend %s %s (Ident=%s), %d inflight URBs\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent, pState->cInflightUrbs));
+
+ mutex_exit(&pState->Mtx);
+ return VERR_RESOURCE_BUSY;
+ }
+
+ pState->cInflightUrbs = 0;
+
+ /*
+ * Serialize access to not race with Open/Detach/Close and
+ * Close all pipes including the default pipe.
+ */
+ mutex_exit(&pState->Mtx);
+ usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
+ mutex_enter(&pState->Mtx);
+
+ vboxUsbSolarisCloseAllPipes(pState, true /* default pipe */);
+ vboxUsbSolarisNotifyUnplug(pState);
+
+ mutex_exit(&pState->Mtx);
+ usb_release_access(pState->StateMulti);
+
+ LogRel((DEVICE_NAME ": Suspended %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
+ pState->ClientInfo.szDeviceIdent));
+ return VINF_SUCCESS;
+ }
+ }
+
+ mutex_exit(&pState->Mtx);
+ Log((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Restores device state after a reconnect or resume.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceResume: pState=%p\n", pState));
+ return vboxUsbSolarisDeviceRestore(pState);
+}
+
+
+/**
+ * Flags the PM component as busy so the system will not manage it's power.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerBusy: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ mutex_enter(&pState->Mtx);
+ if (pState->pPower)
+ {
+ pState->pPower->PowerBusy++;
+ mutex_exit(&pState->Mtx);
+
+ int rc = pm_busy_component(pState->pDip, 0 /* component */);
+ if (rc != DDI_SUCCESS)
+ {
+ Log((DEVICE_NAME ": vboxUsbSolarisPowerBusy: Busy component failed! rc=%d\n", rc));
+ mutex_enter(&pState->Mtx);
+ pState->pPower->PowerBusy--;
+ mutex_exit(&pState->Mtx);
+ }
+ }
+ else
+ mutex_exit(&pState->Mtx);
+}
+
+
+/**
+ * Flags the PM component as idle so its power managed by the system.
+ *
+ * @param pState The USB device instance.
+ */
+LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState)
+{
+ LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerIdle: pState=%p\n", pState));
+ AssertPtrReturnVoid(pState);
+
+ if (pState->pPower)
+ {
+ int rc = pm_idle_component(pState->pDip, 0 /* component */);
+ if (rc == DDI_SUCCESS)
+ {
+ mutex_enter(&pState->Mtx);
+ Assert(pState->pPower->PowerBusy > 0);
+ pState->pPower->PowerBusy--;
+ mutex_exit(&pState->Mtx);
+ }
+ else
+ Log((DEVICE_NAME ": vboxUsbSolarisPowerIdle: Idle component failed! rc=%d\n", rc));
+ }
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c
new file mode 100644
index 00000000..27e3868c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c
@@ -0,0 +1,1064 @@
+/* $Id: VBoxUSBMon-solaris.c $ */
+/** @file
+ * VirtualBox USB Monitor Driver, Solaris Hosts.
+ */
+
+/*
+ * Copyright (C) 2008-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_USB_DRV
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/usblib-solaris.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+#include <sys/usb/usba.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/mutex.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/open.h>
+#include <sys/cmn_err.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxusbmon"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox USBMon"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
+static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
+static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
+static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
+static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
+static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxUSBMonSolarisCbOps =
+{
+ VBoxUSBMonSolarisOpen,
+ VBoxUSBMonSolarisClose,
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ VBoxUSBMonSolarisRead,
+ VBoxUSBMonSolarisWrite,
+ VBoxUSBMonSolarisIOCtl,
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxUSBMonSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxUSBMonSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxUSBMonSolarisAttach,
+ VBoxUSBMonSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxUSBMonSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxUSBMonSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxUSBMonSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxUSBMonSolarisModLinkage =
+{
+ MODREV_1,
+ &g_VBoxUSBMonSolarisModule,
+ NULL,
+};
+
+/**
+ * Client driver info.
+ */
+typedef struct vboxusbmon_client_t
+{
+ dev_info_t *pDip; /* Client device info. pointer */
+ VBOXUSB_CLIENT_INFO Info; /* Client registration data. */
+ struct vboxusbmon_client_t *pNext; /* Pointer to next client */
+} vboxusbmon_client_t;
+
+/**
+ * Device state info.
+ */
+typedef struct
+{
+ RTPROCESS Process; /* The process (id) of the session */
+} vboxusbmon_state_t;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global Device handle we only support one instance. */
+static dev_info_t *g_pDip = NULL;
+/** Global Mutex. */
+static kmutex_t g_VBoxUSBMonSolarisMtx;
+/** Global list of client drivers registered with us. */
+vboxusbmon_client_t *g_pVBoxUSBMonSolarisClients = NULL;
+/** Opaque pointer to list of soft states. */
+static void *g_pVBoxUSBMonSolarisState;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData);
+static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach);
+
+
+/*********************************************************************************************************************************
+* Monitor Global Hooks *
+*********************************************************************************************************************************/
+static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo);
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip);
+int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port,
+ char **ppszDrv, void *pvReserved);
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _init\n"));
+
+ g_pDip = NULL;
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxUSBMonSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ": _init: Failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize global mutex.
+ */
+ mutex_init(&g_VBoxUSBMonSolarisMtx, NULL, MUTEX_DRIVER, NULL);
+ rc = VBoxUSBFilterInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = ddi_soft_state_init(&g_pVBoxUSBMonSolarisState, sizeof(vboxusbmon_state_t), 1);
+ if (!rc)
+ {
+ rc = mod_install(&g_VBoxUSBMonSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc));
+ ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState);
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: ddi_soft_state_init failed! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: VBoxUSBFilterInit failed! rc=%d\n", rc));
+
+ mutex_destroy(&g_VBoxUSBMonSolarisMtx);
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc));
+
+ return -1;
+}
+
+
+int _fini(void)
+{
+ int rc;
+
+ LogFunc((DEVICE_NAME ": _fini\n"));
+
+ rc = mod_remove(&g_VBoxUSBMonSolarisModLinkage);
+ if (!rc)
+ {
+ ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState);
+ VBoxUSBFilterTerm();
+ mutex_destroy(&g_VBoxUSBMonSolarisMtx);
+
+ RTR0Term();
+ }
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ": _info\n"));
+
+ return mod_info(&g_VBoxUSBMonSolarisModLinkage, pModInfo);
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ if (RT_UNLIKELY(g_pDip))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Global instance already initialized\n"));
+ return DDI_FAILURE;
+ }
+
+ g_pDip = pDip;
+ int rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, 0 /* flags */,
+ "none", "none", 0660);
+ if (rc == DDI_SUCCESS)
+ {
+ rc = usb_register_dev_driver(g_pDip, VBoxUSBMonSolarisElectDriver);
+ if (rc == DDI_SUCCESS)
+ {
+ ddi_report_dev(pDip);
+ return DDI_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Failed to register driver election callback! rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc));
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* We don't have to bother about power management. */
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisDetach\n"));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ /*
+ * Free all registered clients' info.
+ */
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ while (pCur)
+ {
+ vboxusbmon_client_t *pNext = pCur->pNext;
+ RTMemFree(pCur);
+ pCur = pNext;
+ }
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ usb_unregister_dev_driver(g_pDip);
+
+ ddi_remove_minor_node(pDip, NULL);
+ g_pDip = NULL;
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+ /* We don't have to bother about power management. */
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ int rc = DDI_SUCCESS;
+
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisGetInfo\n"));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = (void *)g_pDip;
+ if (!*ppvResult)
+ rc = DDI_FAILURE;
+ break;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ break;
+ }
+
+ default:
+ rc = DDI_FAILURE;
+ break;
+ }
+ return rc;
+}
+
+
+static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
+{
+ vboxusbmon_state_t *pState = NULL;
+ unsigned iOpenInstance;
+
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisOpen\n"));
+
+ /*
+ * Verify we are being opened as a character device.
+ */
+ if (fType != OTYP_CHR)
+ return EINVAL;
+
+ /*
+ * Verify that we're called after attach.
+ */
+ if (!g_pDip)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Invalid state for opening\n"));
+ return ENXIO;
+ }
+
+ for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++)
+ {
+ if ( !ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance) /* faster */
+ && ddi_soft_state_zalloc(g_pVBoxUSBMonSolarisState, iOpenInstance) == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance);
+ break;
+ }
+ }
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Too many open instances"));
+ return ENXIO;
+ }
+
+ pState->Process = RTProcSelf();
+ *pDev = makedevice(getmajor(*pDev), iOpenInstance);
+
+ NOREF(fFlag);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred)
+{
+ vboxusbmon_state_t *pState = NULL;
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisClose\n"));
+
+ pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisClose: Failed to get state\n"));
+ return EFAULT;
+ }
+
+ /*
+ * Remove all filters for this client process.
+ */
+ VBoxUSBFilterRemoveOwner(pState->Process);
+
+ ddi_soft_state_free(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ pState = NULL;
+
+ NOREF(fFlag);
+ NOREF(fType);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRead\n"));
+ return 0;
+}
+
+
+static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisWrite\n"));
+ return 0;
+}
+
+
+/** @def IOCPARM_LEN
+ * Gets the length from the ioctl number.
+ * This is normally defined by sys/ioccom.h on BSD systems...
+ */
+#ifndef IOCPARM_LEN
+# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK)
+#endif
+
+static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg));
+
+ /*
+ * Get the session from the soft state item.
+ */
+ vboxusbmon_state_t *pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev));
+ if (!pState)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: No state data for minor instance %d\n", getminor(Dev)));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request wrapper. Though We don't really need wrapper struct. now
+ * it's room for the future as Solaris isn't generous regarding the size.
+ */
+ VBOXUSBREQ ReqWrap;
+ if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd),
+ sizeof(ReqWrap)));
+ return ENOTTY;
+ }
+
+ int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc));
+ return EINVAL;
+ }
+
+ if (ReqWrap.u32Magic != VBOXUSBMON_MAGIC)
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd));
+ return EINVAL;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData == 0
+ || ReqWrap.cbData > _1M*16))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd));
+ return EINVAL;
+ }
+
+ /*
+ * Read the request.
+ */
+ void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
+ if (RT_UNLIKELY(!pvBuf))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData));
+ return ENOMEM;
+ }
+
+ rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd,
+ rc));
+ return EFAULT;
+ }
+ if (RT_UNLIKELY( ReqWrap.cbData != 0
+ && !RT_VALID_PTR(pvBuf)))
+ {
+ RTMemTmpFree(pvBuf);
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pvBuf Invalid pointer %p\n", pvBuf));
+ return EINVAL;
+ }
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pid=%d\n", (int)RTProcSelf()));
+
+ /*
+ * Process the IOCtl.
+ */
+ size_t cbDataReturned = 0;
+ rc = vboxUSBMonSolarisProcessIOCtl(Cmd, pState, pvBuf, ReqWrap.cbData, &cbDataReturned);
+ ReqWrap.rc = rc;
+ rc = 0;
+
+ if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData));
+ cbDataReturned = ReqWrap.cbData;
+ }
+
+ ReqWrap.cbData = cbDataReturned;
+
+ /*
+ * Copy the request back to user space.
+ */
+ rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Copy the payload (if any) back to user space.
+ */
+ if (cbDataReturned > 0)
+ {
+ rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode);
+ if (RT_UNLIKELY(rc))
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf,
+ pArg, Cmd, rc));
+ rc = EFAULT;
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout(1) failed pArg=%p Cmd=%d\n", pArg, Cmd));
+ rc = EFAULT;
+ }
+
+ *pVal = rc;
+ RTMemTmpFree(pvBuf);
+ return rc;
+}
+
+
+/**
+ * IOCtl processor for user to kernel and kernel to kernel communication.
+ *
+ * @returns VBox status code.
+ *
+ * @param iFunction The requested function.
+ * @param pvState Opaque pointer to driver state used for getting
+ * ring-3 process (Id).
+ * @param pvData The input/output data buffer. Can be NULL
+ * depending on the function.
+ * @param cbData The max size of the data buffer.
+ * @param pcbReturnedData Where to store the amount of returned data. Can
+ * be NULL.
+ */
+static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData)
+{
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: iFunction=%d pvBuf=%p cbBuf=%zu\n", iFunction, pvData, cbData));
+
+ AssertPtrReturn(pvState, VERR_INVALID_POINTER);
+ vboxusbmon_state_t *pState = (vboxusbmon_state_t *)pvState;
+ int rc;
+
+#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
+ do { \
+ if (RT_UNLIKELY(cbData < (cbMin))) \
+ { \
+ LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
+ cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
+ return VERR_BUFFER_OVERFLOW; \
+ } \
+ if (RT_UNLIKELY((cbMin) != 0 && !RT_VALID_PTR(pvData))) \
+ { \
+ LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvData)); \
+ return VERR_INVALID_POINTER; \
+ } \
+ } while (0)
+
+ switch (iFunction)
+ {
+ case VBOXUSBMON_IOCTL_ADD_FILTER:
+ {
+ CHECKRET_MIN_SIZE("ADD_FILTER", sizeof(VBOXUSBREQ_ADD_FILTER));
+
+ VBOXUSBREQ_ADD_FILTER *pReq = (VBOXUSBREQ_ADD_FILTER *)pvData;
+ PUSBFILTER pFilter = (PUSBFILTER)&pReq->Filter;
+
+ Log(("vboxUSBMonSolarisProcessIOCtl: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x "
+ "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
+ 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)));
+ Log(("vboxUSBMonSolarisProcessIOCtl: 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>"));
+
+ rc = USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false /* fMustBePresent */); AssertRC(rc);
+
+ rc = VBoxUSBFilterAdd(pFilter, pState->Process, &pReq->uId);
+ *pcbReturnedData = cbData;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: ADD_FILTER (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_REMOVE_FILTER:
+ {
+ CHECKRET_MIN_SIZE("REMOVE_FILTER", sizeof(VBOXUSBREQ_REMOVE_FILTER));
+
+ VBOXUSBREQ_REMOVE_FILTER *pReq = (VBOXUSBREQ_REMOVE_FILTER *)pvData;
+ rc = VBoxUSBFilterRemove(pState->Process, (uintptr_t)pReq->uId);
+ *pcbReturnedData = 0;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: REMOVE_FILTER (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_RESET_DEVICE:
+ {
+ CHECKRET_MIN_SIZE("RESET_DEVICE", sizeof(VBOXUSBREQ_RESET_DEVICE));
+
+ VBOXUSBREQ_RESET_DEVICE *pReq = (VBOXUSBREQ_RESET_DEVICE *)pvData;
+ rc = vboxUSBMonSolarisResetDevice(pReq->szDevicePath, pReq->fReattach);
+ *pcbReturnedData = 0;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: RESET_DEVICE (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_CLIENT_INFO:
+ {
+ CHECKRET_MIN_SIZE("CLIENT_INFO", sizeof(VBOXUSBREQ_CLIENT_INFO));
+
+ VBOXUSBREQ_CLIENT_INFO *pReq = (VBOXUSBREQ_CLIENT_INFO *)pvData;
+ rc = vboxUSBMonSolarisClientInfo(pState, pReq);
+ *pcbReturnedData = cbData;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: CLIENT_INFO (Process:%d) returned %d\n", pState->Process, rc));
+ break;
+ }
+
+ case VBOXUSBMON_IOCTL_GET_VERSION:
+ {
+ CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION));
+
+ PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvData;
+ pGetVersionReq->u32Major = VBOXUSBMON_VERSION_MAJOR;
+ pGetVersionReq->u32Minor = VBOXUSBMON_VERSION_MINOR;
+ *pcbReturnedData = sizeof(VBOXUSBREQ_GET_VERSION);
+ rc = VINF_SUCCESS;
+ Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: GET_VERSION returned %d\n", rc));
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: Unknown request (Process:%d) %#x\n", pState->Process,
+ iFunction));
+ *pcbReturnedData = 0;
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ return rc;
+}
+
+
+static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach)
+{
+ int rc = VERR_GENERAL_FAILURE;
+
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: pszDevicePath=%s fReattach=%d\n", pszDevicePath, fReattach));
+
+ /*
+ * Try grabbing the dev_info_t.
+ */
+ dev_info_t *pDeviceInfo = e_ddi_hold_devi_by_path(pszDevicePath, 0);
+ if (pDeviceInfo)
+ {
+ ddi_release_devi(pDeviceInfo);
+
+ /*
+ * Grab the root device node from the parent hub for resetting.
+ */
+ dev_info_t *pTmpDeviceInfo = NULL;
+ for (;;)
+ {
+ pTmpDeviceInfo = ddi_get_parent(pDeviceInfo);
+ if (!pTmpDeviceInfo)
+ {
+ LogRel((DEVICE_NAME ":vboxUSBMonSolarisResetDevice: Failed to get parent device info for %s\n", pszDevicePath));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ if (ddi_prop_exists(DDI_DEV_T_ANY, pTmpDeviceInfo, DDI_PROP_DONTPASS, "usb-port-count")) /* parent hub */
+ break;
+
+ pDeviceInfo = pTmpDeviceInfo;
+ }
+
+ /*
+ * Try re-enumerating the device.
+ */
+ rc = usb_reset_device(pDeviceInfo, fReattach ? USB_RESET_LVL_REATTACH : USB_RESET_LVL_DEFAULT);
+ Log((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: usb_reset_device for %s level=%s rc=%d\n", pszDevicePath,
+ fReattach ? "ReAttach" : "Default", rc));
+
+ switch (rc)
+ {
+ case USB_SUCCESS: rc = VINF_SUCCESS; break;
+ case USB_INVALID_PERM: rc = VERR_PERMISSION_DENIED; break;
+ case USB_INVALID_ARGS: rc = VERR_INVALID_PARAMETER; break;
+ case USB_BUSY: rc = VERR_RESOURCE_BUSY; break;
+ case USB_INVALID_CONTEXT: rc = VERR_INVALID_CONTEXT; break;
+ case USB_FAILURE: rc = VERR_GENERAL_FAILURE; break;
+
+ default: rc = VERR_UNRESOLVED_ERROR; break;
+ }
+ }
+ else
+ {
+ rc = VERR_INVALID_HANDLE;
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: Cannot obtain device info for %s\n", pszDevicePath));
+ }
+
+ return rc;
+}
+
+
+/**
+ * Query client driver information. This also has a side-effect that it informs
+ * the client driver which upcoming VM process should be allowed to open it.
+ *
+ * @returns VBox status code.
+ * @param pState Pointer to the device state.
+ * @param pClientInfo Pointer to the client info. object.
+ */
+static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo)
+{
+ LogFunc((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pState=%p pClientInfo=%p\n", pState, pClientInfo));
+
+ AssertPtrReturn(pState, VERR_INVALID_POINTER);
+ AssertPtrReturn(pClientInfo, VERR_INVALID_POINTER);
+
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ vboxusbmon_client_t *pPrev = NULL;
+ while (pCur)
+ {
+ if (strncmp(pClientInfo->szDeviceIdent, pCur->Info.szDeviceIdent, sizeof(pCur->Info.szDeviceIdent) - 1) == 0)
+ {
+ pClientInfo->Instance = pCur->Info.Instance;
+ RTStrPrintf(pClientInfo->szClientPath, sizeof(pClientInfo->szClientPath), "%s", pCur->Info.szClientPath);
+
+ /*
+ * Inform the client driver that this is the client process that is going to open it. We can predict the future!
+ */
+ int rc;
+ if (pCur->Info.pfnSetConsumerCredentials)
+ {
+ rc = pCur->Info.pfnSetConsumerCredentials(pState->Process, pCur->Info.Instance, NULL /* pvReserved */);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pfnSetConsumerCredentials failed! rc=%d\n", rc));
+ }
+ else
+ rc = VERR_INVALID_FUNCTION;
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Found %s, rc=%d\n", pClientInfo->szDeviceIdent, rc));
+ return rc;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Failed to find client %s\n", pClientInfo->szDeviceIdent));
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Registers client driver.
+ *
+ * @returns VBox status code.
+ */
+int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient: pClientDip=%p pClientInfo=%p\n", pClientDip, pClientInfo));
+ AssertPtrReturn(pClientInfo, VERR_INVALID_PARAMETER);
+
+ if (RT_LIKELY(g_pDip))
+ {
+ vboxusbmon_client_t *pClient = RTMemAllocZ(sizeof(vboxusbmon_client_t));
+ if (RT_LIKELY(pClient))
+ {
+ pClient->Info.Instance = pClientInfo->Instance;
+ strncpy(pClient->Info.szClientPath, pClientInfo->szClientPath, sizeof(pClient->Info.szClientPath));
+ strncpy(pClient->Info.szDeviceIdent, pClientInfo->szDeviceIdent, sizeof(pClient->Info.szDeviceIdent));
+ pClient->Info.pfnSetConsumerCredentials = pClientInfo->pfnSetConsumerCredentials;
+ pClient->pDip = pClientDip;
+
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+ pClient->pNext = g_pVBoxUSBMonSolarisClients;
+ g_pVBoxUSBMonSolarisClients = pClient;
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": Client registered (ClientPath=%s Ident=%s)\n", pClient->Info.szClientPath,
+ pClient->Info.szDeviceIdent));
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+ }
+ return VERR_INVALID_STATE;
+}
+
+
+/**
+ * Deregisters client driver.
+ *
+ * @returns VBox status code.
+ */
+int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: pClientDip=%p\n", pClientDip));
+ AssertReturn(pClientDip, VERR_INVALID_PARAMETER);
+
+ if (RT_LIKELY(g_pDip))
+ {
+ mutex_enter(&g_VBoxUSBMonSolarisMtx);
+
+ vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients;
+ vboxusbmon_client_t *pPrev = NULL;
+ while (pCur)
+ {
+ if (pCur->pDip == pClientDip)
+ {
+ if (pPrev)
+ pPrev->pNext = pCur->pNext;
+ else
+ g_pVBoxUSBMonSolarisClients = pCur->pNext;
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ Log((DEVICE_NAME ": Client unregistered (ClientPath=%s Ident=%s)\n", pCur->Info.szClientPath,
+ pCur->Info.szDeviceIdent));
+ RTMemFree(pCur);
+ return VINF_SUCCESS;
+ }
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ mutex_exit(&g_VBoxUSBMonSolarisMtx);
+
+ LogRel((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: Failed to find registered client %p\n", pClientDip));
+ return VERR_NOT_FOUND;
+ }
+ return VERR_INVALID_STATE;
+}
+
+
+/**
+ * USBA driver election callback.
+ *
+ * @returns USB_SUCCESS if we want to capture the device, USB_FAILURE otherwise.
+ * @param pDevDesc The parsed device descriptor (does not include subconfigs).
+ * @param pDevStrings Device strings: Manufacturer, Product, Serial Number.
+ * @param pszDevicePath The physical path of the device being attached.
+ * @param Bus The Bus number on which the device is on.
+ * @param Port The Port number on the bus.
+ * @param ppszDrv The name of the driver we wish to capture the device with.
+ * @param pvReserved Reserved for future use.
+ */
+int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port,
+ char **ppszDrv, void *pvReserved)
+{
+ LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: pDevDesc=%p pDevStrings=%p pszDevicePath=%s Bus=%d Port=%d\n", pDevDesc,
+ pDevStrings, pszDevicePath, Bus, Port));
+
+ AssertPtrReturn(pDevDesc, USB_FAILURE);
+ AssertPtrReturn(pDevStrings, USB_FAILURE);
+
+ /*
+ * Create a filter from the device being attached.
+ */
+ USBFILTER Filter;
+ USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_VENDOR_ID, pDevDesc->idVendor, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_PRODUCT_ID, pDevDesc->idProduct, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_REV, pDevDesc->bcdDevice, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_CLASS, pDevDesc->bDeviceClass, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pDevDesc->bDeviceSubClass, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pDevDesc->bDeviceProtocol, true);
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_BUS, 0x0 /* Bus */, true); /* Use 0x0 as userland initFilterFromDevice function in Main: see comment on "SetMustBePresent" below */
+ USBFilterSetNumExact(&Filter, USBFILTERIDX_PORT, Port, true);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_MANUFACTURER_STR, pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_PRODUCT_STR, pDevStrings->usb_product ? pDevStrings->usb_product : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+ USBFilterSetStringExact(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR, pDevStrings->usb_serialno ? pDevStrings->usb_serialno : "",
+ true /*fMustBePresent*/, true /*fPurge*/);
+
+ /* This doesn't work like it should (USBFilterMatch fails on matching field (6) i.e. Bus despite this. Investigate later. */
+ USBFilterSetMustBePresent(&Filter, USBFILTERIDX_BUS, false /* fMustBePresent */);
+
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x "
+ "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
+ USBFilterGetNum(&Filter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(&Filter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(&Filter, USBFILTERIDX_BUS),
+ USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+
+ /*
+ * Run through user filters and try to see if it has a match.
+ */
+ uintptr_t uId = 0;
+ RTPROCESS Owner = VBoxUSBFilterMatch(&Filter, &uId);
+ USBFilterDelete(&Filter);
+ if (Owner == NIL_RTPROCESS)
+ {
+ Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: No matching filters, device %#x:%#x uninteresting\n",
+ pDevDesc->idVendor, pDevDesc->idProduct));
+ return USB_FAILURE;
+ }
+
+ *ppszDrv = ddi_strdup(VBOXUSB_DRIVER_NAME, KM_SLEEP);
+#if 0
+ LogRel((DEVICE_NAME ": Capturing %s %s %#x:%#x:%s Bus=%d Port=%d\n",
+ pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>",
+ pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>",
+ pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port));
+#else
+ /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
+ cmn_err(CE_CONT, "Capturing %s %s 0x%x:0x%x:%s Bus=%d Port=%d\n",
+ pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>",
+ pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>",
+ pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port);
+#endif
+ return USB_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h b/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h
new file mode 100644
index 00000000..2e61d804
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/include/usbai_private.h
@@ -0,0 +1,161 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009-2010 Oracle Corporation. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBA_USBAI_PRIVATE_H
+#define _SYS_USB_USBA_USBAI_PRIVATE_H
+
+/*
+ * Unstable interfaces not part of USBAI but used by Solaris client drivers.
+ * These interfaces may not be present in future releases and are highly
+ * unstable.
+ *
+ * Status key:
+ * C = Remove from Sun client drivers before removing from this file
+ * D = May be needed by legacy (DDK) drivers.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * convenience function for getting default config index
+ * as saved in usba_device structure
+ *
+ * Status: C
+ */
+uint_t usb_get_current_cfgidx(dev_info_t *);
+
+/*
+ * Usb logging, debug and console message handling.
+ */
+typedef struct usb_log_handle *usb_log_handle_t;
+
+#define USB_LOG_L0 0 /* warnings, console & syslog buffer */
+#define USB_LOG_L1 1 /* errors, syslog buffer */
+#define USB_LOG_L2 2 /* recoverable errors, debug only */
+#define USB_LOG_L3 3 /* interesting data, debug only */
+#define USB_LOG_L4 4 /* tracing, debug only */
+
+#define USB_CHK_BASIC 0 /* Empty mask. Basics always done. */
+#define USB_CHK_SERIAL 0x00000001 /* Compare device serial numbers. */
+#define USB_CHK_CFG 0x00000002 /* Compare raw config clouds. */
+#define USB_CHK_VIDPID 0x00000004 /* Compare product and vendor ID. */
+#define USB_CHK_ALL 0xFFFFFFFF /* Perform maximum checking. */
+
+int usb_check_same_device(dev_info_t *dip,
+ usb_log_handle_t log_handle,
+ int log_level,
+ int log_mask,
+ uint_t check_mask,
+ char *device_string);
+
+/*
+ * **************************************************************************
+ * Serialization functions remaining Contracted Consolidation Private
+ * **************************************************************************
+ */
+
+/* This whole section: status: C and D. */
+
+/*
+ * opaque serialization handle.
+ * Used by all usb_serialization routines.
+ *
+ * This handle is opaque to the client driver.
+ */
+typedef struct usb_serialization *usb_serialization_t;
+
+/*
+ * usb_init_serialization
+ * setup for serialization
+ *
+ * ARGUMENTS:
+ * s_dip - devinfo pointer
+ * flag - USB_INIT_SER_CHECK_SAME_THREAD
+ * when set, usb_release_access() will verify that the same
+ * thread releases access. If not, a console warning will
+ * be issued but access will be released anyways.
+ *
+ * RETURNS:
+ * usb_serialization handle
+ *
+ */
+usb_serialization_t usb_init_serialization(dev_info_t *s_dip,
+ uint_t flag);
+
+#define USB_INIT_SER_CHECK_SAME_THREAD 1
+
+/* fini for serialization */
+void usb_fini_serialization(usb_serialization_t usb_serp);
+
+/*
+ * Various ways of calling usb_serialize_access. These correspond to
+ * their cv_*wait* function counterparts for usb_serialize_access.
+ */
+#define USB_WAIT 0
+#define USB_WAIT_SIG 1
+#define USB_TIMEDWAIT 2
+#define USB_TIMEDWAIT_SIG 3
+
+/*
+ * usb_serialize_access:
+ * acquire serialized access
+ *
+ * ARGUMENTS:
+ * usb_serp - usb_serialization handle
+ * how_to_wait - Which cv_*wait* function to wait for condition.
+ * USB_WAIT: use cv_wait
+ * USB_WAIT_SIG: use cv_wait_sig
+ * USB_TIMEDWAIT: use cv_timedwait
+ * USB_TIMEDWAIT_SIG: use cv_timedwait_sig
+ * delta_timeout - Time in ms from current time to timeout. Checked
+ * only if USB_TIMEDWAIT or USB_TIMEDWAIT_SIG
+ * specified in how_to_wait.
+ * RETURNS:
+ * Same as values returned by cv_*wait* functions,
+ * except for when how_to_wait == USB_WAIT, where 0 is always returned.
+ * For calls where a timeout or signal could be expected, use this value
+ * to tell whether a kill(2) signal or timeout occurred.
+ */
+int usb_serialize_access(usb_serialization_t usb_serp,
+ uint_t how_to_wait,
+ uint_t delta_timeout);
+
+/*
+ * usb_release_access:
+ * release serialized access
+ *
+ * ARGUMENTS:
+ * usb_serp - usb_serialization handle
+ */
+void usb_release_access(usb_serialization_t usb_serp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_USB_USBA_USBAI_PRIVATE_H */
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf
new file mode 100644
index 00000000..17993e73
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusb.conf
@@ -0,0 +1,37 @@
+#
+# Solaris Host USB Client Driver Configuration
+#
+
+#
+# Copyright (C) 2010-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
+#
+
+name="vboxusb" parent="pseudo";
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf
new file mode 100644
index 00000000..98a3b5e1
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/solaris/vboxusbmon.conf
@@ -0,0 +1,37 @@
+#
+# Solaris Host USB Monitor Driver Configuration
+#
+
+#
+# Copyright (C) 2010-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
+#
+
+name="vboxusbmon" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup b/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/testcase/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp b/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp
new file mode 100644
index 00000000..123682a9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/testcase/tstUSBFilter.cpp
@@ -0,0 +1,391 @@
+/** $Id: tstUSBFilter.cpp $ */
+/** @file
+ * VirtualBox USB filter abstraction - testcase.
+ */
+
+/*
+ * Copyright (C) 2007-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 <VBox/usbfilter.h>
+#include <iprt/errcore.h>
+
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define TESTCASE "tstUSBFilter"
+
+#define TST_CHECK_RC(expr) do { \
+ int rc = expr; \
+ if (RT_FAILURE(rc)) \
+ { \
+ RTPrintf( TESTCASE "(%d): %Rrc - %s\n", __LINE__, rc, #expr); \
+ cErrors++; \
+ } \
+ } while (0)
+
+#define TST_CHECK_EXPR(expr) do { \
+ int rc = (intptr_t)(expr); \
+ if (!rc) \
+ { \
+ RTPrintf( TESTCASE "(%d): %s -> %d\n", __LINE__, #expr, rc); \
+ cErrors++; \
+ } \
+} while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const char g_szString64[64+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345"
+};
+
+static const char g_szString128[128+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+};
+
+static const char g_szString256[256+1] =
+{
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+ "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345"
+};
+
+
+int main()
+{
+ unsigned cErrors = 0;
+ RTR3InitExeNoArguments(0);
+
+ /*
+ * Basic property setting and simple matching.
+ */
+ USBFILTER Flt1;
+ USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
+ /* numbers */
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_VENDOR_ID, 0x1111, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PRODUCT_ID, 0x2222, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_PROTOCOL, 0xff, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_BUS));
+ TST_CHECK_RC(USBFilterSetPresentOnly(&Flt1, USBFILTERIDX_BUS));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, false));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, false));
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PORT));
+ /* strings */
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString128, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "barbar", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "vendor", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false ));
+
+ /* cloning */
+ USBFILTER Dev;
+ USBFilterClone(&Dev, &Flt1);
+
+ TST_CHECK_EXPR(USBFilterIsIdentical(&Dev, &Flt1));
+ TST_CHECK_EXPR(USBFilterMatch(&Dev, &Flt1));
+
+ USBFilterDelete(&Flt1);
+ USBFilterDelete(&Dev);
+
+ /* make a sample device */
+ USBFilterInit(&Dev, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_VENDOR_ID, 0x1111, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_PRODUCT_ID, 0x2222, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_DEVICE_PROTOCOL, 0xff, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_RC(USBFilterSetNumExact(&Dev, USBFILTERIDX_PORT, 2, true));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_MANUFACTURER_STR, "vendor", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_RC(USBFilterSetStringExact(&Dev, USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false ));
+
+ /* do some basic matching tests */
+ USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(!USBFilterHasAnySubstatialCriteria(&Flt1));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev) /* 100% ignore filter */);
+
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 3, true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 2, true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 2, true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "no match", true, false ));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ /* string patterns */
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "product*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*duct", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*x", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*oduct*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*produc*", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*d*t", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*x*t", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PRODUCT_STR));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ /* numeric patterns */
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111-0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369-4369", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421-010421", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1110-0x1112", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4360-4370", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010420-010422", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112-0x1110", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0x1f", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-19", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-017", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0xffff", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-65535", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-177777", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0XABCD", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0EF-0XABCD", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X0ef-0Xabcd", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|1|0x1111", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|0x1111|1", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111|42|1", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112|42|1", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+ /* numeric patterns - interval filters */
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "int:0x0-0xffff", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "int: 0x0 - 0xffff ", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PRODUCT_ID, "int:0x0028-", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_DEVICE_REV, "int:-0x0045", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PORT, "int:1,4", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_PORT, "int:( 1, 3 )", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "39-59|0x256-0x101f|0xfffff-0xf000|0x1000-0x2000", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x000256-0x0101f|0xf000-0xfffff|0x000008000-0x2000|39-59", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f | 0xf000 - 0xfeff\t| 0x1000 -\t0x6000 | 1- 0512", true));
+ TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
+ TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f | 0xf000 - 0xfeff\t| 0x1112 -\t0x6000 | 1- 0512", true));
+ TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
+
+
+ USBFilterDelete(&Flt1);
+
+ /*
+ * string overflow
+ */
+ struct
+ {
+ uint64_t u64Pre;
+ USBFILTER Flt;
+ uint64_t u64Post;
+ } sOf;
+ sOf.u64Pre = sOf.u64Post = UINT64_C(0x1234567887654321);
+
+ USBFilterInit(&sOf.Flt, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+
+ AssertCompileMemberSize(USBFILTER, achStrTab, 256);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[0], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[1], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString256[3], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 0 + 1 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, "", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString256[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString256[1], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 0 + 2 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString128[2], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString128[1], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ /* 3 */
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR, &g_szString64[0], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR, &g_szString64[0], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[4], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[4], true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR, &g_szString128[3], true, false ) == VERR_BUFFER_OVERFLOW);
+ TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
+
+ /*
+ * Check for a string replacement bug.
+ */
+ USBFILTER Dev2;
+ USBFilterInit(&Dev2, USBFILTERTYPE_CAPTURE);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_VENDOR_ID, 0x19b6, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_PRODUCT_ID, 0x1024, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_REV, 0x0141, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_CLASS, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_DEVICE_PROTOCOL, 0, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2, USBFILTERIDX_PORT, 0x1, true) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_MANUFACTURER_STR, "Generic", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_PRODUCT_STR, "Mass Storage Device", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2, USBFILTERIDX_MANUFACTURER_STR, "YBU1PPRS", true, false ) == VINF_SUCCESS);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_VENDOR_ID) == 0x19b6);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PRODUCT_ID) == 0x1024);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_REV) == 0x0141);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_CLASS) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_SUB_CLASS) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_PROTOCOL) == 0);
+ TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PORT) == 1);
+
+
+ /*
+ * Summary.
+ */
+ if (!cErrors)
+ RTPrintf(TESTCASE ": SUCCESS\n");
+ else
+ RTPrintf(TESTCASE ": FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
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