Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb Various improvement from t-hash (and a bit from ...



details:   https://anonhg.NetBSD.org/src/rev/37c26498899d
branches:  trunk
changeset: 816675:37c26498899d
user:      skrll <skrll%NetBSD.org@localhost>
date:      Sun Jul 17 20:13:19 2016 +0000

description:
Various improvement from t-hash (and a bit from me).

- Flesh out the abort routine
- Avoid using EVENT_DATA
- Miscellaneous debug
- Comment improvement

diffstat:

 sys/dev/usb/xhci.c |  191 +++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 150 insertions(+), 41 deletions(-)

diffs (truncated from 378 to 300 lines):

diff -r cf2eb03fc183 -r 37c26498899d sys/dev/usb/xhci.c
--- a/sys/dev/usb/xhci.c        Sun Jul 17 19:44:56 2016 +0000
+++ b/sys/dev/usb/xhci.c        Sun Jul 17 20:13:19 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: xhci.c,v 1.62 2016/07/08 05:38:31 skrll Exp $  */
+/*     $NetBSD: xhci.c,v 1.63 2016/07/17 20:13:19 skrll Exp $  */
 
 /*
  * Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.62 2016/07/08 05:38:31 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.63 2016/07/17 20:13:19 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -520,6 +520,17 @@
        return 0;
 }
 
+static unsigned int
+xhci_get_epstate(struct xhci_softc * const sc, struct xhci_slot * const xs,
+    u_int dci)
+{
+       uint32_t *cp;
+
+       usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
+       cp = xhci_slot_get_dcv(sc, xs, dci);
+       return XHCI_EPCTX_0_EPSTATE_GET(le32toh(cp[0]));
+}
+
 /* --- */
 
 void
@@ -1279,7 +1290,7 @@
 
 /* 4.6.8, 6.4.3.7 */
 static usbd_status
-xhci_reset_endpoint(struct usbd_pipe *pipe)
+xhci_reset_endpoint_locked(struct usbd_pipe *pipe)
 {
        struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
        struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
@@ -1290,17 +1301,31 @@
        XHCIHIST_FUNC(); XHCIHIST_CALLED();
        DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
+       KASSERT(mutex_owned(&sc->sc_lock));
+
        trb.trb_0 = 0;
        trb.trb_2 = 0;
        trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
            XHCI_TRB_3_EP_SET(dci) |
            XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
 
-       err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+       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;
+}
+
 /*
  * 4.6.9, 6.4.3.8
  * Stop execution of TDs on xfer ring.
@@ -1340,7 +1365,7 @@
  * error will be generated.
  */
 static usbd_status
-xhci_set_dequeue(struct usbd_pipe *pipe)
+xhci_set_dequeue_locked(struct usbd_pipe *pipe)
 {
        struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
        struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
@@ -1352,6 +1377,8 @@
        XHCIHIST_FUNC(); XHCIHIST_CALLED();
        DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
+       KASSERT(mutex_owned(&sc->sc_lock));
+
        xhci_host_dequeue(xr);
 
        /* set DCS */
@@ -1361,11 +1388,23 @@
            XHCI_TRB_3_EP_SET(dci) |
            XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
 
-       err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+       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;
+}
+
 /*
  * Open new pipe: called from usbd_setup_pipe_flags.
  * Fills methods of pipe.
@@ -1508,12 +1547,14 @@
 
 /*
  * Abort transfer.
- * May be called from softintr context.
+ * Should be called with sc_lock held.
  */
 static void
 xhci_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
 {
        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_FUNC(); XHCIHIST_CALLED();
        DPRINTFN(4, "xfer %p pipe %p status %d",
@@ -1530,10 +1571,66 @@
                return;
        }
 
-       /* XXX need more stuff */
+       /*
+        * If an abort is already in progress then just wait for it to
+        * complete and return.
+        */
+       if (xfer->ux_hcflags & UXFER_ABORTING) {
+               DPRINTFN(4, "already aborting", 0, 0, 0, 0);
+#ifdef DIAGNOSTIC
+               if (status == USBD_TIMEOUT)
+                       DPRINTFN(4, "TIMEOUT while aborting", 0, 0, 0, 0);
+#endif
+               /* Override the status which might be USBD_TIMEOUT. */
+               xfer->ux_status = status;
+               DPRINTFN(4, "xfer %p waiting for abort to finish", xfer, 0, 0,
+                   0);
+               xfer->ux_hcflags |= UXFER_ABORTWAIT;
+               while (xfer->ux_hcflags & UXFER_ABORTING)
+                       cv_wait(&xfer->ux_hccv, &sc->sc_lock);
+               return;
+       }
+       xfer->ux_hcflags |= UXFER_ABORTING;
+
+       /*
+        * Step 1: Stop xfer timeout timer.
+        */
        xfer->ux_status = status;
        callout_stop(&xfer->ux_callout);
+
+       /*
+        * Step 2: 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 %u not stopped %u", dci, epst, 0, 0);
+#endif
+
+       /*
+        * Step 3: Remove any vestiges of the xfer from the ring.
+        */
+       xhci_set_dequeue_locked(xfer->ux_pipe);
+
+       /*
+        * Step 4: Notify completion to waiting xfers.
+        */
+       int wake = xfer->ux_hcflags & UXFER_ABORTWAIT;
+       xfer->ux_hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
        usb_transfer_complete(xfer);
+       if (wake) {
+               cv_broadcast(&xfer->ux_hccv);
+       }
        DPRINTFN(14, "end", 0, 0, 0, 0);
 
        KASSERT(mutex_owned(&sc->sc_lock));
@@ -1665,12 +1762,12 @@
                }
                xx = xr->xr_cookies[idx];
 
+               /* clear cookie of consumed TRB */
+               xr->xr_cookies[idx] = NULL;
+
                /*
-                * If endpoint is stopped between TDs, TRB pointer points at
-                * next TRB, however, it is not put yet or is a garbage TRB.
-                * That's why xr_cookies may be NULL or look like broken.
-                * Note: this ev happens only when hciversion >= 1.0 or
-                * hciversion == 0.96 and FSE of hcc1 is set.
+                * xx is NULL if pipe is opened but xfer is not started.
+                * It happens when stopping idle pipe.
                 */
                if (xx == NULL || trbcode == XHCI_TRB_ERROR_LENGTH) {
                        DPRINTFN(1, "Ignore #%u: cookie %p cc %u dci %u",
@@ -1678,6 +1775,7 @@
                        DPRINTFN(1, " orig TRB %"PRIx64" type %u", trb_0,
                            XHCI_TRB_3_TYPE_GET(le32toh(xr->xr_trb[idx].trb_3)),
                            0, 0);
+                       return;
                }
        } else {
                /* When ED != 0, trb_0 is virtual addr of struct xhci_xfer. */
@@ -1718,10 +1816,10 @@
        case XHCI_TRB_ERROR_SHORT_PKT:
        case XHCI_TRB_ERROR_SUCCESS:
                /*
-                * A ctrl transfer generates two events if it has a Data stage.
-                * After a successful Data stage we cannot call call
-                * usb_transfer_complete - this can only happen after the Data
-                * stage.
+                * A ctrl transfer can generate two events if it has a Data
+                * stage.  A short data stage can be OK and should not
+                * complete the transfer as the status stage needs to be
+                * performed.
                 *
                 * Note: Data and Status stage events point at same xfer.
                 * ux_actlen and ux_dmabuf will be passed to
@@ -1737,10 +1835,32 @@
                 * ctrl xfer uses EVENT_DATA, and others do not.
                 * Thus driver can switch the flow by checking ED bit.
                 */
-               xfer->ux_actlen =
-                   xfer->ux_length - XHCI_TRB_2_REM_GET(trb_2);
+               if ((trb_3 & XHCI_TRB_3_ED_BIT) == 0) {
+                       if (xfer->ux_actlen == 0)
+                               xfer->ux_actlen = xfer->ux_length -
+                                   XHCI_TRB_2_REM_GET(trb_2);
+                       if (XHCI_TRB_3_TYPE_GET(le32toh(xr->xr_trb[idx].trb_3))
+                           == XHCI_TRB_TYPE_DATA_STAGE) {
+                               return;
+                       }
+               } else if ((trb_0 & 0x3) == 0x3) {
+                       return;
+               }
                err = USBD_NORMAL_COMPLETION;
                break;
+       case XHCI_TRB_ERROR_STOPPED:
+       case XHCI_TRB_ERROR_LENGTH:
+       case XHCI_TRB_ERROR_STOPPED_SHORT:
+               /*
+                * don't complete the transfer being aborted
+                * as abort_xfer does instead.
+                */
+               if (xfer->ux_hcflags & UXFER_ABORTING) {
+                       DPRINTFN(14, "ignore aborting xfer %p", xfer, 0, 0, 0);
+                       return;
+               }
+               err = USBD_CANCELLED;
+               break;
        case XHCI_TRB_ERROR_STALL:
        case XHCI_TRB_ERROR_BABBLE:
                DPRINTFN(1, "ERR %u slot %u dci %u", trbcode, slot, dci, 0);
@@ -2073,8 +2193,10 @@
 
                /* 4.3.2 */
                err = xhci_enable_slot(sc, &slot);
-               if (err)
+               if (err) {
+                       DPRINTFN(1, "enable slot %u", err, 0, 0, 0);
                        goto bad;
+               }
 
                xs = &sc->sc_slots[slot];
                dev->ud_hcpriv = xs;
@@ -2082,6 +2204,7 @@
                /* 4.3.3 initialize slot structure */
                err = xhci_init_slot(dev, slot);
                if (err) {
+                       DPRINTFN(1, "init slot %u", err, 0, 0, 0);
                        dev->ud_hcpriv = NULL;
                        /*



Home | Main Index | Thread Index | Old Index