summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authoraugustss <augustss@NetBSD.org>2001-11-09 15:01:57 +0000
committeraugustss <augustss@NetBSD.org>2001-11-09 15:01:57 +0000
commit495b9bd7d5f577f3d6cde4a8a09b28ec1beee695 (patch)
treea757e8f3f2363c445a0d6aabdf6a11f39150bd69 /sys/dev/usb
parent3dd8433864cde55e8106489cd2f1992f12a0a22e (diff)
Fix a bug in xfer abort processing when the HC executes ahead of what
the driver aborts. Don't block RHSC interrupts.
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/ohci.c130
1 files changed, 73 insertions, 57 deletions
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c
index 00fc80471de..1140f84f28d 100644
--- a/sys/dev/usb/ohci.c
+++ b/sys/dev/usb/ohci.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ohci.c,v 1.105 2001/11/07 02:55:04 augustss Exp $ */
+/* $NetBSD: ohci.c,v 1.106 2001/11/09 15:01:57 augustss Exp $ */
/* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $ */
/*
@@ -208,7 +208,6 @@ Static void ohci_rhsc_enable(void *);
Static void ohci_close_pipe(usbd_pipe_handle, ohci_soft_ed_t *);
Static void ohci_abort_xfer(usbd_xfer_handle, usbd_status);
-Static void ohci_abort_xfer_end(void *);
Static void ohci_device_clear_toggle(usbd_pipe_handle pipe);
Static void ohci_noop(usbd_pipe_handle pipe);
@@ -1114,13 +1113,13 @@ ohci_intr1(ohci_softc_t *sc)
sc->sc_overrun_cnt = 0;
}
/* XXX do what */
- intrs &= ~OHCI_SO;
+ eintrs &= ~OHCI_SO;
}
if (eintrs & OHCI_WDH) {
ohci_add_done(sc, done &~ OHCI_DONE_INTRS);
sc->sc_hcca->hcca_done_head = 0;
usb_schedsoftintr(&sc->sc_bus);
- intrs &= ~OHCI_WDH;
+ eintrs &= ~OHCI_WDH;
}
if (eintrs & OHCI_RD) {
printf("%s: resume detect\n", USBDEVNAME(sc->sc_bus.bdev));
@@ -1134,8 +1133,6 @@ ohci_intr1(ohci_softc_t *sc)
}
if (eintrs & OHCI_RHSC) {
ohci_rhsc(sc, sc->sc_intrxfer);
- intrs &= ~OHCI_RHSC;
-
/*
* Disable RHSC interrupt for now, because it will be
* on until the port has been reset.
@@ -1143,13 +1140,18 @@ ohci_intr1(ohci_softc_t *sc)
ohci_rhsc_able(sc, 0);
/* Do not allow RHSC interrupts > 1 per second */
usb_callout(sc->sc_tmo_rhsc, hz, ohci_rhsc_enable, sc);
+ eintrs &= ~OHCI_RHSC;
}
sc->sc_bus.intr_context--;
- /* Block unprocessed interrupts. XXX */
- OWRITE4(sc, OHCI_INTERRUPT_DISABLE, intrs);
- sc->sc_eintrs &= ~intrs;
+ if (eintrs != 0) {
+ /* Block unprocessed interrupts. XXX */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, eintrs);
+ sc->sc_eintrs &= ~eintrs;
+ printf("%s: blocking intrs 0x%x\n",
+ USBDEVNAME(sc->sc_bus.bdev), eintrs);
+ }
return (1);
}
@@ -1871,7 +1873,7 @@ ohci_dump_ed(ohci_soft_ed_t *sed)
bitmask_snprintf((u_long)le32toh(sed->ed.ed_headp),
"\20\1HALT\2CARRY", sbuf2, sizeof(sbuf2));
- DPRINTF(("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d %s\ntailp=0x%08lx "
+ DPRINTF(("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d flags=%s\ntailp=0x%08lx "
"headflags=%s headp=0x%08lx nexted=0x%08lx\n",
sed, (u_long)sed->physaddr,
OHCI_ED_GET_FA(le32toh(sed->ed.ed_flags)),
@@ -2012,18 +2014,18 @@ ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head)
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
(le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) {
- ohci_physaddr_t td = le32toh(sed->ed.ed_headp);
ohci_soft_td_t *std;
- for (std = LIST_FIRST(&sc->sc_hash_tds[HASH(td)]);
- std != NULL;
- std = LIST_NEXT(std, hnext))
- if (std->physaddr == td)
- break;
+ std = ohci_hash_find_td(sc, le32toh(sed->ed.ed_headp));
printf("ohci_close_pipe: pipe not empty sed=%p hd=0x%x "
"tl=0x%x pipe=%p, std=%p\n", sed,
(int)le32toh(sed->ed.ed_headp),
(int)le32toh(sed->ed.ed_tailp),
pipe, std);
+#ifdef OHCI_DEBUG
+ ohci_dump_ed(sed);
+ if (std)
+ ohci_dump_td(std);
+#endif
usb_delay_ms(&sc->sc_bus, 2);
if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
(le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
@@ -2049,68 +2051,82 @@ void
ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
{
struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
- ohci_soft_ed_t *sed;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *p, *n;
+ ohci_physaddr_t headp;
+ int s, hit;
- DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p\n", xfer, opipe));
+ DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed));
- xfer->status = status;
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("ohci_abort_xfer: not in process context\n");
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
-
- sed = opipe->sed;
+ splx(s);
DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
-#if 1
- if (xfer->device->bus->intr_context) {
- /* We have no process context, so we can't use tsleep(). */
- usb_callout(xfer->pipe->abort_handle,
- hz / USB_FRAMES_PER_SECOND, ohci_abort_xfer_end, xfer);
- } else {
-#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
- KASSERT(intr_nesting_level == 0,
- ("ohci_abort_req in interrupt context"));
-#endif
- usb_delay_ms(opipe->pipe.device->bus, 1);
- ohci_abort_xfer_end(xfer);
- }
-#else
- delay(1000);
- ohci_abort_xfer_end(xfer);
-#endif
-}
-
-void
-ohci_abort_xfer_end(void *v)
-{
- usbd_xfer_handle xfer = v;
- struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
- ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
- ohci_soft_ed_t *sed;
- ohci_soft_td_t *p, *n;
- int s;
-
- s = splusb();
-
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ usb_delay_ms(opipe->pipe.device->bus, 1); /* Hardware finishes in 1ms */
+ usb_delay_ms(opipe->pipe.device->bus, 50); /* XXX software finish */
+
+ /*
+ * Step 3: Remove any vestiges of the xfer from the hardware.
+ * The complication here is that the hardware may have executed
+ * beyond the xfer we're trying to abort. So as we're scanning
+ * the TDs of this xfer we check if the hardware points to
+ * any of them.
+ */
+ s = splusb(); /* XXX why? */
p = xfer->hcpriv;
#ifdef DIAGNOSTIC
if (p == NULL) {
splx(s);
- printf("ohci_abort_xfer: hcpriv==0\n");
+ printf("ohci_abort_xfer: hcpriv is NULL\n");
return;
}
#endif
+#ifdef OHCI_DEBUG
+ if (ohcidebug > 1) {
+ DPRINTF(("ohci_abort_xfer: sed=\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(p);
+ }
+#endif
+ headp = le32toh(sed->ed.ed_headp) & OHCI_HEADMASK;
+ hit = 0;
for (; p->xfer == xfer; p = n) {
+ hit |= headp == p->physaddr;
n = p->nexttd;
ohci_free_std(sc, p);
}
+ /* Zap headp register if hardware pointed inside the xfer. */
+ if (hit) {
+ DPRINTFN(1,("ohci_abort_xfer: set hd=0x08%x, tl=0x%08x\n",
+ (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp)));
+ sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */
+ } else {
+ DPRINTFN(1,("ohci_abort_xfer: no hit\n"));
+ }
- sed = opipe->sed;
- DPRINTFN(2,("ohci_abort_xfer: set hd=%x, tl=%x\n",
- (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp)));
- sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */
+ /*
+ * Step 4: Turn on hardware again.
+ */
sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
+ /*
+ * Step 5: Execute callback.
+ */
usb_transfer_complete(xfer);
splx(s);