Re: xhci: recover halted endpoints on USB Transaction Errors
Atanas Vladimirov <[email protected]>
| Newsgroups | gmane.os.openbsd.tech |
|---|---|
| Message-ID | <[email protected]> |
Hello,
Here is an updated patch:
xhci(4): recover halted endpoints on USB Transaction Errors.
XHCI_CODE_TXERR / XHCI_CODE_SPLITERR leave the endpoint in the Halted
state (xHCI r1.1 4.10.2.6). The old code just flagged USBD_IOERROR
and broke, so every subsequent xfer queued on the pipe was silently
dropped by the halted endpoint and the device stopped responding.
Treat these errors like XHCI_CODE_STALL: share its recovery path so
the endpoint is always reset (async reset-ep + set-tr-deq) before the
xfer completes with USBD_IOERROR, and the stack can restart the pipe
on a clean endpoint. Keep a per-pipe counter of consecutive errors;
past XHCI_TXERR_RETRIES additionally call usb_needs_reattach() so the
hub explore task re-enumerates the device instead of the pipe retrying
forever. The counter is cleared on any successful completion and on
pipe (re-)init.
Fixes a stuck USB keyboard/mouse after a BMC/iKVM reset on Supermicro
X10/X11 boards, without needing a host reboot.
Index: sys/dev/usb/xhci.c
===================================================================
--- sys/dev/usb/xhci.c
+++ sys/dev/usb/xhci.c
@@ -70,6 +70,7 @@ struct xhci_pipe {
struct usbd_xfer *pending_xfers[XHCI_MAX_XFER];
struct usbd_xfer *aborted_xfer;
int halted;
+ u_int txerr_count;
size_t free_trbs;
int skip;
#define TRB_PROCESSED_NO 0
@@ -78,6 +79,8 @@ struct xhci_pipe {
uint8_t trb_processed[XHCI_MAX_XFER];
};
+#define XHCI_TXERR_RETRIES 3
+
int xhci_reset(struct xhci_softc *);
void xhci_suspend(struct xhci_softc *);
int xhci_intr1(struct xhci_softc *);
@@ -953,6 +956,7 @@ xhci_event_xfer_generic(struct xhci_softc *sc, struct usbd_xfer *xfer,
usbd_xfer_isread(xfer) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
xfer->status = USBD_NORMAL_COMPLETION;
+ xp->txerr_count = 0;
break;
case XHCI_CODE_SHORT_XFER:
/*
@@ -977,12 +981,22 @@ xhci_event_xfer_generic(struct xhci_softc *sc, struct usbd_xfer *xfer,
usbd_xfer_isread(xfer) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
xfer->status = USBD_NORMAL_COMPLETION;
+ xp->txerr_count = 0;
break;
case XHCI_CODE_TXERR:
case XHCI_CODE_SPLITERR:
DPRINTF(("%s: txerr? code %d\n", DEVNAME(sc), code));
- xfer->status = USBD_IOERROR;
- break;
+ /*
+ * A USB Transaction Error leaves the endpoint Halted
+ * (xHCI r1.1 4.10.2.6), so reset it like a STALL to
+ * let the stack restart the pipe on a clean endpoint.
+ * If the endpoint keeps failing, additionally ask the
+ * hub to re-enumerate the device instead of retrying
+ * forever.
+ */
+ if (++xp->txerr_count > XHCI_TXERR_RETRIES)
+ usb_needs_reattach(xfer->device);
+ /* FALLTHROUGH */
case XHCI_CODE_STALL:
case XHCI_CODE_BABBLE:
DPRINTF(("%s: babble code %d\n", DEVNAME(sc), code));
@@ -1623,6 +1637,7 @@ xhci_pipe_init(struct xhci_softc *sc, struct usbd_pipe *pipe)
xp->free_trbs = xp->ring.ntrb;
xp->halted = 0;
+ xp->txerr_count = 0;
sdev->pipes[xp->dci - 1] = xp;