diff options
Diffstat (limited to 'sys/dev/usb/ohci.c')
-rw-r--r-- | sys/dev/usb/ohci.c | 443 |
1 files changed, 381 insertions, 62 deletions
diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 3bbc809..c5a2d81 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,4 +1,4 @@ -/* $NetBSD: ohci.c,v 1.55 1999/11/18 23:32:26 augustss Exp $ */ +/* $NetBSD: ohci.c,v 1.64 2000/01/19 00:23:58 augustss Exp $ */ /* $FreeBSD$ */ /* @@ -115,15 +115,21 @@ static void ohci_free_sed __P((ohci_softc_t *, ohci_soft_ed_t *)); static ohci_soft_td_t *ohci_alloc_std __P((ohci_softc_t *)); static void ohci_free_std __P((ohci_softc_t *, ohci_soft_td_t *)); +static ohci_soft_itd_t *ohci_alloc_sitd __P((ohci_softc_t *)); +static void ohci_free_sitd __P((ohci_softc_t *,ohci_soft_itd_t *)); + #if 0 static void ohci_free_std_chain __P((ohci_softc_t *, ohci_soft_td_t *, ohci_soft_td_t *)); #endif static usbd_status ohci_alloc_std_chain __P((struct ohci_pipe *, - ohci_softc_t *, int, int, int, usb_dma_t *, + ohci_softc_t *, int, int, u_int16_t, usb_dma_t *, ohci_soft_td_t *, ohci_soft_td_t **)); +#if defined(__NetBSD__) || defined(__OpenBSD__) +static void ohci_shutdown __P((void *v)); static void ohci_power __P((int, void *)); +#endif static usbd_status ohci_open __P((usbd_pipe_handle)); static void ohci_poll __P((struct usbd_bus *)); static void ohci_waitintr __P((ohci_softc_t *, @@ -142,10 +148,16 @@ static void ohci_hash_rem_td __P((ohci_softc_t *, static ohci_soft_td_t *ohci_hash_find_td __P((ohci_softc_t *, ohci_physaddr_t)); +static usbd_status ohci_setup_isoc __P((usbd_pipe_handle pipe)); +static void ohci_device_isoc_enter __P((usbd_xfer_handle)); + static usbd_status ohci_allocm __P((struct usbd_bus *, usb_dma_t *, u_int32_t)); static void ohci_freem __P((struct usbd_bus *, usb_dma_t *)); +static usbd_xfer_handle ohci_allocx __P((struct usbd_bus *)); +static void ohci_freex __P((struct usbd_bus *, usbd_xfer_handle)); + static usbd_status ohci_root_ctrl_transfer __P((usbd_xfer_handle)); static usbd_status ohci_root_ctrl_start __P((usbd_xfer_handle)); static void ohci_root_ctrl_abort __P((usbd_xfer_handle)); @@ -175,13 +187,11 @@ static void ohci_device_intr_abort __P((usbd_xfer_handle)); static void ohci_device_intr_close __P((usbd_pipe_handle)); static void ohci_device_intr_done __P((usbd_xfer_handle)); -#if 0 static usbd_status ohci_device_isoc_transfer __P((usbd_xfer_handle)); static usbd_status ohci_device_isoc_start __P((usbd_xfer_handle)); static void ohci_device_isoc_abort __P((usbd_xfer_handle)); static void ohci_device_isoc_close __P((usbd_pipe_handle)); static void ohci_device_isoc_done __P((usbd_xfer_handle)); -#endif static usbd_status ohci_device_setintr __P((ohci_softc_t *sc, struct ohci_pipe *pipe, int ival)); @@ -221,7 +231,10 @@ static u_int8_t revbits[OHCI_NO_INTRS] = struct ohci_pipe { struct usbd_pipe pipe; ohci_soft_ed_t *sed; - ohci_soft_td_t *tail; + union { + ohci_soft_td_t *td; + ohci_soft_itd_t *itd; + } tail; /* Info needed for different pipe kinds. */ union { /* Control pipe */ @@ -242,7 +255,7 @@ struct ohci_pipe { } bulk; /* Iso pipe */ struct iso { - int xxxxx; + int next, inuse; } iso; } u; }; @@ -254,6 +267,8 @@ static struct usbd_bus_methods ohci_bus_methods = { ohci_poll, ohci_allocm, ohci_freem, + ohci_allocx, + ohci_freex, }; static struct usbd_pipe_methods ohci_root_ctrl_methods = { @@ -301,7 +316,6 @@ static struct usbd_pipe_methods ohci_device_bulk_methods = { ohci_device_bulk_done, }; -#if 0 static struct usbd_pipe_methods ohci_device_isoc_methods = { ohci_device_isoc_transfer, ohci_device_isoc_start, @@ -310,7 +324,6 @@ static struct usbd_pipe_methods ohci_device_isoc_methods = { ohci_noop, ohci_device_isoc_done, }; -#endif #if defined(__NetBSD__) || defined(__OpenBSD__) int @@ -348,6 +361,9 @@ ohci_detach(sc, flags) return (rv); powerhook_disestablish(sc->sc_powerhook); +#if defined(__NetBSD__) || defined(__OpenBSD__) + shutdownhook_disestablish(sc->sc_shutdownhook); +#endif /* free data structures XXX */ return (rv); @@ -433,28 +449,32 @@ ohci_free_std(sc, std) } usbd_status -ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep) - struct ohci_pipe *upipe; +ohci_alloc_std_chain(opipe, sc, len, rd, flags, dma, sp, ep) + struct ohci_pipe *opipe; ohci_softc_t *sc; - int len, rd, shortok; + int len, rd; + u_int16_t flags; usb_dma_t *dma; ohci_soft_td_t *sp, **ep; { ohci_soft_td_t *next, *cur; ohci_physaddr_t dataphys, dataphysend; - u_int32_t intr; + u_int32_t intr, tdflags; int curlen; DPRINTFN(len < 4096,("ohci_alloc_std_chain: start len=%d\n", len)); cur = sp; dataphys = DMAADDR(dma); dataphysend = OHCI_PAGE(dataphys + len - 1); + tdflags = + (rd ? OHCI_TD_IN : OHCI_TD_OUT) | + OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | + (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0); + for (;;) { next = ohci_alloc_std(sc); - if (next == 0) { - /* XXX free chain */ - return (USBD_NOMEM); - } + if (next == 0) + goto nomem; /* The OHCI hardware can handle at most one page crossing. */ if (OHCI_PAGE(dataphys) == dataphysend || @@ -473,10 +493,7 @@ ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep) len -= curlen; intr = len == 0 ? OHCI_TD_SET_DI(1) : OHCI_TD_NOINTR; - cur->td.td_flags = LE( - (rd ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | - intr | OHCI_TD_TOGGLE_CARRY | - (shortok ? OHCI_TD_R : 0)); + cur->td.td_flags = LE(tdflags | intr); cur->td.td_cbp = LE(dataphys); cur->nexttd = next; cur->td.td_nexttd = LE(next->physaddr); @@ -491,10 +508,31 @@ ohci_alloc_std_chain(upipe, sc, len, rd, shortok, dma, sp, ep) dataphys += curlen; cur = next; } + if ((flags & USBD_FORCE_SHORT_XFER) && + len % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) { + /* Force a 0 length transfer at the end. */ + next = ohci_alloc_std(sc); + if (next == 0) + goto nomem; + + cur->td.td_flags = LE(tdflags | OHCI_TD_SET_DI(1)); + cur->td.td_cbp = 0; /* indicate 0 length packet */ + cur->nexttd = next; + cur->td.td_nexttd = LE(next->physaddr); + cur->td.td_be = LE(dataphys - 1); + cur->len = 0; + cur->flags = 0; + cur = next; + DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n")); + } cur->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; *ep = next; return (USBD_NORMAL_COMPLETION); + + nomem: + /* XXX free chain */ + return (USBD_NOMEM); } #if 0 @@ -513,15 +551,53 @@ ohci_free_std_chain(sc, std, stdend) } #endif +ohci_soft_itd_t * +ohci_alloc_sitd(sc) + ohci_softc_t *sc; +{ + ohci_soft_itd_t *sitd; + usbd_status err; + int i, offs; + usb_dma_t dma; + + if (sc->sc_freeitds == NULL) { + DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK, + OHCI_TD_ALIGN, &dma); + if (err) + return (0); + for(i = 0; i < OHCI_STD_CHUNK; i++) { + offs = i * OHCI_STD_SIZE; + sitd = (ohci_soft_itd_t *)((char*)KERNADDR(&dma)+offs); + sitd->physaddr = DMAADDR(&dma) + offs; + sitd->nextitd = sc->sc_freeitds; + sc->sc_freeitds = sitd; + } + } + sitd = sc->sc_freeitds; + sc->sc_freeitds = sitd->nextitd; + memset(&sitd->itd, 0, sizeof(ohci_itd_t)); + sitd->nextitd = 0; + return (sitd); +} + +void +ohci_free_sitd(sc, sitd) + ohci_softc_t *sc; + ohci_soft_itd_t *sitd; +{ + sitd->nextitd = sc->sc_freeitds; + sc->sc_freeitds = sitd; +} + usbd_status ohci_init(sc) ohci_softc_t *sc; { ohci_soft_ed_t *sed, *psed; usbd_status err; - int rev; int i; - u_int32_t s, ctl, ival, hcr, fm, per; + u_int32_t s, ctl, ival, hcr, fm, per, rev; DPRINTF(("ohci_init: start\n")); #if defined(__OpenBSD__) @@ -544,6 +620,8 @@ ohci_init(sc) for (i = 0; i < OHCI_HASH_SIZE; i++) LIST_INIT(&sc->sc_hash_tds[i]); + SIMPLEQ_INIT(&sc->sc_free_xfers); + /* Allocate the HCCA area. */ err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE, OHCI_HCCA_ALIGN, &sc->sc_hccadma); @@ -554,6 +632,7 @@ ohci_init(sc) sc->sc_eintrs = OHCI_NORMAL_INTRS; + /* Allocate dummy ED that starts the control list. */ sc->sc_ctrl_head = ohci_alloc_sed(sc); if (sc->sc_ctrl_head == NULL) { err = USBD_NOMEM; @@ -561,6 +640,7 @@ ohci_init(sc) } sc->sc_ctrl_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + /* Allocate dummy ED that starts the bulk list. */ sc->sc_bulk_head = ohci_alloc_sed(sc); if (sc->sc_bulk_head == NULL) { err = USBD_NOMEM; @@ -568,6 +648,14 @@ ohci_init(sc) } sc->sc_bulk_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + /* Allocate dummy ED that starts the isochronous list. */ + sc->sc_isoc_head = ohci_alloc_sed(sc); + if (sc->sc_isoc_head == NULL) { + err = USBD_NOMEM; + goto bad3; + } + sc->sc_isoc_head->ed.ed_flags |= LE(OHCI_ED_SKIP); + /* Allocate all the dummy EDs that make up the interrupt tree. */ for (i = 0; i < OHCI_NO_EDS; i++) { sed = ohci_alloc_sed(sc); @@ -575,16 +663,17 @@ ohci_init(sc) while (--i >= 0) ohci_free_sed(sc, sc->sc_eds[i]); err = USBD_NOMEM; - goto bad3; + goto bad4; } /* All ED fields are set to 0. */ sc->sc_eds[i] = sed; sed->ed.ed_flags |= LE(OHCI_ED_SKIP); - if (i != 0) { + if (i != 0) psed = sc->sc_eds[(i-1) / 2]; - sed->next = psed; - sed->ed.ed_nexted = LE(psed->physaddr); - } + else + psed= sc->sc_isoc_head; + sed->next = psed; + sed->ed.ed_nexted = LE(psed->physaddr); } /* * Fill HCCA interrupt table. The bit reversal is to get @@ -647,14 +736,14 @@ ohci_init(sc) if (hcr) { printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev)); err = USBD_IOERROR; - goto bad3; + goto bad5; } #ifdef OHCI_DEBUG if (ohcidebug > 15) ohci_dumpregs(sc); #endif - /* The controller is now in suspend state, we have 2ms to finish. */ + /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* Set up HC registers. */ OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma)); @@ -696,9 +785,16 @@ ohci_init(sc) sc->sc_bus.pipe_size = sizeof(struct ohci_pipe); sc->sc_powerhook = powerhook_establish(ohci_power, sc); - +#if defined(__NetBSD__) || defined(__OpenBSD__) + sc->sc_shutdownhook = shutdownhook_establish(ohci_shutdown, sc); +#endif return (USBD_NORMAL_COMPLETION); + bad5: + for (i = 0; i < OHCI_NO_EDS; i++) + ohci_free_sed(sc, sc->sc_eds[i]); + bad4: + ohci_free_sed(sc, sc->sc_isoc_head); bad3: ohci_free_sed(sc, sc->sc_ctrl_head); bad2: @@ -733,7 +829,54 @@ ohci_freem(bus, dma) usb_freemem(&sc->sc_bus, dma); } -#if defined(__NetBSD__) +usbd_xfer_handle +ohci_allocx(bus) + struct usbd_bus *bus; +{ + struct ohci_softc *sc = (struct ohci_softc *)bus; + usbd_xfer_handle xfer; + + xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); + if (xfer != NULL) + SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); + else + xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT); + if (xfer != NULL) + memset(xfer, 0, sizeof *xfer); + return (xfer); +} + +void +ohci_freex(bus, xfer) + struct usbd_bus *bus; + usbd_xfer_handle xfer; +{ + struct ohci_softc *sc = (struct ohci_softc *)bus; + + SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); +} + +/* + * Shut down the controller when the system is going down. + */ +#if defined(__NetBSD__) || defined(__OpenBSD__) +void +ohci_shutdown(v) + void *v; +{ + ohci_softc_t *sc = v; + + DPRINTF(("ohci_shutdown: stopping the HC\n")); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); +} + +/* + * Handle suspend/resume. + * + * We need to switch to polling mode here, because this routine is + * called from an intterupt context. This is all right since we + * are almost suspended anyway. + */ void ohci_power(why, v) int why; @@ -747,7 +890,7 @@ ohci_power(why, v) ohci_dumpregs(sc); #endif } -#endif /* defined(__NetBSD__) */ +#endif #ifdef OHCI_DEBUG void @@ -801,8 +944,9 @@ ohci_intr(p) /* If we get an interrupt while polling, then just ignore it. */ if (sc->sc_bus.use_polling) { - DPRINTF(("%s: Ignored interrupt while polling", - USBDEVNAME(sc->sc_bus.bdev))); +#ifdef DIAGNOSTIC + printf("ohci_intr: ignored interrupt while polling\n"); +#endif return (0); } @@ -948,12 +1092,12 @@ ohci_process_done(sc, done) #endif for (std = sdone; std; std = stdnext) { - stdnext = std->dnext; xfer = std->xfer; - usb_untimeout(ohci_timeout, xfer, xfer->timo_handle); + stdnext = std->dnext; DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n", std, xfer, xfer->hcpriv)); cc = OHCI_TD_GET_CC(LE(std->td.td_flags)); + usb_untimeout(ohci_timeout, xfer, xfer->timo_handle); if (xfer->status == USBD_CANCELLED || xfer->status == USBD_TIMEOUT) { DPRINTF(("ohci_process_done: cancel/timeout %p\n", @@ -1036,7 +1180,7 @@ ohci_device_intr_done(xfer) xfer->hcpriv = NULL; if (xfer->pipe->repeat) { - data = opipe->tail; + data = opipe->tail.td; tail = ohci_alloc_std(sc); /* XXX should reuse TD */ if (tail == NULL) { xfer->status = USBD_NOMEM; @@ -1061,7 +1205,7 @@ ohci_device_intr_done(xfer) ohci_hash_add_td(sc, data); sed->ed.ed_tailp = LE(tail->physaddr); - opipe->tail = tail; + opipe->tail.td = tail; } } @@ -1191,7 +1335,7 @@ ohci_device_request(xfer) UGETW(req->wIndex), len, addr, opipe->pipe.endpoint->edesc->bEndpointAddress)); - setup = opipe->tail; + setup = opipe->tail.td; stat = ohci_alloc_std(sc); if (stat == NULL) { err = USBD_NOMEM; @@ -1244,7 +1388,7 @@ ohci_device_request(xfer) memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof *req); setup->td.td_flags = LE(OHCI_TD_SETUP | OHCI_TD_NOCC | - OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); + OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); setup->td.td_cbp = LE(DMAADDR(&opipe->u.ctl.reqdma)); setup->nexttd = next; setup->td.td_nexttd = LE(next->physaddr); @@ -1279,7 +1423,7 @@ ohci_device_request(xfer) ohci_hash_add_td(sc, data); ohci_hash_add_td(sc, stat); sed->ed.ed_tailp = LE(tail->physaddr); - opipe->tail = tail; + opipe->tail.td = tail; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); if (xfer->timeout && !sc->sc_bus.use_polling) { usb_timeout(ohci_timeout, xfer, @@ -1289,6 +1433,7 @@ ohci_device_request(xfer) #ifdef OHCI_DEBUG if (ohcidebug > 5) { + usb_delay_ms(&sc->sc_bus, 5); DPRINTF(("ohci_device_request: status=%x\n", OREAD4(sc, OHCI_COMMAND_STATUS))); ohci_dump_ed(sed); @@ -1462,10 +1607,15 @@ ohci_open(pipe) usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; u_int8_t addr = dev->address; + u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE; ohci_soft_ed_t *sed; - ohci_soft_td_t *std; + ohci_soft_td_t *std = NULL; + ohci_soft_itd_t *sitd; + ohci_physaddr_t tdphys; + u_int32_t fmt; usbd_status err; int s; + int ival; DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, addr, ed->bEndpointAddress, sc->sc_addr)); @@ -1484,22 +1634,35 @@ ohci_open(pipe) sed = ohci_alloc_sed(sc); if (sed == NULL) goto bad0; - std = ohci_alloc_std(sc); - if (std == NULL) - goto bad1; opipe->sed = sed; - opipe->tail = std; + if (xfertype == UE_ISOCHRONOUS) { + sitd = ohci_alloc_sitd(sc); + if (sitd == NULL) { + ohci_free_sitd(sc, sitd); + goto bad1; + } + opipe->tail.itd = sitd; + tdphys = LE(sitd->physaddr); + fmt = OHCI_ED_FORMAT_ISO; + } else { + std = ohci_alloc_std(sc); + if (std == NULL) { + ohci_free_std(sc, std); + goto bad1; + } + opipe->tail.td = std; + tdphys = LE(std->physaddr); + fmt = OHCI_ED_FORMAT_GEN; + } sed->ed.ed_flags = LE( OHCI_ED_SET_FA(addr) | OHCI_ED_SET_EN(ed->bEndpointAddress) | OHCI_ED_DIR_TD | - (dev->lowspeed ? OHCI_ED_SPEED : 0) | - ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS ? - OHCI_ED_FORMAT_ISO : OHCI_ED_FORMAT_GEN) | + (dev->lowspeed ? OHCI_ED_SPEED : 0) | fmt | OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); - sed->ed.ed_headp = sed->ed.ed_tailp = LE(std->physaddr); + sed->ed.ed_headp = sed->ed.ed_tailp = tdphys; - switch (ed->bmAttributes & UE_XFERTYPE) { + switch (xfertype) { case UE_CONTROL: pipe->methods = &ohci_device_ctrl_methods; err = usb_allocmem(&sc->sc_bus, @@ -1513,10 +1676,13 @@ ohci_open(pipe) break; case UE_INTERRUPT: pipe->methods = &ohci_device_intr_methods; - return (ohci_device_setintr(sc, opipe, ed->bInterval)); + ival = pipe->interval; + if (ival == USBD_DEFAULT_INTERVAL) + ival = ed->bInterval; + return (ohci_device_setintr(sc, opipe, ival)); case UE_ISOCHRONOUS: - printf("ohci_open: open iso unimplemented\n"); - return (USBD_INVAL); + pipe->methods = &ohci_device_isoc_methods; + return (ohci_setup_isoc(pipe)); case UE_BULK: pipe->methods = &ohci_device_bulk_methods; s = splusb(); @@ -1574,7 +1740,6 @@ ohci_close_pipe(pipe, head) #endif ohci_rem_ed(sed, head); splx(s); - ohci_free_std(sc, opipe->tail); ohci_free_sed(sc, opipe->sed); } @@ -1606,6 +1771,7 @@ ohci_abort_xfer(xfer, status) DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed)); sed->ed.ed_flags |= LE(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(). */ timeout(ohci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); @@ -1617,6 +1783,10 @@ ohci_abort_xfer(xfer, status) usb_delay_ms(opipe->pipe.device->bus, 1); ohci_abort_xfer_end(xfer); } +#else + delay(1000); + ohci_abort_xfer_end(xfer); +#endif } void @@ -2181,10 +2351,12 @@ static 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; DPRINTF(("ohci_device_ctrl_close: pipe=%p\n", pipe)); ohci_close_pipe(pipe, sc->sc_ctrl_head); + ohci_free_std(sc, opipe->tail.td); } /************************/ @@ -2258,9 +2430,8 @@ ohci_device_bulk_start(xfer) OHCI_ED_SET_FA(addr)); /* Allocate a chain of new TDs (including a new tail). */ - data = opipe->tail; - err = ohci_alloc_std_chain(opipe, sc, len, isread, - xfer->flags & USBD_SHORT_XFER_OK, + data = opipe->tail.td; + err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer->flags, &xfer->dmabuf, data, &tail); if (err) return (err); @@ -2287,7 +2458,7 @@ ohci_device_bulk_start(xfer) ohci_hash_add_td(sc, tdp); } sed->ed.ed_tailp = LE(tail->physaddr); - opipe->tail = tail; + opipe->tail.td = tail; sed->ed.ed_flags &= LE(~OHCI_ED_SKIP); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); if (xfer->timeout && !sc->sc_bus.use_polling) { @@ -2326,10 +2497,12 @@ static void ohci_device_bulk_close(pipe) usbd_pipe_handle pipe; { + struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; DPRINTF(("ohci_device_bulk_close: pipe=%p\n", pipe)); ohci_close_pipe(pipe, sc->sc_bulk_head); + ohci_free_std(sc, opipe->tail.td); } /************************/ @@ -2372,7 +2545,7 @@ ohci_device_intr_start(xfer) len = xfer->length; - data = opipe->tail; + data = opipe->tail.td; tail = ohci_alloc_std(sc); if (tail == NULL) return (USBD_NOMEM); @@ -2404,7 +2577,7 @@ ohci_device_intr_start(xfer) s = splusb(); ohci_hash_add_td(sc, data); sed->ed.ed_tailp = LE(tail->physaddr); - opipe->tail = tail; + opipe->tail.td = tail; sed->ed.ed_flags &= LE(~OHCI_ED_SKIP); #if 0 @@ -2472,7 +2645,7 @@ ohci_device_intr_close(pipe) for (j = 0; j < nslots; j++) --sc->sc_bws[(pos * nslots + j) % OHCI_NO_INTRS]; - ohci_free_std(sc, opipe->tail); + ohci_free_std(sc, opipe->tail.td); ohci_free_sed(sc, opipe->sed); } @@ -2540,3 +2713,149 @@ ohci_device_setintr(sc, opipe, ival) DPRINTFN(5, ("ohci_setintr: returns %p\n", opipe)); return (USBD_NORMAL_COMPLETION); } + +/***********************/ + +usbd_status +ohci_device_isoc_transfer(xfer) + usbd_xfer_handle xfer; +{ + usbd_status err; + + DPRINTFN(5,("ohci_device_isoc_transfer: xfer=%p\n", xfer)); + + /* Put it on our queue, */ + err = usb_insert_transfer(xfer); + + /* bail out on error, */ + if (err && err != USBD_IN_PROGRESS) + return (err); + + /* XXX should check inuse here */ + + /* insert into schedule, */ + ohci_device_isoc_enter(xfer); + + /* and put on interrupt list if the pipe wasn't running */ + if (!err) + ohci_device_isoc_start(SIMPLEQ_FIRST(&xfer->pipe->queue)); + + return (err); +} + +void +ohci_device_isoc_enter(xfer) + usbd_xfer_handle xfer; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; + usbd_device_handle dev = opipe->pipe.device; + ohci_softc_t *sc = (ohci_softc_t *)dev->bus; + ohci_soft_ed_t *sed = opipe->sed; + struct iso *iso = &opipe->u.iso; + ohci_soft_itd_t *sitd, *nsitd; + ohci_physaddr_t buf, offs; + int i, ncur, nframes; + int ncross = 0; + int s; + + s = splusb(); + sitd = opipe->tail.itd; + buf = DMAADDR(&xfer->dmabuf); + sitd->itd.itd_bp0 = LE(buf & OHCI_ITD_PAGE_MASK); + nframes = xfer->nframes; + offs = buf & OHCI_ITD_OFFSET_MASK; + for (i = ncur = 0; i < nframes; i++, ncur++) { + if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */ + ncross > 1) { /* too many page crossings */ + + nsitd = ohci_alloc_sitd(sc); + if (nsitd == NULL) { + /* XXX what now? */ + return; + } + sitd->nextitd = nsitd; + sitd->itd.itd_nextitd = LE(nsitd->physaddr); + sitd->itd.itd_flags = LE( + OHCI_ITD_NOCC | + OHCI_ITD_SET_SF(iso->next) | + OHCI_ITD_NOINTR | + OHCI_ITD_SET_FC(OHCI_ITD_NOFFSET)); + sitd->itd.itd_be = LE(LE(sitd->itd.itd_bp0) + offs - 1); + nsitd->itd.itd_bp0 = LE((buf + offs) & OHCI_ITD_PAGE_MASK); + sitd = nsitd; + iso->next = iso->next + ncur; + ncur = 0; + ncross = 0; + } + /* XXX byte order */ + sitd->itd.itd_offset[i] = + offs | (ncross == 1 ? OHCI_ITD_PAGE_SELECT : 0); + offs += xfer->frlengths[i]; + /* XXX update ncross */ + } + nsitd = ohci_alloc_sitd(sc); + if (nsitd == NULL) { + /* XXX what now? */ + return; + } + sitd->nextitd = nsitd; + sitd->itd.itd_nextitd = LE(nsitd->physaddr); + sitd->itd.itd_flags = LE( + OHCI_ITD_NOCC | + OHCI_ITD_SET_SF(iso->next) | + OHCI_ITD_SET_DI(0) | + OHCI_ITD_SET_FC(ncur)); + sitd->itd.itd_be = LE(LE(sitd->itd.itd_bp0) + offs - 1); + iso->next = iso->next + ncur; + + opipe->tail.itd = nsitd; + sed->ed.ed_tailp = LE(nsitd->physaddr); + /* XXX update ED */ + splx(s); +} + +usbd_status +ohci_device_isoc_start(xfer) + usbd_xfer_handle xfer; +{ + printf("ohci_device_isoc_start: not implemented\n"); + return (USBD_INVAL); +} + +void +ohci_device_isoc_abort(xfer) + usbd_xfer_handle xfer; +{ +} + +void +ohci_device_isoc_done(xfer) + usbd_xfer_handle xfer; +{ + printf("ohci_device_isoc_done: not implemented\n"); +} + +usbd_status +ohci_setup_isoc(pipe) + usbd_pipe_handle pipe; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; + struct iso *iso = &opipe->u.iso; + + iso->next = -1; + iso->inuse = 0; + + return (USBD_NORMAL_COMPLETION); +} + +void +ohci_device_isoc_close(pipe) + usbd_pipe_handle pipe; +{ + struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; + ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; + + DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe)); + ohci_close_pipe(pipe, sc->sc_isoc_head); + ohci_free_sitd(sc, opipe->tail.itd); +} |