Everyone who has seen the ohci_hash_find_td not found panic should try
this one and let me know whether the machine panics or not and
where. This patch is against CURRENT.

It looks like the hardware kind of deviates from the OHCI spec when
putting things on the Done queue in case of an error.

Important: Your system will panic and completely destroy your
filesystem. If it doesn't you got lucky.

Cheers,

Nick
--
[EMAIL PROTECTED]
[EMAIL PROTECTED]                                          USB project
http://www.etla.net/~n_hibma/


Index: ohci.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ohci.c,v
retrieving revision 1.36
diff -w -u -r1.36 ohci.c
--- ohci.c      2000/01/29 16:25:55     1.36
+++ ohci.c      2000/01/29 17:04:44
@@ -91,7 +91,7 @@
 #ifdef OHCI_DEBUG
 #define DPRINTF(x)     if (ohcidebug) logprintf x
 #define DPRINTFN(n,x)  if (ohcidebug>(n)) logprintf x
-int ohcidebug = 0;
+int ohcidebug = 1;
 #else
 #define DPRINTF(x)
 #define DPRINTFN(n,x)
@@ -417,6 +417,7 @@
        usbd_status err;
        int i, offs;
        usb_dma_t dma;
+       int s;
 
        if (sc->sc_freetds == NULL) {
                DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));
@@ -436,6 +437,11 @@
        sc->sc_freetds = std->nexttd;
        memset(&std->td, 0, sizeof(ohci_td_t));
        std->nexttd = NULL;
+
+       s = splusb();
+       ohci_hash_add_td(sc, std);
+       splx(s);
+
        return (std);
 }
 
@@ -444,6 +450,12 @@
        ohci_softc_t *sc;
        ohci_soft_td_t *std;
 {
+       int s;
+
+       s = splusb();
+       ohci_hash_rem_td(sc, std);
+       splx(s);
+
        std->nexttd = sc->sc_freetds;
        sc->sc_freetds = std;
 }
@@ -1127,7 +1139,8 @@
 #ifdef OHCI_DEBUG
        if (ohcidebug > 10) {
                DPRINTF(("ohci_process_done: TD done:\n"));
-               ohci_dump_tds(sdone);
+               for (std = sdone; std; std = std->dnext)
+                       ohci_dump_td(sdone);
        }
 #endif
 
@@ -1135,7 +1148,16 @@
                xfer = std->xfer;
                stdnext = std->dnext;
                DPRINTFN(5, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n",
-                               std, xfer, xfer->hcpriv));
+                               std, xfer, (xfer? xfer->hcpriv:NULL)));
+               if (xfer == NULL || (std->flags & OHCI_TD_HANDLED)) {
+                       /* xfer == NULL: There seems to be no xfer associated
+                        * with this TD. It is tailp that happened to end up on
+                        * the done queue.
+                        * flags & OHCI_TD_HANDLED: The TD has already been
+                        * handled by process_done and should not be done again.
+                        */
+                       continue;
+               }
                cc = OHCI_TD_GET_CC(LE(std->td.td_flags));
                usb_untimeout(ohci_timeout, xfer, xfer->timo_handle);
                if (xfer->status == USBD_CANCELLED ||
@@ -1156,7 +1178,6 @@
                                xfer->status = USBD_NORMAL_COMPLETION;
                                usb_transfer_complete(xfer);
                        }
-                       ohci_hash_rem_td(sc, std);
                        ohci_free_std(sc, std);
                } else {
                        /*
@@ -1172,16 +1193,23 @@
                          OHCI_TD_GET_CC(LE(std->td.td_flags)),
                          ohci_cc_strs[OHCI_TD_GET_CC(LE(std->td.td_flags))],
                          xfer));
+
+                       /* Mark all the TDs in the done queue for the current
+                        * xfer as handled
+                        */
+                       for (p = stdnext; p; p = p->dnext) {
+                               if (p->xfer == xfer)
+                                       p->flags |= OHCI_TD_HANDLED;
+                       }
 
-                       /* remove TDs */
+                       /* remove TDs for the current xfer from the ED */
                        for (p = std; p->xfer == xfer; p = n) {
                                n = p->nexttd;
-                               ohci_hash_rem_td(sc, p);                        
                                ohci_free_std(sc, p);
                        }
-
-                       /* clear halt */
                        opipe->sed->ed.ed_headp = LE(p->physaddr);
+
+                       /* XXX why is this being done? Why not OHCI_BLF too */
                        OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
 
                        if (cc == OHCI_CC_STALL)
@@ -1462,10 +1490,6 @@
 
        /* Insert ED in schedule */
        s = splusb();
-       ohci_hash_add_td(sc, setup);
-       if (len != 0)
-               ohci_hash_add_td(sc, data);
-       ohci_hash_add_td(sc, stat);
        sed->ed.ed_tailp = LE(tail->physaddr);
        opipe->tail.td = tail;
        OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
@@ -1870,7 +1894,6 @@
 #endif
        for (; p->xfer == xfer; p = n) {
                n = p->nexttd;
-               ohci_hash_rem_td(sc, p);
                ohci_free_std(sc, p);
        }
 
Index: ohcivar.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ohcivar.h,v
retrieving revision 1.15
diff -w -u -r1.15 ohcivar.h
--- ohcivar.h   2000/01/27 23:25:58     1.15
+++ ohcivar.h   2000/01/29 12:28:25
@@ -57,6 +57,7 @@
        u_int16_t flags;
 #define OHCI_CALL_DONE 0x0001
 #define OHCI_ADD_LEN   0x0002
+#define OHCI_TD_HANDLED        0x0004          /* signal process_done has seen it */
 } ohci_soft_td_t;
 #define OHCI_STD_SIZE ((sizeof (struct ohci_soft_td) + OHCI_TD_ALIGN - 1) / 
OHCI_TD_ALIGN * OHCI_TD_ALIGN)
 #define OHCI_STD_CHUNK 128

Reply via email to