replace ad hoc driver loading with a proper driver manager, new DDK, new PnP manager with device matching, asynchronous startup, resource ownership and removal, add shared PCI capability helpers, replace typed, rigid services with a proper service manager, implement proper system shutdown and restart, shutting down unloads drivers and shuts down services, add simple power button, remove heavyweight boot self-tests and SMP stress runs, add monotonic timestamps to kernel debug output, fruitless USB and PS/2 fixes, make test builds opt in.
This commit is contained in:
1 parent
c0ad63c27f
commit
d36f19509c
160 files changed
+6563
-7366
No files matched your search
+293
-104
@@ -48,27 +48,22 @@ void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value) {
|
||||
}
|
||||
|
||||
// Implements the delay Us operation.
|
||||
void UsbDelayUs(const NTL_DDK* ddk, uint32_t us) {
|
||||
if (ddk && ddk->Timer && ddk->Timer->Stall) ddk->Timer->Stall(us);
|
||||
else {
|
||||
for (uint32_t i = 0; i < us * 32u; i++) _mm_pause();
|
||||
}
|
||||
void UsbDelayUs(uint32_t us) {
|
||||
KeStallExecution(us);
|
||||
}
|
||||
|
||||
bool UsbWaitSet32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg,
|
||||
uint32_t mask, uint32_t timeoutUs) {
|
||||
bool UsbWaitSet32(volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
if ((XhciRead32(base, reg) & mask) == mask) return true;
|
||||
UsbDelayUs(ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UsbWaitClear32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg,
|
||||
uint32_t mask, uint32_t timeoutUs) {
|
||||
bool UsbWaitClear32(volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
if ((XhciRead32(base, reg) & mask) == 0) return true;
|
||||
UsbDelayUs(ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -83,7 +78,7 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci,
|
||||
return (void*)0;
|
||||
|
||||
IO_DMA_BUFFER* buffer = (IO_DMA_BUFFER*)0;
|
||||
KSTATUS status = xhci->Ddk->Dma->AllocateCommonBuffer(xhci->DmaAdapter,
|
||||
KSTATUS status = IoDmaAllocateCommonBuffer(xhci->DmaAdapter,
|
||||
pages * (size_t)PAGE_SIZE,
|
||||
PAGE_SIZE,
|
||||
&buffer);
|
||||
@@ -93,14 +88,14 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci,
|
||||
return (void*)0;
|
||||
}
|
||||
|
||||
*outDeviceAddress = xhci->Ddk->Dma->GetCommonBufferDeviceAddress(buffer);
|
||||
*outDeviceAddress = IoDmaGetCommonBufferDeviceAddress(buffer);
|
||||
*outBuffer = buffer;
|
||||
return xhci->Ddk->Dma->GetCommonBufferAddress(buffer);
|
||||
return IoDmaGetCommonBufferAddress(buffer);
|
||||
}
|
||||
|
||||
// Releases all DMA allocations owned by one xHCI slot.
|
||||
void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
if (!dev || !gUsbDdk || !gUsbDdk->Dma)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
for (uint32_t endpoint = 1; endpoint < 32; endpoint++) {
|
||||
@@ -110,15 +105,15 @@ void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
UsbDereferencePipe(pipe);
|
||||
}
|
||||
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->DescriptorDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassOutRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassInRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->Ep0RingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->DeviceContextDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->InputContextDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->HidBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->DescriptorDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassOutRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassInRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->HidRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->Ep0RingDma);
|
||||
IoDmaFreeCommonBuffer(dev->DeviceContextDma);
|
||||
IoDmaFreeCommonBuffer(dev->InputContextDma);
|
||||
|
||||
dev->MassBufferDma = (IO_DMA_BUFFER*)0;
|
||||
dev->HidBufferDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -143,22 +138,22 @@ void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
|
||||
// Releases controller-wide xHCI DMA allocations.
|
||||
static void XhciReleaseControllerDma(XHCI_CONTROLLER* xhci) {
|
||||
if (!xhci || !xhci->Ddk || !xhci->Ddk->Dma)
|
||||
if (!xhci)
|
||||
return;
|
||||
|
||||
for (uint32_t slot = 1; slot <= XHCI_MAX_TRACKED_SLOTS; slot++)
|
||||
XhciReleaseDeviceDma(&xhci->Devices[slot]);
|
||||
for (uint16_t i = 0; i < XHCI_MAX_SCRATCHPADS; i++) {
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ScratchpadDma[i]);
|
||||
IoDmaFreeCommonBuffer(xhci->ScratchpadDma[i]);
|
||||
xhci->ScratchpadDma[i] = (IO_DMA_BUFFER*)0;
|
||||
xhci->ScratchpadDeviceAddress[i] = 0;
|
||||
}
|
||||
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ScratchpadArrayDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ErstDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->EventRingDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->CommandRingDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->DcbaaDma);
|
||||
IoDmaFreeCommonBuffer(xhci->ScratchpadArrayDma);
|
||||
IoDmaFreeCommonBuffer(xhci->ErstDma);
|
||||
IoDmaFreeCommonBuffer(xhci->EventRingDma);
|
||||
IoDmaFreeCommonBuffer(xhci->CommandRingDma);
|
||||
IoDmaFreeCommonBuffer(xhci->DcbaaDma);
|
||||
xhci->ScratchpadArrayDma = (IO_DMA_BUFFER*)0;
|
||||
xhci->ErstDma = (IO_DMA_BUFFER*)0;
|
||||
xhci->EventRingDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -170,7 +165,7 @@ static void XhciReleaseControllerDma(XHCI_CONTROLLER* xhci) {
|
||||
xhci->CommandRing = (XHCI_TRB*)0;
|
||||
xhci->Dcbaa = (uint64_t*)0;
|
||||
|
||||
xhci->Ddk->Dma->PutAdapter(xhci->DmaAdapter);
|
||||
IoDmaPutAdapter(xhci->DmaAdapter);
|
||||
xhci->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
}
|
||||
|
||||
@@ -220,6 +215,76 @@ uint32_t* XhciInputEndpointContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return (uint32_t*)(dev->InputContext + (uint32_t)(endpointId + 1u) * xhci->ContextBytes);
|
||||
}
|
||||
|
||||
// Reconstructs the addressed device topology when configuring an endpoint or hub.
|
||||
// Configure Endpoint must not discard route, TT, or hub attributes on the slot.
|
||||
void XhciBuildConfigureSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint32_t* slot, uint8_t endpointId) {
|
||||
if (!xhci || !dev || !slot)
|
||||
return;
|
||||
memset(slot, 0, xhci->ContextBytes);
|
||||
|
||||
uint8_t lastContext = endpointId;
|
||||
if (dev->DeviceContext) {
|
||||
uint32_t previous = ((const uint32_t*)dev->DeviceContext)[0];
|
||||
uint8_t previousLast = (uint8_t)((previous >> 27) & 31u);
|
||||
if (previousLast > lastContext)
|
||||
lastContext = previousLast;
|
||||
}
|
||||
uint8_t rootPort = dev->Path.RootPort ? dev->Path.RootPort : dev->Port;
|
||||
slot[0] = XHCI_CONTEXT_ENTRIES(lastContext) | XHCI_SLOT_SPEED(dev->Speed) |
|
||||
(dev->Path.RouteString & 0xfffffu);
|
||||
slot[1] = XHCI_SLOT_ROOT_PORT(rootPort);
|
||||
if (dev->Path.BehindHub && dev->Speed <= 2u && dev->Path.ParentHubSlot) {
|
||||
slot[2] = (uint32_t)dev->Path.ParentHubSlot |
|
||||
((uint32_t)dev->Path.ParentHubPort << 8);
|
||||
}
|
||||
if (dev->HubPortCount) {
|
||||
slot[0] |= XHCI_SLOT_HUB;
|
||||
slot[1] |= XHCI_SLOT_PORT_COUNT(dev->HubPortCount);
|
||||
if (dev->Speed == 3u)
|
||||
slot[2] |= XHCI_SLOT_TT_THINK(dev->HubCharacteristics >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Publishes an external hub's topology to xHCI before its child ports are used.
|
||||
KSTATUS XhciConfigureHubContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint8_t portCount, uint16_t characteristics) {
|
||||
if (!xhci || !dev || !dev->Addressed || !dev->InputContext ||
|
||||
!dev->DeviceContext || !portCount)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t oldPortCount = dev->HubPortCount;
|
||||
uint16_t oldCharacteristics = dev->HubCharacteristics;
|
||||
dev->HubPortCount = portCount;
|
||||
dev->HubCharacteristics = characteristics;
|
||||
|
||||
memset(dev->InputContext, 0, PAGE_SIZE);
|
||||
uint32_t* icc = XhciInputControlContext(xhci, dev);
|
||||
uint32_t* slot = XhciInputSlotContext(xhci, dev);
|
||||
// Update only the slot context; existing endpoint contexts remain active.
|
||||
icc[1] = 1u;
|
||||
XhciBuildConfigureSlotContext(xhci, dev, slot, 1u);
|
||||
|
||||
XHCI_COMMAND_RESULT result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u hub-slot=%u configure-context begin ports=%u tt=%u speed=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, dev->SlotId,
|
||||
portCount, (characteristics >> 5) & 3u, dev->Speed);
|
||||
KSTATUS st = XhciSubmitCommand(xhci, dev->InputContextDeviceAddress, 0,
|
||||
(XHCI_TRB_TYPE_CONFIGURE_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
|
||||
((uint32_t)dev->SlotId << 24),
|
||||
&result);
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
DbgPrint("usb-stage: hub-slot=%u configure-context failed st=0x%x cc=%u\n",
|
||||
dev->SlotId, st, result.CompletionCode);
|
||||
dev->HubPortCount = oldPortCount;
|
||||
dev->HubCharacteristics = oldCharacteristics;
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
DbgPrint("usb-stage: hub-slot=%u configure-context done\n", dev->SlotId);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the ep Max Packet operation.
|
||||
uint32_t XhciEpMaxPacket(uint8_t speed) {
|
||||
switch (speed) {
|
||||
@@ -254,6 +319,15 @@ const char* XhciPortSpeedName(uint32_t speed) {
|
||||
}
|
||||
}
|
||||
|
||||
// Advances an xHCI extended-capability offset using the relative DWORD link.
|
||||
static uint32_t XhciNextExtendedCapabilityDword(uint32_t current, uint8_t next,
|
||||
uint32_t mappedBytes) {
|
||||
if (!next) return 0;
|
||||
uint64_t following = (uint64_t)current + next;
|
||||
if (following * 4u + sizeof(uint32_t) > mappedBytes) return 0;
|
||||
return (uint32_t)following;
|
||||
}
|
||||
|
||||
// Parses supported Protocols.
|
||||
void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
memset(xhci->PortProtocolMajor, 0, sizeof(xhci->PortProtocolMajor));
|
||||
@@ -297,8 +371,18 @@ void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
xecp = next;
|
||||
xecp = XhciNextExtendedCapabilityDword(xecp, next, xhci->MmioBytes);
|
||||
}
|
||||
|
||||
uint32_t usb2Ports = 0;
|
||||
uint32_t usb3Ports = 0;
|
||||
for (uint32_t port = 1; port <= xhci->MaxPorts; port++) {
|
||||
if (xhci->PortProtocolMajor[port] == 2u) usb2Ports++;
|
||||
else if (xhci->PortProtocolMajor[port] >= 3u) usb3Ports++;
|
||||
}
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u protocol ports usb2=%u usb3=%u unknown=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, usb2Ports, usb3Ports,
|
||||
(uint32_t)xhci->MaxPorts - usb2Ports - usb3Ports);
|
||||
}
|
||||
|
||||
// Implements the port Protocol Major operation.
|
||||
@@ -385,7 +469,7 @@ void XhciPowerRootPorts(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
|
||||
if (powered) {
|
||||
UsbDelayUs(xhci->Ddk, 20000u);
|
||||
UsbDelayUs(20000u);
|
||||
DbgPrint("usb: xHCI powered %u previously-off root port(s)\n", powered);
|
||||
}
|
||||
}
|
||||
@@ -465,12 +549,43 @@ static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
|
||||
return w;
|
||||
}
|
||||
|
||||
// Implements the drain Event Ring operation.
|
||||
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
(void)target;
|
||||
bool processed = false;
|
||||
// A completion leaves EventLock with its class-device rundown reference held.
|
||||
// Client callbacks can rearm transfers or acquire unrelated PnP locks.
|
||||
#define XHCI_COMPLETION_BATCH 16u
|
||||
|
||||
while (true) {
|
||||
typedef struct XHCI_ASYNC_COMPLETION {
|
||||
USB_URB* Urb;
|
||||
IRP* Irp;
|
||||
KSTATUS Status;
|
||||
uint32_t ActualLength;
|
||||
} XHCI_ASYNC_COMPLETION;
|
||||
|
||||
// Claims an asynchronous TD exactly once while the event lock is owned.
|
||||
static bool XhciClaimAsyncCompletion(XHCI_CONTROLLER* xhci, uint64_t trbAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue,
|
||||
XHCI_ASYNC_COMPLETION* completion);
|
||||
|
||||
// Executes callbacks outside the event lock; class remove locks own their lifetime.
|
||||
static void XhciDispatchAsyncCompletion(const XHCI_ASYNC_COMPLETION* completed) {
|
||||
USB_URB* urb = completed->Urb;
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
if (completed->Irp)
|
||||
IoCompleteRequest(completed->Irp, completed->Status,
|
||||
KSUCCESS(completed->Status) ? completed->ActualLength : 0);
|
||||
}
|
||||
|
||||
// Drains a bounded batch while the event lock is held.
|
||||
static bool XhciDrainEventRing(XHCI_CONTROLLER* xhci,
|
||||
XHCI_ASYNC_COMPLETION* completions, uint32_t* completionCount,
|
||||
uint32_t* changedPorts) {
|
||||
bool processed = false;
|
||||
*completionCount = 0;
|
||||
|
||||
for (uint32_t drained = 0; drained < XHCI_EVENT_TRBS; drained++) {
|
||||
if (*completionCount == XHCI_COMPLETION_BATCH)
|
||||
break;
|
||||
XHCI_TRB* ev = &xhci->EventRing[xhci->EventDequeue];
|
||||
uint32_t control = ev->Control;
|
||||
uint8_t cycle = (uint8_t)(control & XHCI_TRB_CYCLE);
|
||||
@@ -495,15 +610,15 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
UsbTrace("usb: xHCI transfer event trb=0x%016llX cc=%u slot=%u ep=%u residue=%u\n",
|
||||
(unsigned long long)param, cc, slotId, epId, residue);
|
||||
}
|
||||
|
||||
XHCI_WAIT_TARGET* waiter = XhciClaimTransferWaiter(xhci, param, slotId, epId, cc, residue);
|
||||
if (!waiter)
|
||||
XhciHandleAsyncTransferEvent(xhci, param, cc, slotId, epId, residue);
|
||||
if (!waiter && XhciClaimAsyncCompletion(xhci, param, cc, slotId, epId, residue,
|
||||
&completions[*completionCount]))
|
||||
(*completionCount)++;
|
||||
} else if (type == XHCI_TRB_TYPE_PORT_SC) {
|
||||
uint32_t portId = ev->ParameterLo >> 24;
|
||||
UsbTrace("usb: xHCI port-status-change event port=%u cc=%u\n", portId, cc);
|
||||
if (portId && portId <= xhci->MaxPorts)
|
||||
UsbHubQueueRootPortChange(&xhci->Hcd, (uint8_t)portId);
|
||||
if (portId && portId <= xhci->MaxPorts && portId <= USB_ROOT_HUB_MAX_PORTS)
|
||||
changedPorts[portId / 32u] |= 1u << (portId & 31u);
|
||||
} else {
|
||||
DbgPrint("usb: xHCI event type=%u cc=%u p=0x%016llX status=%08x ctrl=%08x\n",
|
||||
type, cc, (unsigned long long)param, ev->Status, ev->Control);
|
||||
@@ -521,12 +636,27 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
// Implements the drain Event Ring Locked operation.
|
||||
// Serializes event-ring ownership without calling into class drivers under the lock.
|
||||
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
(void)target;
|
||||
if (!xhci) return false;
|
||||
XHCI_ASYNC_COMPLETION completions[XHCI_COMPLETION_BATCH];
|
||||
uint32_t changedPorts[8] = { 0 };
|
||||
uint32_t count = 0;
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
bool processed = XhciDrainEventRing(xhci, target);
|
||||
bool processed = XhciDrainEventRing(xhci, completions, &count, changedPorts);
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
|
||||
// Hub wakeups can take scheduler locks; keep them outside EventLock too.
|
||||
for (uint32_t word = 0; word < 8; word++) {
|
||||
uint32_t bits = changedPorts[word];
|
||||
for (uint32_t bit = 0; bit < 32; bit++) {
|
||||
if (bits & (1u << bit))
|
||||
UsbHubQueueRootPortChange(&xhci->Hcd, (uint8_t)(word * 32u + bit));
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
XhciDispatchAsyncCompletion(&completions[i]);
|
||||
return processed;
|
||||
}
|
||||
|
||||
@@ -591,7 +721,7 @@ KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
KeInsertQueueDpc(&xhci->EventDpc, (void*)0, (void*)0);
|
||||
}
|
||||
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
XhciUnregisterWaitTarget(xhci, target);
|
||||
@@ -617,9 +747,17 @@ void XhciEventDpcRoutine(KDPC* dpc, void* context,
|
||||
return;
|
||||
|
||||
xhci->EventDpcCount++;
|
||||
while (XhciDrainEventRingLocked(xhci, NULL)) {
|
||||
// Keep draining until the software dequeue catches up to the producer.
|
||||
// Processing is bounded even if connected hubs continuously report changes.
|
||||
// Updating ERDP clears EHB; remaining events cause the interrupter to
|
||||
// assert again, allowing other DPCs and scheduled threads to make progress.
|
||||
for (uint32_t pass = 0; pass < 4u; pass++) {
|
||||
if (!XhciDrainEventRingLocked(xhci, NULL))
|
||||
return;
|
||||
}
|
||||
uint32_t hits = ++xhci->EventDpcBudgetHits;
|
||||
if ((hits & (hits - 1u)) == 0u)
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u event DPC budget reached count=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hits);
|
||||
}
|
||||
|
||||
#define XHCI_CONTROLLER_MAGIC 0x58484349u // XHCI
|
||||
@@ -700,7 +838,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
if (!ok) {
|
||||
DbgPrint("usb: xHCI BIOS handoff timed out; continuing anyway\n");
|
||||
@@ -712,7 +850,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
xecp = next;
|
||||
xecp = XhciNextExtendedCapabilityDword(xecp, next, xhci->MmioBytes);
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -723,7 +861,7 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
if (cmd & XHCI_USBCMD_RS) {
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd & ~XHCI_USBCMD_RS);
|
||||
if (!UsbWaitSet32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitSet32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH, 1000000u)) {
|
||||
DbgPrint("usb: xHCI stop timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -732,14 +870,14 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, XHCI_USBCMD_HCRST);
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBCMD,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBCMD,
|
||||
XHCI_USBCMD_HCRST, 1000000u)) {
|
||||
DbgPrint("usb: xHCI reset timeout usbcmd=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBCMD));
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_CNR, 1000000u)) {
|
||||
DbgPrint("usb: xHCI controller-not-ready timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -769,8 +907,7 @@ static void XhciHaltForDmaRelease(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t command = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
command &= ~(XHCI_USBCMD_RS | XHCI_USBCMD_INTE);
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, command);
|
||||
if (!UsbWaitSet32(xhci->Ddk,
|
||||
xhci->Op,
|
||||
if (!UsbWaitSet32(xhci->Op,
|
||||
XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH,
|
||||
1000000u))
|
||||
@@ -1048,7 +1185,7 @@ KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
|
||||
cmd |= XHCI_USBCMD_RS;
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd);
|
||||
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH, 1000000u)) {
|
||||
DbgPrint("usb: xHCI start timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -1099,6 +1236,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
|
||||
uint32_t speed = (portsc & XHCI_PORTSC_SPEED_MASK) >> XHCI_PORTSC_SPEED_SHIFT;
|
||||
uint8_t protocol = XhciPortProtocolMajor(xhci, port, portsc);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u reset begin proto=%u speed=%u portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, protocol, speed, portsc);
|
||||
|
||||
XhciClearPortChanges(xhci, port);
|
||||
portsc = XhciRead32(xhci->Op, off);
|
||||
@@ -1120,7 +1259,7 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
break;
|
||||
}
|
||||
XhciDrainEventRingLocked(xhci, NULL);
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
XhciClearPortChanges(xhci, port);
|
||||
@@ -1133,6 +1272,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
(portsc & XHCI_PORTSC_PED) ? 1u : 0u,
|
||||
protocol, XhciPortSpeedName(speed), speed);
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u superspeed ready=%u portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, enabled ? 1u : 0u, portsc);
|
||||
if (!enabled || !(portsc & XHCI_PORTSC_CCS) || !(portsc & XHCI_PORTSC_PED))
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -1149,11 +1290,12 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
break;
|
||||
}
|
||||
XhciDrainEventRingLocked(xhci, NULL);
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
if (!resetDone) {
|
||||
DbgPrint("usb: xHCI port %u reset timeout portsc=%08x\n", port, portsc);
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u port %u reset timeout portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc);
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
@@ -1167,6 +1309,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
(portsc & XHCI_PORTSC_PED) ? 1u : 0u,
|
||||
protocol, XhciPortSpeedName(speed), speed);
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u reset done portsc=%08x speed=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc, speed);
|
||||
if (!(portsc & XHCI_PORTSC_CCS) || !(portsc & XHCI_PORTSC_PED))
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
|
||||
@@ -1674,10 +1818,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
icc[0] = 0;
|
||||
icc[1] = (1u << 0) | (1u << dci);
|
||||
|
||||
slot[0] = XHCI_CONTEXT_ENTRIES(dci) | XHCI_SLOT_SPEED(dev->Speed);
|
||||
slot[1] = XHCI_SLOT_ROOT_PORT(dev->Port);
|
||||
slot[2] = 0;
|
||||
slot[3] = 0;
|
||||
XhciBuildConfigureSlotContext(xhci, dev, slot, dci);
|
||||
|
||||
uint16_t maxPacket = pipe->MaxPacketSize & 0x07FFu;
|
||||
if (maxPacket == 0) maxPacket = 8u;
|
||||
@@ -1692,7 +1833,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
} else if (pipe->Type == UsbPipeBulk) {
|
||||
endpointType = XHCI_EP_TYPE_BULK_OUT;
|
||||
} else {
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
MmFreePool(ctx);
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
@@ -1715,7 +1856,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
DbgPrint("usb: Configure pipe endpoint failed slot=%u ep=%u type=%u st=0x%x cc=%u\n",
|
||||
dev->SlotId, dci, pipe->Type, st, result.CompletionCode);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
MmFreePool(ctx);
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
@@ -1740,8 +1881,15 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)urb->Pipe->HcdPipeContext;
|
||||
if (!ctx || !ctx->Configured)
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
if (ctx->ActiveTransfer)
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
if (ctx->ActiveTransfer) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_QUEUE_FULL;
|
||||
}
|
||||
if (urb->Pipe->Halted) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
}
|
||||
|
||||
uint64_t trbDeviceAddress = 0;
|
||||
st = XhciRingAppend(ctx->Ring,
|
||||
@@ -1755,7 +1903,10 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
XHCI_TRB_ISP |
|
||||
XHCI_TRB_IOC,
|
||||
&trbDeviceAddress);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
if (!KSUCCESS(st)) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return st;
|
||||
}
|
||||
|
||||
ctx->ActiveTransfer = true;
|
||||
ctx->ActiveTrbDeviceAddress = trbDeviceAddress;
|
||||
@@ -1768,58 +1919,51 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
|
||||
XhciBarrier();
|
||||
XhciWrite32(xhci->Doorbells, (uint32_t)dev->SlotId * 4u, ctx->EndpointId);
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbDeviceAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue) {
|
||||
if (!xhci || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS || endpointId >= 32)
|
||||
return;
|
||||
// Completes ownership of an interrupt URB before its callback is dispatched.
|
||||
static bool XhciClaimAsyncCompletion(XHCI_CONTROLLER* xhci, uint64_t trbDeviceAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue,
|
||||
XHCI_ASYNC_COMPLETION* completed) {
|
||||
if (!xhci || !completed || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS || endpointId >= 32)
|
||||
return false;
|
||||
|
||||
XHCI_DEVICE* dev = &xhci->Devices[slotId];
|
||||
USB_PIPE* pipe = dev->EndpointPipes[endpointId];
|
||||
if (!pipe || !pipe->HcdPipeContext)
|
||||
return;
|
||||
return false;
|
||||
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)pipe->HcdPipeContext;
|
||||
if (!ctx->ActiveTransfer || ctx->ActiveTrbDeviceAddress != trbDeviceAddress)
|
||||
return;
|
||||
return false;
|
||||
|
||||
USB_URB* urb = ctx->ActiveUrb;
|
||||
ctx->ActiveTransfer = false;
|
||||
ctx->ActiveTrbDeviceAddress = 0;
|
||||
ctx->ActiveUrb = (USB_URB*)0;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
return false;
|
||||
|
||||
KSTATUS st = NTLSTATUS_SUCCESS;
|
||||
if (urb->Cancelled) {
|
||||
if (urb->Cancelled)
|
||||
st = NTLSTATUS_CANCELLED;
|
||||
} else if (completionCode != XHCI_CC_SUCCESS &&
|
||||
completionCode != XHCI_CC_SHORT_PACKET) {
|
||||
else if (completionCode != XHCI_CC_SUCCESS && completionCode != XHCI_CC_SHORT_PACKET)
|
||||
st = NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
uint32_t actual = 0;
|
||||
if (KSUCCESS(st)) {
|
||||
actual = (residue <= urb->Length) ? (urb->Length - residue) : 0;
|
||||
}
|
||||
|
||||
uint32_t actual = KSUCCESS(st) && residue <= urb->Length ? urb->Length - residue : 0;
|
||||
urb->Pending = false;
|
||||
urb->Status = st;
|
||||
urb->ActualLength = actual;
|
||||
urb->HcdCompletionCode = completionCode;
|
||||
|
||||
IRP* completeIrp = urb->Irp;
|
||||
completed->Urb = urb;
|
||||
completed->Irp = urb->Irp;
|
||||
completed->Status = st;
|
||||
completed->ActualLength = actual;
|
||||
urb->Irp = (IRP*)0;
|
||||
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
|
||||
if (completeIrp)
|
||||
IoCompleteRequest(completeIrp, st, KSUCCESS(st) ? actual : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the hcd Submit Bulk Urb operation.
|
||||
@@ -1905,13 +2049,59 @@ static KSTATUS XhciHcdSubmitUrb(USB_HCD* hcd, USB_URB* urb) {
|
||||
return st;
|
||||
}
|
||||
|
||||
// Implements the hcd Cancel Urb operation.
|
||||
static KSTATUS XhciHcdCancelUrb(USB_HCD* hcd, USB_URB* urb) {
|
||||
(void)hcd;
|
||||
if (!urb) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!hcd || !urb || !urb->Device || !urb->Pipe)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hcd->ControllerContext;
|
||||
XHCI_DEVICE* dev = (XHCI_DEVICE*)urb->Device->HcdDeviceContext;
|
||||
if (!xhci || !dev || dev->SlotId == 0 || !urb->Pipe->HcdPipeContext)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)urb->Pipe->HcdPipeContext;
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
urb->Cancelled = true;
|
||||
if (!urb->Pending)
|
||||
urb->Status = NTLSTATUS_CANCELLED;
|
||||
bool active = ctx->ActiveTransfer && ctx->ActiveUrb == urb;
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
if (!active)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
XHCI_COMMAND_RESULT result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
KSTATUS st = XhciSubmitCommand(xhci, 0, 0,
|
||||
(XHCI_TRB_TYPE_STOP_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
|
||||
((uint32_t)ctx->EndpointId << 16) |
|
||||
((uint32_t)dev->SlotId << 24),
|
||||
&result);
|
||||
|
||||
flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
if (!ctx->ActiveTransfer || ctx->ActiveUrb != urb) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
DbgPrint("usb: xHCI STOP_ENDPOINT failed slot=%u ep=%u st=0x%x cc=%u\n",
|
||||
dev->SlotId, ctx->EndpointId, st, result.CompletionCode);
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
|
||||
ctx->ActiveTransfer = false;
|
||||
ctx->ActiveTrbDeviceAddress = 0;
|
||||
ctx->ActiveUrb = (USB_URB*)0;
|
||||
urb->Pipe->Halted = true;
|
||||
urb->Pending = false;
|
||||
urb->Status = NTLSTATUS_CANCELLED;
|
||||
urb->ActualLength = 0;
|
||||
urb->HcdCompletionCode = result.CompletionCode;
|
||||
|
||||
IRP* completeIrp = urb->Irp;
|
||||
urb->Irp = (IRP*)0;
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
if (completeIrp)
|
||||
IoCompleteRequest(completeIrp, NTLSTATUS_CANCELLED, 0);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1933,8 +2123,7 @@ static void XhciHcdDestroyPipe(USB_HCD* hcd, USB_PIPE* pipe) {
|
||||
if (!pipe || !pipe->HcdPipeContext)
|
||||
return;
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)pipe->HcdPipeContext;
|
||||
if (gUsbDdk && gUsbDdk->Dma)
|
||||
gUsbDdk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
ctx->RingDma = (IO_DMA_BUFFER*)0;
|
||||
ctx->Ring = (XHCI_TRB*)0;
|
||||
pipe->HcdPipeContext = (void*)0;
|
||||
@@ -2109,7 +2298,7 @@ KSTATUS XhciStartController(XHCI_CONTROLLER* xhci) {
|
||||
dmaRequirements.Boundary = 0x10000u;
|
||||
dmaRequirements.MaximumSegmentLength = 0x10000u;
|
||||
dmaRequirements.MaximumSegments = XHCI_PIPE_RING_TRBS - 1u;
|
||||
st = xhci->Ddk->Dma->GetAdapter(xhci->Fdo,
|
||||
st = IoDmaGetAdapter(xhci->Fdo,
|
||||
&dmaRequirements,
|
||||
&xhci->DmaAdapter);
|
||||
if (!KSUCCESS(st)) {
|
||||
@@ -2165,7 +2354,7 @@ KSTATUS XhciStartController(XHCI_CONTROLLER* xhci) {
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("usb: root hub async scan scheduling failed st=0x%x\n", st);
|
||||
else
|
||||
DbgPrint("usb: root hub initial scan queued asynchronously\n");
|
||||
DbgPrint("usb: root hub initial scan requested\n");
|
||||
|
||||
DbgPrint("usb: xHCI host controller initialised; HID boot input active=%u mass-storage will arrive through hotplug\n",
|
||||
xhci->HidDeviceCount);
|
||||
@@ -2247,7 +2436,7 @@ KSTATUS XhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)fdo->DeviceExtension;
|
||||
memset(xhci, 0, sizeof(*xhci));
|
||||
xhci->Magic = XHCI_CONTROLLER_MAGIC;
|
||||
xhci->Ddk = gUsbDdk;
|
||||
|
||||
xhci->Fdo = fdo;
|
||||
xhci->Pdo = physicalDeviceObject;
|
||||
xhci->Irq = 0xFFu;
|
||||
|
||||
Reference in new issue
Block a user