Files
nt-lite/code/drivers/bus/usb/usb_hub.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

1140 lines
43 KiB
C

// USB hub driver. This file owns hub probing, port status handling, and child-device enumeration.
// Root hubs and external hubs are represented by the same FDO abstraction. The
// xHCI root hub feeds port-change events directly into this driver, while an
// external hub feeds the same port state machine from its interrupt-IN change
// bitmap.
#include "usb.h"
DRIVER_OBJECT gUsbHubDriver;
// Releases the external-hub change bitmap after transfer rundown.
static void UsbHubReleaseDma(USB_HUB_EXTENSION* hub) {
if (!hub)
return;
UsbFreeDmaBuffer(hub->ChangeBitmapDma);
hub->ChangeBitmapDma = (IO_DMA_BUFFER*)0;
hub->ChangeBitmap = (uint8_t*)0;
hub->ChangeBitmapDeviceAddress = 0;
}
static const IO_DEVICE_ID gUsbHubIds[] = {
{ IoBusUsb, { "USB\\ROOT_HUB", sizeof("USB\\ROOT_HUB") - 1 } },
{ IoBusUsb, { "USB\\CLASS_09", sizeof("USB\\CLASS_09") - 1 } },
};
static const IO_DRIVER_ID_TABLE gUsbHubIdTable = {
gUsbHubIds,
sizeof(gUsbHubIds) / sizeof(gUsbHubIds[0]),
};
#define USB_HUB_PORT_ENUMERATING 0xffu
#define USB_HUB_PORT_FAILED 0xfeu
// Implements the hub Slot Busy operation.
static bool UsbHubSlotBusy(uint8_t slot) {
return slot != 0 && slot != USB_HUB_PORT_ENUMERATING && slot != USB_HUB_PORT_FAILED;
}
// Implements the hub Root Port Connected operation.
static bool UsbHubRootPortConnected(XHCI_CONTROLLER* xhci, uint8_t port) {
if (!xhci || !xhci->Op || port == 0) return false;
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
return (portsc & XHCI_PORTSC_CCS) != 0;
}
// Implements the hub Clear Root Port Failure operation.
static void UsbHubClearRootPortFailure(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || port == 0 || port > USB_ROOT_HUB_MAX_PORTS) return;
hub->RootPortFailCount[port] = 0;
if (hub->RootPortSlot[port] == USB_HUB_PORT_FAILED)
hub->RootPortSlot[port] = 0;
}
// Implements the hub Mark Root Port Failed operation.
static void UsbHubMarkRootPortFailed(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || port == 0 || port > USB_ROOT_HUB_MAX_PORTS) return;
hub->RootPortSlot[port] = USB_HUB_PORT_FAILED;
hub->RootPortState[port] = UsbHubPortFailedConnected;
}
static KSTATUS UsbHubEnumerateRootPortWithRetries(USB_HUB_EXTENSION* hub,
XHCI_CONTROLLER* xhci,
uint8_t port,
uint8_t* outSlot) {
if (outSlot) *outSlot = 0;
if (!hub || !xhci || port == 0 || port > USB_ROOT_HUB_MAX_PORTS)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS last = NTLSTATUS_DEVICE_ERROR;
uint8_t maxAttempts = USB_ROOT_PORT_ENUM_RETRIES;
if (maxAttempts == 0) maxAttempts = 1;
for (uint8_t attempt = 1; attempt <= maxAttempts; attempt++) {
if (!UsbHubRootPortConnected(xhci, port)) {
last = NTLSTATUS_NOT_FOUND;
break;
}
uint8_t slot = 0;
last = XhciEnumeratePortEx(xhci, port, &slot);
if (KSUCCESS(last)) {
if (outSlot) *outSlot = slot;
hub->RootPortFailCount[port] = 0;
hub->RootPortState[port] = UsbHubPortChildPresent;
return NTLSTATUS_SUCCESS;
}
hub->RootPortFailCount[port] = attempt;
// On real Intel laptop controllers some internal full-speed devices
// respond with xHCI USB Transaction Error during Address Device while
// the port is still settling. Retry that boundedly while the port is
// held in ENUMERATING state so queued PSC events cannot start parallel
// enumerations. Descriptor/control timeouts are not retried here; they
// are usually dead/firmware-owned ports and retrying them stalls boot.
if (last != NTLSTATUS_DEVICE_ERROR || attempt == maxAttempts)
break;
DbgPrint("usb-hub: root port %u enumeration attempt %u failed st=0x%x; retrying\n",
port, attempt, last);
UsbDelayUs(xhci->Ddk, 50000);
}
if (UsbHubRootPortConnected(xhci, port)) {
UsbHubMarkRootPortFailed(hub, port);
} else {
UsbHubClearRootPortFailure(hub, port);
hub->RootPortState[port] = UsbHubPortDisconnected;
}
return last;
}
// Enumerates one connected root port through the same debounce path at boot and hotplug.
static KSTATUS UsbHubEnumerateConnectedRootPort(USB_HUB_EXTENSION* hub,
XHCI_CONTROLLER* xhci,
uint8_t port,
uint8_t* outSlot) {
if (outSlot)
*outSlot = 0;
if (!hub || !xhci || port == 0 || port > hub->PortCount ||
port > USB_ROOT_HUB_MAX_PORTS)
return NTLSTATUS_INVALID_PARAMETER;
hub->RootPortSlot[port] = USB_HUB_PORT_ENUMERATING;
hub->RootPortState[port] = UsbHubPortConnectedDebounce;
UsbDelayUs(xhci->Ddk, 30000u);
if (!UsbHubRootPortConnected(xhci, port)) {
UsbHubClearRootPortFailure(hub, port);
hub->RootPortSlot[port] = 0;
hub->RootPortState[port] = UsbHubPortDisconnected;
return NTLSTATUS_NOT_FOUND;
}
hub->RootPortState[port] = UsbHubPortEnumerating;
uint8_t slot = 0;
KSTATUS status = UsbHubEnumerateRootPortWithRetries(hub, xhci, port, &slot);
if (KSUCCESS(status)) {
hub->RootPortSlot[port] = slot;
hub->RootPortState[port] = UsbHubPortChildPresent;
if (outSlot)
*outSlot = slot;
}
return status;
}
// Implements the hub Pop Pending Root Port operation.
static bool UsbHubPopPendingRootPort(USB_HUB_EXTENSION* hub, uint8_t* outPort) {
if (!hub || !outPort) return false;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
for (uint32_t word = 0; word < 8; word++) {
uint32_t bits = hub->PendingRootPorts[word];
if (!bits)
continue;
for (uint8_t bit = 0; bit < 32; bit++) {
if (!(bits & (1u << bit)))
continue;
hub->PendingRootPorts[word] &= ~(1u << bit);
KeReleaseSpinlock(&hub->PendingLock, flags);
*outPort = (uint8_t)(word * 32u + bit);
return true;
}
}
KeReleaseSpinlock(&hub->PendingLock, flags);
return false;
}
// Implements the hub Pop Pending External Port operation.
static bool UsbHubPopPendingExternalPort(USB_HUB_EXTENSION* hub, uint8_t* outPort) {
if (!hub || !outPort) return false;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
uint32_t bits = hub->PendingHubPorts[0];
if (!bits) {
KeReleaseSpinlock(&hub->PendingLock, flags);
return false;
}
for (uint8_t port = 1; port <= hub->PortCount && port <= USB_EXTERNAL_HUB_MAX_PORTS; port++) {
if (!(bits & (1u << port)))
continue;
hub->PendingHubPorts[0] &= ~(1u << port);
KeReleaseSpinlock(&hub->PendingLock, flags);
*outPort = port;
return true;
}
hub->PendingHubPorts[0] = 0;
KeReleaseSpinlock(&hub->PendingLock, flags);
return false;
}
// Implements the hub Record Root Port Change operation.
static void UsbHubRecordRootPortChange(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || port == 0 || port > hub->PortCount)
return;
uint32_t word = (uint32_t)port / 32u;
uint32_t bit = (uint32_t)port & 31u;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
hub->PendingRootPorts[word] |= (1u << bit);
KeReleaseSpinlock(&hub->PendingLock, flags);
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
}
// Implements the hub Queue External Port Change operation.
void UsbHubQueueExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || port == 0 || port > hub->PortCount || port > USB_EXTERNAL_HUB_MAX_PORTS)
return;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
hub->PendingHubPorts[0] |= (1u << port);
KeReleaseSpinlock(&hub->PendingLock, flags);
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
}
// Implements the hub Remove Slot Child operation.
static void UsbHubRemoveSlotChild(USB_HUB_EXTENSION* hub, uint8_t port, uint8_t slot) {
if (!hub || !hub->Hcd || slot == 0)
return;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
if (!xhci || slot > XHCI_MAX_TRACKED_SLOTS)
return;
XHCI_DEVICE* xdev = &xhci->Devices[slot];
DbgPrint("usb-hub: %s port %u removing slot=%u\n",
hub->Kind == UsbHubKindRoot ? "root" : "hub", port, slot);
if (xdev->Allocated && xhci->Running)
(void)XhciDisableSlot(xhci, slot);
if (xdev->UsbPdo)
(void)UsbRemovePdoSubtree(xdev->UsbPdo, true);
if (xdev->UsbDevice) {
xdev->UsbDevice->State = UsbDeviceRemoved;
UsbDereferenceDevice(xdev->UsbDevice);
xdev->UsbDevice = (USB_DEVICE*)0;
}
XhciReleaseDeviceDma(xdev);
memset(xdev, 0, sizeof(*xdev));
if (hub->Kind == UsbHubKindRoot && port <= USB_ROOT_HUB_MAX_PORTS) {
hub->RootPortSlot[port] = 0;
hub->RootPortState[port] = UsbHubPortDisconnected;
} else if (port <= USB_EXTERNAL_HUB_MAX_PORTS) {
hub->ChildSlot[port] = 0;
hub->ChildPortState[port] = UsbHubPortDisconnected;
}
}
// Implements the hub Remove Root Child operation.
static void UsbHubRemoveRootChild(USB_HUB_EXTENSION* hub, uint8_t port, uint8_t slot) {
UsbHubRemoveSlotChild(hub, port, slot);
}
static KSTATUS UsbHubControlIn(USB_HUB_EXTENSION* hub,
uint8_t requestType,
uint8_t request,
uint16_t value,
uint16_t index,
void* buffer,
uint64_t bufferDeviceAddress,
uint16_t length) {
if (!hub || !hub->LowerDevice || !buffer || !bufferDeviceAddress || !length)
return NTLSTATUS_INVALID_PARAMETER;
USB_URB urb;
memset(&urb, 0, sizeof(urb));
urb.Function = UrbFunctionControlTransfer;
urb.Direction = UsbTransferIn;
urb.Device = hub->Device;
urb.Buffer = buffer;
urb.BufferDeviceAddress = bufferDeviceAddress;
urb.Length = length;
urb.Setup.bmRequestType = requestType;
urb.Setup.bRequest = request;
urb.Setup.wValue = value;
urb.Setup.wIndex = index;
urb.Setup.wLength = length;
return UsbSubmitUrbSync(hub->LowerDevice, &urb);
}
static KSTATUS UsbHubControlNoData(USB_HUB_EXTENSION* hub,
uint8_t requestType,
uint8_t request,
uint16_t value,
uint16_t index) {
if (!hub || !hub->LowerDevice)
return NTLSTATUS_INVALID_PARAMETER;
USB_URB urb;
memset(&urb, 0, sizeof(urb));
urb.Function = UrbFunctionControlTransfer;
urb.Direction = UsbTransferNoData;
urb.Device = hub->Device;
urb.Setup.bmRequestType = requestType;
urb.Setup.bRequest = request;
urb.Setup.wValue = value;
urb.Setup.wIndex = index;
urb.Setup.wLength = 0;
return UsbSubmitUrbSync(hub->LowerDevice, &urb);
}
// Implements the hub Read Descriptor operation.
static KSTATUS UsbHubReadDescriptor(USB_HUB_EXTENSION* hub, USB_HUB_DESCRIPTOR* outDesc) {
if (!hub || !outDesc || !hub->ChangeBitmap || !hub->ChangeBitmapDeviceAddress)
return NTLSTATUS_INVALID_PARAMETER;
memset(hub->ChangeBitmap, 0, USB_HUB_CHANGE_BITMAP_BYTES);
KSTATUS st = UsbHubControlIn(hub,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
USB_REQ_GET_DESCRIPTOR,
(uint16_t)(USB_DESC_HUB << 8),
0,
hub->ChangeBitmap,
hub->ChangeBitmapDeviceAddress,
USB_HUB_CHANGE_BITMAP_BYTES);
if (!KSUCCESS(st))
return st;
memcpy(outDesc, hub->ChangeBitmap, sizeof(USB_HUB_DESCRIPTOR));
if (outDesc->bDescriptorType != USB_DESC_HUB || outDesc->bNbrPorts == 0)
return NTLSTATUS_DEVICE_ERROR;
return NTLSTATUS_SUCCESS;
}
// Implements the hub Get Port Status operation.
static KSTATUS UsbHubGetPortStatus(USB_HUB_EXTENSION* hub, uint8_t port, USB_HUB_PORT_STATUS* outStatus) {
if (!hub || !outStatus || port == 0)
return NTLSTATUS_INVALID_PARAMETER;
memset(hub->ChangeBitmap, 0, USB_HUB_CHANGE_BITMAP_BYTES);
KSTATUS st = UsbHubControlIn(hub,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER,
USB_REQ_GET_STATUS,
0,
port,
hub->ChangeBitmap,
hub->ChangeBitmapDeviceAddress,
sizeof(USB_HUB_PORT_STATUS));
if (!KSUCCESS(st))
return st;
memcpy(outStatus, hub->ChangeBitmap, sizeof(USB_HUB_PORT_STATUS));
return NTLSTATUS_SUCCESS;
}
// Implements the hub Set Port Feature operation.
static KSTATUS UsbHubSetPortFeature(USB_HUB_EXTENSION* hub, uint8_t port, uint16_t feature) {
return UsbHubControlNoData(hub,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER,
USB_REQ_SET_FEATURE,
feature,
port);
}
// Implements the hub Clear Port Feature operation.
static KSTATUS UsbHubClearPortFeature(USB_HUB_EXTENSION* hub, uint8_t port, uint16_t feature) {
return UsbHubControlNoData(hub,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER,
USB_REQ_CLEAR_FEATURE,
feature,
port);
}
// Implements the hub Speed From Port Status operation.
static uint8_t UsbHubSpeedFromPortStatus(uint16_t status) {
if (status & USB_HUB_PORT_STATUS_LOW_SPEED)
return 2u;
if (status & USB_HUB_PORT_STATUS_HIGH_SPEED)
return 3u;
return 1u;
}
// Implements the hub Reset External Port operation.
static KSTATUS UsbHubResetExternalPort(USB_HUB_EXTENSION* hub, uint8_t port, uint8_t* outSpeed) {
if (outSpeed) *outSpeed = 0;
if (!hub || port == 0)
return NTLSTATUS_INVALID_PARAMETER;
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_CONNECTION);
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_ENABLE);
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
KSTATUS st = UsbHubSetPortFeature(hub, port, USB_HUB_PORT_FEAT_RESET);
if (!KSUCCESS(st))
return st;
USB_HUB_PORT_STATUS ps;
memset(&ps, 0, sizeof(ps));
for (uint32_t i = 0; i < 500u; i++) {
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 1000u);
st = UsbHubGetPortStatus(hub, port, &ps);
if (!KSUCCESS(st))
return st;
if (ps.Change & USB_HUB_PORT_CHANGE_RESET)
break;
}
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_ENABLE);
st = UsbHubGetPortStatus(hub, port, &ps);
if (!KSUCCESS(st))
return st;
if (!(ps.Status & USB_HUB_PORT_STATUS_CONNECTION))
return NTLSTATUS_NOT_FOUND;
if (!(ps.Status & USB_HUB_PORT_STATUS_ENABLE))
return NTLSTATUS_DEVICE_ERROR;
if (outSpeed)
*outSpeed = UsbHubSpeedFromPortStatus(ps.Status);
return NTLSTATUS_SUCCESS;
}
// Implements the hub Handle External Port Change operation.
static void UsbHubHandleExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || hub->Kind != UsbHubKindExternal || port == 0 || port > hub->PortCount || port > USB_EXTERNAL_HUB_MAX_PORTS)
return;
USB_HUB_PORT_STATUS ps;
memset(&ps, 0, sizeof(ps));
KSTATUS st = UsbHubGetPortStatus(hub, port, &ps);
if (!KSUCCESS(st)) {
DbgPrint("usb-hub: slot=%u port=%u GET_STATUS failed st=0x%x\n",
hub->Device ? hub->Device->SlotId : 0, port, st);
return;
}
if (ps.Change & USB_HUB_PORT_CHANGE_CONNECTION)
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_CONNECTION);
if (ps.Change & USB_HUB_PORT_CHANGE_ENABLE)
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_ENABLE);
if (ps.Change & USB_HUB_PORT_CHANGE_RESET)
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
uint8_t oldSlot = hub->ChildSlot[port];
if (!(ps.Status & USB_HUB_PORT_STATUS_CONNECTION)) {
if (UsbHubSlotBusy(oldSlot)) {
hub->ChildPortState[port] = UsbHubPortRemoving;
UsbHubRemoveSlotChild(hub, port, oldSlot);
} else {
hub->ChildPortState[port] = UsbHubPortDisconnected;
UsbTrace("usb-hub: slot=%u port=%u disconnected\n", hub->Device ? hub->Device->SlotId : 0, port);
}
return;
}
if (oldSlot == USB_HUB_PORT_ENUMERATING) {
UsbTrace("usb-hub: slot=%u port=%u change ignored; enumeration already in progress\n",
hub->Device ? hub->Device->SlotId : 0, port);
return;
}
if (UsbHubSlotBusy(oldSlot)) {
UsbTrace("usb-hub: slot=%u port=%u change ignored; child slot=%u still present\n",
hub->Device ? hub->Device->SlotId : 0, port, oldSlot);
return;
}
hub->ChildSlot[port] = USB_HUB_PORT_ENUMERATING;
hub->ChildPortState[port] = UsbHubPortConnectedDebounce;
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 30000u);
uint8_t speed = 0;
hub->ChildPortState[port] = UsbHubPortResetting;
st = UsbHubResetExternalPort(hub, port, &speed);
if (!KSUCCESS(st)) {
hub->ChildSlot[port] = 0;
hub->ChildPortState[port] = UsbHubPortFailedConnected;
DbgPrint("usb-hub: slot=%u port=%u reset failed st=0x%x\n",
hub->Device ? hub->Device->SlotId : 0, port, st);
return;
}
hub->ChildPortState[port] = UsbHubPortEnumerating;
uint8_t slot = 0;
st = XhciEnumerateHubPortEx((XHCI_CONTROLLER*)hub->Hcd->ControllerContext, hub, port, speed, &slot);
if (KSUCCESS(st)) {
hub->ChildSlot[port] = slot;
hub->ChildPortState[port] = UsbHubPortChildPresent;
DbgPrint("usb-hub: slot=%u port=%u attached child-slot=%u speed=%s(%u)\n",
hub->Device ? hub->Device->SlotId : 0,
port,
slot,
XhciPortSpeedName(speed),
speed);
} else {
hub->ChildSlot[port] = 0;
hub->ChildPortState[port] = UsbHubPortFailedConnected;
DbgPrint("usb-hub: slot=%u port=%u enumeration failed st=0x%x\n",
hub->Device ? hub->Device->SlotId : 0, port, st);
}
}
// Implements the hub Handle Root Port Change operation.
static void UsbHubHandleRootPortChange(USB_HUB_EXTENSION* hub, uint8_t port) {
if (!hub || !hub->Hcd || port == 0 || port > hub->PortCount || port > USB_ROOT_HUB_MAX_PORTS)
return;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
if (!xhci || !xhci->Op)
return;
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
uint32_t changes = portsc & XHCI_PORTSC_CHANGE_MASK;
bool connected = (portsc & XHCI_PORTSC_CCS) != 0;
uint8_t oldSlot = hub->RootPortSlot[port];
if (changes)
XhciClearPortChanges(xhci, port);
if (!connected) {
if (UsbHubSlotBusy(oldSlot)) {
hub->RootPortState[port] = UsbHubPortRemoving;
UsbHubRemoveRootChild(hub, port, oldSlot);
} else if (oldSlot == USB_HUB_PORT_ENUMERATING)
UsbTrace("usb-hub: root port %u disconnect while enumeration is already in progress\n", port);
else {
hub->RootPortState[port] = UsbHubPortDisconnected;
DbgPrint("usb-hub: root port %u disconnected\n", port);
}
UsbHubClearRootPortFailure(hub, port);
return;
}
if (oldSlot == USB_HUB_PORT_ENUMERATING) {
UsbTrace("usb-hub: root port %u change ignored; enumeration already in progress\n", port);
return;
}
if (oldSlot == USB_HUB_PORT_FAILED) {
if (changes & XHCI_PORTSC_CSC) {
UsbTrace("usb-hub: root port %u retrying after connection-change on failed port\n", port);
UsbHubClearRootPortFailure(hub, port);
oldSlot = 0;
} else {
UsbTrace("usb-hub: root port %u change ignored; previous enumeration failed and device is still connected\n", port);
return;
}
}
if (UsbHubSlotBusy(oldSlot)) {
UsbTrace("usb-hub: root port %u change ignored; slot=%u still present\n", port, oldSlot);
return;
}
uint8_t slot = 0;
KSTATUS st = UsbHubEnumerateConnectedRootPort(hub, xhci, port, &slot);
if (KSUCCESS(st)) {
hub->RootPortSlot[port] = slot;
hub->RootPortState[port] = UsbHubPortChildPresent;
DbgPrint("usb-hub: root port %u attached slot=%u\n", port, slot);
} else {
DbgPrint("usb-hub: root port %u hotplug enumeration failed st=0x%x after %u attempt(s)\n",
port, st, hub->RootPortFailCount[port]);
}
}
static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub);
static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd);
// Implements the hub Take Initial Root Scan operation.
static bool UsbHubTakeInitialRootScan(USB_HUB_EXTENSION* hub) {
if (!hub || hub->Kind != UsbHubKindRoot)
return false;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
bool take = hub->InitialScanPending && !hub->InitialScanInProgress && !hub->InitialScanComplete;
if (take) {
hub->InitialScanPending = false;
hub->InitialScanInProgress = true;
}
KeReleaseSpinlock(&hub->PendingLock, flags);
return take;
}
// Implements the hub Worker operation.
static void UsbHubWorker(void* arg) {
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)arg;
while (hub && hub->WorkerRunning) {
KSTATUS st = KeWaitSemaphore(hub->WorkerSemaphore, KE_WAIT_INFINITE);
if (!KSUCCESS(st))
continue;
if (!hub->WorkerRunning)
break;
if (hub->Kind == UsbHubKindRoot) {
if (UsbHubTakeInitialRootScan(hub))
(void)UsbHubDoRootPortScan(hub->Hcd);
uint8_t port = 0;
while (UsbHubPopPendingRootPort(hub, &port))
UsbHubHandleRootPortChange(hub, port);
} else {
uint8_t port = 0;
while (UsbHubPopPendingExternalPort(hub, &port))
UsbHubHandleExternalPortChange(hub, port);
if (hub->Started && !hub->Removed && !hub->SurpriseRemoved && !hub->ChangeUrbPending)
UsbHubArmExternalInterrupt(hub);
}
}
if (hub && hub->WorkerFinished)
KeSignalEvent(hub->WorkerFinished);
}
// Stops the hub worker and releases its synchronization objects.
static void UsbHubStopWorker(USB_HUB_EXTENSION* hub) {
if (!hub)
return;
hub->WorkerRunning = false;
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
if (hub->WorkerThread && hub->WorkerFinished)
(void)KeWaitForEvent(hub->WorkerFinished, 0);
if (hub->WorkerThread) {
ObDereferenceObject((OBJECT*)hub->WorkerThread);
hub->WorkerThread = (THREAD*)0;
}
if (hub->WorkerFinished) {
ObDereferenceObject(&hub->WorkerFinished->Header);
hub->WorkerFinished = (EVENT*)0;
}
if (hub->WorkerSemaphore) {
ObDereferenceObject(&hub->WorkerSemaphore->Header);
hub->WorkerSemaphore = (SEMAPHORE*)0;
}
}
// Creates the synchronization objects and thread for one hub worker.
static KSTATUS UsbHubStartWorker(USB_HUB_EXTENSION* hub) {
if (!hub)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = KeSemaphoreCreate(0, 0x7fffffff, &hub->WorkerSemaphore);
if (!KSUCCESS(status))
return status;
status = KeEventCreate(EventManualReset, 0, &hub->WorkerFinished);
if (!KSUCCESS(status)) {
UsbHubStopWorker(hub);
return status;
}
hub->WorkerRunning = true;
const KE_THREAD_CREATE_PARAMETERS workerParameters =
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "usb.hub");
status = KeCreateKernelThread((uintptr_t)UsbHubWorker,
(uintptr_t)hub,
&workerParameters,
&hub->WorkerThread);
if (!KSUCCESS(status)) {
hub->WorkerRunning = false;
UsbHubStopWorker(hub);
return status;
}
return NTLSTATUS_SUCCESS;
}
// Implements the hub Do Root Port Scan operation.
static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
if (!hcd || !hcd->RootHubContext)
return NTLSTATUS_INVALID_PARAMETER;
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)hcd->RootHubContext;
if (!hub->Started)
return NTLSTATUS_OBJECT_DEAD;
uint32_t attempted = 0;
uint32_t configured = 0;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hcd->ControllerContext;
if (!xhci || !xhci->Op)
return NTLSTATUS_INVALID_PARAMETER;
for (uint8_t port = 1; port <= hub->PortCount; port++) {
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
if (!(portsc & XHCI_PORTSC_CCS))
continue;
if (hub->RootPortSlot[port]) {
UsbTrace("usb-hub: root port %u initial scan skipped; state=%u\n",
port, hub->RootPortSlot[port]);
continue;
}
attempted++;
uint8_t slot = 0;
KSTATUS st = UsbHubEnumerateConnectedRootPort(hub, xhci, port, &slot);
if (KSUCCESS(st)) {
hub->RootPortSlot[port] = slot;
hub->RootPortState[port] = UsbHubPortChildPresent;
configured++;
} else {
DbgPrint("usb-hub: root port %u enumeration failed st=0x%x after %u attempt(s)\n",
port, st, hub->RootPortFailCount[port]);
}
}
DbgPrint("usb-hub: root-port scan complete; attempted=%u configured=%u\n", attempted, configured);
hub->InitialScanComplete = true;
hub->InitialScanInProgress = false;
return NTLSTATUS_SUCCESS;
}
// Implements the hub Schedule Root Scan operation.
KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd) {
if (!hcd || !hcd->RootHubContext)
return NTLSTATUS_INVALID_PARAMETER;
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)hcd->RootHubContext;
if (hub->Kind != UsbHubKindRoot || !hub->Started)
return NTLSTATUS_OBJECT_DEAD;
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
if (!hub->InitialScanComplete && !hub->InitialScanInProgress)
hub->InitialScanPending = true;
KeReleaseSpinlock(&hub->PendingLock, flags);
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
return NTLSTATUS_SUCCESS;
}
// Implements the hub Queue Root Port Change operation.
void UsbHubQueueRootPortChange(USB_HCD* hcd, uint8_t port) {
if (!hcd || !hcd->RootHubContext)
return;
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)hcd->RootHubContext;
UsbHubRecordRootPortChange(hub, port);
}
// Implements the hub Scan External Ports operation.
static KSTATUS UsbHubScanExternalPorts(USB_HUB_EXTENSION* hub) {
if (!hub || hub->Kind != UsbHubKindExternal)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t attempted = 0;
uint32_t configured = 0;
for (uint8_t port = 1; port <= hub->PortCount && port <= USB_EXTERNAL_HUB_MAX_PORTS; port++) {
USB_HUB_PORT_STATUS ps;
memset(&ps, 0, sizeof(ps));
KSTATUS st = UsbHubGetPortStatus(hub, port, &ps);
if (!KSUCCESS(st))
continue;
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_CONNECTION);
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_ENABLE);
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
if (!(ps.Status & USB_HUB_PORT_STATUS_CONNECTION))
continue;
attempted++;
UsbHubHandleExternalPortChange(hub, port);
if (UsbHubSlotBusy(hub->ChildSlot[port]))
configured++;
}
DbgPrint("usb-hub: external hub slot=%u scan complete; attempted=%u configured=%u\n",
hub->Device ? hub->Device->SlotId : 0, attempted, configured);
return NTLSTATUS_SUCCESS;
}
// Implements the hub Find Interrupt In operation.
static const USB_ENDPOINT_DESCRIPTOR* UsbHubFindInterruptIn(USB_PDO_EXTENSION* pdoExt) {
if (!pdoExt)
return (const USB_ENDPOINT_DESCRIPTOR*)0;
if (pdoExt->Kind == UsbPdoInterface) {
for (uint8_t i = 0; i < pdoExt->EndpointCount; i++) {
const USB_ENDPOINT_DESCRIPTOR* ep = &pdoExt->Endpoints[i];
if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_IN) &&
((ep->bmAttributes & USB_ENDPOINT_ATTR_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT))
return ep;
}
return (const USB_ENDPOINT_DESCRIPTOR*)0;
}
if (pdoExt->Kind == UsbPdoDevice && pdoExt->Device) {
XHCI_DEVICE* xdev = (XHCI_DEVICE*)pdoExt->Device->HcdDeviceContext;
if (!xdev)
return (const USB_ENDPOINT_DESCRIPTOR*)0;
for (uint8_t i = 0; i < xdev->InterfaceCount; i++) {
USB_TRACKED_INTERFACE* iface = &xdev->Interfaces[i];
if (!iface->Present || iface->Descriptor.bInterfaceClass != USB_CLASS_HUB)
continue;
for (uint8_t epn = 0; epn < iface->EndpointCount; epn++) {
const USB_ENDPOINT_DESCRIPTOR* ep = &iface->Endpoints[epn];
if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_IN) &&
((ep->bmAttributes & USB_ENDPOINT_ATTR_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT))
return ep;
}
}
}
return (const USB_ENDPOINT_DESCRIPTOR*)0;
}
// Implements the hub External Change Complete operation.
static void UsbHubExternalChangeComplete(USB_URB* urb, void* context) {
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)context;
if (!hub || urb != &hub->ChangeUrb)
return;
hub->ChangeUrbPending = false;
if (!KSUCCESS(urb->Status) || hub->Removed || hub->SurpriseRemoved)
return;
uint32_t actual = urb->ActualLength;
if (actual > USB_HUB_CHANGE_BITMAP_BYTES)
actual = USB_HUB_CHANGE_BITMAP_BYTES;
for (uint8_t port = 1; port <= hub->PortCount && port <= USB_EXTERNAL_HUB_MAX_PORTS; port++) {
uint8_t byteIndex = port / 8u;
uint8_t bit = port & 7u;
if (byteIndex < actual && (hub->ChangeBitmap[byteIndex] & (1u << bit)))
UsbHubQueueExternalPortChange(hub, port);
}
}
// Implements the hub Arm External Interrupt operation.
static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub) {
if (!hub || !hub->InterruptPipe || !hub->ChangeBitmap || hub->ChangeUrbPending)
return;
memset(hub->ChangeBitmap, 0, USB_HUB_CHANGE_BITMAP_BYTES);
memset(&hub->ChangeUrb, 0, sizeof(hub->ChangeUrb));
hub->ChangeUrb.Function = UrbFunctionInterruptTransfer;
hub->ChangeUrb.Direction = UsbTransferIn;
hub->ChangeUrb.Device = hub->Device;
hub->ChangeUrb.Pipe = hub->InterruptPipe;
hub->ChangeUrb.Buffer = hub->ChangeBitmap;
hub->ChangeUrb.BufferDeviceAddress = hub->ChangeBitmapDeviceAddress;
hub->ChangeUrb.Length = hub->InterruptMaxPacket;
if (hub->ChangeUrb.Length > USB_HUB_CHANGE_BITMAP_BYTES)
hub->ChangeUrb.Length = USB_HUB_CHANGE_BITMAP_BYTES;
hub->ChangeUrb.Complete = UsbHubExternalChangeComplete;
hub->ChangeUrb.CompleteContext = hub;
KSTATUS st = UsbSubmitUrbAsync(hub->LowerDevice, &hub->ChangeUrb);
if (st == NTLSTATUS_PENDING) {
hub->ChangeUrbPending = true;
} else if (!KSUCCESS(st)) {
DbgPrint("usb-hub: slot=%u interrupt arm failed st=0x%x\n",
hub->Device ? hub->Device->SlotId : 0, st);
}
}
// Implements the hub Start Root Device operation.
static KSTATUS UsbHubStartRootDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSION* hub) {
hub->PortCount = hub->Hcd ? hub->Hcd->MaxRootPorts : 0;
if (hub->PortCount > USB_ROOT_HUB_MAX_PORTS)
hub->PortCount = USB_ROOT_HUB_MAX_PORTS;
KSTATUS st = UsbHubStartWorker(hub);
if (!KSUCCESS(st))
return st;
hub->Started = true;
device->PnpState = DevicePnpStarted;
if (hub->HubPdo)
hub->HubPdo->PnpState = DevicePnpStarted;
DbgPrint("usb-hub: root hub started ports=%u\n", hub->PortCount);
return NTLSTATUS_SUCCESS;
}
// Implements the hub Start External Device operation.
static KSTATUS UsbHubStartExternalDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSION* hub) {
if (!hub || !hub->Device)
return NTLSTATUS_INVALID_PARAMETER;
hub->ChangeBitmap = (uint8_t*)UsbAllocateDmaBuffer(hub->Device, 1, &hub->ChangeBitmapDeviceAddress, &hub->ChangeBitmapDma);
if (!hub->ChangeBitmap)
return NTLSTATUS_NO_MEMORY;
USB_HUB_DESCRIPTOR desc;
memset(&desc, 0, sizeof(desc));
KSTATUS st = UsbHubReadDescriptor(hub, &desc);
if (!KSUCCESS(st)) {
DbgPrint("usb-hub: slot=%u descriptor read failed st=0x%x\n", hub->Device->SlotId, st);
UsbHubReleaseDma(hub);
return st;
}
hub->PortCount = desc.bNbrPorts;
if (hub->PortCount > USB_EXTERNAL_HUB_MAX_PORTS) {
DbgPrint("usb-hub: slot=%u reports %u ports; limiting to %u\n",
hub->Device->SlotId, hub->PortCount, USB_EXTERNAL_HUB_MAX_PORTS);
hub->PortCount = USB_EXTERNAL_HUB_MAX_PORTS;
}
hub->PowerOnToPowerGoodMs = desc.bPwrOn2PwrGood ? (uint8_t)(desc.bPwrOn2PwrGood * 2u) : 100u;
for (uint8_t port = 1; port <= hub->PortCount; port++)
(void)UsbHubSetPortFeature(hub, port, USB_HUB_PORT_FEAT_POWER);
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk,
(uint32_t)hub->PowerOnToPowerGoodMs * 1000u);
st = UsbHubStartWorker(hub);
if (!KSUCCESS(st)) {
UsbHubReleaseDma(hub);
return st;
}
st = UsbCreatePipe(hub->Device,
UsbPipeInterrupt,
hub->InterruptEndpointAddress,
hub->InterruptMaxPacket,
hub->InterruptInterval,
&hub->InterruptPipe);
if (!KSUCCESS(st)) {
UsbHubStopWorker(hub);
UsbHubReleaseDma(hub);
return st;
}
hub->Started = true;
device->PnpState = DevicePnpStarted;
if (hub->HubPdo)
hub->HubPdo->PnpState = DevicePnpStarted;
DbgPrint("usb-hub: external hub started slot=%u ports=%u ep=%02x maxpkt=%u interval=%u\n",
hub->Device->SlotId,
hub->PortCount,
hub->InterruptEndpointAddress,
hub->InterruptMaxPacket,
hub->InterruptInterval);
(void)UsbHubScanExternalPorts(hub);
UsbHubArmExternalInterrupt(hub);
return NTLSTATUS_SUCCESS;
}
// Implements the hub Start Device operation.
static KSTATUS UsbHubStartDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSION* hub) {
if (!device || !hub || hub->Started)
return NTLSTATUS_SUCCESS;
if (hub->Kind == UsbHubKindRoot)
return UsbHubStartRootDevice(device, hub);
return UsbHubStartExternalDevice(device, hub);
}
// Implements the hub Dispatch Pnp operation.
KSTATUS UsbHubDispatchPnp(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_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)device->DeviceExtension;
KSTATUS st = NTLSTATUS_SUCCESS;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
st = UsbHubStartDevice(device, hub);
if (!KSUCCESS(st)) {
IoCompleteRequest(irp, st, 0);
return NTLSTATUS_PENDING;
}
return IoForwardIrpToLower(device, irp);
case IRP_MN_QUERY_REMOVE_DEVICE:
device->PnpState = DevicePnpRemovePending;
return IoForwardIrpToLower(device, irp);
case IRP_MN_CANCEL_REMOVE_DEVICE:
if (!hub || !hub->SurpriseRemoved)
device->PnpState = DevicePnpStarted;
return IoForwardIrpToLower(device, irp);
case IRP_MN_SURPRISE_REMOVAL:
device->PnpState = DevicePnpSurpriseRemoved;
if (hub) {
hub->SurpriseRemoved = true;
hub->WorkerRunning = false;
if (hub->ChangeUrbPending)
(void)UsbCancelUrb(&hub->ChangeUrb);
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
UsbHubStopWorker(hub);
// Remove downstream PDOs owned by this hub FDO. The explicit
// slot arrays are still used as the hardware lookup table; the
// child PDO tree is the authoritative software lifetime tree.
(void)UsbRemoveDeviceObjectChildren(device, true);
if (hub->Kind == UsbHubKindExternal) {
for (uint8_t port = 1; port <= hub->PortCount && port <= USB_EXTERNAL_HUB_MAX_PORTS; port++) {
if (UsbHubSlotBusy(hub->ChildSlot[port]))
UsbHubRemoveSlotChild(hub, port, hub->ChildSlot[port]);
}
} else if (hub->Kind == UsbHubKindRoot) {
for (uint8_t port = 1; port <= hub->PortCount && port <= USB_ROOT_HUB_MAX_PORTS; port++) {
if (UsbHubSlotBusy(hub->RootPortSlot[port]))
UsbHubRemoveRootChild(hub, port, hub->RootPortSlot[port]);
}
}
}
return IoForwardIrpToLower(device, irp);
case IRP_MN_REMOVE_DEVICE: {
device->PnpState = DevicePnpDeleted;
if (hub) {
hub->Removed = true;
hub->WorkerRunning = false;
if (hub->ChangeUrbPending)
(void)UsbCancelUrb(&hub->ChangeUrb);
(void)UsbRemoveDeviceObjectChildren(device, false);
if (hub->InterruptPipe) {
hub->InterruptPipe->Open = false;
UsbDereferencePipe(hub->InterruptPipe);
hub->InterruptPipe = (USB_PIPE*)0;
}
UsbHubStopWorker(hub);
if (hub->Kind == UsbHubKindRoot && hub->Hcd && hub->Hcd->RootHubContext == hub)
hub->Hcd->RootHubContext = (void*)0;
if (hub->RemoveLockReady) {
IoReleaseRemoveLockAndWait(&hub->RemoveLock, irp);
IoDestroyRemoveLock(&hub->RemoveLock);
hub->RemoveLockReady = false;
}
UsbHubReleaseDma(hub);
}
KSTATUS lower = hub && hub->LowerDevice ? IoForwardIrpToLower(device, irp) : NTLSTATUS_SUCCESS;
if (hub && hub->LowerDevice) {
IoDetachDevice(device);
hub->LowerDevice = (DEVICE_OBJECT*)0;
}
IoDeleteDevice(device);
if (lower == NTLSTATUS_NOT_SUPPORTED) {
IoCompleteRequest(irp, NTLSTATUS_SUCCESS, 0);
return NTLSTATUS_PENDING;
}
if (lower != NTLSTATUS_PENDING && !KSUCCESS(lower)) {
IoCompleteRequest(irp, lower, 0);
return NTLSTATUS_PENDING;
}
return lower;
}
default:
return IoForwardIrpToLower(device, irp);
}
}
// Implements the hub Add Device operation.
KSTATUS UsbHubAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return NTLSTATUS_INVALID_PARAMETER;
USB_PDO_EXTENSION* pdoExt = (USB_PDO_EXTENSION*)physicalDeviceObject->DeviceExtension;
if (!pdoExt)
return NTLSTATUS_INVALID_PARAMETER;
USB_HUB_KIND kind;
USB_DEVICE* usbDevice = (USB_DEVICE*)0;
const USB_ENDPOINT_DESCRIPTOR* intr = (const USB_ENDPOINT_DESCRIPTOR*)0;
if (pdoExt->Kind == UsbPdoRootHub && pdoExt->Hcd) {
kind = UsbHubKindRoot;
} else if (pdoExt->Kind == UsbPdoDevice && pdoExt->Device &&
pdoExt->Device->DeviceClass == USB_CLASS_HUB) {
kind = UsbHubKindExternal;
usbDevice = pdoExt->Device;
intr = UsbHubFindInterruptIn(pdoExt);
if (!intr)
return NTLSTATUS_NOT_FOUND;
} else if (pdoExt->Kind == UsbPdoInterface && pdoExt->Device &&
pdoExt->InterfaceDescriptor.bInterfaceClass == USB_CLASS_HUB) {
kind = UsbHubKindExternal;
usbDevice = pdoExt->Device;
intr = UsbHubFindInterruptIn(pdoExt);
if (!intr)
return NTLSTATUS_NOT_FOUND;
} else {
return NTLSTATUS_INVALID_PARAMETER;
}
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(USB_HUB_EXTENSION), &fdo);
if (!KSUCCESS(st))
return st;
(void)IoSetDeviceName(fdo, kind == UsbHubKindRoot ? SYS_SV_LITERAL("usb-root-hub-driver")
: SYS_SV_LITERAL("usb-hub"));
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)fdo->DeviceExtension;
hub->Self = fdo;
hub->HubPdo = physicalDeviceObject;
hub->Kind = kind;
hub->Hcd = pdoExt->Hcd;
hub->Device = usbDevice;
if (kind == UsbHubKindExternal && hub->Device)
hub->Hcd = hub->Device->Hcd;
hub->PendingLock = (SPINLOCK)SPINLOCK_INIT;
if (intr) {
hub->InterruptEndpointAddress = intr->bEndpointAddress;
hub->InterruptMaxPacket = intr->wMaxPacketSize;
if (hub->InterruptMaxPacket == 0 || hub->InterruptMaxPacket > USB_HUB_CHANGE_BITMAP_BYTES)
hub->InterruptMaxPacket = USB_HUB_CHANGE_BITMAP_BYTES;
hub->InterruptInterval = intr->bInterval ? intr->bInterval : 12u;
if (pdoExt->Kind == UsbPdoInterface)
hub->InterfaceNumber = pdoExt->InterfaceDescriptor.bInterfaceNumber;
}
st = IoInitializeRemoveLock(&hub->RemoveLock);
if (!KSUCCESS(st)) {
IoDeleteDevice(fdo);
return st;
}
hub->RemoveLockReady = true;
hub->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!hub->LowerDevice) {
IoDestroyRemoveLock(&hub->RemoveLock);
hub->RemoveLockReady = false;
IoDeleteDevice(fdo);
return NTLSTATUS_FAILURE;
}
if (kind == UsbHubKindRoot) {
hub->Hcd->RootHubContext = hub;
DbgPrint("usb-hub: root hub FDO attached\n");
} else {
DbgPrint("usb-hub: external hub FDO attached slot=%u ep=%02x\n",
hub->Device ? hub->Device->SlotId : 0,
hub->InterruptEndpointAddress);
}
return NTLSTATUS_SUCCESS;
}
// Initializes hub Driver.
KSTATUS UsbInitializeHubDriver(void) {
memset(&gUsbHubDriver, 0, sizeof(gUsbHubDriver));
IoInitializeDriverObject(&gUsbHubDriver, SYS_SV_LITERAL("usb-hub"));
gUsbHubDriver.IdTable = &gUsbHubIdTable;
gUsbHubDriver.DriverAddDevice = UsbHubAddDevice;
gUsbHubDriver.MajorFunction[IRP_MJ_PNP] = UsbHubDispatchPnp;
return IoRegisterDriverObject(&gUsbHubDriver);
}