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:
parent
f1310c18b4
commit
138483b713
@ -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--;
|
||||
|
||||
/* 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 +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);
|
||||
|
||||
} 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 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);
|
||||
|
Loading…
Reference in New Issue
Block a user