summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB/win/dev
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/win/dev')
-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
12 files changed, 3324 insertions, 0 deletions
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 */