usb hubs, hotplug support, unplug support, saving log files to disk, smt
This commit is contained in:
1 parent
b838f8d83d
commit
27b31f62b6
39 files changed
+6294
-735
No files matched your search
@@ -0,0 +1,656 @@
|
||||
// usb_bus.c - USB bus/HCD/PDO/URB core model.
|
||||
//
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void UsbPipeDestroy(OBJECT* object) {
|
||||
USB_PIPE* pipe = (USB_PIPE*)object;
|
||||
if (pipe->Device) {
|
||||
UsbDereferenceDevice(pipe->Device);
|
||||
pipe->Device = (USB_DEVICE*)0;
|
||||
}
|
||||
}
|
||||
|
||||
void UsbReferenceDevice(USB_DEVICE* dev) {
|
||||
if (dev) ObReferenceObject(&dev->Header);
|
||||
}
|
||||
|
||||
void UsbDereferenceDevice(USB_DEVICE* dev) {
|
||||
if (dev) ObDereferenceObject(&dev->Header);
|
||||
}
|
||||
|
||||
void UsbReferencePipe(USB_PIPE* pipe) {
|
||||
if (pipe) ObReferenceObject(&pipe->Header);
|
||||
}
|
||||
|
||||
void UsbDereferencePipe(USB_PIPE* pipe) {
|
||||
if (pipe) ObDereferenceObject(&pipe->Header);
|
||||
}
|
||||
|
||||
void* UsbAllocateDmaBuffer(USB_DEVICE* dev, size_t pages, PHYSICAL_ADDRESS* outPhys) {
|
||||
if (!dev || !dev->Hcd || !dev->Hcd->Operations || !dev->Hcd->Operations->AllocateDma)
|
||||
return (void*)0;
|
||||
return dev->Hcd->Operations->AllocateDma(dev->Hcd, pages, outPhys);
|
||||
}
|
||||
|
||||
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 STATUS_INVALID_PARAMETER;
|
||||
|
||||
USB_PIPE* pipe = (USB_PIPE*)ObAllocateObject(ObjectTypeUsbPipe, sizeof(USB_PIPE));
|
||||
if (!pipe) return STATUS_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 STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static SYS_STRING_VIEW UsbIdFromBuffer(char* buf) {
|
||||
return SysSvFromCstr(buf);
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
static void UsbIdAppendLiteral(char* buf, size_t cap, size_t* pos, const char* text) {
|
||||
if (!text)
|
||||
return;
|
||||
while (*text)
|
||||
UsbIdPutChar(buf, cap, pos, *text++);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
static void UsbFormatComposite(char* buf, size_t cap) {
|
||||
size_t pos = 0;
|
||||
if (cap) buf[0] = '\0';
|
||||
UsbIdAppendLiteral(buf, cap, &pos, "USB\\COMPOSITE");
|
||||
}
|
||||
|
||||
static void UsbFormatPciXhci(char* buf, size_t cap, uint16_t vendor, uint16_t device) {
|
||||
size_t pos = 0;
|
||||
if (cap) buf[0] = '\0';
|
||||
UsbIdAppendLiteral(buf, cap, &pos, "PCI\\VEN_");
|
||||
UsbIdAppendHex(buf, cap, &pos, vendor, 4);
|
||||
UsbIdAppendLiteral(buf, cap, &pos, "&DEV_");
|
||||
UsbIdAppendHex(buf, cap, &pos, device, 4);
|
||||
UsbIdAppendLiteral(buf, cap, &pos, "&CC_0C0330");
|
||||
}
|
||||
|
||||
static void UsbFormatRootHub(char* buf, size_t cap) {
|
||||
size_t pos = 0;
|
||||
if (cap) buf[0] = '\0';
|
||||
UsbIdAppendLiteral(buf, cap, &pos, "USB\\ROOT_HUB");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
KSTATUS UsbInitializeBusModel(void) {
|
||||
if (gUsbBusModelReady)
|
||||
return STATUS_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 STATUS_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;
|
||||
}
|
||||
|
||||
KSTATUS UsbCreateControllerPdo(USB_HCD* hcd, uint16_t vendor, uint16_t device) {
|
||||
if (!hcd) return STATUS_INVALID_PARAMETER;
|
||||
if (hcd->ControllerPdo) return STATUS_ALREADY_EXISTS;
|
||||
|
||||
IO_DEVICE_ID ids[2];
|
||||
char idbuf[2][USB_MAX_DEVICE_ID_TEXT];
|
||||
UsbFormatPciXhci(idbuf[0], USB_MAX_DEVICE_ID_TEXT, vendor, device);
|
||||
{
|
||||
size_t pos = 0;
|
||||
idbuf[1][0] = '\0';
|
||||
UsbIdAppendLiteral(idbuf[1], USB_MAX_DEVICE_ID_TEXT, &pos, "PCI\\CC_0C0330");
|
||||
}
|
||||
ids[0] = (IO_DEVICE_ID){ IoBusPci, UsbIdFromBuffer(idbuf[0]) };
|
||||
ids[1] = (IO_DEVICE_ID){ IoBusPci, UsbIdFromBuffer(idbuf[1]) };
|
||||
|
||||
DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0;
|
||||
KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver, (DEVICE_OBJECT*)0, ids, 2, sizeof(USB_PDO_EXTENSION), &pdo);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
(void)IoSetDeviceName(pdo, SYS_SV_LITERAL("xhci-controller"));
|
||||
|
||||
USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)pdo->DeviceExtension;
|
||||
ext->Kind = UsbPdoController;
|
||||
ext->Hcd = hcd;
|
||||
hcd->ControllerPdo = pdo;
|
||||
|
||||
st = IoRegisterPhysicalDevice(pdo);
|
||||
if (!KSUCCESS(st) && st != STATUS_ALREADY_EXISTS)
|
||||
return st;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS UsbCreateRootHubPdo(USB_HCD* hcd) {
|
||||
if (!hcd) return STATUS_INVALID_PARAMETER;
|
||||
if (hcd->RootHubPdo) return STATUS_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->ControllerPdo,
|
||||
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 != STATUS_ALREADY_EXISTS)
|
||||
return st;
|
||||
|
||||
DbgPrint("usb: created root hub PDO ports=%u\n", hcd->MaxRootPorts);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS UsbCreateDevicePdo(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* parent) {
|
||||
if (!hcd || !xdev) return STATUS_INVALID_PARAMETER;
|
||||
if (xdev->UsbPdo) return STATUS_ALREADY_EXISTS;
|
||||
|
||||
USB_DEVICE* udev = UsbAllocateDevice();
|
||||
if (!udev) return STATUS_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->ControllerPdo),
|
||||
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 != STATUS_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 STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS UsbCreateInterfacePdos(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* parent) {
|
||||
if (!hcd || !xdev || !xdev->UsbDevice) return STATUS_INVALID_PARAMETER;
|
||||
DEVICE_OBJECT* deviceParent = parent ? parent : xdev->UsbPdo;
|
||||
if (!deviceParent) return STATUS_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 != STATUS_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 STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void UsbSendPnpIfAlive(DEVICE_OBJECT* pdo, IO_PNP_MINOR_FUNCTION minor) {
|
||||
if (!pdo || pdo->DeletePending)
|
||||
return;
|
||||
(void)IoSendPnpIrpSync(pdo, minor);
|
||||
}
|
||||
|
||||
KSTATUS UsbRemoveDeviceObjectChildren(DEVICE_OBJECT* parent, bool surprise) {
|
||||
if (!parent)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
for (;;) {
|
||||
DEVICE_OBJECT* child = UsbReferenceFirstChild(parent);
|
||||
if (!child)
|
||||
break;
|
||||
|
||||
if (child->Type == DeviceObjectPhysical)
|
||||
(void)UsbRemovePdoSubtree(child, surprise);
|
||||
else
|
||||
(void)UsbRemoveDeviceObjectChildren(child, surprise);
|
||||
|
||||
IoDereferenceDeviceObject(child);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS UsbRemovePdoSubtree(DEVICE_OBJECT* pdo, bool surprise) {
|
||||
if (!pdo || pdo->Type != DeviceObjectPhysical)
|
||||
return STATUS_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 STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS UsbSubmitUrbToHcd(USB_PDO_EXTENSION* ext, USB_URB* urb) {
|
||||
if (!ext || !urb) return STATUS_INVALID_PARAMETER;
|
||||
USB_HCD* hcd = ext->Hcd;
|
||||
if (!hcd || !hcd->Operations || !hcd->Operations->SubmitUrb)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
if (!urb->Device)
|
||||
urb->Device = ext->Device;
|
||||
if (!urb->Device)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
return hcd->Operations->SubmitUrb(hcd, urb);
|
||||
}
|
||||
|
||||
KSTATUS UsbCancelUrb(USB_URB* urb) {
|
||||
if (!urb || !urb->Device || !urb->Device->Hcd)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
USB_HCD* hcd = urb->Device->Hcd;
|
||||
if (!hcd->Operations || !hcd->Operations->CancelUrb)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
return hcd->Operations->CancelUrb(hcd, urb);
|
||||
}
|
||||
|
||||
KSTATUS UsbSubmitUrbSync(DEVICE_OBJECT* target, USB_URB* urb) {
|
||||
if (!target || !urb) return STATUS_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;
|
||||
}
|
||||
|
||||
static void UsbAsyncUrbIrpComplete(IRP* irp, void* context) {
|
||||
(void)context;
|
||||
IoFreeIrp(irp);
|
||||
}
|
||||
|
||||
KSTATUS UsbSubmitUrbAsync(DEVICE_OBJECT* target, USB_URB* urb) {
|
||||
if (!target || !urb) return STATUS_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 != STATUS_PENDING && !(irp->Flags & IRP_FLAG_COMPLETED)) {
|
||||
IoFreeIrp(irp);
|
||||
return st;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
KSTATUS UsbDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (!device || !irp) return STATUS_INVALID_PARAMETER;
|
||||
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
|
||||
if (!ios) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (ios->Parameters.DeviceIoControl.IoControlCode != USB_IOCTL_SUBMIT_URB) {
|
||||
IoCompleteRequest(irp, STATUS_NOT_SUPPORTED, 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
if (ios->Parameters.DeviceIoControl.InputLength < sizeof(USB_URB) ||
|
||||
!ios->Parameters.DeviceIoControl.InputBuffer) {
|
||||
IoCompleteRequest(irp, STATUS_INVALID_PARAMETER, 0);
|
||||
return STATUS_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 == STATUS_PENDING) {
|
||||
urb->Pending = true;
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
urb->Pending = false;
|
||||
IoCompleteRequest(irp, st, KSUCCESS(st) ? urb->ActualLength : 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
KSTATUS UsbDispatchPdoPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (!device || !irp) return STATUS_INVALID_PARAMETER;
|
||||
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
|
||||
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)device->DeviceExtension;
|
||||
KSTATUS st = STATUS_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 = STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (st == STATUS_NOT_SUPPORTED)
|
||||
return st;
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
Reference in new issue
Block a user