MFNetBSD:

revision 1.106
    date: 2001/11/09 15:01:57;  author: augustss;  state: Exp;  lines: +73 -57
    Fix a bug in xfer abort processing when the HC executes ahead of what
    the driver aborts.
    Don't block RHSC interrupts.
This commit is contained in:
Josef Karthauser 2002-04-07 10:21:16 +00:00
parent f1310c18b4
commit 138483b713

View File

@ -195,7 +195,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);
@ -1081,13 +1080,13 @@ ohci_intr1(ohci_softc_t *sc)
if (eintrs & OHCI_SO) {
printf("%s: scheduling overrun\n",USBDEVNAME(sc->sc_bus.bdev));
/* 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));
@ -1110,13 +1109,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--;
if (eintrs != 0) {
/* Block unprocessed interrupts. XXX */
OWRITE4(sc, OHCI_INTERRUPT_DISABLE, intrs);
sc->sc_eintrs &= ~intrs;
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 +1875,7 @@ ohci_dump_ed(ohci_soft_ed_t *sed)
bitmask_snprintf((int)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,
@ -2014,18 +2018,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 = 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))
@ -2051,72 +2055,88 @@ 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);
sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
#ifdef OHCI_DEBUG
DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
ohci_dump_ed(sed);
#endif
#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);
/*
* 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 */
} else {
#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
KASSERT(curthread->td_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 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);
}
sed = opipe->sed;
DPRINTFN(2,("ohci_abort_xfer: set hd=%x, tl=%x\n",
/* 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"));
}
/*
* 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);