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.
This commit is contained in:
Lionel Debroux
2026-08-18 16:02:30 +02:00
parent cebd39cea4
commit f611b3595f
+14 -1
View File
@@ -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;