feat: wait up to 10s for a late MONTAUKROOT, retry USB enumeration after a port reset, recover halted EP0/bulk endpoints, MSC card reader settle

This commit is contained in:
daniel committed 2026-10-25 17:57:20 +01:00
1 parent ffee9002a6
commit 93d8becce3
14 files changed
+340 -57

No files matched your search

+107 -1
View File
@@ -926,6 +926,30 @@ namespace Drivers::USB::Xhci {
// ControlTransfer - perform a control transfer on EP0
// -------------------------------------------------------------------------
// A STALL or a transaction error halts EP0 (xHCI 4.10.2.1), with the
// rest of the TD still queued; until it is reset every later request on
// the slot times out. Reset Endpoint, then Set TR Dequeue past the TD.
static void RecoverEP0(uint8_t slotId) {
UsbDeviceInfo& dev = g_devices[slotId];
TRB resetTrb = {};
resetTrb.Control = (TRB_RESET_ENDPOINT << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| (1 << 16); // DCI=1 (EP0)
SendCommand(resetTrb);
uint64_t newDeq = dev.EP0RingPhys + (uint64_t)dev.EP0RingEnqueue * sizeof(TRB);
if (dev.EP0RingCCS) newDeq |= 1; // DCS bit
TRB deqTrb = {};
deqTrb.Parameter0 = (uint32_t)(newDeq & 0xFFFFFFFF);
deqTrb.Parameter1 = (uint32_t)(newDeq >> 32);
deqTrb.Control = (TRB_SET_TR_DEQUEUE << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| (1 << 16); // DCI=1 (EP0)
SendCommand(deqTrb);
}
static uint32_t ControlTransferLocked(uint8_t slotId, uint8_t bmRequestType, uint8_t bRequest,
uint16_t wValue, uint16_t wIndex, uint16_t wLength,
void* data, uint64_t dataPhys, bool dirIn, bool trueNested);
@@ -1068,7 +1092,9 @@ namespace Drivers::USB::Xhci {
while (Timekeeping::GetMilliseconds() - xferStart < 2000) {
PollEvents();
if (g_xferCompleted[slotId]) {
return g_xferCompletionCode[slotId];
uint32_t cc = g_xferCompletionCode[slotId];
if (cc != CC_SUCCESS && cc != CC_SHORT_PACKET) RecoverEP0(slotId);
return cc;
}
for (int j = 0; j < 100; j++) {
Arch::CompilerBarrier();
@@ -1239,6 +1265,86 @@ namespace Drivers::USB::Xhci {
SendCommand(deqTrb);
}
// -------------------------------------------------------------------------
// ResetRootPort - reset a connected root hub port again
// -------------------------------------------------------------------------
bool ResetRootPort(uint8_t portId, uint32_t& speed) {
if (!g_initialized || portId == 0 || portId > g_maxPorts) return false;
uint32_t reg = OP_PORTSC_BASE + (portId - 1) * OP_PORTSC_STRIDE;
uint32_t portsc = ReadOp(reg);
if (!(portsc & PORTSC_CCS)) return false;
WriteOp(reg, (portsc & PORTSC_PRESERVE) | PORTSC_PR | PORTSC_CHANGE_BITS);
bool resetDone = false;
for (uint32_t i = 0; i < 100000; i++) {
PollEvents();
if (PortResetComplete(ReadOp(reg))) { resetDone = true; break; }
for (int j = 0; j < 100; j++) Arch::CompilerBarrier();
}
if (!resetDone) return false;
portsc = ReadOp(reg);
WriteOp(reg, (portsc & PORTSC_PRESERVE) | PORTSC_CHANGE_BITS);
if (!(portsc & PORTSC_PED)) return false;
speed = (portsc >> 10) & 0xF;
return true;
}
// -------------------------------------------------------------------------
// RecoverBulkEndpoint - clear a halt or a timed-out TRB on a bulk endpoint
// -------------------------------------------------------------------------
void RecoverBulkEndpoint(uint8_t slotId, bool dirIn) {
if (slotId == 0 || slotId > MAX_SLOTS || !g_devices[slotId].Active) return;
UsbDeviceInfo& dev = g_devices[slotId];
uint8_t epNum = dirIn ? dev.BulkInEpNum : dev.BulkOutEpNum;
TRB* ring = dirIn ? dev.BulkInRing : dev.BulkOutRing;
if (epNum == 0 || ring == nullptr) return;
uint8_t dci = dirIn ? (uint8_t)(epNum * 2 + 1) : (uint8_t)(epNum * 2);
// Reset Endpoint succeeds only on a halted endpoint; one that is still
// running (the transfer timed out) is stopped instead. Either way it
// ends up Stopped, which Set TR Dequeue requires.
TRB resetTrb = {};
resetTrb.Control = (TRB_RESET_ENDPOINT << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| ((uint32_t)dci << 16);
bool wasHalted = SendCommand(resetTrb) == CC_SUCCESS;
if (!wasHalted) {
TRB stopTrb = {};
stopTrb.Control = (TRB_STOP_ENDPOINT << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| ((uint32_t)dci << 16);
SendCommand(stopTrb);
}
uint64_t newDeq = dirIn
? dev.BulkInRingPhys + (uint64_t)dev.BulkInRingEnqueue * sizeof(TRB)
: dev.BulkOutRingPhys + (uint64_t)dev.BulkOutRingEnqueue * sizeof(TRB);
if (dirIn ? dev.BulkInRingCCS : dev.BulkOutRingCCS) newDeq |= 1; // DCS bit
TRB deqTrb = {};
deqTrb.Parameter0 = (uint32_t)(newDeq & 0xFFFFFFFF);
deqTrb.Parameter1 = (uint32_t)(newDeq >> 32);
deqTrb.Control = (TRB_SET_TR_DEQUEUE << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| ((uint32_t)dci << 16);
SendCommand(deqTrb);
// Reset Endpoint put the host's data toggle back to DATA0; CLEAR_FEATURE
// (ENDPOINT_HALT) clears the device's stall and does the same on its
// side (USB 2.0 9.4.5). Without a reset the toggles still agree.
if (wasHalted) {
ControlTransfer(slotId, 0x02 /* host-to-dev, standard, endpoint */,
0x01 /* CLEAR_FEATURE */, 0 /* ENDPOINT_HALT */,
(uint16_t)((dirIn ? 0x80 : 0x00) | epNum), 0, nullptr, false);
}
}
// -------------------------------------------------------------------------
// QueueBulkInTransfer
// -------------------------------------------------------------------------