Fix xHCI endpoint ring loopback issue

This commit is contained in:
2026-08-25 22:42:46 +03:00
parent 8cb3ace92f
commit 0f32f37f0e
+14 -5
View File
@@ -538,7 +538,7 @@ uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uin
} }
xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) { xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) {
LOG_LN_INFO("Opening endpoint slot %d address %hhx...", slot, address); LOG_LN_STEP("Opening endpoint slot %d address %hhx...", slot, address);
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached."); ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached.");
@@ -549,11 +549,11 @@ xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t ty
uint8_t ep_dir = (address >> 7) & 1; uint8_t ep_dir = (address >> 7) & 1;
uint8_t dci = ep_num * 2 + ep_dir; uint8_t dci = ep_num * 2 + ep_dir;
LOG_LN_INFO("Setting up endpoint ring..."); LOG_LN_STEP("Setting up endpoint ring...");
RING_WRAP(ep); RING_WRAP(ep);
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle); XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle);
LOG_LN_INFO("Configuring endpoint..."); LOG_LN_STEP("Configuring endpoint...");
memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx); memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx);
input_ctx.slot = device_ctx.slot; input_ctx.slot = device_ctx.slot;
input_ctx.control.add_flags = (1 << 0) | (1 << dci); input_ctx.control.add_flags = (1 << 0) | (1 << dci);
@@ -597,9 +597,10 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL); XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL);
XHCI_TRB_IOC_W(ere, 1); XHCI_TRB_IOC_W(ere, 1);
XHCI_TRB_CYCLE_W(ere, ep_r_cycle); XHCI_TRB_CYCLE_W(ere, ep_r_cycle);
RING_ADVANCE(ep);
} }
ep_r_cycle ^= 1;
db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint; db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint;
return 0; return 0;
@@ -607,6 +608,14 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
uint16_t transferred = 0; uint16_t transferred = 0;
while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) { while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) {
if (ep_processed_events == 0) {
// A bit of a hack: flip link TRB cycle only after the first event is processed. There is some delay between controller
// posting the last success event of the batch, and consuming the link TRB. Flipping the link cycle together with the batch
// might happen before the controller got a chance to check the link cycle, and stall the consumption. If we received the
// first event of the next batch, we're certain the link TRB has just been processed, and there's plenty of time before the next loop.
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], !ep_r_cycle);
}
xhci_trb_t *cmp = &evt_r[evt_r_i]; xhci_trb_t *cmp = &evt_r[evt_r_i];
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) { if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) {
@@ -648,7 +657,7 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
} }
void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) { void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) {
LOG_LN_INFO("Closing endpoind %hhx...", endpoint); LOG_LN_STEP("Closing endpoind %hhx...", endpoint);
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached."); ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached.");