661 lines
23 KiB
C
661 lines
23 KiB
C
// 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 && gUsbDdk && gUsbDdk->Dma)
|
|
gUsbDdk->Dma->FreeCommonBuffer(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;
|
|
|
|
KSTATUS st = IoRegisterDriverObject(&gUsbBusDriver);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
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->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;
|
|
}
|