Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/usb Add some interrupt processing.



details:   https://anonhg.NetBSD.org/src/rev/5ae8cd8115ab
branches:  trunk
changeset: 517917:5ae8cd8115ab
user:      augustss <augustss%NetBSD.org@localhost>
date:      Wed Nov 21 12:28:23 2001 +0000

description:
Add some interrupt processing.

diffstat:

 sys/dev/usb/ehci.c    |  265 ++++++++++++++++++++++++++++++++++++++++++++++---
 sys/dev/usb/ehcireg.h |    3 +-
 sys/dev/usb/ehcivar.h |   29 ++--
 3 files changed, 262 insertions(+), 35 deletions(-)

diffs (truncated from 556 to 300 lines):

diff -r 14f244260de0 -r 5ae8cd8115ab sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Wed Nov 21 12:25:55 2001 +0000
+++ b/sys/dev/usb/ehci.c        Wed Nov 21 12:28:23 2001 +0000
@@ -1,7 +1,7 @@
 /* TODO
 Add intrinfo.
 */
-/*     $NetBSD: ehci.c,v 1.17 2001/11/21 08:18:39 augustss Exp $       */
+/*     $NetBSD: ehci.c,v 1.18 2001/11/21 12:28:23 augustss Exp $       */
 
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.17 2001/11/21 08:18:39 augustss Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.18 2001/11/21 12:28:23 augustss Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -119,6 +119,8 @@
 Static void            ehci_softintr(void *);
 Static int             ehci_intr1(ehci_softc_t *);
 Static void            ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
+Static void            ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+Static void            ehci_idone(struct ehci_xfer *);
 Static void            ehci_timeout(void *);
 Static void            ehci_timeout_task(void *);
 
@@ -181,6 +183,8 @@
 Static usbd_status     ehci_alloc_std_chain(struct ehci_pipe *,
                            ehci_softc_t *, int, int, usbd_xfer_handle,
                            ehci_soft_qtd_t **, ehci_soft_qtd_t **);
+Static void            ehci_free_std_chain(ehci_softc_t *, ehci_soft_qtd_t *,
+                                           ehci_soft_qtd_t *);
 
 Static usbd_status     ehci_device_request(usbd_xfer_handle xfer);
 
@@ -193,7 +197,7 @@
 Static void            ehci_abort_xfer(usbd_xfer_handle, usbd_status);
 
 #ifdef EHCI_DEBUG
-Static void            ehci_dumpregs(ehci_softc_t *);
+Static void            ehci_dump_regs(ehci_softc_t *);
 Static void            ehci_dump(void);
 Static ehci_softc_t    *theehci;
 Static void            ehci_dump_link(ehci_link_t, int);
@@ -201,6 +205,7 @@
 Static void            ehci_dump_sqtd(ehci_soft_qtd_t *);
 Static void            ehci_dump_qtd(ehci_qtd_t *);
 Static void            ehci_dump_sqh(ehci_soft_qh_t *);
+Static void            ehci_dump_exfer(struct ehci_xfer *);
 #endif
 
 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
@@ -209,6 +214,11 @@
 
 #define EHCI_INTR_ENDPT 1
 
+#define ehci_add_intr_list(sc, ex) \
+       LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext);
+#define ehci_del_intr_list(ex) \
+       LIST_REMOVE((ex), inext)
+
 Static struct usbd_bus_methods ehci_bus_methods = {
        ehci_open,
        ehci_softintr,
@@ -477,13 +487,10 @@
                wakeup(&sc->sc_async_head);
                eintrs &= ~EHCI_STS_INT;
        }
-       if (eintrs & EHCI_STS_INT) {
-               DPRINTF(("ehci_intr1: something is done\n"));
-               eintrs &= ~EHCI_STS_INT;
-       }
-       if (eintrs & EHCI_STS_ERRINT) {
-               DPRINTF(("ehci_intr1: some error\n"));
-               eintrs &= ~EHCI_STS_HSE;
+       if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) {
+               DPRINTF(("ehci_intr1: INT/ERRINT\n"));
+               usb_schedsoftintr(&sc->sc_bus);
+               eintrs &= ~(EHCI_STS_INT | EHCI_STS_HSE);
        }
        if (eintrs & EHCI_STS_HSE) {
                printf("%s: unrecoverable error, controller halted\n",
@@ -568,12 +575,168 @@
 void
 ehci_softintr(void *v)
 {
-       //ehci_softc_t *sc = v;
+       ehci_softc_t *sc = v;
+       struct ehci_xfer *ex;
+
+       DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev),
+                    sc->sc_bus.intr_context));
+
+       sc->sc_bus.intr_context++;
+
+       /*
+        * The only explanation I can think of for why EHCI is as brain dead
+        * as UHCI interrupt-wise is that Intel was involved in both.
+        * An interrupt just tells us that something is done, we have no
+        * clue what, so we need to scan through all active transfers. :-(
+        */
+       for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext))
+               ehci_check_intr(sc, ex);
+
+       sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+       ehci_soft_qtd_t *sqtd, *lsqtd;
+       u_int32_t status;
+
+       DPRINTFN(15, ("ehci_check_intr: ex=%p\n", ex));
+
+       if (ex->sqtdstart == NULL) {
+               printf("ehci_check_intr: sqtdstart=NULL\n");
+               return;
+       }
+       lsqtd = ex->sqtdend;
+#ifdef DIAGNOSTIC
+       if (lsqtd == NULL) {
+               printf("ehci_check_intr: sqtd==0\n");
+               return;
+       }
+#endif
+       /* 
+        * If the last TD is still active we need to check whether there
+        * is a an error somewhere in the middle, or whether there was a
+        * short packet (SPD and not ACTIVE).
+        */
+       if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) {
+               DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex));
+               for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) {
+                       status = le32toh(sqtd->qtd.qtd_status);
+                       /* If there's an active QTD the xfer isn't done. */
+                       if (status & EHCI_QTD_ACTIVE)
+                               break;
+                       /* Any kind of error makes the xfer done. */
+                       if (status & EHCI_QTD_HALTED)
+                               goto done;
+                       /* We want short packets, and it is short: it's done */
+                       if (EHCI_QTD_SET_BYTES(status) != 0)
+                               goto done;
+               }
+               DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n",
+                             ex, ex->sqtdstart));
+               return;
+       }
+ done:
+       DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
+       usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex);
+       ehci_idone(ex);
+}
+
+void
+ehci_idone(struct ehci_xfer *ex)
+{
+       usbd_xfer_handle xfer = &ex->xfer;
+       struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+       ehci_soft_qtd_t *sqtd;
+       u_int32_t status = 0, nstatus;
+       int actlen;
+
+       DPRINTFN(12, ("ehci_idone: ex=%p\n", ex));
+#ifdef DIAGNOSTIC
+       {
+               int s = splhigh();
+               if (ex->isdone) {
+                       splx(s);
+#ifdef EHCI_DEBUG
+                       printf("ehci_idone: ex is done!\n   ");
+                       ehci_dump_exfer(ex);
+#else
+                       printf("ehci_idone: ex=%p is done!\n", ex);
+#endif
+                       return;
+               }
+               ex->isdone = 1;
+               splx(s);
+       }
+#endif
+
+       if (xfer->status == USBD_CANCELLED ||
+           xfer->status == USBD_TIMEOUT) {
+               DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
+               return;
+       }
+
+#ifdef EHCI_DEBUG
+       DPRINTFN(10, ("ehci_idone: ex=%p, xfer=%p, pipe=%p ready\n",
+                     ex, xfer, epipe));
+       if (ehcidebug > 10)
+               ehci_dump_sqtds(ex->sqtdstart);
+#endif
+
+       /* The transfer is done, compute actual length and status. */
+       actlen = 0;
+       for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
+               nstatus = le32toh(sqtd->qtd.qtd_status);
+               if (nstatus & EHCI_QTD_ACTIVE)
+                       break;
+
+               status = nstatus;
+               if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
+                       actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
+       }
+#if 0
+       /* If there are left over TDs we need to update the toggle. */
+       if (sqtd != NULL)
+               epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token));
+#endif
+
+       status &= EHCI_QTD_STATERRS;
+       DPRINTFN(10, ("ehci_idone: actlen=%d, status=0x%x\n", actlen, status));
+       xfer->actlen = actlen;
+       if (status != 0) {
+#ifdef EHCI_DEBUG
+               char sbuf[128];
+
+               bitmask_snprintf((u_int32_t)status,
+                                "\20\2MISSEDMICRO\3XACT\4BABBLE\5BABBLE"
+                                "\6HALTED",
+                                sbuf, sizeof(sbuf));
+
+               DPRINTFN((status == EHCI_QTD_HALTED)*10,
+                        ("ehci_idone: error, addr=%d, endpt=0x%02x, "
+                         "status 0x%s\n",
+                         xfer->pipe->device->address,
+                         xfer->pipe->endpoint->edesc->bEndpointAddress,
+                         sbuf));
+#endif
+
+               if (status == EHCI_QTD_HALTED)
+                       xfer->status = USBD_STALLED;
+               else
+                       xfer->status = USBD_IOERROR; /* more info XXX */
+       } else {
+               xfer->status = USBD_NORMAL_COMPLETION;
+       }
+
+       usb_transfer_complete(xfer);
+       DPRINTFN(12, ("ehci_idone: ex=%p done\n", ex));
 }
 
 /*
  * Wait here until controller claims to have an interrupt.
- * Then call ohci_intr and return.  Use timeout to avoid waiting
+ * Then call ehci_intr and return.  Use timeout to avoid waiting
  * too long.
  */
 void
@@ -593,7 +756,7 @@
                DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs));
 #ifdef OHCI_DEBUG
                if (ehcidebug > 15)
-                       ehci_dumpregs(sc);
+                       ehci_dump_regs(sc);
 #endif
                if (intrs) {
                        ehci_intr1(sc);
@@ -689,7 +852,7 @@
 
 #ifdef EHCI_DEBUG
        DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
-       ehci_dumpregs(sc);
+       ehci_dump_regs(sc);
 #endif
 
        s = splhardusb();
@@ -787,8 +950,13 @@
                SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next);
        else
                xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
-       if (xfer != NULL)
+       if (xfer != NULL) {
                memset(xfer, 0, sizeof (struct ehci_xfer));
+#ifdef DIAGNOSTIC
+               EXFER(xfer)->isdone = 1;
+               xfer->busy_free = XFER_BUSY;
+#endif
+       }
        return (xfer);
 }
 
@@ -797,6 +965,18 @@
 {
        struct ehci_softc *sc = (struct ehci_softc *)bus;
 
+#ifdef DIAGNOSTIC
+       if (xfer->busy_free != XFER_BUSY) {
+               printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+                      xfer->busy_free);
+               return;
+       }



Home | Main Index | Thread Index | Old Index