drivers/usbhost: Release the xHCI slot when enumeration fails.

A device slot is a finite controller resource: HCSPARAMS1 reports how many
exist and Enable Slot fails with No Slots Available once they are gone.
Two paths took one and returned without giving it back.

xhci_device_init() enables a slot before initialising the transfer ring,
the slot context and the device address, and each of those returned
directly on failure.  It also treated a slot number larger than the
controller supports as success, since Enable Slot itself had succeeded.

xhci_enumerate() is the larger leak: the device is addressed by the time
usbhost_enumerate() runs, so a device whose descriptor cannot be read, or
that no class driver claims, leaves the slot held.  That path clears
hport->connected so the port is retried, taking another slot each time.

Release the slot on both paths with xhci_device_deinit(), which issues
Disable Slot, clears the DCBAA entry and resets the context.  The endpoint
ring is left allocated; xhci_ring_init() reuses an existing one.

Tested on an EIC7700X board with a device no class driver claims, so the
port retries indefinitely: previously the eighth attempt failed with
completion code 9 and the controller enumerated nothing further on either
port; now 1104 consecutive attempts produced no slot failure.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
This commit is contained in:
Justin Hammond 2026-08-08 12:37:50 +08:00 • committed by Xiang Xiao
parent fc58227802
commit 18c834b7d3

View file

@ -1810,7 +1810,15 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv,
ret = xhci_cmd_sloten(priv, &slot);
if (ret < 0 || slot > priv->no_slots)
{
/* Something goes wrong ! */
/* A slot the controller cannot address is no more usable than no
* slot at all, and the command itself succeeds in that case, so the
* caller needs an error either way.
*/
if (ret >= 0)
{
ret = -EINVAL;
}
usbhost_vtrace1(XHCI_TRACE1_SLOTEN_FAILED, ret);
return ret;
@ -1834,7 +1842,7 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv,
if (ret < 0)
{
uerr("ep0 ring init failed\n");
return ret;
goto errout_with_slot;
}
rhport->ep0.slot = slot;
@ -1845,7 +1853,7 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv,
ret = xhci_slot_init(priv, dev);
if (ret < 0)
{
return ret;
goto errout_with_slot;
}
/* Step 6: Assign and address to the device and enable its Default
@ -1860,12 +1868,21 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv,
if (ret < 0)
{
uerr("failed to set address %d\n", ret);
return ret;
goto errout_with_slot;
}
/* Steps 7-12 don't belong here! */
return OK;
errout_with_slot:
/* Nothing else gives the slot back, and the controller has a fixed
* number of them.
*/
xhci_device_deinit(priv, rhport);
return ret;
}
/****************************************************************************
@ -3770,6 +3787,23 @@ static int xhci_enumerate(FAR struct usbhost_connection_s *conn,
{
/* Failed to enumerate */
/* The device is addressed by now, so it holds a slot, and the retry
* below asks for another.
*/
#ifdef CONFIG_USBHOST_HUB
if (ROOTHUB(hport))
#endif
{
FAR struct usbhost_xhci_s *priv = XHCI_PRIV_FROM_CONN(conn);
FAR struct xhci_rhport_s *rhport = &priv->rhport[hport->port];
if (rhport->dev != NULL)
{
xhci_device_deinit(priv, rhport);
}
}
/* If this is a root hub port, then marking the hub port not connected
* will cause xhci_wait() to return and we will try the connection
* again.