// USB bus manager. This file owns device addressing, descriptor reads, and child PDO creation. // This is the structural USB bus layer used by the upcoming composite, hub, // and hotplug stages. The legacy direct xHCI class path is still present for // boot HID/storage during migration, but newly enumerated xHCI devices now also // get first-class USB PDOs and can accept internal URB IRPs. #include "usb.h" DRIVER_OBJECT gUsbBusDriver; static bool gUsbBusModelReady; static uint8_t gUsbNextControllerIndex; // Implements the device Destroy operation. static void UsbDeviceDestroy(OBJECT* object) { USB_DEVICE* dev = (USB_DEVICE*)object; if (dev->RemoveLockReady) { IoDestroyRemoveLock(&dev->RemoveLock); dev->RemoveLockReady = false; } if (dev->Pdo) { IoDereferenceDeviceObject(dev->Pdo); dev->Pdo = (DEVICE_OBJECT*)0; } } // Destroys a pipe after the host controller releases its private context. static void UsbPipeDestroy(OBJECT* object) { USB_PIPE* pipe = (USB_PIPE*)object; if (pipe->Device && pipe->Device->Hcd && pipe->Device->Hcd->Operations && pipe->Device->Hcd->Operations->DestroyPipe) pipe->Device->Hcd->Operations->DestroyPipe(pipe->Device->Hcd, pipe); if (pipe->Device) { UsbDereferenceDevice(pipe->Device); pipe->Device = (USB_DEVICE*)0; } } // Implements the reference Device operation. void UsbReferenceDevice(USB_DEVICE* dev) { if (dev) ObReferenceObject(&dev->Header); } // Implements the dereference Device operation. void UsbDereferenceDevice(USB_DEVICE* dev) { if (dev) ObDereferenceObject(&dev->Header); } // Implements the reference Pipe operation. void UsbReferencePipe(USB_PIPE* pipe) { if (pipe) ObReferenceObject(&pipe->Header); } // Implements the dereference Pipe operation. void UsbDereferencePipe(USB_PIPE* pipe) { if (pipe) ObDereferenceObject(&pipe->Header); } // Allocates a DMA buffer through the device host-controller adapter. void* UsbAllocateDmaBuffer(USB_DEVICE* dev, size_t pages, uint64_t* outDeviceAddress, IO_DMA_BUFFER** outBuffer) { if (!dev || !dev->Hcd || !dev->Hcd->Operations || !dev->Hcd->Operations->AllocateDma || !outDeviceAddress || !outBuffer) return (void*)0; return dev->Hcd->Operations->AllocateDma(dev->Hcd, pages, outDeviceAddress, outBuffer); } // Frees a DMA buffer allocated for a USB class driver. void UsbFreeDmaBuffer(IO_DMA_BUFFER* buffer) { if (buffer) IoDmaFreeCommonBuffer(buffer); } // Creates device. static USB_DEVICE* UsbAllocateDevice(void) { USB_DEVICE* dev = (USB_DEVICE*)ObAllocateObject(ObjectTypeUsbDevice, sizeof(USB_DEVICE)); if (!dev) return (USB_DEVICE*)0; dev->Header.Destroy = UsbDeviceDestroy; KSTATUS st = IoInitializeRemoveLock(&dev->RemoveLock); if (!KSUCCESS(st)) { ObDereferenceObject(&dev->Header); return (USB_DEVICE*)0; } dev->RemoveLockReady = true; dev->State = UsbDeviceAttached; return dev; } KSTATUS UsbCreatePipe(USB_DEVICE* dev, USB_PIPE_TYPE type, uint8_t endpointAddress, uint16_t maxPacketSize, uint8_t interval, USB_PIPE** outPipe) { if (!dev || !outPipe) return NTLSTATUS_INVALID_PARAMETER; USB_PIPE* pipe = (USB_PIPE*)ObAllocateObject(ObjectTypeUsbPipe, sizeof(USB_PIPE)); if (!pipe) return NTLSTATUS_NO_MEMORY; pipe->Header.Destroy = UsbPipeDestroy; pipe->Device = dev; UsbReferenceDevice(dev); pipe->Type = type; pipe->EndpointAddress = endpointAddress; pipe->EndpointId = XhciEndpointIdFromAddress(endpointAddress); pipe->MaxPacketSize = maxPacketSize; pipe->Interval = interval; pipe->Open = true; *outPipe = pipe; return NTLSTATUS_SUCCESS; } // Implements the id From Buffer operation. static SYS_STRING_VIEW UsbIdFromBuffer(char* buf) { return SysSvFromCstr(buf); } // Implements the id Put Char operation. static void UsbIdPutChar(char* buf, size_t cap, size_t* pos, char ch) { if (!buf || !pos || cap == 0) return; if (*pos + 1 < cap) buf[*pos] = ch; (*pos)++; if (*pos < cap) buf[*pos] = '\0'; else buf[cap - 1] = '\0'; } // Implements the id Append Literal operation. static void UsbIdAppendLiteral(char* buf, size_t cap, size_t* pos, const char* text) { if (!text) return; while (*text) UsbIdPutChar(buf, cap, pos, *text++); } // Implements the id Append Hex operation. static void UsbIdAppendHex(char* buf, size_t cap, size_t* pos, uint32_t value, uint8_t digits) { static const char kHex[] = "0123456789ABCDEF"; while (digits) { digits--; UsbIdPutChar(buf, cap, pos, kHex[(value >> (digits * 4u)) & 0xFu]); } } // Implements the format Vid Pid Rev operation. static void UsbFormatVidPidRev(char* buf, size_t cap, uint16_t vid, uint16_t pid, uint16_t rev) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\VID_"); UsbIdAppendHex(buf, cap, &pos, vid, 4); UsbIdAppendLiteral(buf, cap, &pos, "&PID_"); UsbIdAppendHex(buf, cap, &pos, pid, 4); UsbIdAppendLiteral(buf, cap, &pos, "&REV_"); UsbIdAppendHex(buf, cap, &pos, rev, 4); } // Implements the format Vid Pid operation. static void UsbFormatVidPid(char* buf, size_t cap, uint16_t vid, uint16_t pid) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\VID_"); UsbIdAppendHex(buf, cap, &pos, vid, 4); UsbIdAppendLiteral(buf, cap, &pos, "&PID_"); UsbIdAppendHex(buf, cap, &pos, pid, 4); } // Implements the format Vid Pid Mi operation. static void UsbFormatVidPidMi(char* buf, size_t cap, uint16_t vid, uint16_t pid, uint8_t mi) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\VID_"); UsbIdAppendHex(buf, cap, &pos, vid, 4); UsbIdAppendLiteral(buf, cap, &pos, "&PID_"); UsbIdAppendHex(buf, cap, &pos, pid, 4); UsbIdAppendLiteral(buf, cap, &pos, "&MI_"); UsbIdAppendHex(buf, cap, &pos, mi, 2); } // Implements the format Class Sub Prot operation. static void UsbFormatClassSubProt(char* buf, size_t cap, uint8_t cls, uint8_t sub, uint8_t proto) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\CLASS_"); UsbIdAppendHex(buf, cap, &pos, cls, 2); UsbIdAppendLiteral(buf, cap, &pos, "&SUBCLASS_"); UsbIdAppendHex(buf, cap, &pos, sub, 2); UsbIdAppendLiteral(buf, cap, &pos, "&PROT_"); UsbIdAppendHex(buf, cap, &pos, proto, 2); } // Implements the format Class operation. static void UsbFormatClass(char* buf, size_t cap, uint8_t cls) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\CLASS_"); UsbIdAppendHex(buf, cap, &pos, cls, 2); } // Implements the format Composite operation. static void UsbFormatComposite(char* buf, size_t cap) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\COMPOSITE"); } // Implements the format Root Hub operation. static void UsbFormatRootHub(char* buf, size_t cap) { size_t pos = 0; if (cap) buf[0] = '\0'; UsbIdAppendLiteral(buf, cap, &pos, "USB\\ROOT_HUB"); } // Creates device Ids. static void UsbMakeDeviceIds(const XHCI_DEVICE* xdev, IO_DEVICE_ID* ids, char idbuf[][USB_MAX_DEVICE_ID_TEXT], size_t* outCount) { size_t n = 0; UsbFormatVidPidRev(idbuf[n], USB_MAX_DEVICE_ID_TEXT, xdev->VendorId, xdev->ProductId, xdev->BcdDevice); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; UsbFormatVidPid(idbuf[n], USB_MAX_DEVICE_ID_TEXT, xdev->VendorId, xdev->ProductId); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; UsbFormatClassSubProt(idbuf[n], USB_MAX_DEVICE_ID_TEXT, xdev->DeviceClass, xdev->DeviceSubclass, xdev->DeviceProtocol); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; UsbFormatClass(idbuf[n], USB_MAX_DEVICE_ID_TEXT, xdev->DeviceClass); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; if (xdev->DeviceClass == 0 || xdev->InterfaceCount > 1) { UsbFormatComposite(idbuf[n], USB_MAX_DEVICE_ID_TEXT); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; } *outCount = n; } static void UsbMakeInterfaceIds(const XHCI_DEVICE* xdev, const USB_TRACKED_INTERFACE* iface, IO_DEVICE_ID* ids, char idbuf[][USB_MAX_DEVICE_ID_TEXT], size_t* outCount) { size_t n = 0; const USB_INTERFACE_DESCRIPTOR* d = &iface->Descriptor; UsbFormatVidPidMi(idbuf[n], USB_MAX_DEVICE_ID_TEXT, xdev->VendorId, xdev->ProductId, d->bInterfaceNumber); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; UsbFormatClassSubProt(idbuf[n], USB_MAX_DEVICE_ID_TEXT, d->bInterfaceClass, d->bInterfaceSubClass, d->bInterfaceProtocol); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; UsbFormatClass(idbuf[n], USB_MAX_DEVICE_ID_TEXT, d->bInterfaceClass); ids[n] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[n]) }; n++; *outCount = n; } // Initializes bus Model. KSTATUS UsbInitializeBusModel(void) { if (gUsbBusModelReady) return NTLSTATUS_SUCCESS; memset(&gUsbBusDriver, 0, sizeof(gUsbBusDriver)); IoInitializeDriverObject(&gUsbBusDriver, SYS_SV_LITERAL("usb-bus")); gUsbBusDriver.MajorFunction[IRP_MJ_PNP] = UsbDispatchPdoPnp; gUsbBusDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = UsbDispatchInternalDeviceControl; gUsbBusModelReady = true; return NTLSTATUS_SUCCESS; } void UsbInitializeHcd(USB_HCD* hcd, const USB_HCD_OPERATIONS* ops, void* controllerContext, uint8_t controllerIndex, uint8_t maxRootPorts) { if (!hcd) return; memset(hcd, 0, sizeof(*hcd)); hcd->Operations = ops; hcd->RootHubLock = (SPINLOCK)SPINLOCK_INIT; hcd->ControllerContext = controllerContext; hcd->DriverObject = &gUsbBusDriver; hcd->ControllerIndex = controllerIndex ? controllerIndex : ++gUsbNextControllerIndex; hcd->MaxRootPorts = maxRootPorts; } // Creates root Hub Pdo. KSTATUS UsbCreateRootHubPdo(USB_HCD* hcd) { if (!hcd) return NTLSTATUS_INVALID_PARAMETER; if (hcd->RootHubPdo) return NTLSTATUS_ALREADY_EXISTS; IO_DEVICE_ID ids[2]; char idbuf[2][USB_MAX_DEVICE_ID_TEXT]; UsbFormatRootHub(idbuf[0], USB_MAX_DEVICE_ID_TEXT); UsbFormatClass(idbuf[1], USB_MAX_DEVICE_ID_TEXT, USB_CLASS_HUB); ids[0] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[0]) }; ids[1] = (IO_DEVICE_ID){ IoBusUsb, UsbIdFromBuffer(idbuf[1]) }; DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver, hcd->ControllerDevice, ids, 2, sizeof(USB_PDO_EXTENSION), &pdo); if (!KSUCCESS(st)) return st; (void)IoSetDeviceName(pdo, SYS_SV_LITERAL("usb-root-hub")); USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)pdo->DeviceExtension; ext->Kind = UsbPdoRootHub; ext->Hcd = hcd; ext->Device = (USB_DEVICE*)0; hcd->RootHubPdo = pdo; st = IoRegisterPhysicalDevice(pdo); if (!KSUCCESS(st) && st != NTLSTATUS_ALREADY_EXISTS) return st; DbgPrint("usb: created root hub PDO ports=%u\n", hcd->MaxRootPorts); return NTLSTATUS_SUCCESS; } // Creates device Pdo. KSTATUS UsbCreateDevicePdo(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* parent) { if (!hcd || !xdev) return NTLSTATUS_INVALID_PARAMETER; if (xdev->UsbPdo) return NTLSTATUS_ALREADY_EXISTS; USB_DEVICE* udev = UsbAllocateDevice(); if (!udev) return NTLSTATUS_NO_MEMORY; udev->Hcd = hcd; udev->HcdDeviceContext = xdev; udev->State = xdev->Configured ? UsbDeviceConfigured : (xdev->Addressed ? UsbDeviceAddressed : UsbDeviceAttached); udev->SlotId = xdev->SlotId; udev->Speed = xdev->Speed; udev->Address = xdev->SlotId; udev->MaxPacketSize0 = xdev->MaxPacketSize0; udev->VendorId = xdev->VendorId; udev->ProductId = xdev->ProductId; udev->BcdUsb = xdev->BcdUsb; udev->BcdDevice = xdev->BcdDevice; udev->DeviceClass = xdev->DeviceClass; udev->DeviceSubclass = xdev->DeviceSubclass; udev->DeviceProtocol = xdev->DeviceProtocol; udev->ConfigurationValue = xdev->ConfigurationValue; udev->Path = xdev->Path; if (udev->Path.RootPort == 0) { udev->Path.RootPort = xdev->Port; udev->Path.Depth = 0; udev->Path.RouteString = 0; } IO_DEVICE_ID ids[5]; char idbuf[5][USB_MAX_DEVICE_ID_TEXT]; size_t idCount = 0; UsbMakeDeviceIds(xdev, ids, idbuf, &idCount); DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver, parent ? parent : (hcd->RootHubPdo ? hcd->RootHubPdo : hcd->ControllerDevice), ids, idCount, sizeof(USB_PDO_EXTENSION), &pdo); if (!KSUCCESS(st)) { UsbDereferenceDevice(udev); return st; } (void)IoSetDeviceName(pdo, SYS_SV_LITERAL("usb-device")); udev->Pdo = pdo; IoReferenceDeviceObject(pdo); USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)pdo->DeviceExtension; ext->Kind = UsbPdoDevice; ext->Hcd = hcd; ext->Device = udev; xdev->UsbDevice = udev; xdev->UsbPdo = pdo; st = IoRegisterPhysicalDevice(pdo); if (!KSUCCESS(st) && st != NTLSTATUS_ALREADY_EXISTS) return st; DbgPrint("usb: created device PDO slot=%u vid=%04x pid=%04x ids=%llu\n", xdev->SlotId, xdev->VendorId, xdev->ProductId, (unsigned long long)idCount); return NTLSTATUS_SUCCESS; } // Creates interface Pdos. KSTATUS UsbCreateInterfacePdos(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* parent) { if (!hcd || !xdev || !xdev->UsbDevice) return NTLSTATUS_INVALID_PARAMETER; DEVICE_OBJECT* deviceParent = parent ? parent : xdev->UsbPdo; if (!deviceParent) return NTLSTATUS_INVALID_PARAMETER; for (uint8_t i = 0; i < xdev->InterfaceCount; i++) { USB_TRACKED_INTERFACE* iface = &xdev->Interfaces[i]; if (!iface->Present) continue; IO_DEVICE_ID ids[3]; char idbuf[3][USB_MAX_DEVICE_ID_TEXT]; size_t idCount = 0; UsbMakeInterfaceIds(xdev, iface, ids, idbuf, &idCount); DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreatePhysicalDevice(&gUsbCompositeDriver, deviceParent, ids, idCount, sizeof(USB_PDO_EXTENSION), &pdo); if (!KSUCCESS(st)) { DbgPrint("usb: create interface PDO slot=%u if=%u failed st=0x%x\n", xdev->SlotId, iface->Descriptor.bInterfaceNumber, st); return st; } (void)IoSetDeviceName(pdo, SYS_SV_LITERAL("usb-interface")); USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)pdo->DeviceExtension; ext->Kind = UsbPdoInterface; ext->Hcd = hcd; ext->Device = xdev->UsbDevice; ext->InterfaceIndex = i; memcpy(&ext->InterfaceDescriptor, &iface->Descriptor, sizeof(iface->Descriptor)); ext->EndpointCount = iface->EndpointCount; for (uint8_t ep = 0; ep < iface->EndpointCount && ep < USB_MAX_TRACKED_ENDPOINTS; ep++) memcpy(&ext->Endpoints[ep], &iface->Endpoints[ep], sizeof(USB_ENDPOINT_DESCRIPTOR)); st = IoRegisterPhysicalDevice(pdo); if (!KSUCCESS(st) && st != NTLSTATUS_ALREADY_EXISTS) return st; DbgPrint("usb: created interface PDO slot=%u if=%u class=%02x subclass=%02x proto=%02x\n", xdev->SlotId, iface->Descriptor.bInterfaceNumber, iface->Descriptor.bInterfaceClass, iface->Descriptor.bInterfaceSubClass, iface->Descriptor.bInterfaceProtocol); } return NTLSTATUS_SUCCESS; } // Implements the reference First Child operation. static DEVICE_OBJECT* UsbReferenceFirstChild(DEVICE_OBJECT* parent) { if (!parent) return (DEVICE_OBJECT*)0; uintptr_t flags = KeAcquireSpinlock(&parent->Lock); DEVICE_OBJECT* child = parent->FirstChild; if (child) IoReferenceDeviceObject(child); KeReleaseSpinlock(&parent->Lock, flags); return child; } // Implements the send Pnp If Alive operation. static void UsbSendPnpIfAlive(DEVICE_OBJECT* pdo, IO_PNP_MINOR_FUNCTION minor) { if (!pdo || pdo->DeletePending) return; (void)IoSendPnpIrpSync(pdo, minor); } // Releases device Object Children. KSTATUS UsbRemoveDeviceObjectChildren(DEVICE_OBJECT* parent, bool surprise) { if (!parent) return NTLSTATUS_INVALID_PARAMETER; while (true) { DEVICE_OBJECT* child = UsbReferenceFirstChild(parent); if (!child) break; if (child->Type == DeviceObjectPhysical) (void)UsbRemovePdoSubtree(child, surprise); else (void)UsbRemoveDeviceObjectChildren(child, surprise); IoDereferenceDeviceObject(child); } return NTLSTATUS_SUCCESS; } // Releases pdo Subtree. KSTATUS UsbRemovePdoSubtree(DEVICE_OBJECT* pdo, bool surprise) { if (!pdo || pdo->Type != DeviceObjectPhysical) return NTLSTATUS_INVALID_PARAMETER; // Child PDOs may be owned either directly by the PDO or by an attached FDO // such as usb-composite or usb-hub. The attached driver's PnP handler is // therefore responsible for removing its own child PDOs before forwarding // REMOVE_DEVICE to the lower PDO. (void)UsbRemoveDeviceObjectChildren(pdo, surprise); if (surprise) UsbSendPnpIfAlive(pdo, IRP_MN_SURPRISE_REMOVAL); UsbSendPnpIfAlive(pdo, IRP_MN_REMOVE_DEVICE); return NTLSTATUS_SUCCESS; } // Submits urb To Hcd. static KSTATUS UsbSubmitUrbToHcd(USB_PDO_EXTENSION* ext, USB_URB* urb) { if (!ext || !urb) return NTLSTATUS_INVALID_PARAMETER; USB_HCD* hcd = ext->Hcd; if (!hcd || !hcd->Operations || !hcd->Operations->SubmitUrb) return NTLSTATUS_NOT_SUPPORTED; if (!urb->Device) urb->Device = ext->Device; if (!urb->Device) return NTLSTATUS_INVALID_PARAMETER; return hcd->Operations->SubmitUrb(hcd, urb); } // Cancels urb. KSTATUS UsbCancelUrb(USB_URB* urb) { if (!urb || !urb->Device || !urb->Device->Hcd) return NTLSTATUS_INVALID_PARAMETER; USB_HCD* hcd = urb->Device->Hcd; if (!hcd->Operations || !hcd->Operations->CancelUrb) return NTLSTATUS_NOT_SUPPORTED; return hcd->Operations->CancelUrb(hcd, urb); } // Submits urb Sync. KSTATUS UsbSubmitUrbSync(DEVICE_OBJECT* target, USB_URB* urb) { if (!target || !urb) return NTLSTATUS_INVALID_PARAMETER; IRP irp; IoInitializeIrp(&irp, 1); IoBuildDeviceIoControlIrp(&irp, IRP_MJ_INTERNAL_DEVICE_CONTROL, USB_IOCTL_SUBMIT_URB, urb, sizeof(*urb), urb, sizeof(*urb)); KSTATUS st = IoSubmitIrpSync(target, &irp); return KSUCCESS(st) ? urb->Status : st; } // Implements the async Urb Irp Complete operation. static void UsbAsyncUrbIrpComplete(IRP* irp, void* context) { (void)context; IoFreeIrp(irp); } // Submits urb Async. KSTATUS UsbSubmitUrbAsync(DEVICE_OBJECT* target, USB_URB* urb) { if (!target || !urb) return NTLSTATUS_INVALID_PARAMETER; IRP* irp = (IRP*)0; KSTATUS st = IoAllocateIrp(1, &irp); if (!KSUCCESS(st)) return st; IoBuildDeviceIoControlIrp(irp, IRP_MJ_INTERNAL_DEVICE_CONTROL, USB_IOCTL_SUBMIT_URB, urb, sizeof(*urb), urb, sizeof(*urb)); IoSetCompletionRoutine(irp, UsbAsyncUrbIrpComplete, (void*)0); st = IoSubmitIrp(target, irp); if (st != NTLSTATUS_PENDING && !(irp->Flags & IRP_FLAG_COMPLETED)) { IoFreeIrp(irp); return st; } return st; } // Dispatches internal Device Control. KSTATUS UsbDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp) { if (!device || !irp) return NTLSTATUS_INVALID_PARAMETER; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios) return NTLSTATUS_INVALID_PARAMETER; if (ios->Parameters.DeviceIoControl.IoControlCode != USB_IOCTL_SUBMIT_URB) { IoCompleteRequest(irp, NTLSTATUS_NOT_SUPPORTED, 0); return NTLSTATUS_PENDING; } if (ios->Parameters.DeviceIoControl.InputLength < sizeof(USB_URB) || !ios->Parameters.DeviceIoControl.InputBuffer) { IoCompleteRequest(irp, NTLSTATUS_INVALID_PARAMETER, 0); return NTLSTATUS_PENDING; } USB_URB* urb = (USB_URB*)ios->Parameters.DeviceIoControl.InputBuffer; USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)device->DeviceExtension; urb->Irp = irp; KSTATUS st = UsbSubmitUrbToHcd(ext, urb); urb->Status = st; if (st == NTLSTATUS_PENDING) { urb->Pending = true; return NTLSTATUS_PENDING; } urb->Pending = false; IoCompleteRequest(irp, st, KSUCCESS(st) ? urb->ActualLength : 0); return NTLSTATUS_PENDING; } // Dispatches pdo Pnp. KSTATUS UsbDispatchPdoPnp(DEVICE_OBJECT* device, IRP* irp) { if (!device || !irp) return NTLSTATUS_INVALID_PARAMETER; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios || ios->MajorFunction != IRP_MJ_PNP) return NTLSTATUS_INVALID_PARAMETER; USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)device->DeviceExtension; KSTATUS st = NTLSTATUS_SUCCESS; switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) { case IRP_MN_START_DEVICE: device->PnpState = DevicePnpStarted; if (ext && ext->Device && ext->Device->State < UsbDeviceConfigured) ext->Device->State = UsbDeviceConfigured; break; case IRP_MN_QUERY_REMOVE_DEVICE: device->PnpState = DevicePnpRemovePending; break; case IRP_MN_CANCEL_REMOVE_DEVICE: device->PnpState = DevicePnpStarted; break; case IRP_MN_SURPRISE_REMOVAL: device->PnpState = DevicePnpSurpriseRemoved; if (ext && ext->Device) ext->Device->State = UsbDeviceSurpriseRemoved; break; case IRP_MN_REMOVE_DEVICE: device->PnpState = DevicePnpDeleted; if (ext && ext->Device) ext->Device->State = UsbDeviceRemoved; IoDeleteDevice(device); break; default: st = NTLSTATUS_NOT_SUPPORTED; break; } if (st == NTLSTATUS_NOT_SUPPORTED) return st; IoCompleteRequest(irp, st, 0); return NTLSTATUS_PENDING; }