Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/usb xhci(4): Fix handling of endpoint reset/stop.
details: https://anonhg.NetBSD.org/src/rev/0e66ba57729a
branches: trunk
changeset: 359901:0e66ba57729a
user: riastradh <riastradh%NetBSD.org@localhost>
date: Sat Jan 29 21:36:12 2022 +0000
description:
xhci(4): Fix handling of endpoint reset/stop.
Use the same asynchronous task resetting a stalled/halted endpoint
and stopping a running endpoint -- either way we need to put the
endpoint back into a known state and, if there are transfers waiting
to run, start them up again.
- xhci_abortx must not drop the pipe (bus) lock -- usbdi(9) requires
this. So arrange to stop the endpoint and empty the queue
asynchronously.
- If the xhci softint claims an xfer for completion with
usbd_xfer_trycomplete, it must call usb_transfer_complete without
ever releasing the pipe (bus) lock -- it can't claim the xfer and
then defer the usb_transfer_complete to a task. So arrange to
reset the endpoint asynchronously, hold up new transfers until the
endpoint has been reset, and then do usb_transfer_complete
immediately.
diffstat:
sys/dev/usb/xhci.c | 320 +++++++++++++++++++++++++---------------------------
1 files changed, 156 insertions(+), 164 deletions(-)
diffs (truncated from 513 to 300 lines):
diff -r 9d6bbe445f76 -r 0e66ba57729a sys/dev/usb/xhci.c
--- a/sys/dev/usb/xhci.c Sat Jan 29 20:54:58 2022 +0000
+++ b/sys/dev/usb/xhci.c Sat Jan 29 21:36:12 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: xhci.c,v 1.154 2022/01/25 11:17:39 msaitoh Exp $ */
+/* $NetBSD: xhci.c,v 1.155 2022/01/29 21:36:12 riastradh Exp $ */
/*
* Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.154 2022/01/25 11:17:39 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.155 2022/01/29 21:36:12 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
@@ -154,15 +154,18 @@
static int xhci_roothub_ctrl(struct usbd_bus *, usb_device_request_t *,
void *, int);
+static void xhci_pipe_async_task(void *);
+static void xhci_pipe_restart(struct usbd_pipe *);
+
static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
//static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
-static usbd_status xhci_reset_endpoint(struct usbd_pipe *);
+static void xhci_reset_endpoint(struct usbd_pipe *);
static usbd_status xhci_stop_endpoint_cmd(struct xhci_softc *,
struct xhci_slot *, u_int, uint32_t);
static usbd_status xhci_stop_endpoint(struct usbd_pipe *);
static void xhci_host_dequeue(struct xhci_ring * const);
-static usbd_status xhci_set_dequeue(struct usbd_pipe *);
+static void xhci_set_dequeue(struct usbd_pipe *);
static usbd_status xhci_do_command(struct xhci_softc * const,
struct xhci_soft_trb * const, int);
@@ -1858,14 +1861,13 @@
#endif
/* 4.6.8, 6.4.3.7 */
-static usbd_status
-xhci_reset_endpoint_locked(struct usbd_pipe *pipe)
+static void
+xhci_reset_endpoint(struct usbd_pipe *pipe)
{
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
struct xhci_soft_trb trb;
- usbd_status err;
XHCIHIST_FUNC();
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
@@ -1878,21 +1880,10 @@
XHCI_TRB_3_EP_SET(dci) |
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
- err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
-
- return err;
-}
-
-static usbd_status
-xhci_reset_endpoint(struct usbd_pipe *pipe)
-{
- struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
-
- mutex_enter(&sc->sc_lock);
- usbd_status ret = xhci_reset_endpoint_locked(pipe);
- mutex_exit(&sc->sc_lock);
-
- return ret;
+ if (xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT)) {
+ device_printf(sc->sc_dev, "%s: endpoint 0x%x: timed out\n",
+ __func__, pipe->up_endpoint->ue_edesc->bEndpointAddress);
+ }
}
/*
@@ -1947,15 +1938,14 @@
* EPSTATE of endpoint must be ERROR or STOPPED, otherwise CONTEXT_STATE
* error will be generated.
*/
-static usbd_status
-xhci_set_dequeue_locked(struct usbd_pipe *pipe)
+static void
+xhci_set_dequeue(struct usbd_pipe *pipe)
{
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
struct xhci_ring * const xr = xs->xs_xr[dci];
struct xhci_soft_trb trb;
- usbd_status err;
XHCIHIST_FUNC();
XHCIHIST_CALLARGS("slot %ju dci %ju", xs->xs_idx, dci, 0, 0);
@@ -1972,21 +1962,10 @@
XHCI_TRB_3_EP_SET(dci) |
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
- err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
-
- return err;
-}
-
-static usbd_status
-xhci_set_dequeue(struct usbd_pipe *pipe)
-{
- struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
-
- mutex_enter(&sc->sc_lock);
- usbd_status ret = xhci_set_dequeue_locked(pipe);
- mutex_exit(&sc->sc_lock);
-
- return ret;
+ if (xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT)) {
+ device_printf(sc->sc_dev, "%s: endpoint 0x%x: timed out\n",
+ __func__, pipe->up_endpoint->ue_edesc->bEndpointAddress);
+ }
}
/*
@@ -2036,6 +2015,9 @@
return USBD_NORMAL_COMPLETION;
}
+ usb_init_task(&xpipe->xp_async_task, xhci_pipe_async_task, xpipe,
+ USB_TASKQ_MPSAFE);
+
switch (xfertype) {
case UE_CONTROL:
pipe->up_methods = &xhci_device_ctrl_methods;
@@ -2081,6 +2063,8 @@
static void
xhci_close_pipe(struct usbd_pipe *pipe)
{
+ struct xhci_pipe * const xp =
+ container_of(pipe, struct xhci_pipe, xp_pipe);
struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
@@ -2090,6 +2074,9 @@
XHCIHIST_FUNC();
+ usb_rem_task_wait(pipe->up_dev, &xp->xp_async_task, USB_TASKQ_HC,
+ &sc->sc_lock);
+
if (sc->sc_dying)
return;
@@ -2149,68 +2136,27 @@
/*
* Abort transfer.
- * Should be called with sc_lock held.
+ * Should be called with sc_lock held. Must not drop sc_lock.
*/
static void
xhci_abortx(struct usbd_xfer *xfer)
{
XHCIHIST_FUNC();
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
- struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
- const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
XHCIHIST_CALLARGS("xfer %#jx pipe %#jx",
(uintptr_t)xfer, (uintptr_t)xfer->ux_pipe, 0, 0);
KASSERT(mutex_owned(&sc->sc_lock));
- ASSERT_SLEEPABLE();
-
KASSERTMSG((xfer->ux_status == USBD_CANCELLED ||
xfer->ux_status == USBD_TIMEOUT),
"bad abort status: %d", xfer->ux_status);
- /*
- * If we're dying, skip the hardware action and just notify the
- * software that we're done.
- */
- if (sc->sc_dying) {
- DPRINTFN(4, "xfer %#jx dying %ju", (uintptr_t)xfer,
- xfer->ux_status, 0, 0);
- goto dying;
- }
-
- /*
- * HC Step 1: Stop execution of TD on the ring.
- */
- switch (xhci_get_epstate(sc, xs, dci)) {
- case XHCI_EPSTATE_HALTED:
- (void)xhci_reset_endpoint_locked(xfer->ux_pipe);
- break;
- case XHCI_EPSTATE_STOPPED:
- break;
- default:
- (void)xhci_stop_endpoint(xfer->ux_pipe);
- break;
- }
-#ifdef DIAGNOSTIC
- uint32_t epst = xhci_get_epstate(sc, xs, dci);
- if (epst != XHCI_EPSTATE_STOPPED)
- DPRINTFN(4, "dci %ju not stopped %ju", dci, epst, 0, 0);
-#endif
-
- /*
- * HC Step 2: Remove any vestiges of the xfer from the ring.
- */
- xhci_set_dequeue_locked(xfer->ux_pipe);
-
- /*
- * Final Step: Notify completion to waiting xfers.
- */
-dying:
+ xhci_pipe_restart(xfer->ux_pipe);
+
usb_transfer_complete(xfer);
+
DPRINTFN(14, "end", 0, 0, 0, 0);
-
- KASSERT(mutex_owned(&sc->sc_lock));
}
static void
@@ -2227,69 +2173,102 @@
}
/*
- * Recover STALLed endpoint.
+ * Recover STALLed endpoint, or stop endpoint to abort a pipe.
* xHCI 1.1 sect 4.10.2.1
* Issue RESET_EP to recover halt condition and SET_TR_DEQUEUE to remove
* all transfers on transfer ring.
* These are done in thread context asynchronously.
*/
static void
-xhci_clear_endpoint_stall_async_task(void *cookie)
+xhci_pipe_async_task(void *cookie)
{
- struct usbd_xfer * const xfer = cookie;
- struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
- struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
- const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
+ struct xhci_pipe * const xp = cookie;
+ struct usbd_pipe * const pipe = &xp->xp_pipe;
+ struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
+ struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
+ const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
struct xhci_ring * const tr = xs->xs_xr[dci];
+ bool restart = false;
XHCIHIST_FUNC();
- XHCIHIST_CALLARGS("xfer %#jx slot %ju dci %ju", (uintptr_t)xfer, xs->xs_idx,
- dci, 0);
+ XHCIHIST_CALLARGS("pipe %#jx slot %ju dci %ju",
+ (uintptr_t)pipe, xs->xs_idx, dci, 0);
+
+ mutex_enter(&sc->sc_lock);
/*
- * XXXMRG: Stall task can run after slot is disabled when yanked.
- * This hack notices that the xs has been memset() in
- * xhci_disable_slot() and returns. Both xhci_reset_endpoint()
- * and xhci_set_dequeue() rely upon a valid ring setup for correct
- * operation, and the latter will fault, as would
- * usb_transfer_complete() if it got that far.
+ * - If the endpoint is halted, indicating a stall, reset it.
+ * - If the endpoint is stopped, we're already good.
+ * - Otherwise, someone wanted to abort the pipe, so stop the
+ * endpoint.
+ *
+ * In any case, clear the ring.
*/
- if (xs->xs_idx == 0) {
- DPRINTFN(4, "ends xs_idx is 0", 0, 0, 0, 0);
- return;
+ switch (xhci_get_epstate(sc, xs, dci)) {
+ case XHCI_EPSTATE_HALTED:
+ xhci_reset_endpoint(pipe);
+ break;
+ case XHCI_EPSTATE_STOPPED:
+ break;
+ default:
+ xhci_stop_endpoint(pipe);
+ break;
+ }
+ switch (xhci_get_epstate(sc, xs, dci)) {
+ case XHCI_EPSTATE_STOPPED:
+ break;
+ case XHCI_EPSTATE_ERROR:
+ device_printf(sc->sc_dev, "endpoint 0x%x error\n",
+ pipe->up_endpoint->ue_edesc->bEndpointAddress);
+ break;
+ default:
+ device_printf(sc->sc_dev, "endpoint 0x%x failed to stop\n",
+ pipe->up_endpoint->ue_edesc->bEndpointAddress);
Home |
Main Index |
Thread Index |
Old Index