diff options
| author | augustss <augustss@NetBSD.org> | 2001-11-09 15:01:57 +0000 |
|---|---|---|
| committer | augustss <augustss@NetBSD.org> | 2001-11-09 15:01:57 +0000 |
| commit | 495b9bd7d5f577f3d6cde4a8a09b28ec1beee695 (patch) | |
| tree | a757e8f3f2363c445a0d6aabdf6a11f39150bd69 /sys/dev/usb | |
| parent | 3dd8433864cde55e8106489cd2f1992f12a0a22e (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.c | 130 |
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); |
