Index: ehci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci.c,v retrieving revision 1.15 diff -u -r1.15 ehci.c --- ehci.c 30 Oct 2004 15:13:09 -0000 1.15 +++ ehci.c 31 Oct 2004 11:04:03 -0000 @@ -1,15 +1,4 @@ -/* $NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $ */ - -/* Also ported from NetBSD: - * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $ - * $NetBSD: ehci.c,v 1.54 2004/01/17 13:15:05 jdolecek Exp $ - * up to - * $NetBSD: ehci.c,v 1.64 2004/06/23 06:45:56 mycroft Exp $ - * $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $ - * $NetBSD: ehci.c,v 1.67 2004/07/06 04:18:05 mycroft Exp $ - * $NetBSD: ehci.c,v 1.68 2004/07/09 05:07:06 mycroft Exp $ - * $NetBSD: ehci.c,v 1.69 2004/07/17 20:12:02 mycroft Exp $ - */ +/* $NetBSD: ehci.c,v 1.87 2004/10/25 10:29:49 augustss Exp $ */ /* * TODO @@ -74,7 +63,7 @@ * Interrupt transfers are not difficult, it's just not done. * * 3) The meaty part to implement is the support for USB 2.0 hubs. - * They are quite compolicated since the need to be able to do + * They are quite complicated since the need to be able to do * "transaction translation", i.e., converting to/from USB 2 and USB 1. * So the hub driver needs to handle and schedule these things, to * assign place in frame where different devices get to go. See chapter @@ -125,8 +114,8 @@ #ifdef USB_DEBUG #define EHCI_DEBUG USB_DEBUG -#define DPRINTF(x) if (ehcidebug) logprintf x -#define DPRINTFN(n,x) if (ehcidebug>(n)) logprintf x +#define DPRINTF(x) do { if (ehcidebug) logprintf x; } while (0) +#define DPRINTFN(n,x) do { if (ehcidebug>(n)) logprintf x; } while (0) int ehcidebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, @@ -256,7 +245,7 @@ #ifdef EHCI_DEBUG Static void ehci_dump_regs(ehci_softc_t *); -void ehci_dump(void); +Static void ehci_dump(void); Static ehci_softc_t *theehci; Static void ehci_dump_link(ehci_link_t, int); Static void ehci_dump_sqtds(ehci_soft_qtd_t *); @@ -412,13 +401,13 @@ /* frame list size at default, read back what we got and use that */ switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { - case 0: sc->sc_flsize = 1024*4; break; - case 1: sc->sc_flsize = 512*4; break; - case 2: sc->sc_flsize = 256*4; break; + case 0: sc->sc_flsize = 1024; break; + case 1: sc->sc_flsize = 512; break; + case 2: sc->sc_flsize = 256; break; case 3: return (USBD_IOERROR); } - err = usb_allocmem(&sc->sc_bus, sc->sc_flsize, - EHCI_FLALIGN_ALIGN, &sc->sc_fldma); + err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), + EHCI_FLALIGN_ALIGN, &sc->sc_fldma); if (err) return (err); DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); @@ -473,7 +462,7 @@ sqh->sqtd = NULL; } /* Point the frame list at the last level (128ms). */ - for (i = 0; i < sc->sc_flsize / 4; i++) { + for (i = 0; i < sc->sc_flsize; i++) { sc->sc_flist[i] = htole32(EHCI_LINK_QH | sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1, i)].sqh->physaddr); @@ -516,7 +505,7 @@ /* Turn on controller */ EOWRITE4(sc, EHCI_USBCMD, - EHCI_CMD_ITC_8 | /* 8 microframes */ + EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | EHCI_CMD_ASE | EHCI_CMD_PSE | @@ -557,8 +546,12 @@ /* If we get an interrupt while polling, then just ignore it. */ if (sc->sc_bus.use_polling) { + u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + + if (intrs) + EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ #ifdef DIAGNOSTIC - printf("ehci_intr: ignored interrupt while polling\n"); + DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n")); #endif return (0); } @@ -576,7 +569,7 @@ /* In case the interrupt occurs before initialization has completed. */ if (sc == NULL) { #ifdef DIAGNOSTIC - printf("ehci_intr: sc == NULL\n"); + printf("ehci_intr1: sc == NULL\n"); #endif return (0); } @@ -586,7 +579,7 @@ return (0); eintrs = intrs & sc->sc_eintrs; - DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", + DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), (u_int)eintrs)); if (!eintrs) @@ -733,7 +726,7 @@ lsqtd = ex->sqtdend; #ifdef DIAGNOSTIC if (lsqtd == NULL) { - printf("ehci_check_intr: sqtd==0\n"); + printf("ehci_check_intr: lsqtd==0\n"); return; } #endif @@ -771,9 +764,10 @@ { usbd_xfer_handle xfer = &ex->xfer; struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - ehci_soft_qtd_t *sqtd; - u_int32_t status, nstatus; + ehci_soft_qtd_t *sqtd, *lsqtd; + u_int32_t status = 0, nstatus = 0; int actlen; + u_int pkts_left; DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); #ifdef DIAGNOSTIC @@ -807,10 +801,9 @@ #endif /* The transfer is done, compute actual length and status. */ + lsqtd = ex->sqtdend; actlen = 0; - nstatus = 0; - status = 0; - for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) { + for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) { nstatus = le32toh(sqtd->qtd.qtd_status); if (nstatus & EHCI_QTD_ACTIVE) break; @@ -824,9 +817,14 @@ actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); } - /* If there are left over TDs we need to update the toggle. */ - if (sqtd != NULL) { - printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus); + /* + * If there are left over TDs we need to update the toggle. + * The default pipe doesn't need it since control transfers + * start the toggle at 0 every time. + */ + if (sqtd != lsqtd->nextqtd && + xfer->pipe->device->default_pipe != xfer->pipe) { + DPRINTF(("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus)); #if 0 ehci_dump_sqh(epipe->sqh); ehci_dump_sqtds(ex->sqtdstart); @@ -834,6 +832,14 @@ epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); } + /* + * For a short transfer we need to update the toggle for the missing + * packets within the qTD. + */ + pkts_left = EHCI_QTD_GET_BYTES(status) / + UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize); + epipe->nexttoggle ^= pkts_left % 2; + status &= EHCI_QTD_STATERRS; DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", xfer->length, actlen, status)); @@ -846,7 +852,7 @@ "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR" "\3MISSED", sbuf, sizeof(sbuf)); - DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, + DPRINTFN((status == EHCI_QTD_HALTED) ? 2 : 0, ("ehci_idone: error, addr=%d, endpt=0x%02x, " "status 0x%s\n", xfer->pipe->device->address, @@ -995,7 +1001,8 @@ #ifdef EHCI_DEBUG DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why)); - ehci_dump_regs(sc); + if (ehcidebug > 0) + ehci_dump_regs(sc); #endif s = splhardusb(); @@ -1154,7 +1161,7 @@ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_FREE) { - printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer, + printf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer, xfer->busy_free); } #endif @@ -1162,7 +1169,7 @@ xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT); } if (xfer != NULL) { - memset(xfer, 0, sizeof (struct ehci_xfer)); + memset(xfer, 0, sizeof(struct ehci_xfer)); #ifdef DIAGNOSTIC EXFER(xfer)->isdone = 1; xfer->busy_free = XFER_BUSY; @@ -1269,7 +1276,7 @@ stop = 0; for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { ehci_dump_sqtd(sqtd); - stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE; + stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE); } if (sqtd) printf("dump aborted, too many TDs\n"); @@ -1354,10 +1361,19 @@ usbd_status err; int s; int ival, speed, naks; + int hshubaddr, hshubport; DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + if (dev->myhsport) { + hshubaddr = dev->myhsport->parent->address; + hshubport = dev->myhsport->portno; + } else { + hshubaddr = 0; + hshubport = 0; + } + if (sc->sc_dying) return (USBD_IOERROR); @@ -1384,6 +1400,16 @@ case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; default: panic("ehci_open: bad device speed %d", dev->speed); } + if (speed != EHCI_QH_SPEED_HIGH) { + printf("%s: *** WARNING: opening low/full speed device, this " + "does not work yet.\n", + USBDEVNAME(sc->sc_bus.bdev)); + DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n", + hshubaddr, hshubport)); + if (xfertype != UE_CONTROL) + return USBD_INVAL; + } + naks = 8; /* XXX */ sqh = ehci_alloc_sqh(sc); if (sqh == NULL) @@ -1401,7 +1427,9 @@ ); sqh->qh.qh_endphub = htole32( EHCI_QH_SET_MULT(1) | - /* XXX TT stuff */ + EHCI_QH_SET_HUBA(hshubaddr) | + EHCI_QH_SET_PORT(hshubport) | + EHCI_QH_SET_CMASK(0xf0) | /* XXX */ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0) ); sqh->qh.qh_curqtd = EHCI_NULL; @@ -1497,13 +1525,24 @@ void ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) { - /* Halt while we are messing. */ - sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + int i; + u_int32_t status; + + /* Save toggle bit and ping status. */ + status = sqh->qh.qh_qtd.qtd_status & + htole32(EHCI_QTD_TOGGLE_MASK | + EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE)); + /* Set HALTED to make hw leave it alone. */ + sqh->qh.qh_qtd.qtd_status = + htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED)); sqh->qh.qh_curqtd = 0; sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); + sqh->qh.qh_qtd.qtd_altnext = 0; + for (i = 0; i < EHCI_QTD_NBUFFERS; i++) + sqh->qh.qh_qtd.qtd_buffer[i] = 0; sqh->sqtd = sqtd; - /* Clear halt */ - sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED); + /* Set !HALTED && !ACTIVE to start execution, preserve some fields */ + sqh->qh.qh_qtd.qtd_status = status; } /* @@ -1617,10 +1656,7 @@ }; Static int -ehci_str(p, l, s) - usb_string_descriptor_t *p; - int l; - char *s; +ehci_str(usb_string_descriptor_t *p, int l, char *s) { int i; @@ -1676,7 +1712,7 @@ #endif req = &xfer->request; - DPRINTFN(4,("ehci_root_ctrl_control type=0x%02x request=%02x\n", + DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n", req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); @@ -1703,7 +1739,7 @@ } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): - DPRINTFN(8,("ehci_root_ctrl_control wValue=0x%04x\n", value)); + DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value)); switch(value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { @@ -1757,6 +1793,9 @@ *(u_int8_t *)buf = 0; totlen = 1; switch (value & 0xff) { + case 0: /* Language table */ + totlen = ehci_str(buf, len, "\001"); + break; case 1: /* Vendor */ totlen = ehci_str(buf, len, sc->sc_vendor); break; @@ -1818,7 +1857,7 @@ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): - DPRINTFN(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " + DPRINTFN(8, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if (index < 1 || index > sc->sc_noport) { @@ -1838,11 +1877,11 @@ EOWRITE4(sc, port, v &~ EHCI_PS_PP); break; case UHF_PORT_TEST: - DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test " + DPRINTFN(2,("ehci_root_ctrl_start: clear port test " "%d\n", index)); break; case UHF_PORT_INDICATOR: - DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind " + DPRINTFN(2,("ehci_root_ctrl_start: clear port ind " "%d\n", index)); EOWRITE4(sc, port, v &~ EHCI_PS_PIC); break; @@ -1891,7 +1930,7 @@ v = EOREAD4(sc, EHCI_HCSPARAMS); USETW(hubd.wHubCharacteristics, EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | - EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS)) + EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ? UHD_PORT_IND : 0); hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) @@ -1910,7 +1949,7 @@ totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): - DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n", + DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n", index)); if (index < 1 || index > sc->sc_noport) { err = USBD_IOERROR; @@ -1921,7 +1960,7 @@ goto ret; } v = EOREAD4(sc, EHCI_PORTSC(index)); - DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n", + DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n", v)); i = UPS_HIGH_SPEED; if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; @@ -1961,7 +2000,7 @@ EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_PORT_RESET: - DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n", + DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n", index)); if (EHCI_PS_IS_LOWSPEED(v)) { /* Low speed device, give up ownership. */ @@ -2002,16 +2041,16 @@ index, v)); break; case UHF_PORT_POWER: - DPRINTFN(2,("ehci_root_ctrl_transfer: set port power " + DPRINTFN(2,("ehci_root_ctrl_start: set port power " "%d\n", index)); EOWRITE4(sc, port, v | EHCI_PS_PP); break; case UHF_PORT_TEST: - DPRINTFN(2,("ehci_root_ctrl_transfer: set port test " + DPRINTFN(2,("ehci_root_ctrl_start: set port test " "%d\n", index)); break; case UHF_PORT_INDICATOR: - DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind " + DPRINTFN(2,("ehci_root_ctrl_start: set port ind " "%d\n", index)); EOWRITE4(sc, port, v | EHCI_PS_PIC); break; @@ -2085,6 +2124,7 @@ void ehci_root_intr_done(usbd_xfer_handle xfer) { + xfer->hcpriv = NULL; } Static usbd_status @@ -2145,6 +2185,7 @@ void ehci_root_ctrl_done(usbd_xfer_handle xfer) { + xfer->hcpriv = NULL; } /************************/ @@ -2672,7 +2713,7 @@ isread = req->bmRequestType & UT_READ; len = UGETW(req->wLength); - DPRINTFN(3,("ehci_device_control type=0x%02x, request=0x%02x, " + DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, " "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", req->bmRequestType, req->bRequest, UGETW(req->wValue), UGETW(req->wIndex), len, addr, @@ -2713,6 +2754,7 @@ &next, &end); if (err) goto bad3; + end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC); end->nextqtd = stat; end->qtd.qtd_next = end->qtd.qtd_altnext = htole32(stat->physaddr); @@ -2834,7 +2876,7 @@ int len, isread, endpt; int s; - DPRINTFN(2, ("ehci_device_bulk_transfer: xfer=%p len=%d flags=%d\n", + DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n", xfer, xfer->length, xfer->flags)); if (sc->sc_dying) @@ -2842,7 +2884,7 @@ #ifdef DIAGNOSTIC if (xfer->rqflags & URQ_REQUEST) - panic("ehci_device_bulk_transfer: a request"); + panic("ehci_device_bulk_start: a request"); #endif len = xfer->length; @@ -2855,7 +2897,7 @@ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, &dataend); if (err) { - DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n")); + DPRINTFN(-1,("ehci_device_bulk_start: no memory\n")); xfer->status = err; usb_transfer_complete(xfer); return (err); @@ -2863,7 +2905,7 @@ #ifdef EHCI_DEBUG if (ehcidebug > 5) { - DPRINTF(("ehci_device_bulk_transfer: data(1)\n")); + DPRINTF(("ehci_device_bulk_start: data(1)\n")); ehci_dump_sqh(sqh); ehci_dump_sqtds(data); } @@ -2874,7 +2916,7 @@ exfer->sqtdend = dataend; #ifdef DIAGNOSTIC if (!exfer->isdone) { - printf("ehci_device_bulk_transfer: not done, ex=%p\n", exfer); + printf("ehci_device_bulk_start: not done, ex=%p\n", exfer); } exfer->isdone = 0; #endif @@ -2891,9 +2933,9 @@ #ifdef EHCI_DEBUG if (ehcidebug > 10) { - DPRINTF(("ehci_device_bulk_transfer: data(2)\n")); + DPRINTF(("ehci_device_bulk_start: data(2)\n")); delay(10000); - DPRINTF(("ehci_device_bulk_transfer: data(3)\n")); + DPRINTF(("ehci_device_bulk_start: data(3)\n")); ehci_dump_regs(sc); #if 0 printf("async_head:\n"); @@ -2963,17 +3005,16 @@ break; /* Pick an interrupt slot at the right level. */ - /* XXX, could do better than picking at random. */ + /* XXX could do better than picking at random. */ islot = EHCI_IQHIDX(lev, arc4random()); sqh->islot = islot; - isp = &sc->sc_islots[sqh->islot]; + isp = &sc->sc_islots[islot]; ehci_add_qh(sqh, isp->sqh); return (USBD_NORMAL_COMPLETION); } - Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { @@ -3025,7 +3066,7 @@ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, &dataend); if (err) { - DPRINTFN(-1,("ehci_device_intr_start: no memory\n")); + DPRINTFN(-1, ("ehci_device_intr_start: no memory\n")); xfer->status = err; usb_transfer_complete(xfer); return (err); @@ -3081,9 +3122,9 @@ Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { - DPRINTFN(1,("ehci_device_intr_abort: xfer=%p\n", xfer)); + DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer)); if (xfer->pipe->intrxfer == xfer) { - DPRINTFN(1,("ehci_device_intr_abort: remove\n")); + DPRINTFN(1, ("ehci_device_intr_abort: remove\n")); xfer->pipe->intrxfer = NULL; } ehci_abort_xfer(xfer, USBD_CANCELLED); @@ -3112,8 +3153,8 @@ usbd_status err; int len, isread, endpt, s; - DPRINTFN(10,("ehci_device_intr_done: xfer=%p, actlen=%d\n", - xfer, xfer->actlen)); + DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n", + xfer, xfer->actlen)); if (xfer->pipe->repeat) { ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); @@ -3124,10 +3165,10 @@ isread = UE_GET_DIR(endpt) == UE_DIR_IN; sqh = epipe->sqh; - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, - &dataend); + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, + &data, &dataend); if (err) { - DPRINTFN(-1,("ehci_device_intr_done: no memory\n")); + DPRINTFN(-1, ("ehci_device_intr_done: no memory\n")); xfer->status = err; return; } Index: ehcireg.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehcireg.h,v retrieving revision 1.5 diff -u -r1.5 ehcireg.h --- ehcireg.h 2 Aug 2004 12:56:00 -0000 1.5 +++ ehcireg.h 31 Oct 2004 09:40:22 -0000 @@ -84,7 +84,7 @@ #define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */ #define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf) -#define EHCI_HCS_P_INCICATOR(x) ((x) & 0x10000) +#define EHCI_HCS_P_INDICATOR(x) ((x) & 0x10000) #define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */ #define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */ #define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */ @@ -218,6 +218,7 @@ ehci_link_t qtd_altnext; u_int32_t qtd_status; #define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff) +#define EHCI_QTD_SET_STATUS(x) ((x) << 0) #define EHCI_QTD_ACTIVE 0x80 #define EHCI_QTD_HALTED 0x40 #define EHCI_QTD_BUFERR 0x20 Index: uhub.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhub.c,v retrieving revision 1.63 diff -u -r1.63 uhub.c --- uhub.c 9 Sep 2004 20:43:49 -0000 1.63 +++ uhub.c 30 Oct 2004 20:20:15 -0000 @@ -85,6 +85,9 @@ u_int8_t sc_status[1]; /* XXX more ports */ u_char sc_running; }; +#define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol) +#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) +#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) Static usbd_status uhub_explore(usbd_device_handle hub); Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status); @@ -162,12 +165,13 @@ usbd_device_handle dev = uaa->device; char *devinfo; usbd_status err; - struct usbd_hub *hub; + struct usbd_hub *hub = NULL; usb_device_request_t req; usb_hub_descriptor_t hubdesc; int p, port, nports, nremov, pwrdly; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; + struct usbd_tt *tts = NULL; devinfo = malloc(1024, M_TEMP, M_NOWAIT); if (devinfo == NULL) { @@ -178,6 +182,12 @@ usbd_devinfo(dev, 1, devinfo); USB_ATTACH_SETUP; + if (UHUB_IS_HIGH_SPEED(sc)) { + printf("%s: %s transaction translator%s\n", + USBDEVNAME(sc->sc_dev), + UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple", + UHUB_IS_SINGLE_TT(sc) ? "" : "s"); + } err = usbd_set_config_index(dev, 0, 1); if (err) { DPRINTF(("%s: configuration failed, error=%s\n", @@ -220,6 +230,11 @@ USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "", nremov, dev->self_powered ? "self" : "bus"); + if (nports == 0) { + printf("%s: no ports, hub ignored\n", USBDEVNAME(sc->sc_dev)); + goto bad; + } + hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), M_USBDEV, M_NOWAIT); if (hub == NULL) { @@ -299,10 +314,17 @@ * proceed with device attachment */ + if (UHUB_IS_HIGH_SPEED(sc)) { + tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) * + sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT); + if (!tts) + goto bad; + } + /* Set up data structures */ for (p = 0; p < nports; p++) { struct usbd_port *up = &hub->ports[p]; - up->device = 0; + up->device = NULL; up->parent = dev; up->portno = p+1; if (dev->self_powered) @@ -311,6 +333,12 @@ else up->power = USB_MIN_POWER; up->restartcnt = 0; + if (UHUB_IS_HIGH_SPEED(sc)) { + up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p]; + up->tt->hub = hub; + } else { + up->tt = NULL; + } } /* XXX should check for none, individual, or ganged power? */ @@ -336,9 +364,10 @@ USB_ATTACH_SUCCESS_RETURN; bad: - free(hub, M_USBDEV); + if (hub) + free(hub, M_USBDEV); free(devinfo, M_TEMP); - dev->hub = 0; + dev->hub = NULL; USB_ATTACH_ERROR_RETURN; } @@ -475,6 +504,15 @@ continue; } +#if 0 + if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) { + printf("%s: port %d, transaction translation not " + "implemented, low/full speed device ignored\n", + USBDEVNAME(sc->sc_dev), port); + continue; + } +#endif + /* Figure out device speed */ if (status & UPS_HIGH_SPEED) speed = USB_SPEED_HIGH; @@ -574,6 +612,8 @@ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub, USBDEV(sc->sc_dev)); + if (hub->ports[0].tt) + free(hub->ports[0].tt, M_USBDEV); free(hub, M_USBDEV); sc->sc_hub->hub = NULL; Index: usb_subr.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb_subr.c,v retrieving revision 1.72 diff -u -r1.72 usb_subr.c --- usb_subr.c 30 Sep 2004 02:13:42 -0000 1.72 +++ usb_subr.c 30 Oct 2004 20:20:15 -0000 @@ -1046,13 +1046,14 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, int speed, int port, struct usbd_port *up) { - usbd_device_handle dev; + usbd_device_handle dev, adev; struct usbd_device *hub; usb_device_descriptor_t *dd; usb_port_status_t ps; usbd_status err; int addr; int i; + int p; DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n", bus, port, depth, speed)); @@ -1086,11 +1087,27 @@ dev->depth = depth; dev->powersrc = up; dev->myhub = up->parent; - for (hub = up->parent; + + up->device = dev; + + /* Locate port on upstream high speed hub */ + for (adev = dev, hub = up->parent; hub != NULL && hub->speed != USB_SPEED_HIGH; - hub = hub->myhub) + adev = hub, hub = hub->myhub) ; - dev->myhighhub = hub; + if (hub) { + for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) { + if (hub->hub->ports[p].device == adev) { + dev->myhsport = &hub->hub->ports[p]; + goto found; + } + } + panic("usbd_new_device: cannot find HS port\n"); + found: + DPRINTFN(1,("usbd_new_device: high speed port %d\n", p)); + } else { + dev->myhsport = NULL; + } dev->speed = speed; dev->langid = USBD_NOLANG; dev->cookie.cookie = ++usb_cookie_no; @@ -1103,8 +1120,6 @@ return (err); } - up->device = dev; - /* Set the address. Do this early; some devices need that. */ /* Try a few times in case the device is slow (i.e. outside specs.) */ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); @@ -1228,8 +1243,8 @@ if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); - up->device = 0; - dev->bus->devices[dev->address] = 0; + up->device = NULL; + dev->bus->devices[dev->address] = NULL; free(dev, M_USB); } Index: usbdivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.41 diff -u -r1.41 usbdivar.h --- usbdivar.h 30 Jun 2004 02:56:24 -0000 1.41 +++ usbdivar.h 30 Oct 2004 20:20:15 -0000 @@ -73,6 +73,10 @@ void (*done)(usbd_xfer_handle xfer); }; +struct usbd_tt { + struct usbd_hub *hub; +}; + struct usbd_port { usb_port_status_t status; u_int16_t power; /* mA of current on port */ @@ -81,6 +85,7 @@ #define USBD_RESTART_MAX 5 struct usbd_device *device; /* Connected device */ struct usbd_device *parent; /* The ports hub */ + struct usbd_tt *tt; /* Transaction translator (if any) */ }; struct usbd_hub { @@ -141,7 +146,7 @@ usb_event_cookie_t cookie; /* unique connection id */ struct usbd_port *powersrc; /* upstream hub port, or 0 */ struct usbd_device *myhub; /* upstream hub */ - struct usbd_device *myhighhub; /* closest high speed hub */ + struct usbd_port *myhsport; /* closest high speed port */ struct usbd_endpoint def_ep; /* for pipe 0 */ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */ struct usbd_interface *ifaces; /* array of all interfaces */