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:
1 parent
ffee9002a6
commit
93d8becce3
14 files changed
+340
-57
No files matched your search
@@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user