/* $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 . * * 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 #include /********************************************************************************************************************************* * 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