From f611b3595fdcf88c20567a8267f218c16957f3c8 Mon Sep 17 00:00:00 2001 From: Lionel Debroux Date: Tue, 18 Aug 2026 10:52:20 +0200 Subject: [PATCH] xhci: Reposition the control ring dequeue from the enqueue state The two-step address sequence (BLOCK SET ADDRESS pass, then real ADDRESS_DEVICE pass after reading the first 8 bytes of the device descriptor) ran three TRBs on the control ring in between. The hard-coded 'tr_dequeue_ptr += 3 * sizeof(xhci_trb_t)' had to move the dequeue pointer in the input context past them, because the second ADDRESS_DEVICE command copies the whole input context into the slot context and would otherwise reset the dequeue to the head of the ring, making the controller re-execute the three stale TRBs. That arithmetic is fragile: it silently breaks as soon as the ring wraps (the cycle bit would no longer be valid) or fewer/more TRBs are consumed. The ring already tracks its true position in ep_tr->enqueue_state, so the dequeue pointer is now computed from it with the same index/cycle decomposition used to reposition a halted endpoint in reset_endpoint. --- system/xhci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/system/xhci.c b/system/xhci.c index 00311f1..b9010bd 100644 --- a/system/xhci.c +++ b/system/xhci.c @@ -904,7 +904,20 @@ static bool assign_address(const usb_hcd_t *hcd, const usb_hub_t *hub, int port_ ep_context->max_packet_size = (device_speed == USB_SPEED_SUPER) ? (1 << device->max_packet_size) : device->max_packet_size; - ep_context->tr_dequeue_ptr += 3 * sizeof(xhci_trb_t); + + // The GET_DESCRIPTOR request just completed consumed three TRBs on + // the control ring, so the ring's enqueue state (and the hardware + // dequeue with it) has moved on. The input context is about to be + // copied into the slot context by the second ADDRESS_DEVICE + // command, which would otherwise reset the control endpoint + // dequeue pointer to the head of the ring and make the controller + // re-execute the three stale TRBs. Reposition the dequeue pointer + // at the current enqueue state instead of hard-coding the number + // of TRBs consumed; the same decomposition of the enqueue state + // into ring index and cycle is used when repositioning a halted + // endpoint (see reset_endpoint). + ep_context->tr_dequeue_ptr = (uintptr_t)(&ep_tr->tr[ep_tr->enqueue_state % EP_TR_SIZE]) + | (ep_tr->enqueue_state / EP_TR_SIZE); fetch_length = sizeof(usb_device_desc_t); command_flags = 0;