drivers/usbhost: Acknowledge xHCI events before walking the ring.

The event ring was acknowledged after being walked.  An event arriving
during the walk sets the pending bit again, and clearing the bit
afterwards discards it.  Transfers have no timeout, so the transfer that
event belonged to waits forever.

Acknowledge first.  A spurious second pass over an empty ring costs
nothing.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
This commit is contained in:
Justin Hammond 2026-08-16 17:10:27 +08:00 • committed by Xiang Xiao
parent e03c23c4ae
commit f78decb390

View file

@ -2907,6 +2907,20 @@ static void xhci_interrupt_work(FAR void *arg)
FAR struct usbhost_xhci_s *priv = arg;
uint32_t iman;
/* Acknowledge before walking the ring, not after. An event arriving
* during the walk sets the pending bit again, and clearing after the
* walk discards it. Transfers have no timeout, so the one it belonged
* to would wait forever.
*/
xhci_oper_putreg(priv, XHCI_USBSTS, priv->pending);
iman = xhci_runt_getreg(priv, XHCI_IMAN(0));
if (iman & XHCI_IMAN_IP)
{
xhci_runt_putreg(priv, XHCI_IMAN(0), iman);
}
xhci_events_poll(priv);
/* Port Change Detect */
@ -2939,18 +2953,6 @@ static void xhci_interrupt_work(FAR void *arg)
uinfo("Host Controller Error\n");
}
/* ACK interrupts */
xhci_oper_putreg(priv, XHCI_USBSTS, priv->pending);
/* Clear interrupter pending bit */
iman = xhci_runt_getreg(priv, XHCI_IMAN(0));
if (iman & XHCI_IMAN_IP)
{
xhci_runt_putreg(priv, XHCI_IMAN(0), iman);
}
/* Clear pending bits */
priv->pending = 0;