diff options
author | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 1999-08-15 18:58:59 +0000 |
---|---|---|
committer | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 1999-08-15 18:58:59 +0000 |
commit | 7e39b3e00cd3ccd0b07addb9ef65831475e736c1 (patch) | |
tree | 5c3cee704d4d1180d7b1d7cb48f68519db061291 /sys/dev/usb/ohci.c | |
parent | 34bf829dcf646f92caf4b5016e3ccb92dd3b611b (diff) |
Sync with NetBSD.
Implement timeouts and abort requests.
Compute length of short transfers correctly.
Diffstat (limited to 'sys/dev/usb/ohci.c')
-rw-r--r-- | sys/dev/usb/ohci.c | 317 |
1 files changed, 220 insertions, 97 deletions
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index b92435c9176..eead92f1ebc 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ohci.c,v 1.2 1999/08/13 08:55:57 fgsch Exp $ */ +/* $OpenBSD: ohci.c,v 1.3 1999/08/15 18:58:58 fgsch Exp $ */ /* $NetBSD: ohci.c,v 1.33 1999/06/30 06:44:23 augustss Exp $ */ /* @@ -112,7 +112,8 @@ void ohci_poll __P((struct usbd_bus *)); void ohci_waitintr __P((ohci_softc_t *, usbd_request_handle)); void ohci_rhsc __P((ohci_softc_t *, usbd_request_handle)); void ohci_process_done __P((ohci_softc_t *, ohci_physaddr_t)); -void ohci_ii_done __P((ohci_softc_t *, usbd_request_handle)); +void ohci_idone __P((ohci_softc_t *, usbd_request_handle)); +void ohci_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_ctrl_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_intr_done __P((ohci_softc_t *, usbd_request_handle)); void ohci_bulk_done __P((ohci_softc_t *, usbd_request_handle)); @@ -156,6 +157,10 @@ int ohci_str __P((usb_string_descriptor_t *, int, char *)); void ohci_timeout __P((void *)); void ohci_rhsc_able __P((ohci_softc_t *, int)); +void ohci_close_pipe __P((usbd_pipe_handle pipe, + ohci_soft_ed_t *head)); +void ohci_abort_request __P((usbd_request_handle reqh)); + #ifdef USB_DEBUG ohci_softc_t *thesc; void ohci_dumpregs __P((ohci_softc_t *)); @@ -169,7 +174,7 @@ void ohci_dump_ed __P((ohci_soft_ed_t *)); #define OREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r)) #define OREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r)) #elif defined(__FreeBSD__) -#define OWRITE4(sc, r, x) *(u_int32_t *) ((sc)->sc_iobase + (r)) = x +#define OWRITE4(sc, r, x) (*(u_int32_t *) ((sc)->sc_iobase + (r)) = (x)) #define OREAD4(sc, r) (*(u_int32_t *) ((sc)->sc_iobase + (r))) #define OREAD2(sc, r) (*(u_int16_t *) ((sc)->sc_iobase + (r))) #endif @@ -394,6 +399,7 @@ ohci_init(sc) goto bad1; } sc->sc_ctrl_head->ed->ed_flags |= LE(OHCI_ED_SKIP); + sc->sc_bulk_head = ohci_alloc_sed(sc); if (!sc->sc_bulk_head) { r = USBD_NOMEM; @@ -611,7 +617,7 @@ ohci_intr(p) ohci_physaddr_t done; /* In case the interrupt occurs before initialization has completed. */ - if (sc == NULL || sc->sc_hcca == NULL) { /* NWH added sc==0 */ + if (sc == NULL || sc->sc_hcca == NULL) { #ifdef DIAGNOSTIC printf("ohci_intr: sc->sc_hcca == NULL\n"); #endif @@ -741,31 +747,21 @@ ohci_process_done(sc, done) DPRINTFN(10, ("ohci_process_done: std=%p reqh=%p hcpriv=%p\n", std, reqh, reqh->hcpriv)); cc = OHCI_TD_GET_CC(LE(std->td->td_flags)); - if (cc == OHCI_CC_NO_ERROR) { - if (std->td->td_cbp == 0) - len = std->len; - else - len = LE(std->td->td_be) - - LE(std->td->td_cbp) + 1; - /* - * Only do a callback on the last stage of a transfer. - * Others have hcpriv = 0. - */ - if ((reqh->pipe->endpoint->edesc->bmAttributes & - UE_XFERTYPE) == UE_CONTROL) { - /* For a control transfer the length is in - * the xfer stage */ - if (reqh->hcpriv == std) { - reqh->status = USBD_NORMAL_COMPLETION; - ohci_ii_done(sc, reqh); - } else - reqh->actlen = len; - } else { - if (reqh->hcpriv == std) { - reqh->actlen = len; - reqh->status = USBD_NORMAL_COMPLETION; - ohci_ii_done(sc, reqh); - } + if (reqh->status == USBD_CANCELLED || + reqh->status == USBD_TIMEOUT) { + DPRINTF(("ohci_process_done: cancel/timeout %p\n", + reqh)); + ohci_idone(sc, reqh); + } else if (cc == OHCI_CC_NO_ERROR) { + len = std->len; + if (std->td->td_cbp != 0) + len -= LE(std->td->td_be) - + LE(std->td->td_cbp) + 1; + if (std->flags & OHCI_SET_LEN) + reqh->actlen = len; + if (std->flags & OHCI_CALL_DONE) { + reqh->status = USBD_NORMAL_COMPLETION; + ohci_idone(sc, reqh); } } else { ohci_soft_td_t *p, *n; @@ -792,7 +788,7 @@ ohci_process_done(sc, done) reqh->status = USBD_STALLED; else reqh->status = USBD_IOERROR; - ohci_ii_done(sc, reqh); + ohci_idone(sc, reqh); } ohci_hash_rem_td(sc, std); ohci_free_std(sc, std); @@ -800,25 +796,38 @@ ohci_process_done(sc, done) } void -ohci_ii_done(sc, reqh) +ohci_idone(sc, reqh) + ohci_softc_t *sc; + usbd_request_handle reqh; +{ + ohci_done(sc, reqh); + if (reqh->pipe->intrreqh != reqh) + usb_start_next(reqh->pipe); +} + +void +ohci_done(sc, reqh) ohci_softc_t *sc; usbd_request_handle reqh; { +#ifdef DIAGNOSTIC + if (!reqh->hcpriv) + printf("ohci_done: reqh=%p, no hcpriv\n", reqh); +#endif + reqh->hcpriv = 0; + switch (reqh->pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ohci_ctrl_done(sc, reqh); - usb_start_next(reqh->pipe); break; case UE_INTERRUPT: ohci_intr_done(sc, reqh); break; case UE_BULK: ohci_bulk_done(sc, reqh); - usb_start_next(reqh->pipe); break; case UE_ISOCHRONOUS: printf("ohci_process_done: ISO done?\n"); - usb_start_next(reqh->pipe); break; } @@ -868,7 +877,7 @@ ohci_intr_done(sc, reqh) dma = &opipe->u.intr.datadma; memcpy(reqh->buffer, KERNADDR(dma), reqh->actlen); - if (reqh->pipe->repeat) { + if (reqh->pipe->intrreqh == reqh) { xfer = opipe->tail; tail = ohci_alloc_std(sc); /* XXX should reuse TD */ if (!tail) { @@ -888,7 +897,7 @@ ohci_intr_done(sc, reqh) xfer->td->td_be = LE(LE(xfer->td->td_cbp) + reqh->length - 1); xfer->len = reqh->length; xfer->reqh = reqh; - + xfer->flags = OHCI_CALL_DONE | OHCI_SET_LEN; reqh->hcpriv = xfer; ohci_hash_add_td(sc, xfer); @@ -896,7 +905,6 @@ ohci_intr_done(sc, reqh) opipe->tail = tail; } else { usb_freemem(sc->sc_dmatag, dma); - usb_start_next(reqh->pipe); } } @@ -913,12 +921,10 @@ ohci_bulk_done(sc, reqh) DPRINTFN(10,("ohci_bulk_done: reqh=%p, actlen=%d\n", reqh, reqh->actlen)); - if (len != 0) { - dma = &opipe->u.bulk.datadma; - if (opipe->u.bulk.isread) - memcpy(reqh->buffer, KERNADDR(dma), len); - usb_freemem(sc->sc_dmatag, dma); - } + dma = &opipe->u.bulk.datadma; + if (opipe->u.bulk.isread) + memcpy(reqh->buffer, KERNADDR(dma), len); + usb_freemem(sc->sc_dmatag, dma); usb_untimeout(ohci_timeout, reqh, reqh->timo_handle); } @@ -957,7 +963,7 @@ ohci_rhsc(sc, reqh) reqh->status = USBD_NORMAL_COMPLETION; reqh->xfercb(reqh); - if (!reqh->pipe->repeat) { + if (reqh->pipe->intrreqh != reqh) { sc->sc_intrreqh = 0; usb_freemem(sc->sc_dmatag, &opipe->u.intr.datadma); usb_start_next(reqh->pipe); @@ -997,7 +1003,7 @@ ohci_waitintr(sc, reqh) /* Timeout */ DPRINTF(("ohci_waitintr: timeout\n")); reqh->status = USBD_TIMEOUT; - ohci_ii_done(sc, reqh); + ohci_idone(sc, reqh); /* XXX should free TD */ } @@ -1081,10 +1087,14 @@ ohci_device_request(reqh) xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; + xfer->flags = OHCI_SET_LEN; next = xfer; - } else + stat->flags = OHCI_CALL_DONE; + } else { next = stat; + stat->flags = OHCI_CALL_DONE | OHCI_SET_LEN; + } memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof *req); if (!isread && len != 0) @@ -1098,6 +1108,8 @@ ohci_device_request(reqh) setup->td->td_be = LE(LE(setup->td->td_cbp) + sizeof *req - 1); setup->len = 0; /* XXX The number of byte we count */ setup->reqh = reqh; + setup->flags = 0; + reqh->hcpriv = setup; stat->td->td_flags = LE( (isread ? OHCI_TD_OUT : OHCI_TD_IN) | OHCI_TD_NOCC | @@ -1109,8 +1121,6 @@ ohci_device_request(reqh) stat->len = 0; stat->reqh = reqh; - reqh->hcpriv = stat; - #if USB_DEBUG if (ohcidebug > 5) { printf("ohci_device_request:\n"); @@ -1268,7 +1278,7 @@ ohci_dump_td(std) printf("TD(%p) at %08lx: %b delay=%d ec=%d cc=%d\ncbp=0x%08lx " "nexttd=0x%08lx be=0x%08lx\n", std, (u_long)std->physaddr, - (u_long)LE(std->td->td_flags), + (int)LE(std->td->td_flags), "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE", OHCI_TD_GET_DI(LE(std->td->td_flags)), OHCI_TD_GET_EC(LE(std->td->td_flags)), @@ -1287,7 +1297,7 @@ ohci_dump_ed(sed) OHCI_ED_GET_FA(LE(sed->ed->ed_flags)), OHCI_ED_GET_EN(LE(sed->ed->ed_flags)), OHCI_ED_GET_MAXP(LE(sed->ed->ed_flags)), - (u_long)LE(sed->ed->ed_flags), + (int)LE(sed->ed->ed_flags), "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO", (u_long)LE(sed->ed->ed_tailp), (u_long)LE(sed->ed->ed_headp), "\20\1HALT\2CARRY", @@ -1379,6 +1389,113 @@ ohci_open(pipe) } /* + * Close a reqular pipe. + * Assumes that there are no pending transactions. + */ +void +ohci_close_pipe(pipe, head) + usbd_pipe_handle pipe; + ohci_soft_ed_t *head; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; + ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; + ohci_soft_ed_t *sed = opipe->sed; + int s; + + s = splusb(); +#ifdef DIAGNOSTIC + sed->ed->ed_flags |= LE(OHCI_ED_SKIP); + if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) != + (sed->ed->ed_headp & LE(OHCI_TAILMASK))) { + ohci_physaddr_t td = sed->ed->ed_headp; + ohci_soft_td_t *std; + for (std = LIST_FIRST(&sc->sc_hash_tds[HASH(td)]); + std != 0; + std = LIST_NEXT(std, hnext)) + if (std->physaddr == td) + break; + printf("ohci_close_pipe: pipe not empty sed=%p hd=0x%x " + "tl=0x%x pipe=%p, std=%p\n", sed, + (int)LE(sed->ed->ed_headp), (int)LE(sed->ed->ed_tailp), + pipe, std); + usb_delay_ms(&sc->sc_bus, 2); + if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) != + (sed->ed->ed_headp & LE(OHCI_TAILMASK))) + printf("ohci_close_pipe: pipe still not empty\n"); + } +#endif + ohci_rem_ed(sed, head); + splx(s); + ohci_free_std(sc, opipe->tail); + ohci_free_sed(sc, opipe->sed); +} + +/* + * Abort a device request. + * If this routine is called at splusb() it guarantees that the request + * will be removed from the hardware scheduling and that the callback + * for it will be called with USBD_CANCELLED status. + * It's impossible to guarantee that the requested transfer will not + * have happened since the hardware runs concurrently. + * If the transaction has already happened we rely on the ordinary + * interrupt processing to process it. + */ +void +ohci_abort_request(reqh) + usbd_request_handle reqh; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)reqh->pipe; + usbd_device_handle dev = opipe->pipe.device; + ohci_softc_t *sc = (ohci_softc_t *)dev->bus; + ohci_soft_ed_t *sed; + ohci_soft_td_t *p, *n; + int s; + + DPRINTF(("ohci_abort_request: reqh=%p pipe=%p\n", reqh, opipe)); + s = splusb(); + + reqh->status = USBD_CANCELLED; /* mark as cancelled */ + + if (!reqh->hcpriv) { + /* Not scheduled */ + reqh->xfercb(reqh); + return; + } + + sed = opipe->sed; + DPRINTFN(1,("ohci_abort_request: stop ed=%p\n", sed)); + sed->ed->ed_flags |= LE(OHCI_ED_SKIP); /* force hardware skip */ + delay(10); /* give HC hardware a little time */ + + /* if already processed by hardware let interrupt routine handle it */ + if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) == + (sed->ed->ed_headp & LE(OHCI_TAILMASK))) { + DPRINTF(("ohci_abort_request: request processed\n")); + usb_delay_ms(dev->bus, 2); + } else { + p = reqh->hcpriv; +#ifdef DIAGNOSTIC + if (!p) { + printf("ohci_abort_request: hcpriv==0\n"); + return; + } +#endif + ohci_done(sc, reqh); + for (; p->reqh == reqh; p = n) { + n = p->nexttd; + ohci_hash_rem_td(sc, p); + ohci_free_std(sc, p); + } + DPRINTFN(2,("ohci_abort_request: set hd=%x, tl=%x\n", + (int)LE(p->physaddr), (int)LE(sed->ed->ed_tailp))); + sed->ed->ed_headp = p->physaddr; /* unlink TDs */ + } + + sed->ed->ed_flags &= LE(~OHCI_ED_SKIP); /* remove hardware skip */ + splx(s); +} + +/* * Data structures and routines to emulate the root hub. */ usb_device_descriptor_t ohci_devd = { @@ -1784,6 +1901,7 @@ ohci_root_ctrl_close(pipe) usbd_pipe_handle pipe; { DPRINTF(("ohci_root_ctrl_close\n")); + /* Nothing to do. */ } usbd_status @@ -1840,9 +1958,10 @@ ohci_root_intr_close(pipe) usbd_pipe_handle pipe; { ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; - sc->sc_intrreqh = 0; DPRINTF(("ohci_root_intr_close\n")); + + sc->sc_intrreqh = 0; } /************************/ @@ -1890,9 +2009,8 @@ void ohci_device_ctrl_abort(reqh) usbd_request_handle reqh; { - /* XXX inactivate */ - usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is donw */ - /* XXX call done */ + DPRINTF(("ohci_device_ctrl_abort: reqh=%p\n", reqh)); + ohci_abort_request(reqh); } /* Close a device control pipe. */ @@ -1900,20 +2018,10 @@ void ohci_device_ctrl_close(pipe) usbd_pipe_handle pipe; { - struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; - ohci_soft_ed_t *sed = opipe->sed; - int s; - s = splusb(); - sed->ed->ed_flags |= LE(OHCI_ED_SKIP); - if ((LE(sed->ed->ed_tailp) & OHCI_TAILMASK) != LE(sed->ed->ed_headp)) - usb_delay_ms(&sc->sc_bus, 2); - ohci_rem_ed(sed, sc->sc_ctrl_head); - splx(s); - ohci_free_std(sc, opipe->tail); - ohci_free_sed(sc, opipe->sed); - /* XXX free other resources */ + DPRINTF(("ohci_device_ctrl_close: pipe=%p\n", pipe)); + ohci_close_pipe(pipe, sc->sc_ctrl_head); } /************************/ @@ -1948,17 +2056,23 @@ ohci_device_bulk_start(reqh) usbd_status r; int s, len, isread; +#ifdef DIAGNOSTIC if (reqh->isreq) { /* XXX panic */ - printf("ohci_device_bulk_transfer: a request\n"); + printf("ohci_device_bulk_start: a request\n"); return (USBD_INVAL); } +#endif len = reqh->length; dmap = &opipe->u.bulk.datadma; isread = reqh->pipe->endpoint->edesc->bEndpointAddress & UE_IN; sed = opipe->sed; + DPRINTFN(4,("ohci_device_bulk_start: reqh=%p len=%d isread=%d " + "flags=%d endpt=%d\n", reqh, len, isread, reqh->flags, + reqh->pipe->endpoint->edesc->bEndpointAddress)); + opipe->u.bulk.isread = isread; opipe->u.bulk.length = len; @@ -1990,23 +2104,46 @@ ohci_device_bulk_start(reqh) xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; - + xfer->flags = OHCI_CALL_DONE | OHCI_SET_LEN; reqh->hcpriv = xfer; if (!isread) memcpy(KERNADDR(dmap), reqh->buffer, len); + DPRINTFN(4,("ohci_device_bulk_start: ed_flags=0x%08x td_flags=0x%08x " + "td_cbp=0x%08x td_be=0x%08x\n", + (int)LE(sed->ed->ed_flags), (int)LE(xfer->td->td_flags), + (int)LE(xfer->td->td_cbp), (int)LE(xfer->td->td_be))); + +#ifdef USB_DEBUG + if (ohcidebug > 4) { + ohci_dump_ed(sed); + ohci_dump_tds(xfer); + } +#endif + /* Insert ED in schedule */ s = splusb(); ohci_hash_add_td(sc, xfer); sed->ed->ed_tailp = LE(tail->physaddr); opipe->tail = tail; - sed->ed->ed_flags &= LE(~OHCI_ED_SKIP); /* XXX why */ + sed->ed->ed_flags &= LE(~OHCI_ED_SKIP); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); if (reqh->timeout && !sc->sc_bus.use_polling) { usb_timeout(ohci_timeout, reqh, MS_TO_TICKS(reqh->timeout), reqh->timo_handle); } + +#ifdef USB_DEBUG + if (ohcidebug > 5) { + delay(5000); + printf("ohci_device_intr_transfer: status=%x\n", + OREAD4(sc, OHCI_COMMAND_STATUS)); + ohci_dump_ed(sed); + ohci_dump_tds(xfer); + } +#endif + splx(s); return (USBD_IN_PROGRESS); @@ -2017,37 +2154,25 @@ ohci_device_bulk_start(reqh) return (r); } -/* Abort a device bulk request. */ void ohci_device_bulk_abort(reqh) usbd_request_handle reqh; { -#if 0 - sed->ed->ed_flags |= LE(OHCI_ED_SKIP); - if ((LE(sed->ed->ed_tailp) & OHCI_TAILMASK) != LE(sed->ed->ed_headp)) - usb_delay_ms(reqh->pipe->device->bus, 2); -#endif - /* XXX inactivate */ - usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is done */ - /* XXX call done */ + DPRINTF(("ohci_device_bulk_abort: reqh=%p\n", reqh)); + ohci_abort_request(reqh); } -/* Close a device bulk pipe. */ +/* + * Close a device bulk pipe. + */ void ohci_device_bulk_close(pipe) usbd_pipe_handle pipe; { - struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; - usbd_device_handle dev = opipe->pipe.device; - ohci_softc_t *sc = (ohci_softc_t *)dev->bus; - int s; + ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; - s = splusb(); - ohci_rem_ed(opipe->sed, sc->sc_bulk_head); - splx(s); - ohci_free_std(sc, opipe->tail); - ohci_free_sed(sc, opipe->sed); - /* XXX free other resources */ + DPRINTF(("ohci_device_bulk_close: pipe=%p\n", pipe)); + ohci_close_pipe(pipe, sc->sc_bulk_head); } /************************/ @@ -2117,7 +2242,7 @@ ohci_device_intr_start(reqh) xfer->td->td_be = LE(LE(xfer->td->td_cbp) + len - 1); xfer->len = len; xfer->reqh = reqh; - + xfer->flags = OHCI_CALL_DONE | OHCI_SET_LEN; reqh->hcpriv = xfer; #if USB_DEBUG @@ -2165,13 +2290,11 @@ void ohci_device_intr_abort(reqh) usbd_request_handle reqh; { - /* XXX inactivate */ - usb_delay_ms(reqh->pipe->device->bus, 1); /* make sure it is done */ - if (reqh->pipe->repeat) { + if (reqh->pipe->intrreqh == reqh) { DPRINTF(("ohci_device_intr_abort: remove\n")); - reqh->pipe->repeat = 0; - ohci_intr_done((ohci_softc_t *)reqh->pipe->device->bus, reqh); + reqh->pipe->intrreqh = 0; } + ohci_abort_request(reqh); } /* Close a device interrupt pipe. */ @@ -2191,7 +2314,8 @@ ohci_device_intr_close(pipe) pipe, nslots, pos)); s = splusb(); sed->ed->ed_flags |= LE(OHCI_ED_SKIP); - if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) != sed->ed->ed_headp) + if ((sed->ed->ed_tailp & LE(OHCI_TAILMASK)) != + (sed->ed->ed_headp & LE(OHCI_TAILMASK))) usb_delay_ms(&sc->sc_bus, 2); for (p = sc->sc_eds[pos]; p && p->next != sed; p = p->next) @@ -2207,7 +2331,6 @@ ohci_device_intr_close(pipe) ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); - /* XXX free other resources */ } usbd_status |