diff options
author | Nathan Binkert <nate@cvs.openbsd.org> | 2003-07-08 13:19:10 +0000 |
---|---|---|
committer | Nathan Binkert <nate@cvs.openbsd.org> | 2003-07-08 13:19:10 +0000 |
commit | e375f3615c7463097f3e178339aad0f6ec36c197 (patch) | |
tree | 5080b79ebe15758c7db466ea511c6083b4d9fad2 /sys/dev/usb/uhci.c | |
parent | 18791832a5c844c5d36cd0f7afaf4e92d40bcd09 (diff) |
Sync USB code with NetBSD.
This includes numerous fixes and paves the way for usb 2.0 support.
Diffstat (limited to 'sys/dev/usb/uhci.c')
-rw-r--r-- | sys/dev/usb/uhci.c | 361 |
1 files changed, 258 insertions, 103 deletions
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index 4091fa0527b..a22656d935b 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -1,5 +1,5 @@ -/* $OpenBSD: uhci.c,v 1.28 2003/05/19 04:17:53 nate Exp $ */ -/* $NetBSD: uhci.c,v 1.142 2001/10/25 02:08:13 augustss Exp $ */ +/* $OpenBSD: uhci.c,v 1.29 2003/07/08 13:19:09 nate Exp $ */ +/* $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */ /* @@ -88,8 +88,6 @@ #define delay(d) DELAY(d) #endif -#define MS_TO_TICKS(ms) ((ms) * hz / 1000) - #if defined(__OpenBSD__) struct cfdriver uhci_cd = { NULL, "uhci", DV_DULL @@ -160,6 +158,7 @@ struct uhci_pipe { }; Static void uhci_globalreset(uhci_softc_t *); +Static usbd_status uhci_portreset(uhci_softc_t*, int); Static void uhci_reset(uhci_softc_t *); Static void uhci_shutdown(void *v); Static void uhci_power(int, void *); @@ -187,6 +186,7 @@ Static void uhci_idone(uhci_intr_info_t *); Static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status); Static void uhci_timeout(void *); +Static void uhci_timeout_task(void *); Static void uhci_add_ls_ctrl(uhci_softc_t *, uhci_soft_qh_t *); Static void uhci_add_hs_ctrl(uhci_softc_t *, uhci_soft_qh_t *); Static void uhci_add_bulk(uhci_softc_t *, uhci_soft_qh_t *); @@ -249,7 +249,7 @@ Static void uhci_softintr(void *); Static usbd_status uhci_device_request(usbd_xfer_handle xfer); Static void uhci_add_intr(uhci_softc_t *, uhci_soft_qh_t *); -Static void uhci_remove_intr(uhci_softc_t*, uhci_soft_qh_t*); +Static void uhci_remove_intr(uhci_softc_t *, uhci_soft_qh_t *); Static usbd_status uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *pipe, int ival); @@ -273,11 +273,14 @@ void uhci_dump(void); #define UBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define UWRITE1(sc, r, x) \ - do { UBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); } while (0) + do { UBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); \ + } while (/*CONSTCOND*/0) #define UWRITE2(sc, r, x) \ - do { UBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); } while (0) + do { UBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); \ + } while (/*CONSTCOND*/0) #define UWRITE4(sc, r, x) \ - do { UBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); } while (0) + do { UBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); \ + } while (/*CONSTCOND*/0) #define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r))) #define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r))) #define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r))) @@ -356,9 +359,13 @@ struct usbd_pipe_methods uhci_device_isoc_methods = { }; #define uhci_add_intr_info(sc, ii) \ - LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list); + LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list) #define uhci_del_intr_info(ii) \ - LIST_REMOVE((ii), list) + do { \ + LIST_REMOVE((ii), list); \ + (ii)->list.le_prev = NULL; \ + } while (0) +#define uhci_active_intr_info(ii) ((ii)->list.le_prev != NULL) Static __inline__ uhci_soft_qh_t * uhci_find_prev_qh(uhci_soft_qh_t *pqh, uhci_soft_qh_t *sqh) @@ -401,7 +408,6 @@ uhci_init(uhci_softc_t *sc) uhci_dumpregs(sc); #endif - uhci_run(sc, 0); /* stop the controller */ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ uhci_globalreset(sc); /* reset the controller */ uhci_reset(sc); @@ -412,9 +418,9 @@ uhci_init(uhci_softc_t *sc) UHCI_FRAMELIST_ALIGN, &sc->sc_dma); if (err) return (err); - sc->sc_pframes = KERNADDR(&sc->sc_dma); + sc->sc_pframes = KERNADDR(&sc->sc_dma, 0); UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */ - UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma)); /* set frame list*/ + UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/ /* * Allocate a TD, inactive, that hangs from the last QH. @@ -534,7 +540,6 @@ uhci_activate(device_ptr_t self, enum devact act) switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); - break; case DVACT_DEACTIVATE: if (sc->sc_child != NULL) @@ -585,7 +590,7 @@ uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) /* * XXX * Since we are allocating a buffer we can assume that we will - * need TDs for it. Since we don't want to alolocate those from + * need TDs for it. Since we don't want to allocate those from * an interrupt context, we allocate them here and free them again. * This is no guarantee that we'll get the TDs next time... */ @@ -594,7 +599,8 @@ uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) u_int32_t i; uhci_soft_td_t **stds; DPRINTF(("uhci_allocm: get %d TDs\n", n)); - stds = malloc(sizeof(uhci_soft_td_t *) * n, M_TEMP, M_NOWAIT); + stds = malloc(sizeof(uhci_soft_td_t *) * n, M_TEMP, + M_NOWAIT); if (stds == NULL) panic("uhci_allocm"); memset(stds, 0, sizeof(uhci_soft_td_t *) * n); @@ -732,13 +738,14 @@ uhci_power(int why, void *v) uhci_run(sc, 0); /* in case BIOS has started it */ /* restore saved state */ - UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma)); + UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum); UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof); UHCICMD(sc, cmd | UHCI_CMD_FGR); /* force global resume */ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); UHCICMD(sc, cmd & ~UHCI_CMD_EGSM); /* back to normal */ + UHCICMD(sc, UHCI_CMD_MAXP); UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */ uhci_run(sc, 1); /* and start traffic again */ @@ -792,9 +799,9 @@ uhci_dump_td(uhci_soft_td_t *p) (long)le32toh(p->td.td_token), (long)le32toh(p->td.td_buffer))); - bitmask_snprintf((int)le32toh(p->td.td_link), "\20\1T\2Q\3VF", + bitmask_snprintf((u_int32_t)le32toh(p->td.td_link), "\20\1T\2Q\3VF", sbuf, sizeof(sbuf)); - bitmask_snprintf((int)le32toh(p->td.td_status), + bitmask_snprintf((u_int32_t)le32toh(p->td.td_status), "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", sbuf2, sizeof(sbuf2)); @@ -966,7 +973,7 @@ uhci_poll_hub(void *addr) usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); - p = KERNADDR(&xfer->dmabuf); + p = KERNADDR(&xfer->dmabuf, 0); p[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) p[0] |= 1<<1; @@ -1073,7 +1080,7 @@ uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) } pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh); - pqh->hlink = sqh->hlink; + pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_hctl_end == sqh) @@ -1090,9 +1097,9 @@ uhci_add_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_add_ls_ctrl: sqh=%p\n", sqh)); eqh = sc->sc_lctl_end; - sqh->hlink = eqh->hlink; + sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; - eqh->hlink = sqh; + eqh->hlink = sqh; eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); sc->sc_lctl_end = sqh; } @@ -1112,7 +1119,7 @@ uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) delay(UHCI_QH_REMOVE_DELAY); } pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh); - pqh->hlink = sqh->hlink; + pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; delay(UHCI_QH_REMOVE_DELAY); if (sc->sc_lctl_end == sqh) @@ -1129,9 +1136,9 @@ uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh) DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh)); eqh = sc->sc_bulk_end; - sqh->hlink = eqh->hlink; + sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; - eqh->hlink = sqh; + eqh->hlink = sqh; eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); sc->sc_bulk_end = sqh; uhci_add_loop(sc); @@ -1167,6 +1174,9 @@ uhci_intr(void *arg) { uhci_softc_t *sc = arg; + if (sc->sc_dying) + return (0); + DPRINTFN(15,("uhci_intr: real interrupt\n")); if (sc->sc_bus.use_polling) { #ifdef DIAGNOSTIC @@ -1190,15 +1200,10 @@ uhci_intr1(uhci_softc_t *sc) } #endif - status = UREAD2(sc, UHCI_STS); + status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if (status == 0) /* The interrupt was not for us. */ return (0); -#if defined(DIAGNOSTIC) && defined(__NetBSD__) - if (sc->sc_suspend != PWR_RESUME) - printf("uhci_intr: suspended sts=0x%x\n", status); -#endif - if (sc->sc_suspend != PWR_RESUME) { printf("%s: interrupt while not operating ignored\n", USBDEVNAME(sc->sc_bus.bdev)); @@ -1275,6 +1280,13 @@ uhci_softintr(void *v) for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) uhci_check_intr(sc, ii); +#ifdef USB_USE_SOFTINTR + if (sc->sc_softwake) { + sc->sc_softwake = 0; + wakeup(&sc->sc_softwake); + } +#endif /* USB_USE_SOFTINTR */ + sc->sc_bus.intr_context--; } @@ -1292,6 +1304,12 @@ uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii) return; } #endif + if (ii->xfer->status == USBD_CANCELLED || + ii->xfer->status == USBD_TIMEOUT) { + DPRINTF(("uhci_check_intr: aborted xfer=%p\n", ii->xfer)); + return; + } + if (ii->stdstart == NULL) return; lstd = ii->stdend; @@ -1361,12 +1379,6 @@ uhci_idone(uhci_intr_info_t *ii) } #endif - if (xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) { - DPRINTF(("uhci_idone: aborted xfer=%p\n", xfer)); - return; - } - if (xfer->nframes != 0) { /* Isoc transfer, do things differently. */ uhci_soft_td_t **stds = upipe->u.iso.stds; @@ -1429,7 +1441,8 @@ uhci_idone(uhci_intr_info_t *ii) #ifdef UHCI_DEBUG char sbuf[128]; - bitmask_snprintf((int)status, "\20\22BITSTUFF\23CRCTO\24NAK\25" + bitmask_snprintf((u_int32_t)status, + "\20\22BITSTUFF\23CRCTO\24NAK\25" "BABBLE\26DBUFFER\27STALLED\30ACTIVE", sbuf, sizeof(sbuf)); @@ -1461,17 +1474,33 @@ void uhci_timeout(void *addr) { uhci_intr_info_t *ii = addr; + struct uhci_xfer *uxfer = UXFER(ii->xfer); + struct uhci_pipe *upipe = (struct uhci_pipe *)uxfer->xfer.pipe; + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; - DPRINTF(("uhci_timeout: ii=%p\n", ii)); + DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer)); -#ifdef UHCI_DEBUG - if (uhcidebug > 10) - uhci_dump_tds(ii->stdstart); -#endif + if (sc->sc_dying) { + uhci_abort_xfer(&uxfer->xfer, USBD_TIMEOUT); + return; + } + + /* Execute the abort in a process context. */ + usb_init_task(&uxfer->abort_task, uhci_timeout_task, ii->xfer); + usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task); +} + +void +uhci_timeout_task(void *addr) +{ + usbd_xfer_handle xfer = addr; + int s; + + DPRINTF(("uhci_timeout_task: xfer=%p\n", xfer)); - ii->xfer->device->bus->intr_context++; - uhci_abort_xfer(ii->xfer, USBD_TIMEOUT); - ii->xfer->device->bus->intr_context--; + s = splusb(); + uhci_abort_xfer(xfer, USBD_TIMEOUT); + splx(s); } /* @@ -1594,8 +1623,8 @@ uhci_alloc_std(uhci_softc_t *sc) return (0); for(i = 0; i < UHCI_STD_CHUNK; i++) { offs = i * UHCI_STD_SIZE; - std = (uhci_soft_td_t *)((char *)KERNADDR(&dma) +offs); - std->physaddr = DMAADDR(&dma) + offs; + std = KERNADDR(&dma, offs); + std->physaddr = DMAADDR(&dma, offs); std->link.std = sc->sc_freetds; sc->sc_freetds = std; } @@ -1637,8 +1666,8 @@ uhci_alloc_sqh(uhci_softc_t *sc) return (0); for(i = 0; i < UHCI_SQH_CHUNK; i++) { offs = i * UHCI_SQH_SIZE; - sqh = (uhci_soft_qh_t *)((char *)KERNADDR(&dma) +offs); - sqh->physaddr = DMAADDR(&dma) + offs; + sqh = KERNADDR(&dma, offs); + sqh->physaddr = DMAADDR(&dma, offs); sqh->hlink = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } @@ -1680,9 +1709,9 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, int addr = upipe->pipe.device->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; - DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d ls=%d " + DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d speed=%d " "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, - upipe->pipe.device->lowspeed, flags)); + upipe->pipe.device->speed, flags)); maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); if (maxp == 0) { printf("uhci_alloc_std_chain: maxp=0\n"); @@ -1705,14 +1734,14 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, lastlink = UHCI_PTR_T; ntd--; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE); - if (upipe->pipe.device->lowspeed) + if (upipe->pipe.device->speed == USB_SPEED_LOW) status |= UHCI_TD_LS; if (flags & USBD_SHORT_XFER_OK) status |= UHCI_TD_SPD; for (i = ntd; i >= 0; i--) { p = uhci_alloc_std(sc); if (p == NULL) { - uhci_free_std_chain(sc, lastp, 0); + uhci_free_std_chain(sc, lastp, NULL); return (USBD_NOMEM); } p->link.std = lastp; @@ -1731,7 +1760,7 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, p->td.td_token = htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) : UHCI_TD_OUT(l, endpt, addr, tog)); - p->td.td_buffer = htole32(DMAADDR(dma) + i * maxp); + p->td.td_buffer = htole32(DMAADDR(dma, i * maxp)); tog ^= 1; } *sp = lastp; @@ -1782,8 +1811,8 @@ uhci_device_bulk_start(usbd_xfer_handle xfer) int len, isread, endpt; int s; - DPRINTFN(3, ("uhci_device_bulk_transfer: xfer=%p len=%d flags=%d\n", - xfer, xfer->length, xfer->flags)); + DPRINTFN(3, ("uhci_device_bulk_start: xfer=%p len=%d flags=%d ii=%p\n", + xfer, xfer->length, xfer->flags, ii)); if (sc->sc_dying) return (USBD_IOERROR); @@ -1833,7 +1862,7 @@ uhci_device_bulk_start(usbd_xfer_handle xfer) uhci_add_intr_info(sc, ii); if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + usb_callout(xfer->timeout_handle, mstohz(xfer->timeout), uhci_timeout, ii); } xfer->status = USBD_IN_PROGRESS; @@ -1861,44 +1890,72 @@ uhci_device_bulk_abort(usbd_xfer_handle xfer) } /* - * XXX This way of aborting is neither safe, nor good. - * But it will have to do until I figure out what to do. - * I apologize for the delay(). + * 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 uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) { uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; + struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; uhci_soft_td_t *std; int s; DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status)); - s = splusb(); - - /* Transfer is already done. */ - if (xfer->status != USBD_NOT_STARTED && - xfer->status != USBD_IN_PROGRESS) { + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer); + usb_transfer_complete(xfer); splx(s); - return; } - /* Make interrupt routine ignore it, */ - xfer->status = status; + if (xfer->device->bus->intr_context || !curproc) + panic("uhci_abort_xfer: not in process context"); - /* don't timeout, */ + /* + * Step 1: Make interrupt routine and hardware ignore xfer. + */ + s = splusb(); + xfer->status = status; /* make software ignore it */ usb_uncallout(xfer->timeout_handle, uhci_timeout, ii); - - /* make hardware ignore it, */ + DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii)); for (std = ii->stdstart; std != NULL; std = std->link.std) std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + splx(s); - xfer->hcpriv = ii; - + /* + * 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(upipe->pipe.device->bus, 2); /* Hardware finishes in 1ms */ + s = splusb(); +#ifdef USB_USE_SOFTINTR + sc->sc_softwake = 1; +#endif /* USB_USE_SOFTINTR */ + usb_schedsoftintr(&sc->sc_bus); +#ifdef USB_USE_SOFTINTR + DPRINTFN(1,("uhci_abort_xfer: tsleep\n")); + tsleep(&sc->sc_softwake, PZERO, "uhciab", 0); +#endif /* USB_USE_SOFTINTR */ splx(s); - delay(1000); + /* + * Step 3: Execute callback. + */ + xfer->hcpriv = ii; + DPRINTFN(1,("uhci_abort_xfer: callback\n")); s = splusb(); #ifdef DIAGNOSTIC ii->isdone = 1; @@ -2067,7 +2124,7 @@ uhci_device_intr_abort(usbd_xfer_handle xfer) DPRINTFN(1,("uhci_device_intr_abort: xfer=%p\n", xfer)); if (xfer->pipe->intrxfer == xfer) { DPRINTFN(1,("uhci_device_intr_abort: remove\n")); - xfer->pipe->intrxfer = 0; + xfer->pipe->intrxfer = NULL; } uhci_abort_xfer(xfer, USBD_CANCELLED); } @@ -2125,7 +2182,7 @@ uhci_device_request(usbd_xfer_handle xfer) UGETW(req->wIndex), UGETW(req->wLength), addr, endpt)); - ls = dev->lowspeed ? UHCI_TD_LS : 0; + ls = dev->speed == USB_SPEED_LOW ? UHCI_TD_LS : 0; isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); @@ -2148,14 +2205,14 @@ uhci_device_request(usbd_xfer_handle xfer) } upipe->u.ctl.length = len; - memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req); + memcpy(KERNADDR(&upipe->u.ctl.reqdma, 0), req, sizeof *req); setup->link.std = next; setup->td.td_link = htole32(next->physaddr | UHCI_PTR_VF | UHCI_PTR_TD); setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE); setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof *req, endpt, addr)); - setup->td.td_buffer = htole32(DMAADDR(&upipe->u.ctl.reqdma)); + setup->td.td_buffer = htole32(DMAADDR(&upipe->u.ctl.reqdma, 0)); stat->link.std = NULL; stat->td.td_link = htole32(UHCI_PTR_T); @@ -2188,7 +2245,7 @@ uhci_device_request(usbd_xfer_handle xfer) sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD); s = splusb(); - if (dev->lowspeed) + if (dev->speed == USB_SPEED_LOW) uhci_add_ls_ctrl(sc, sqh); else uhci_add_hs_ctrl(sc, sqh); @@ -2221,7 +2278,7 @@ uhci_device_request(usbd_xfer_handle xfer) } #endif if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + usb_callout(xfer->timeout_handle, mstohz(xfer->timeout), uhci_timeout, ii); } xfer->status = USBD_IN_PROGRESS; @@ -2295,7 +2352,7 @@ uhci_device_isoc_enter(usbd_xfer_handle xfer) xfer->status = USBD_IN_PROGRESS; UXFER(xfer)->curframe = next; - buf = DMAADDR(&xfer->dmabuf); + buf = DMAADDR(&xfer->dmabuf, 0); status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS); @@ -2532,6 +2589,9 @@ uhci_device_isoc_done(usbd_xfer_handle xfer) /* Not on interrupt list, ignore it. */ return; + if (!uhci_active_intr_info(ii)) + return; + #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_BUSY) { printf("uhci_device_isoc_done: xfer=%p not busy 0x%08x\n", @@ -2571,7 +2631,7 @@ uhci_device_intr_done(usbd_xfer_handle xfer) sqh->elink = NULL; sqh->qh.qh_elink = htole32(UHCI_PTR_T); } - uhci_free_std_chain(sc, ii->stdstart, 0); + uhci_free_std_chain(sc, ii->stdstart, NULL); /* XXX Wasteful. */ if (xfer->pipe->repeat) { @@ -2609,7 +2669,8 @@ uhci_device_intr_done(usbd_xfer_handle xfer) /* The ii is already on the examined list, just leave it. */ } else { DPRINTFN(5,("uhci_device_intr_done: removing\n")); - uhci_del_intr_info(ii); + if (uhci_active_intr_info(ii)) + uhci_del_intr_info(ii); } } @@ -2626,9 +2687,12 @@ uhci_device_ctrl_done(usbd_xfer_handle xfer) panic("uhci_ctrl_done: not a request"); #endif + if (!uhci_active_intr_info(ii)) + return; + uhci_del_intr_info(ii); /* remove from active list */ - if (upipe->pipe.device->lowspeed) + if (upipe->pipe.device->speed == USB_SPEED_LOW) uhci_remove_ls_ctrl(sc, upipe->u.ctl.sqh); else uhci_remove_hs_ctrl(sc, upipe->u.ctl.sqh); @@ -2647,11 +2711,17 @@ uhci_device_bulk_done(usbd_xfer_handle xfer) uhci_softc_t *sc = ii->sc; struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; + DPRINTFN(5,("uhci_device_ctrl_done: xfer=%p ii=%p sc=%p upipe=%p\n", + xfer, ii, sc, upipe)); + + if (!uhci_active_intr_info(ii)) + return; + uhci_del_intr_info(ii); /* remove from active list */ uhci_remove_bulk(sc, upipe->u.bulk.sqh); - uhci_free_std_chain(sc, ii->stdstart, 0); + uhci_free_std_chain(sc, ii->stdstart, NULL); DPRINTFN(5, ("uhci_bulk_done: length=%d\n", xfer->actlen)); } @@ -2841,7 +2911,7 @@ usb_device_descriptor_t uhci_devd = { {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ - 0, /* protocol */ + UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indicies */ @@ -2869,7 +2939,7 @@ usb_interface_descriptor_t uhci_ifcd = { 1, UICLASS_HUB, UISUBCLASS_HUB, - 0, + UIPROTO_FSHUB, 0 }; @@ -2910,6 +2980,101 @@ uhci_str(usb_string_descriptor_t *p, int l, char *s) } /* + * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also + * enables the port, and also states that SET_FEATURE(PORT_ENABLE) + * should not be used by the USB subsystem. As we cannot issue a + * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port + * will be enabled as part of the reset. + * + * On the VT83C572, the port cannot be successfully enabled until the + * outstanding "port enable change" and "connection status change" + * events have been reset. + */ +Static usbd_status +uhci_portreset(uhci_softc_t *sc, int index) +{ + int lim, port, x; + + if (index == 1) + port = UHCI_PORTSC1; + else if (index == 2) + port = UHCI_PORTSC2; + else + return (USBD_IOERROR); + + x = URWMASK(UREAD2(sc, port)); + UWRITE2(sc, port, x | UHCI_PORTSC_PR); + + usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); + + DPRINTFN(3,("uhci port %d reset, status0 = 0x%04x\n", + index, UREAD2(sc, port))); + + x = URWMASK(UREAD2(sc, port)); + UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); + + delay(100); + + DPRINTFN(3,("uhci port %d reset, status1 = 0x%04x\n", + index, UREAD2(sc, port))); + + x = URWMASK(UREAD2(sc, port)); + UWRITE2(sc, port, x | UHCI_PORTSC_PE); + + for (lim = 10; --lim > 0;) { + usb_delay_ms(&sc->sc_bus, USB_PORT_RESET_DELAY); + + x = UREAD2(sc, port); + + DPRINTFN(3,("uhci port %d iteration %u, status = 0x%04x\n", + index, lim, x)); + + if (!(x & UHCI_PORTSC_CCS)) { + /* + * No device is connected (or was disconnected + * during reset). Consider the port reset. + * The delay must be long enough to ensure on + * the initial iteration that the device + * connection will have been registered. 50ms + * appears to be sufficient, but 20ms is not. + */ + DPRINTFN(3,("uhci port %d loop %u, device detached\n", + index, lim)); + break; + } + + if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) { + /* + * Port enabled changed and/or connection + * status changed were set. Reset either or + * both raised flags (by writing a 1 to that + * bit), and wait again for state to settle. + */ + UWRITE2(sc, port, URWMASK(x) | + (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC))); + continue; + } + + if (x & UHCI_PORTSC_PE) + /* Port is enabled */ + break; + + UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE); + } + + DPRINTFN(3,("uhci port %d reset, status2 = 0x%04x\n", + index, UREAD2(sc, port))); + + if (lim <= 0) { + DPRINTFN(1,("uhci port %d reset timed out\n", index)); + return (USBD_TIMEOUT); + } + + sc->sc_isreset = 1; + return (USBD_NORMAL_COMPLETION); +} + +/* * Simulate a hardware hub by handling all the necessary requests. */ usbd_status @@ -2957,7 +3122,7 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf); + buf = KERNADDR(&xfer->dmabuf, 0); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { @@ -3219,18 +3384,8 @@ uhci_root_ctrl_start(usbd_xfer_handle xfer) UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: - x = URWMASK(UREAD2(sc, port)); - UWRITE2(sc, port, x | UHCI_PORTSC_PR); - usb_delay_ms(&sc->sc_bus, 50); /*XXX USB v1.1 7.1.7.3 */ - UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); - delay(100); - x = UREAD2(sc, port); - UWRITE2(sc, port, x | UHCI_PORTSC_PE); - usb_delay_ms(&sc->sc_bus, 10); /* XXX */ - DPRINTFN(3,("uhci port %d reset, status = 0x%04x\n", - index, UREAD2(sc, port))); - sc->sc_isreset = 1; - break; + err = uhci_portreset(sc, index); + goto ret; case UHF_PORT_POWER: /* Pretend we turned on power */ err = USBD_NORMAL_COMPLETION; @@ -3325,7 +3480,7 @@ uhci_root_intr_start(usbd_xfer_handle xfer) if (sc->sc_dying) return (USBD_IOERROR); - sc->sc_ival = MS_TO_TICKS(xfer->pipe->endpoint->edesc->bInterval); + sc->sc_ival = mstohz(xfer->pipe->endpoint->edesc->bInterval); usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); sc->sc_intr_xfer = xfer; return (USBD_IN_PROGRESS); |