diff options
author | wpaul <wpaul@FreeBSD.org> | 2000-01-20 07:38:33 +0000 |
---|---|---|
committer | wpaul <wpaul@FreeBSD.org> | 2000-01-20 07:38:33 +0000 |
commit | d5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0 (patch) | |
tree | 605d3c9f2f783654045386042cd04f312155f047 /sys/dev/usb/ohci.c | |
parent | 6dea110be6b504383e7271cd0173acd492dea9e8 (diff) | |
download | FreeBSD-src-d5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0.zip FreeBSD-src-d5c9bbfbf2027c7efc9f44aee8f5cc459198b3f0.tar.gz |
More USB ethernet tweaks:
- Sync ohci, uhci and usbdi modules with NetBSD in order to obtain the
following improvements:
o New USBD_NO_TSLEEP flag can be used in place of UQ_NO_TSLEEP
quirk. This allows drivers to specify busy waiting only for
certain transfers (namely control transfers for reading/writing
registers and stuff).
o New USBD_FORCE_SHORT_XFER flag can be used to deal with
devices like the ADMtek Pegasus that sense the end of bulk OUT
transfers in a special way (if a transfer is exactly a multiple
of 64 bytes in size, you need to send an extra empty packet
to terminate the transfer).
o usbd_open_pipe_intr() now accepts an interval argument which
can be used to change the rate at which the interrupt callback
routine is invoked. Specifying USBD_DEFAULT_INTERVAL uses the
value specified in the device's config data, but drivers can
override it if needed.
- Change if_aue to use USBD_FORCE_SHORT_XFER for packet transmissions.
- Change if_aue, if_kue and if_cue to use USBD_NO_TSLEEP for all
control transfers. We no longer force the non-tsleep hack for
bulk transfers since these are done asynchronously anyway.
- Removed quirk entry fiddling from if_aue and if_kue since we don't
need it anymore now that we have the USBD_NO_TSLEEP flag.
- Tweak ulpt, uhid, ums and ukbd drivers to use the new arg to
usbd_open_pipe_intr().
- Add a flag to the softc struct in the ethernet drivers to indicate
when a device has been detached, and use this flag to perform
tests to prevent the drivers from trying to do control transfers
if this is the case. This is necessary because calling if_detach()
with INET6 enabled will eventually result in a call to the driver's
ioctl() routine to delete the multicast groups on the interface,
which will result in attempts to perform control transfers. (It's
possible this also happens even without INET6 support enabled.) This
is pointless since we know that if the detach method has been called,
the hardware has been unplugged.
- Changed watchdog timeout routines to just call the driver init routines
to initialize the device states without trying to close and re-open the
pipes. This is partly because we don't want to frob things at interrupt
context, but also because this doesn't seem to work right and I don't
want to panic the system just because a USB device may have stopped
responding.
- Fix aue_rxeof() to be a little smarter about detecting when a double
transfer is needed. Unfortunately, the design of the chip makes it hard
to get this exactly right. Hopefully, this will go away once either
Nick or Lennart finds the bug in the uhci driver that makes this ugly
hack necessary.
- Also sync usbdevs with NetBSD.
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); +} |