Index: ehci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci.c,v retrieving revision 1.43 diff -u -r1.43 ehci.c --- ehci.c 18 Mar 2006 17:57:34 -0000 1.43 +++ ehci.c 19 Mar 2006 19:26:22 -0000 @@ -212,9 +212,10 @@ Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *, ehci_softc_t *, int, int, usbd_xfer_handle, + ehci_soft_qtd_t *, ehci_soft_qtd_t *, ehci_soft_qtd_t **, ehci_soft_qtd_t **); -Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *, - ehci_soft_qtd_t *); +Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qh_t *, + ehci_soft_qtd_t *, ehci_soft_qtd_t *); Static usbd_status ehci_device_request(usbd_xfer_handle xfer); @@ -224,7 +225,7 @@ Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *); Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, ehci_soft_qh_t *); -Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); +Static void ehci_activate_qh(ehci_soft_qh_t *, ehci_soft_qtd_t *); Static void ehci_sync_hc(ehci_softc_t *); Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *); @@ -237,7 +238,7 @@ Static void ehci_dump_link(ehci_link_t, int); Static void ehci_dump_sqtds(ehci_soft_qtd_t *); Static void ehci_dump_sqtd(ehci_soft_qtd_t *); -Static void ehci_dump_qtd(ehci_qtd_t *); +Static void ehci_dump_qtd(volatile ehci_qtd_t *); Static void ehci_dump_sqh(ehci_soft_qh_t *); #ifdef DIAGNOSTIC Static void ehci_dump_exfer(struct ehci_xfer *); @@ -450,7 +451,6 @@ sqh->qh.qh_qtd.qtd_next = EHCI_NULL; sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); - sqh->sqtd = NULL; } /* Point the frame list at the last level (128ms). */ for (i = 0; i < sc->sc_flsize; i++) { @@ -476,8 +476,7 @@ /* Fill the overlay qTD */ sqh->qh.qh_qtd.qtd_next = EHCI_NULL; sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; - sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); - sqh->sqtd = NULL; + sqh->qh.qh_qtd.qtd_status = htole32(0); #ifdef EHCI_DEBUG if (ehcidebug) { ehci_dump_sqh(sqh); @@ -520,10 +519,6 @@ return (USBD_NORMAL_COMPLETION); -#if 0 - bad2: - ehci_free_sqh(sc, sc->sc_async_head); -#endif bad1: usb_freemem(&sc->sc_bus, &sc->sc_fldma); return (err); @@ -579,6 +574,7 @@ return (0); EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ + EOREAD4(sc, EHCI_USBCMD); /* Flush posted writes on PCI */ sc->sc_bus.intr_context++; sc->sc_bus.no_intrs++; if (eintrs & EHCI_STS_IAA) { @@ -656,7 +652,7 @@ pipe = xfer->pipe; - p = KERNADDR(&xfer->dmabuf, 0); + p = xfer->buffer; m = min(sc->sc_noport, xfer->length * 8 - 1); memset(p, 0, xfer->length); for (i = 1; i <= m; i++) { @@ -768,9 +764,16 @@ #endif ehci_soft_qtd_t *sqtd, *lsqtd; u_int32_t status = 0, nstatus = 0; + ehci_physaddr_t nextphys, altnextphys; int actlen, cerr; DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); +#ifdef USB_DEBUG + if (ehcidebug > 5) { + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(epipe->sqh->sqtd); + } +#endif #ifdef DIAGNOSTIC { int s = splhigh(); @@ -801,6 +804,19 @@ ehci_dump_sqtds(ex->sqtdstart); #endif + nextphys = EHCI_LINK_ADDR(le32toh(epipe->sqh->qh.qh_qtd.qtd_next)); + altnextphys = + EHCI_LINK_ADDR(le32toh(epipe->sqh->qh.qh_qtd.qtd_altnext)); + for (sqtd = ex->sqtdstart; sqtd != ex->sqtdend->nextqtd; + sqtd = sqtd->nextqtd) { + if (sqtd->physaddr == nextphys) + epipe->sqh->qh.qh_qtd.qtd_next = + htole32(ex->sqtdend->nextqtd->physaddr); + if (sqtd->physaddr == altnextphys) + epipe->sqh->qh.qh_qtd.qtd_altnext = + htole32(ex->sqtdend->nextqtd->physaddr); + } + /* The transfer is done, compute actual length and status. */ lsqtd = ex->sqtdend; actlen = 0; @@ -811,8 +827,7 @@ status = nstatus; /* halt is ok if descriptor is last, and complete */ - if (sqtd->qtd.qtd_next == EHCI_NULL && - EHCI_QTD_GET_BYTES(status) == 0) + if (sqtd == lsqtd && EHCI_QTD_GET_BYTES(status) == 0) status &= ~EHCI_QTD_HALTED; if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP) actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); @@ -840,6 +855,13 @@ ehci_dump_sqh(epipe->sqh); ehci_dump_sqtds(ex->sqtdstart); } + if (actlen < xfer->length) { + int i; + for (i = 0; i < 100 && i < xfer->length; i++) + printf(" %02x", + ((unsigned char *)xfer->buffer)[i]); + printf("\n"); + } #endif if ((status & EHCI_QTD_BABBLE) == 0 && cerr > 0) xfer->status = USBD_STALLED; @@ -1283,7 +1305,7 @@ } void -ehci_dump_qtd(ehci_qtd_t *qtd) +ehci_dump_qtd(volatile ehci_qtd_t *qtd) { u_int32_t s; char sbuf[128]; @@ -1307,10 +1329,11 @@ void ehci_dump_sqh(ehci_soft_qh_t *sqh) { - ehci_qh_t *qh = &sqh->qh; + volatile ehci_qh_t *qh = &sqh->qh; u_int32_t endp, endphub; printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr); + printf(" sqtd=%p inactivesqtd=%p\n", sqh->sqtd, sqh->inactivesqtd); printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); endp = le32toh(qh->qh_endp); printf(" endp=0x%08x\n", endp); @@ -1423,9 +1446,7 @@ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0) ); sqh->qh.qh_curqtd = EHCI_NULL; - /* Fill the overlay qTD */ - sqh->qh.qh_qtd.qtd_next = EHCI_NULL; - sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + /* The overlay qTD was already set up by ehci_alloc_sqh(). */ sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle)); @@ -1514,27 +1535,39 @@ ehci_sync_hc(sc); } +/* Restart a QH following the addition of a qTD. */ void -ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) +ehci_activate_qh(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) { - int i; - u_int32_t status; + KASSERT((sqtd->qtd.qtd_status & htole32(EHCI_QTD_ACTIVE)) == 0, + ("ehci_activate_qh: already active")); - /* 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; - /* Set !HALTED && !ACTIVE to start execution, preserve some fields */ - sqh->qh.qh_qtd.qtd_status = status; + /* + * If this is the first qTD then first point it at the new qTD + * and mark the overlay as not halted and not active. This causes + * the host controller to retrieve the real qTD on each pass (rather + * than just examining the overlay), so it will notice when we + * activate the qTD. + */ + /* XXX */ + if (sqtd == sqh->sqtd) { +#if 0 + if (EHCI_LINK_ADDR(le32toh(sqh->qh.qh_qtd.qtd_next)) != + sqtd->physaddr) { + ehci_dump_sqh(sqh); + ehci_dump_sqtds(sqh->sqtd); + KASSERT(le32toh(sqh->qh.qh_qtd.qtd_status) & + EHCI_QTD_HALTED, ("ehci_activate_qh: not halted")); + } + sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; +#endif + sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_PINGSTATE | + EHCI_QTD_TOGGLE_MASK); + } + + /* Now activate the qTD. */ + sqtd->qtd.qtd_status |= htole32(EHCI_QTD_ACTIVE); } /* @@ -1712,7 +1745,7 @@ index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { @@ -2184,6 +2217,7 @@ ehci_alloc_sqh(ehci_softc_t *sc) { ehci_soft_qh_t *sqh; + ehci_soft_qtd_t *sqtd; usbd_status err; int i, offs; usb_dma_t dma; @@ -2206,17 +2240,39 @@ sc->sc_freeqhs = sqh; } } + /* Allocate the initial inactive sqtd. */ + sqtd = ehci_alloc_sqtd(sc); + if (sqtd == NULL) + return (NULL); + sqtd->qtd.qtd_status = htole32(0); + sqtd->qtd.qtd_next = EHCI_NULL; + sqtd->qtd.qtd_altnext = EHCI_NULL; + sqh = sc->sc_freeqhs; sc->sc_freeqhs = sqh->next; - memset(&sqh->qh, 0, sizeof(ehci_qh_t)); + + /* The overlay QTD should begin zeroed. */ + sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + sqh->qh.qh_qtd.qtd_status = 0; + for (i = 0; i < EHCI_QTD_NBUFFERS; i++) { + sqh->qh.qh_qtd.qtd_buffer[i] = 0; + sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0; + } sqh->next = NULL; sqh->prev = NULL; + sqh->sqtd = sqtd; + sqh->inactivesqtd = sqtd; + sqh->bugsavedsqtd = NULL; return (sqh); } void ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) { + ehci_free_sqtd(sc, sqh->inactivesqtd); + if (sqh->bugsavedsqtd != NULL) + ehci_free_sqtd(sc, sqh->bugsavedsqtd); sqh->next = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } @@ -2254,7 +2310,13 @@ s = splusb(); sqtd = sc->sc_freeqtds; sc->sc_freeqtds = sqtd->nextqtd; - memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); + sqtd->qtd.qtd_next = EHCI_NULL; + sqtd->qtd.qtd_altnext = EHCI_NULL; + sqtd->qtd.qtd_status = 0; + for (i = 0; i < EHCI_QTD_NBUFFERS; i++) { + sqtd->qtd.qtd_buffer[i] = 0; + sqtd->qtd.qtd_buffer_hi[i] = 0; + } sqtd->nextqtd = NULL; sqtd->xfer = NULL; splx(s); @@ -2275,15 +2337,15 @@ usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, - int alen, int rd, usbd_xfer_handle xfer, - ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) + int alen, int rd, usbd_xfer_handle xfer, ehci_soft_qtd_t *start, + ehci_soft_qtd_t *newinactive, ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) { ehci_soft_qtd_t *next, *cur; - ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; + ehci_physaddr_t dataphys, nextphys; u_int32_t qtdstatus; - int len, curlen, mps, offset; - int i, iscontrol; - usb_dma_t *dma = &xfer->dmabuf; + int adj, len, curlen, mps, offset, pagelen, seg, segoff; + int i, iscontrol, forceshort; + struct usb_dma_mapping *dma = &xfer->dmamap; DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); @@ -2291,8 +2353,6 @@ len = alen; iscontrol = (epipe->pipe.endpoint->edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL; - dataphys = DMAADDR(dma, 0); - dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1)); qtdstatus = EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | EHCI_QTD_SET_CERR(3) @@ -2300,6 +2360,7 @@ /* BYTES set below */ ; mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + forceshort = (xfer->flags & USBD_FORCE_SHORT_XFER) && len % mps == 0; /* * The control transfer data stage always starts with a toggle of 1. * For other transfers we let the hardware track the toggle state. @@ -2307,67 +2368,77 @@ if (iscontrol) qtdstatus |= EHCI_QTD_SET_TOGGLE(1); - cur = ehci_alloc_sqtd(sc); - *sp = cur; - if (cur == NULL) - goto nomem; + if (start != NULL) { + /* + * If we are given a starting qTD, assume it is linked into + * an active QH so be careful not to mark it active. + */ + cur = start; + *sp = cur; + qtdstatus &= ~EHCI_QTD_ACTIVE; + } else { + cur = ehci_alloc_sqtd(sc); + *sp = cur; + if (cur == NULL) + goto nomem; + } + seg = 0; + segoff = 0; for (;;) { - dataphyspage = EHCI_PAGE(dataphys); + curlen = 0; + /* The EHCI hardware can handle at most 5 pages. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (dataphyslastpage - dataphyspage < - EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) { - /* we can handle it in this QTD */ - curlen = len; - } -#elif defined(__FreeBSD__) - /* XXX This is pretty broken: Because we do not allocate - * a contiguous buffer (contiguous in physical pages) we - * can only transfer one page in one go. - * So check whether the start and end of the buffer are on - * the same page. - */ - if (dataphyspage == dataphyslastpage) { - curlen = len; + for (i = 0; i < EHCI_QTD_NBUFFERS && curlen < len; i++) { + KASSERT(seg < dma->nsegs, + ("ehci_alloc_sqtd_chain: overrun")); + dataphys = dma->segs[seg].ds_addr + segoff; + pagelen = dma->segs[seg].ds_len - segoff; + if (pagelen > len - curlen) + pagelen = len - curlen; + if (pagelen > EHCI_PAGE_SIZE - + EHCI_PAGE_OFFSET(dataphys)) + pagelen = EHCI_PAGE_SIZE - + EHCI_PAGE_OFFSET(dataphys); + segoff += pagelen; + if (segoff >= dma->segs[seg].ds_len) { + KASSERT(segoff == dma->segs[seg].ds_len, + ("ehci_alloc_sqtd_chain: overlap")); + seg++; + segoff = 0; + } + + cur->qtd.qtd_buffer[i] = htole32(dataphys); + cur->qtd.qtd_buffer_hi[i] = 0; + curlen += pagelen; + + /* + * Must stop if there is any gap before or after + * the page boundary. + */ + if (EHCI_PAGE_OFFSET(dataphys + pagelen) != 0) + break; + if (seg < dma->nsegs && EHCI_PAGE_OFFSET(segoff + + dma->segs[seg].ds_addr) != 0) + break; } -#endif - else { -#if defined(__NetBSD__) || defined(__OpenBSD__) - /* must use multiple TDs, fill as much as possible. */ - curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE - - EHCI_PAGE_OFFSET(dataphys); -#ifdef DIAGNOSTIC - if (curlen > len) { - printf("ehci_alloc_sqtd_chain: curlen=0x%x " - "len=0x%x offs=0x%x\n", curlen, len, - EHCI_PAGE_OFFSET(dataphys)); - printf("lastpage=0x%x page=0x%x phys=0x%x\n", - dataphyslastpage, dataphyspage, - dataphys); - curlen = len; + /* Adjust down to a multiple of mps if not at the end. */ + if (curlen < len && curlen % mps != 0) { + adj = curlen % mps; + curlen -= adj; + KASSERT(curlen > 0, + ("ehci_alloc_sqtd_chain: need to copy")); + segoff -= adj; + if (segoff < 0) { + seg--; + segoff += dma->segs[seg].ds_len; } -#endif -#elif defined(__FreeBSD__) - /* See comment above (XXX) */ - curlen = EHCI_PAGE_SIZE - - EHCI_PAGE_MASK(dataphys); -#endif - /* the length must be a multiple of the max size */ - curlen -= curlen % mps; - DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " - "curlen=%d\n", curlen)); -#ifdef DIAGNOSTIC - if (curlen == 0) - panic("ehci_alloc_std: curlen == 0"); -#endif - } - DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " - "dataphyslastpage=0x%08x len=%d curlen=%d\n", - dataphys, dataphyslastpage, - len, curlen)); + KASSERT(seg >= 0 && segoff >= 0, + ("ehci_alloc_sqtd_chain: adjust to mps")); + } + len -= curlen; - if (len != 0) { + if (len != 0 || forceshort) { next = ehci_alloc_sqtd(sc); if (next == NULL) goto nomem; @@ -2377,40 +2448,31 @@ nextphys = EHCI_NULL; } - for (i = 0; i * EHCI_PAGE_SIZE < curlen; i++) { - ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE; - if (i != 0) /* use offset only in first buffer */ - a = EHCI_PAGE(a); - cur->qtd.qtd_buffer[i] = htole32(a); - cur->qtd.qtd_buffer_hi[i] = 0; -#ifdef DIAGNOSTIC - if (i >= EHCI_QTD_NBUFFERS) { - printf("ehci_alloc_sqtd_chain: i=%d\n", i); - goto nomem; - } -#endif - } cur->nextqtd = next; - cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys; + cur->qtd.qtd_next = nextphys; + /* Make sure to stop after a short transfer. */ + cur->qtd.qtd_altnext = htole32(newinactive->physaddr); cur->qtd.qtd_status = htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen)); cur->xfer = xfer; cur->len = curlen; - DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", - dataphys, dataphys + curlen)); + DPRINTFN(10,("ehci_alloc_sqtd_chain: curlen=%d\n", curlen)); if (iscontrol) { /* * adjust the toggle based on the number of packets * in this qtd */ - if (((curlen + mps - 1) / mps) & 1) + if ((((curlen + mps - 1) / mps) & 1) || curlen == 0) qtdstatus ^= EHCI_QTD_TOGGLE_MASK; } - if (len == 0) - break; + qtdstatus |= EHCI_QTD_ACTIVE; + if (len == 0) { + if (!forceshort) + break; + forceshort = 0; + } DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); offset += curlen; - dataphys = DMAADDR(dma, offset); cur = next; } cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); @@ -2427,19 +2489,38 @@ return (USBD_NOMEM); } +/* Free the chain starting at sqtd and end at the qTD before sqtdend */ Static void -ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd, - ehci_soft_qtd_t *sqtdend) +ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qh_t *sqh, + ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *sqtdend) { - ehci_soft_qtd_t *p; + ehci_soft_qtd_t *p, **prevp; int i; DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n", sqtd, sqtdend)); + /* First unlink the chain from the QH's software qTD list. */ + prevp = &sqh->sqtd; + for (p = sqh->sqtd; p != NULL; p = p->nextqtd) { + if (p == sqtd) { + *prevp = sqtdend; + break; + } + prevp = &p->nextqtd; + } + KASSERT(p != NULL, ("ehci_free_sqtd_chain: chain not found")); for (i = 0; sqtd != sqtdend; sqtd = p, i++) { p = sqtd->nextqtd; - ehci_free_sqtd(sc, sqtd); + if (EHCI_LINK_ADDR(sqh->qh.qh_curqtd) == + htole32(sqtd->physaddr) && + (sc->sc_flags & EHCI_SCFLG_CURQTDBUG)) { + /* Don't allow reuse of a referenced sqtd. */ + if (sqh->bugsavedsqtd != NULL) + ehci_free_sqtd(sc, sqh->bugsavedsqtd); + sqh->bugsavedsqtd = sqtd; + } else + ehci_free_sqtd(sc, sqtd); } } @@ -2482,10 +2563,10 @@ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus; ehci_soft_qh_t *sqh = epipe->sqh; - ehci_soft_qtd_t *sqtd, *snext, **psqtd; + ehci_soft_qtd_t *sqtd, *snext; ehci_physaddr_t cur, us, next; int s; - int hit; + int hit, i; /* int count = 0; */ ehci_soft_qh_t *psqh; @@ -2578,13 +2659,12 @@ /* We will change them to point here */ snext = exfer->sqtdend->nextqtd; - next = snext ? htole32(snext->physaddr) : EHCI_NULL; + next = htole32(snext->physaddr); /* * Now loop through any qTDs before us and keep track of the pointer * that points to us for the end. */ - psqtd = &sqh->sqtd; sqtd = sqh->sqtd; while (sqtd && sqtd != exfer->sqtdstart) { hit |= (cur == sqtd->physaddr); @@ -2592,11 +2672,8 @@ sqtd->qtd.qtd_next = next; if (EHCI_LINK_ADDR(le32toh(sqtd->qtd.qtd_altnext)) == us) sqtd->qtd.qtd_altnext = next; - psqtd = &sqtd->nextqtd; sqtd = sqtd->nextqtd; } - /* make the software pointer bypass us too */ - *psqtd = exfer->sqtdend->nextqtd; /* * If we already saw the active one then we are pretty much done. @@ -2609,7 +2686,6 @@ * (if there is one). We only need to do this if * it was previously pointing to us. */ - sqtd = exfer->sqtdstart; for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { if (cur == sqtd->physaddr) { hit++; @@ -2623,17 +2699,13 @@ * that we are removing. */ if (hit) { - if (snext) { - ehci_set_qh_qtd(sqh, snext); - } else { - - sqh->qh.qh_curqtd = 0; /* unlink qTDs */ - sqh->qh.qh_qtd.qtd_status &= - htole32(EHCI_QTD_TOGGLE_MASK); - sqh->qh.qh_qtd.qtd_next = - sqh->qh.qh_qtd.qtd_altnext - = EHCI_NULL; - DPRINTFN(1,("ehci_abort_xfer: no hit\n")); + sqh->qh.qh_qtd.qtd_next = htole32(snext->physaddr); + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + sqh->qh.qh_qtd.qtd_status &= + htole32(EHCI_QTD_TOGGLE_MASK); + for (i = 0; i < EHCI_QTD_NBUFFERS; i++) { + sqh->qh.qh_qtd.qtd_buffer[i] = 0; + sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0; } } } @@ -2666,8 +2738,11 @@ DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); #ifdef USB_DEBUG - if (ehcidebug > 1) + if (ehcidebug > 1) { usbd_dump_pipe(exfer->xfer.pipe); + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(epipe->sqh->sqtd); + } #endif if (sc->sc_dying) { @@ -2762,7 +2837,7 @@ { struct ehci_xfer *ex = EXFER(xfer); ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; - /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/ + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer)); @@ -2774,7 +2849,8 @@ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { ehci_del_intr_list(ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart, + ex->sqtdend->nextqtd); } DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen)); @@ -2808,7 +2884,7 @@ usbd_device_handle dev = epipe->pipe.device; ehci_softc_t *sc = (ehci_softc_t *)dev->bus; int addr = dev->address; - ehci_soft_qtd_t *setup, *stat, *next; + ehci_soft_qtd_t *newinactive, *setup, *stat, *next; ehci_soft_qh_t *sqh; int isread; int len; @@ -2824,11 +2900,14 @@ UGETW(req->wIndex), len, addr, epipe->pipe.endpoint->edesc->bEndpointAddress)); - setup = ehci_alloc_sqtd(sc); - if (setup == NULL) { + newinactive = ehci_alloc_sqtd(sc); + if (newinactive == NULL) { err = USBD_NOMEM; goto bad1; } + newinactive->qtd.qtd_status = htole32(0); + newinactive->qtd.qtd_next = EHCI_NULL; + newinactive->qtd.qtd_altnext = EHCI_NULL; stat = ehci_alloc_sqtd(sc); if (stat == NULL) { err = USBD_NOMEM; @@ -2836,6 +2915,8 @@ } sqh = epipe->sqh; + setup = sqh->inactivesqtd; + sqh->inactivesqtd = newinactive; epipe->u.ctl.length = len; /* Update device address and length since they may have changed @@ -2854,22 +2935,21 @@ ehci_soft_qtd_t *end; err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, - &next, &end); + NULL, newinactive, &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); + end->qtd.qtd_next = htole32(stat->physaddr); + end->qtd.qtd_altnext = htole32(newinactive->physaddr); } else { next = stat; } memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); - /* Clear toggle */ + /* Clear toggle, and do not activate until complete */ setup->qtd.qtd_status = htole32( - EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(0) | @@ -2878,7 +2958,8 @@ setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); setup->qtd.qtd_buffer_hi[0] = 0; setup->nextqtd = next; - setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); + setup->qtd.qtd_next = htole32(next->physaddr); + setup->qtd.qtd_altnext = htole32(newinactive->physaddr); setup->xfer = xfer; setup->len = sizeof *req; @@ -2891,8 +2972,9 @@ ); stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */ - stat->nextqtd = NULL; - stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL; + stat->nextqtd = newinactive; + stat->qtd.qtd_next = htole32(newinactive->physaddr); + stat->qtd.qtd_altnext = htole32(newinactive->physaddr); stat->xfer = xfer; stat->len = 0; @@ -2913,9 +2995,9 @@ exfer->isdone = 0; #endif - /* Insert qTD in QH list. */ + /* Activate the new qTD in the QH list. */ s = splusb(); - ehci_set_qh_qtd(sqh, setup); + ehci_activate_qh(sqh, setup); if (xfer->timeout && !sc->sc_bus.use_polling) { usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer); @@ -2939,9 +3021,10 @@ return (USBD_NORMAL_COMPLETION); bad3: + sqh->inactivesqtd = setup; ehci_free_sqtd(sc, stat); bad2: - ehci_free_sqtd(sc, setup); + ehci_free_sqtd(sc, newinactive); bad1: DPRINTFN(-1,("ehci_device_request: no memory\n")); xfer->status = err; @@ -2973,7 +3056,7 @@ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; usbd_device_handle dev = epipe->pipe.device; ehci_softc_t *sc = (ehci_softc_t *)dev->bus; - ehci_soft_qtd_t *data, *dataend; + ehci_soft_qtd_t *data, *dataend, *newinactive; ehci_soft_qh_t *sqh; usbd_status err; int len, isread, endpt; @@ -2997,14 +3080,30 @@ epipe->u.bulk.length = len; - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, - &dataend); + newinactive = ehci_alloc_sqtd(sc); + if (newinactive == NULL) { + DPRINTFN(-1,("ehci_device_bulk_start: no sqtd memory\n")); + err = USBD_NOMEM; + xfer->status = err; + usb_transfer_complete(xfer); + return (err); + } + newinactive->qtd.qtd_status = htole32(0); + newinactive->qtd.qtd_next = EHCI_NULL; + newinactive->qtd.qtd_altnext = EHCI_NULL; + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, + sqh->inactivesqtd, newinactive, &data, &dataend); if (err) { DPRINTFN(-1,("ehci_device_bulk_start: no memory\n")); + ehci_free_sqtd(sc, newinactive); xfer->status = err; usb_transfer_complete(xfer); return (err); } + dataend->nextqtd = newinactive; + dataend->qtd.qtd_next = htole32(newinactive->physaddr); + dataend->qtd.qtd_altnext = htole32(newinactive->physaddr); + sqh->inactivesqtd = newinactive; #ifdef EHCI_DEBUG if (ehcidebug > 5) { @@ -3025,7 +3124,7 @@ #endif s = splusb(); - ehci_set_qh_qtd(sqh, data); + ehci_activate_qh(sqh, data); if (xfer->timeout && !sc->sc_bus.use_polling) { usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer); @@ -3081,14 +3180,15 @@ { struct ehci_xfer *ex = EXFER(xfer); ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; - /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/ + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { ehci_del_intr_list(ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart, + ex->sqtdend->nextqtd); } DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen)); @@ -3142,7 +3242,7 @@ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; usbd_device_handle dev = xfer->pipe->device; ehci_softc_t *sc = (ehci_softc_t *)dev->bus; - ehci_soft_qtd_t *data, *dataend; + ehci_soft_qtd_t *data, *dataend, *newinactive; ehci_soft_qh_t *sqh; usbd_status err; int len, isread, endpt; @@ -3166,14 +3266,29 @@ epipe->u.intr.length = len; - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, - &dataend); + newinactive = ehci_alloc_sqtd(sc); + if (newinactive == NULL) { + DPRINTFN(-1,("ehci_device_intr_start: no sqtd memory\n")); + err = USBD_NOMEM; + xfer->status = err; + usb_transfer_complete(xfer); + return (err); + } + newinactive->qtd.qtd_status = htole32(0); + newinactive->qtd.qtd_next = EHCI_NULL; + newinactive->qtd.qtd_altnext = EHCI_NULL; + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, + sqh->inactivesqtd, newinactive, &data, &dataend); if (err) { DPRINTFN(-1, ("ehci_device_intr_start: no memory\n")); xfer->status = err; usb_transfer_complete(xfer); return (err); } + dataend->nextqtd = newinactive; + dataend->qtd.qtd_next = htole32(newinactive->physaddr); + dataend->qtd.qtd_altnext = htole32(newinactive->physaddr); + sqh->inactivesqtd = newinactive; #ifdef EHCI_DEBUG if (ehcidebug > 5) { @@ -3194,7 +3309,7 @@ #endif s = splusb(); - ehci_set_qh_qtd(sqh, data); + ehci_activate_qh(sqh, data); if (xfer->timeout && !sc->sc_bus.use_polling) { usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer); @@ -3251,7 +3366,7 @@ struct ehci_xfer *ex = EXFER(xfer); ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - ehci_soft_qtd_t *data, *dataend; + ehci_soft_qtd_t *data, *dataend, *newinactive; ehci_soft_qh_t *sqh; usbd_status err; int len, isread, endpt, s; @@ -3259,22 +3374,38 @@ DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); + sqh = epipe->sqh; if (xfer->pipe->repeat) { - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart, + ex->sqtdend->nextqtd); len = epipe->u.intr.length; xfer->length = len; endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; isread = UE_GET_DIR(endpt) == UE_DIR_IN; - sqh = epipe->sqh; + newinactive = ehci_alloc_sqtd(sc); + if (newinactive == NULL) { + DPRINTFN(-1, + ("ehci_device_intr_done: no sqtd memory\n")); + err = USBD_NOMEM; + xfer->status = err; + return; + } + newinactive->qtd.qtd_status = htole32(0); + newinactive->qtd.qtd_next = EHCI_NULL; + newinactive->qtd.qtd_altnext = EHCI_NULL; err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, - &data, &dataend); + sqh->inactivesqtd, newinactive, &data, &dataend); if (err) { DPRINTFN(-1, ("ehci_device_intr_done: no memory\n")); xfer->status = err; return; } + dataend->nextqtd = newinactive; + dataend->qtd.qtd_next = htole32(newinactive->physaddr); + dataend->qtd.qtd_altnext = htole32(newinactive->physaddr); + sqh->inactivesqtd = newinactive; /* Set up interrupt info. */ exfer->sqtdstart = data; @@ -3288,7 +3419,7 @@ #endif s = splusb(); - ehci_set_qh_qtd(sqh, data); + ehci_activate_qh(sqh, data); if (xfer->timeout && !sc->sc_bus.use_polling) { usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer); @@ -3298,7 +3429,8 @@ xfer->status = USBD_IN_PROGRESS; } else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { ehci_del_intr_list(ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart, + ex->sqtdend->nextqtd); } #undef exfer } Index: ehci_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci_pci.c,v retrieving revision 1.21 diff -u -r1.21 ehci_pci.c --- ehci_pci.c 16 Jan 2006 19:23:59 -0000 1.21 +++ ehci_pci.c 18 Mar 2006 19:32:00 -0000 @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include #include @@ -380,7 +382,7 @@ switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ATI: case PCI_EHCI_VENDORID_VIA: - sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; + sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG | EHCI_SCFLG_CURQTDBUG; if (bootverbose) device_printf(self, "Dropped interrupts workaround enabled\n"); @@ -420,6 +422,30 @@ } sc->sc_ncomp = ncomp; + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, + USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, + &sc->sc_bus.parent_dmatag); + if (err) { + device_printf(self, "Could not allocate parent DMA tag (%d)\n", + err); + ehci_pci_detach(self); + return ENXIO; + } + + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag); + if (err) { + device_printf(self, "Could not allocate buffer DMA tag (%d)\n", + err); + ehci_pci_detach(self); + return ENXIO; + } + ehci_pci_takecontroller(self); err = ehci_init(sc); if (!err) { @@ -450,6 +476,10 @@ */ if (sc->iot && sc->ioh) bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0); + if (sc->sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.parent_dmatag); + if (sc->sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag); if (sc->irq_res && sc->ih) { int err = bus_teardown_intr(self, sc->irq_res, sc->ih); Index: ehcireg.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehcireg.h,v retrieving revision 1.8 diff -u -r1.8 ehcireg.h --- ehcireg.h 18 Sep 2005 11:45:39 -0000 1.8 +++ ehcireg.h 18 Mar 2006 19:32:00 -0000 @@ -214,9 +214,9 @@ /* Queue Element Transfer Descriptor */ #define EHCI_QTD_NBUFFERS 5 typedef struct { - ehci_link_t qtd_next; - ehci_link_t qtd_altnext; - u_int32_t qtd_status; + volatile ehci_link_t qtd_next; + volatile ehci_link_t qtd_altnext; + volatile 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 @@ -244,15 +244,15 @@ #define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1) #define EHCI_QTD_SET_TOGGLE(x) ((x) << 31) #define EHCI_QTD_TOGGLE_MASK 0x80000000 - ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS]; - ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; + volatile ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS]; + volatile ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS]; } ehci_qtd_t; #define EHCI_QTD_ALIGN 32 /* Queue Head */ typedef struct { - ehci_link_t qh_link; - u_int32_t qh_endp; + volatile ehci_link_t qh_link; + volatile u_int32_t qh_endp; #define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */ #define EHCI_QH_SET_ADDR(x) (x) #define EHCI_QH_ADDRMASK 0x0000007f @@ -276,7 +276,7 @@ #define EHCI_QH_CTL 0x08000000 #define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */ #define EHCI_QH_SET_NRL(x) ((x) << 28) - u_int32_t qh_endphub; + volatile u_int32_t qh_endphub; #define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */ #define EHCI_QH_SET_SMASK(x) ((x) << 0) #define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */ @@ -287,15 +287,15 @@ #define EHCI_QH_SET_PORT(x) ((x) << 23) #define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */ #define EHCI_QH_SET_MULT(x) ((x) << 30) - ehci_link_t qh_curqtd; - ehci_qtd_t qh_qtd; + volatile ehci_link_t qh_curqtd; + volatile ehci_qtd_t qh_qtd; } ehci_qh_t; #define EHCI_QH_ALIGN 32 /* Periodic Frame Span Traversal Node */ typedef struct { - ehci_link_t fstn_link; - ehci_link_t fstn_back; + volatile ehci_link_t fstn_link; + volatile ehci_link_t fstn_back; } ehci_fstn_t; #define EHCI_FSTN_ALIGN 32 Index: ehcivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehcivar.h,v retrieving revision 1.13 diff -u -r1.13 ehcivar.h --- ehcivar.h 16 Jan 2006 19:23:59 -0000 1.13 +++ ehcivar.h 18 Mar 2006 19:32:00 -0000 @@ -38,7 +38,7 @@ */ typedef struct ehci_soft_qtd { - ehci_qtd_t qtd; + volatile ehci_qtd_t qtd; struct ehci_soft_qtd *nextqtd; /* mirrors nextqtd in TD */ ehci_physaddr_t physaddr; usbd_xfer_handle xfer; @@ -49,10 +49,12 @@ #define EHCI_SQTD_CHUNK (EHCI_PAGE_SIZE / EHCI_SQTD_SIZE) typedef struct ehci_soft_qh { - ehci_qh_t qh; + volatile ehci_qh_t qh; struct ehci_soft_qh *next; struct ehci_soft_qh *prev; struct ehci_soft_qtd *sqtd; + struct ehci_soft_qtd *inactivesqtd; + struct ehci_soft_qtd *bugsavedsqtd; /* Saved as bug workaround */ ehci_physaddr_t physaddr; int islot; /* Interrupt list slot. */ } ehci_soft_qh_t; @@ -95,6 +97,7 @@ #define EHCI_SCFLG_DONEINIT 0x0001 /* ehci_init() has been called. */ #define EHCI_SCFLG_LOSTINTRBUG 0x0002 /* workaround for VIA / ATI chipsets */ +#define EHCI_SCFLG_CURQTDBUG 0x0004 /* workaround for VIA chipsets */ typedef struct ehci_softc { struct usbd_bus sc_bus; /* base device */ Index: ohci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohci.c,v retrieving revision 1.159 diff -u -r1.159 ohci.c --- ohci.c 26 Feb 2006 02:57:57 -0000 1.159 +++ ohci.c 23 Apr 2006 23:53:47 -0000 @@ -511,11 +511,11 @@ ohci_soft_td_t *sp, ohci_soft_td_t **ep) { ohci_soft_td_t *next, *cur; - ohci_physaddr_t dataphys; + ohci_physaddr_t dataphys, physend; u_int32_t tdflags; int offset = 0; - int len, curlen; - usb_dma_t *dma = &xfer->dmabuf; + int len, maxp, curlen, curlen2, seg, segoff; + struct usb_dma_mapping *dma = &xfer->dmamap; u_int16_t flags = xfer->flags; DPRINTFN(alen < 4096,("ohci_alloc_std_chain: start len=%d\n", alen)); @@ -523,22 +523,22 @@ len = alen; cur = sp; + maxp = UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize); tdflags = htole32( (rd ? OHCI_TD_IN : OHCI_TD_OUT) | (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) | OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_SET_DI(6)); + seg = 0; + segoff = 0; for (;;) { next = ohci_alloc_std(sc); if (next == NULL) goto nomem; - dataphys = DMAADDR(dma, offset); - /* * The OHCI hardware can handle at most one 4k crossing. - * XXX - currently we only allocate contigous buffers, but - * the OHCI spec says: If during the data transfer the buffer + * The OHCI spec says: If during the data transfer the buffer * address contained in the HC's working copy of * CurrentBufferPointer crosses a 4K boundary, the upper 20 * bits of Buffer End are copied to the working value of @@ -546,34 +546,67 @@ * be the 0th byte in the same 4K page that contains the * last byte of the buffer (the 4K boundary crossing may * occur within a data packet transfer.) - * - * If/when dma has multiple segments, this will need to - * properly handle fragmenting TD's. - * - * Note that if we are gathering data from multiple SMALL - * segments, e.g. mbufs, we need to do special gymnastics, - * e.g. bounce buffering or data aggregation, - * BEFORE WE GET HERE because a bulk USB transfer must - * consist of maximally sized packets right up to the end. - * A shorter than maximal packet means that it is the end - * of the transfer. If the data transfer length is a - * multiple of the packet size, then a 0 byte - * packet will be the signal of the end of transfer. - * Since packets can't cross TDs this means that - * each TD except the last one must cover an exact multiple - * of the maximal packet length. */ - if (OHCI_PAGE_OFFSET(dataphys) + len <= (2 * OHCI_PAGE_SIZE)) { - /* We can handle all that remains in this TD */ + KASSERT(seg < dma->nsegs, ("ohci_alloc_std_chain: overrun")); + dataphys = dma->segs[seg].ds_addr + segoff; + curlen = dma->segs[seg].ds_len - segoff; + if (curlen > len) curlen = len; + physend = dataphys + curlen - 1; + if (OHCI_PAGE(dataphys) != OHCI_PAGE(physend)) { + /* Truncate to two OHCI pages if there are more. */ + if (curlen > 2 * OHCI_PAGE_SIZE - + OHCI_PAGE_OFFSET(dataphys)) + curlen = 2 * OHCI_PAGE_SIZE - + OHCI_PAGE_OFFSET(dataphys); + if (curlen < len) + curlen -= curlen % maxp; + physend = dataphys + curlen - 1; + } else if (OHCI_PAGE_OFFSET(physend + 1) == 0 && curlen < len && + curlen + segoff == dma->segs[seg].ds_len) { + /* We can possibly include another segment. */ + KASSERT(seg + 1 < dma->nsegs, + ("ohci_alloc_std_chain: overrun2")); + seg++; + + /* Determine how much of the second segment to use. */ + curlen2 = dma->segs[seg].ds_len; + if (curlen + curlen2 > len) + curlen2 = len - curlen; + if (OHCI_PAGE(dma->segs[seg].ds_addr) != + OHCI_PAGE(dma->segs[seg].ds_addr + curlen2 - 1)) + curlen2 = OHCI_PAGE_SIZE - + OHCI_PAGE_OFFSET(dma->segs[seg].ds_addr); + if (curlen + curlen2 < len) + curlen2 -= (curlen + curlen2) % maxp; + + if (curlen2 > 0) { + /* We can include a second segment */ + segoff = curlen2; + physend = dma->segs[seg].ds_addr + curlen2 - 1; + curlen += curlen2; + } else { + /* Second segment not usable now. */ + seg--; + segoff += curlen; + } } else { - /* must use multiple TDs, fill as much as possible. */ - curlen = 2 * OHCI_PAGE_SIZE - - OHCI_PAGE_OFFSET(dataphys); - /* the length must be a multiple of the max size */ - curlen -= curlen % - UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize); - KASSERT((curlen != 0), ("ohci_alloc_std: curlen == 0")); + /* Simple case where there is just one OHCI page. */ + segoff += curlen; + } + if (curlen == 0 && len != 0) { + /* + * A maxp length packet would need to be split. + * This shouldn't be possible if PAGE_SIZE >= 4k + * and the buffer is contiguous in virtual memory. + */ + panic("ohci_alloc_std_chain: XXX need to copy"); + } + if (segoff >= dma->segs[seg].ds_len) { + KASSERT(segoff == dma->segs[seg].ds_len, + ("ohci_alloc_std_chain: overlap")); + seg++; + segoff = 0; } DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x " "len=%d curlen=%d\n", @@ -584,7 +617,7 @@ cur->td.td_cbp = htole32(dataphys); cur->nexttd = next; cur->td.td_nexttd = htole32(next->physaddr); - cur->td.td_be = htole32(DMAADDR(dma, offset + curlen - 1)); + cur->td.td_be = htole32(physend); cur->len = curlen; cur->flags = OHCI_ADD_LEN; cur->xfer = xfer; @@ -1010,14 +1043,6 @@ ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) { struct ohci_softc *sc = (struct ohci_softc *)bus; - struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer; - ohci_soft_itd_t *sitd; - - if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) { - for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer; - sitd = sitd->nextitd) - ohci_free_sitd(sc, sitd); - } #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_BUSY) { @@ -1536,6 +1561,11 @@ if (sitd->flags & OHCI_CALL_DONE) break; } + for (sitd = xfer->hcpriv; sitd->xfer == xfer; + sitd = next) { + next = sitd->nextitd; + ohci_free_sitd(sc, sitd); + } s = splusb(); usb_transfer_complete(xfer); @@ -1574,6 +1604,7 @@ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; ohci_soft_ed_t *sed = opipe->sed; ohci_soft_td_t *data, *tail; + ohci_physaddr_t dataphys, physend; DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n", @@ -1595,11 +1626,35 @@ OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= htole32(OHCI_TD_R); - data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0)); + /* + * Assume a short mapping with no complications, which + * should always be true for <= 4k buffers in contiguous + * virtual memory. The data can take the following forms: + * 1 segment in 1 OHCI page + * 1 segment in 2 OHCI pages + * 2 segments in 2 OHCI pages + * (see comment in ohci_alloc_std_chain() for details) + */ + KASSERT(xfer->length > 0 && xfer->length <= OHCI_PAGE_SIZE, + ("ohci_device_intr_done: bad length %u", + (u_int)xfer->length)); + dataphys = xfer->dmamap.segs[0].ds_addr; + physend = dataphys + xfer->length - 1; + if (xfer->dmamap.nsegs == 2) { + KASSERT(OHCI_PAGE_OFFSET(dataphys + + xfer->dmamap.segs[0].ds_len) == 0, + ("ohci_device_intr_done: bad seg 0 termination")); + physend = xfer->dmamap.segs[1].ds_addr + + xfer->length - xfer->dmamap.segs[0].ds_len - 1; + } else { + KASSERT(xfer->dmamap.nsegs == 1, + ("ohci_device_intr_done: bad seg count %d", + (u_int)xfer->dmamap.nsegs)); + } + data->td.td_cbp = htole32(dataphys); data->nexttd = tail; data->td.td_nexttd = htole32(tail->physaddr); - data->td.td_be = htole32(le32toh(data->td.td_cbp) + - xfer->length - 1); + data->td.td_be = htole32(physend); data->len = xfer->length; data->xfer = xfer; data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; @@ -1639,7 +1694,7 @@ pipe = xfer->pipe; - p = KERNADDR(&xfer->dmabuf, 0); + p = xfer->buffer; m = min(sc->sc_noport, xfer->length * 8 - 1); memset(p, 0, xfer->length); for (i = 1; i <= m; i++) { @@ -2520,7 +2575,7 @@ index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { @@ -3104,6 +3159,7 @@ ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; ohci_soft_td_t *data, *tail; + ohci_physaddr_t dataphys, physend; int len; int s; @@ -3132,10 +3188,34 @@ OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY); if (xfer->flags & USBD_SHORT_XFER_OK) data->td.td_flags |= htole32(OHCI_TD_R); - data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0)); + /* + * Assume a short mapping with no complications, which + * should always be true for <= 4k buffers in contiguous + * virtual memory. The data can take the following forms: + * 1 segment in 1 OHCI page + * 1 segment in 2 OHCI pages + * 2 segments in 2 OHCI pages + * (see comment in ohci_alloc_std_chain() for details) + */ + KASSERT(len > 0 && xfer->length <= OHCI_PAGE_SIZE, + ("ohci_device_intr_done: bad length %d", len)); + dataphys = xfer->dmamap.segs[0].ds_addr; + physend = dataphys + len - 1; + if (xfer->dmamap.nsegs == 2) { + KASSERT(OHCI_PAGE_OFFSET(dataphys + + xfer->dmamap.segs[0].ds_len) == 0, + ("ohci_device_intr_done: bad seg 0 termination")); + physend = xfer->dmamap.segs[1].ds_addr + len - + xfer->dmamap.segs[0].ds_len - 1; + } else { + KASSERT(xfer->dmamap.nsegs == 1, + ("ohci_device_intr_done: bad seg count %d", + (u_int)xfer->dmamap.nsegs)); + } + data->td.td_cbp = htole32(dataphys); data->nexttd = tail; data->td.td_nexttd = htole32(tail->physaddr); - data->td.td_be = htole32(le32toh(data->td.td_cbp) + len - 1); + data->td.td_be = htole32(physend); data->len = len; data->xfer = xfer; data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; @@ -3326,10 +3406,10 @@ ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; struct iso *iso = &opipe->u.iso; - struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer; + struct usb_dma_mapping *dma = &xfer->dmamap; ohci_soft_itd_t *sitd, *nsitd; - ohci_physaddr_t buf, offs, noffs, bp0, tdphys; - int i, ncur, nframes; + ohci_physaddr_t dataphys, bp0, physend, prevpage; + int curlen, i, len, ncur, nframes, npages, seg, segoff; int s; DPRINTFN(1,("ohci_device_isoc_enter: used=%d next=%d xfer=%p " @@ -3346,94 +3426,115 @@ iso->next)); } - if (xfer->hcpriv) { - for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer; - sitd = sitd->nextitd) - ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/ - - if (sitd == NULL) { - sitd = ohci_alloc_sitd(sc); - if (sitd == NULL) - panic("cant alloc isoc"); - opipe->tail.itd = sitd; - tdphys = sitd->physaddr; - sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop*/ - sed->ed.ed_headp = - sed->ed.ed_tailp = htole32(tdphys); - sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* Start.*/ - } - } - sitd = opipe->tail.itd; - buf = DMAADDR(&xfer->dmabuf, 0); - bp0 = OHCI_PAGE(buf); - offs = OHCI_PAGE_OFFSET(buf); nframes = xfer->nframes; xfer->hcpriv = sitd; - for (i = ncur = 0; i < nframes; i++, ncur++) { - noffs = offs + xfer->frlengths[i]; - if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */ - OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */ - - /* Allocate next ITD */ - nsitd = ohci_alloc_sitd(sc); - if (nsitd == NULL) { - /* XXX what now? */ - printf("%s: isoc TD alloc failed\n", - USBDEVNAME(sc->sc_bus.bdev)); - return; + seg = 0; + segoff = 0; + i = 0; + while (i < nframes) { + /* + * Fill in as many ITD frames as possible. + */ + KASSERT(seg < dma->nsegs, ("ohci_device_isoc_enter: overrun")); + bp0 = dma->segs[seg].ds_addr + segoff; + sitd->itd.itd_bp0 = htole32(bp0); + prevpage = OHCI_PAGE(bp0); + npages = 1; + + ncur = 0; + while (ncur < OHCI_ITD_NOFFSET && i < nframes) { + /* Find the frame start and end physical addresses. */ + len = xfer->frlengths[i]; + dataphys = dma->segs[seg].ds_addr + segoff; + curlen = dma->segs[seg].ds_len - segoff; + if (len > curlen) { + KASSERT(seg + 1 < dma->nsegs, + ("ohci_device_isoc_enter: overrun2")); + seg++; + segoff = len - curlen; + } else { + segoff += len; } + KASSERT(segoff <= dma->segs[seg].ds_len, + ("ohci_device_isoc_enter: overrun3")); + physend = dma->segs[seg].ds_addr + segoff - 1; + + /* Check if there would be more than 2 pages . */ + if (OHCI_PAGE(dataphys) != prevpage) { + prevpage = OHCI_PAGE(dataphys); + npages++; + } + if (OHCI_PAGE(physend) != prevpage) { + prevpage = OHCI_PAGE(physend); + npages++; + } + if (npages > 2) { + /* We cannot fit this frame now. */ + segoff -= len; + if (segoff < 0) { + seg--; + segoff += dma->segs[seg].ds_len; + } + break; + } + + sitd->itd.itd_be = htole32(physend); + sitd->itd.itd_offset[ncur] = + htole16(OHCI_ITD_MK_OFFS(OHCI_PAGE(dataphys) == + OHCI_PAGE(bp0) ? 0 : 1, dataphys)); + i++; + ncur++; + } + if (segoff >= dma->segs[seg].ds_len) { + KASSERT(segoff == dma->segs[seg].ds_len, + ("ohci_device_isoc_enter: overlap")); + seg++; + segoff = 0; + } + + /* Allocate next ITD */ + nsitd = ohci_alloc_sitd(sc); + if (nsitd == NULL) { + /* XXX what now? */ + printf("%s: isoc TD alloc failed\n", + USBDEVNAME(sc->sc_bus.bdev)); + return; + } - /* Fill current ITD */ + /* Fill out remaining fields of current ITD */ + sitd->nextitd = nsitd; + sitd->itd.itd_nextitd = htole32(nsitd->physaddr); + sitd->xfer = xfer; + if (i < nframes) { sitd->itd.itd_flags = htole32( OHCI_ITD_NOCC | OHCI_ITD_SET_SF(iso->next) | OHCI_ITD_SET_DI(6) | /* delay intr a little */ OHCI_ITD_SET_FC(ncur)); - sitd->itd.itd_bp0 = htole32(bp0); - sitd->nextitd = nsitd; - sitd->itd.itd_nextitd = htole32(nsitd->physaddr); - sitd->itd.itd_be = htole32(bp0 + offs - 1); - sitd->xfer = xfer; sitd->flags = OHCI_ITD_ACTIVE; + } else { + sitd->itd.itd_flags = htole32( + OHCI_ITD_NOCC | + OHCI_ITD_SET_SF(iso->next) | + OHCI_ITD_SET_DI(0) | + OHCI_ITD_SET_FC(ncur)); + sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE; + } + iso->next += ncur; - sitd = nsitd; - iso->next = iso->next + ncur; - bp0 = OHCI_PAGE(buf + offs); - ncur = 0; - } - sitd->itd.itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS(offs)); - offs = noffs; - } - nsitd = ohci_alloc_sitd(sc); - if (nsitd == NULL) { - /* XXX what now? */ - printf("%s: isoc TD alloc failed\n", - USBDEVNAME(sc->sc_bus.bdev)); - return; + sitd = nsitd; } - /* Fixup last used ITD */ - sitd->itd.itd_flags = htole32( - OHCI_ITD_NOCC | - OHCI_ITD_SET_SF(iso->next) | - OHCI_ITD_SET_DI(0) | - OHCI_ITD_SET_FC(ncur)); - sitd->itd.itd_bp0 = htole32(bp0); - sitd->nextitd = nsitd; - sitd->itd.itd_nextitd = htole32(nsitd->physaddr); - sitd->itd.itd_be = htole32(bp0 + offs - 1); - sitd->xfer = xfer; - sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE; - iso->next = iso->next + ncur; iso->inuse += nframes; - xfer->actlen = offs; /* XXX pretend we did it all */ + /* XXX pretend we did it all */ + xfer->actlen = 0; + for (i = 0; i < nframes; i++) + xfer->actlen += xfer->frlengths[i]; xfer->status = USBD_IN_PROGRESS; - oxfer->ohci_xfer_flags |= OHCI_ISOC_DIRTY; - #ifdef USB_DEBUG if (ohcidebug > 5) { DPRINTF(("ohci_device_isoc_enter: frame=%d\n", @@ -3444,9 +3545,9 @@ #endif s = splusb(); - opipe->tail.itd = nsitd; + opipe->tail.itd = sitd; sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); - sed->ed.ed_tailp = htole32(nsitd->physaddr); + sed->ed.ed_tailp = htole32(sitd->physaddr); splx(s); #ifdef USB_DEBUG @@ -3494,7 +3595,7 @@ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; ohci_soft_ed_t *sed; - ohci_soft_itd_t *sitd, *tmp_sitd; + ohci_soft_itd_t *sitd, *sitdnext, *tmp_sitd; int s,undone,num_sitds; s = splusb(); @@ -3557,20 +3658,20 @@ } } while( undone != 0 ); + /* Free the sitds */ + for (sitd = xfer->hcpriv; sitd->xfer == xfer; + sitd = sitdnext) { + sitdnext = sitd->nextitd; + ohci_free_sitd(sc, sitd); + } s = splusb(); /* Run callback. */ usb_transfer_complete(xfer); - if (sitd != NULL) - /* - * Only if there is a `next' sitd in next xfer... - * unlink this xfer's sitds. - */ - sed->ed.ed_headp = htole32(sitd->physaddr); - else - sed->ed.ed_headp = 0; + /* There is always a `next' sitd so link it up. */ + sed->ed.ed_headp = htole32(sitd->physaddr); sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */ Index: ohci_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohci_pci.c,v retrieving revision 1.46 diff -u -r1.46 ohci_pci.c --- ohci_pci.c 11 Feb 2006 03:29:02 -0000 1.46 +++ ohci_pci.c 18 Mar 2006 19:32:00 -0000 @@ -55,6 +55,8 @@ #include #include #include +#include +#include #include #include #include @@ -303,6 +305,30 @@ ohci_pci_detach(self); return ENXIO; } + + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, + USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, + &sc->sc_bus.parent_dmatag); + if (err) { + device_printf(self, "Could not allocate parent DMA tag (%d)\n", + err); + ohci_pci_detach(self); + return ENXIO; + } + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag); + if (err) { + device_printf(self, "Could not allocate transfer tag (%d)\n", + err); + ohci_pci_detach(self); + return ENXIO; + } + err = ohci_init(sc); if (!err) { sc->sc_flags |= OHCI_SCFLG_DONEINIT; @@ -327,6 +353,11 @@ sc->sc_flags &= ~OHCI_SCFLG_DONEINIT; } + if (sc->sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.parent_dmatag); + if (sc->sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag); + if (sc->irq_res && sc->ih) { int err = bus_teardown_intr(self, sc->irq_res, sc->ih); @@ -345,7 +376,8 @@ sc->irq_res = NULL; } if (sc->io_res) { - bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res); + bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, + sc->io_res); sc->io_res = NULL; sc->iot = 0; sc->ioh = 0; Index: ohcireg.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohcireg.h,v retrieving revision 1.22 diff -u -r1.22 ohcireg.h --- ohcireg.h 6 Jan 2005 01:43:28 -0000 1.22 +++ ohcireg.h 18 Mar 2006 19:32:00 -0000 @@ -220,7 +220,8 @@ u_int16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets */ #define itd_pswn itd_offset /* Packet Status Word*/ #define OHCI_ITD_PAGE_SELECT 0x00001000 -#define OHCI_ITD_MK_OFFS(len) (0xe000 | ((len) & 0x1fff)) +#define OHCI_ITD_MK_OFFS(page, off) \ + (0xe000 | ((page) ? OHCI_ITD_PAGE_SELECT : 0) | ((off) & 0xfff)) #define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */ #define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */ } ohci_itd_t; Index: ohcivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.42 diff -u -r1.42 ohcivar.h --- ohcivar.h 11 Feb 2006 03:29:02 -0000 1.42 +++ ohcivar.h 23 Apr 2006 23:54:13 -0000 @@ -157,9 +157,8 @@ struct usb_task abort_task; u_int32_t ohci_xfer_flags; }; -#define OHCI_ISOC_DIRTY 0x01 -#define OHCI_XFER_ABORTING 0x02 /* xfer is aborting. */ -#define OHCI_XFER_ABORTWAIT 0x04 /* abort completion is being awaited. */ +#define OHCI_XFER_ABORTING 0x01 /* xfer is aborting. */ +#define OHCI_XFER_ABORTWAIT 0x02 /* abort completion is being awaited. */ #define OXFER(xfer) ((struct ohci_xfer *)(xfer)) Index: sl811hs.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/sl811hs.c,v retrieving revision 1.2 diff -u -r1.2 sl811hs.c --- sl811hs.c 4 Dec 2005 10:06:04 -0000 1.2 +++ sl811hs.c 18 Mar 2006 19:32:00 -0000 @@ -548,7 +548,7 @@ usb_callout(sc->sc_poll_handle, sc->sc_interval, slhci_poll_hub, xfer); /* USB spec 11.13.3 (p.260) */ - p = KERNADDR(&xfer->dmabuf, 0); + p = xfer->buffer; p[0] = 0; if ((sc->sc_flags & (SLF_INSERT | SLF_RESET))) { p[0] = 2; @@ -767,7 +767,7 @@ index = UGETW(req->wIndex); if (len) - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; #ifdef SLHCI_DEBUG if ((slhci_debug & D_TRACE)) @@ -1197,7 +1197,7 @@ actlen = 0; len = UGETW(req->wLength); if (len) { - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; if (req->bmRequestType & UT_READ) pid = SL11_PID_IN; for (; actlen < len; ) { @@ -1226,7 +1226,7 @@ if((slhci_debug & D_TRACE) && UGETW(req->wLength) > 0){ int i; for(i=0; i < UGETW(req->wLength); i++) - printf("%02x", *(unsigned char*)(KERNADDR(&xfer->dmabuf, i))); + printf("%02x", ((unsigned char *)xfer->buffer)[i]); printf(" "); } #endif @@ -1318,7 +1318,7 @@ /* interrupt transfer */ pid = (UE_GET_DIR(pipe->endpoint->edesc->bEndpointAddress) == UE_DIR_IN) ? SL11_PID_IN : SL11_PID_OUT; - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; r = slhci_transaction(sc, pipe, pid, xfer->length, buf, 0/*toggle*/); if (r < 0) { Index: uhci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhci.c,v retrieving revision 1.164 diff -u -r1.164 uhci.c --- uhci.c 26 Feb 2006 02:57:57 -0000 1.164 +++ uhci.c 21 Mar 2006 02:30:26 -0000 @@ -190,6 +190,10 @@ Static void uhci_free_std(uhci_softc_t *, uhci_soft_td_t *); Static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *); Static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *); +Static usbd_status uhci_aux_dma_alloc(uhci_softc_t *, uhci_soft_td_t *, + void *data, int len); +Static uhci_physaddr_t uhci_aux_dma_prepare(uhci_soft_td_t *, int); +Static void uhci_aux_dma_complete(uhci_soft_td_t *, int); #if 0 Static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *, uhci_intr_info_t *); @@ -199,7 +203,8 @@ Static void uhci_free_std_chain(uhci_softc_t *, uhci_soft_td_t *, uhci_soft_td_t *); Static usbd_status uhci_alloc_std_chain(struct uhci_pipe *, - uhci_softc_t *, int, int, u_int16_t, usb_dma_t *, + uhci_softc_t *, int, int, u_int16_t, + usbd_xfer_handle xfer, uhci_soft_td_t **, uhci_soft_td_t **); Static void uhci_poll_hub(void *); Static void uhci_waitintr(uhci_softc_t *, usbd_xfer_handle); @@ -207,6 +212,7 @@ Static void uhci_idone(uhci_intr_info_t *); Static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status); +Static void uhci_transfer_complete(usbd_xfer_handle xfer); Static void uhci_timeout(void *); Static void uhci_timeout_task(void *); @@ -493,7 +499,7 @@ clsqh = uhci_alloc_sqh(sc); if (clsqh == NULL) return (USBD_NOMEM); - clsqh->hlink = bsqh; + clsqh->hlink = chsqh; clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH); clsqh->elink = NULL; clsqh->qh.qh_elink = htole32(UHCI_PTR_T); @@ -969,7 +975,8 @@ { usbd_xfer_handle xfer = addr; usbd_pipe_handle pipe = xfer->pipe; - uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; + usbd_device_handle dev = pipe->device; + uhci_softc_t *sc = (uhci_softc_t *)dev->bus; int s; u_char *p; @@ -977,7 +984,7 @@ usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); - p = KERNADDR(&xfer->dmabuf, 0); + p = xfer->buffer; p[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC)) p[0] |= 1<<1; @@ -990,9 +997,9 @@ xfer->actlen = 1; xfer->status = USBD_NORMAL_COMPLETION; s = splusb(); - xfer->device->bus->intr_context++; - usb_transfer_complete(xfer); - xfer->device->bus->intr_context--; + dev->bus->intr_context++; + uhci_transfer_complete(xfer); + dev->bus->intr_context--; splx(s); } @@ -1498,7 +1505,7 @@ } end: - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii)); } @@ -1659,6 +1666,9 @@ std = KERNADDR(&dma, offs); std->physaddr = DMAADDR(&dma, offs); std->link.std = sc->sc_freetds; + std->aux_dma.block = NULL; + std->aux_data = NULL; + std->aux_len = 0; sc->sc_freetds = std; } } @@ -1679,6 +1689,12 @@ } std->td.td_token = htole32(TD_IS_FREE); #endif + if (std->aux_dma.block != NULL) { + usb_freemem(&sc->sc_bus, &std->aux_dma); + std->aux_dma.block = NULL; + std->aux_data = NULL; + std->aux_len = 0; + } std->link.std = sc->sc_freetds; sc->sc_freetds = std; } @@ -1732,12 +1748,12 @@ usbd_status uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len, - int rd, u_int16_t flags, usb_dma_t *dma, + int rd, u_int16_t flags, usbd_xfer_handle xfer, uhci_soft_td_t **sp, uhci_soft_td_t **ep) { - uhci_soft_td_t *p, *lastp; - uhci_physaddr_t lastlink; - int i, ntd, l, tog, maxp; + struct usb_dma_mapping *dma = &xfer->dmamap; + uhci_soft_td_t *p, *prevp, *startp; + int err, i, ntd, l, tog, maxp, seg, segoff; u_int32_t status; int addr = upipe->pipe.device->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; @@ -1760,29 +1776,31 @@ return (USBD_NORMAL_COMPLETION); } tog = upipe->nexttoggle; - if (ntd % 2 == 0) - tog ^= 1; - upipe->nexttoggle = tog ^ 1; - lastp = NULL; - lastlink = UHCI_PTR_T; - ntd--; + prevp = NULL; + startp = NULL; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE); 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--) { + seg = 0; + segoff = 0; + for (i = 0; i < ntd; i++) { p = uhci_alloc_std(sc); if (p == NULL) { - uhci_free_std_chain(sc, lastp, NULL); + uhci_free_std_chain(sc, startp, NULL); return (USBD_NOMEM); } - p->link.std = lastp; - p->td.td_link = htole32(lastlink | UHCI_PTR_VF | UHCI_PTR_TD); - lastp = p; - lastlink = p->physaddr; + p->link.std = NULL; + if (prevp != NULL) { + prevp->link.std = p; + prevp->td.td_link = htole32(p->physaddr | UHCI_PTR_VF | + UHCI_PTR_TD); + } else { + startp = p; + } p->td.td_status = htole32(status); - if (i == ntd) { + if (i == ntd - 1) { /* last TD */ l = len % maxp; if (l == 0 && !(flags & USBD_FORCE_SHORT_XFER)) @@ -1793,15 +1811,100 @@ 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)); + + KASSERT(seg < dma->nsegs, + ("uhci_alloc_std_chain: too few segments")); + if (l > dma->segs[seg].ds_len - segoff) { + /* UHCI can't handle non-contiguous data. */ + err = uhci_aux_dma_alloc(sc, p, (char *)xfer->buffer + + i * maxp, l); + if (err) { + uhci_free_std_chain(sc, startp, NULL); + return (err); + } + p->td.td_buffer = htole32(uhci_aux_dma_prepare(p, rd)); + l -= dma->segs[seg].ds_len - segoff; + seg++; + KASSERT(seg < dma->nsegs, + ("uhci_alloc_std_chain: too few segments 2")); + segoff = 0; + } else { + p->td.td_buffer = htole32(dma->segs[seg].ds_addr + + segoff); + } + segoff += l; + if (segoff >= dma->segs[seg].ds_len) { + KASSERT(segoff == dma->segs[seg].ds_len, + ("uhci_alloc_std_chain: overlap")); + if (i * maxp + l != len) { + seg++; + segoff = 0; + } + } + prevp = p; tog ^= 1; } - *sp = lastp; + prevp->td.td_link = htole32(UHCI_PTR_T | UHCI_PTR_VF | UHCI_PTR_TD); + upipe->nexttoggle = tog; + *sp = startp; DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n", upipe->nexttoggle)); return (USBD_NORMAL_COMPLETION); } +/* + * Allocate a physically contiguous buffer to handle cases where UHCI + * cannot handle a packet because it is not physically contiguous. + * If the usb_dma_t was already allocated this just ensures it is + * large enough for the specified size. + */ +Static usbd_status +uhci_aux_dma_alloc(uhci_softc_t *sc, uhci_soft_td_t *std, void *data, int len) +{ + int err, align; + + if (std->aux_dma.block == NULL || std->aux_dma.block->size < len) { + /* Align to avoid crossing a page boundary. */ + if (powerof2(len)) + align = len; + else + align = 1 << fls(len); + + if (std->aux_dma.block != NULL) + usb_freemem(&sc->sc_bus, &std->aux_dma); + std->aux_dma.block = NULL; + err = usb_allocmem(&sc->sc_bus, len, align, &std->aux_dma); + if (err) + return (err); + } + std->aux_data = data; + std->aux_len = len; + + return (USBD_NORMAL_COMPLETION); +} + +Static uhci_physaddr_t +uhci_aux_dma_prepare(uhci_soft_td_t *std, int isread) +{ + if (!isread) { + bcopy(std->aux_data, KERNADDR(&std->aux_dma, 0), std->aux_len); + bus_dmamap_sync(std->aux_dma.block->tag, + std->aux_dma.block->map, BUS_DMASYNC_PREWRITE); + } + + return (DMAADDR(&std->aux_dma, 0)); +} + +Static void +uhci_aux_dma_complete(uhci_soft_td_t *std, int isread) +{ + if (isread) { + bus_dmamap_sync(std->aux_dma.block->tag, + std->aux_dma.block->map, BUS_DMASYNC_POSTREAD); + bcopy(KERNADDR(&std->aux_dma, 0), std->aux_data, std->aux_len); + } +} + void uhci_device_clear_toggle(usbd_pipe_handle pipe) { @@ -1863,8 +1966,8 @@ upipe->u.bulk.isread = isread; upipe->u.bulk.length = len; - err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, - &xfer->dmabuf, &data, &dataend); + err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, xfer, + &data, &dataend); if (err) return (err); dataend->td.td_status |= htole32(UHCI_TD_IOC); @@ -1950,7 +2053,7 @@ xfer->status = status; /* make software ignore it */ usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer); usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task); - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); return; } @@ -2020,10 +2123,50 @@ uxfer->uhci_xfer_flags &= ~UHCI_XFER_ABORTWAIT; wakeup(&uxfer->uhci_xfer_flags); } - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); } +/* + * Perform any UHCI-specific transfer completion operations, then + * call usb_transfer_complete(). + */ +Static void +uhci_transfer_complete(usbd_xfer_handle xfer) +{ + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; + struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; + uhci_soft_td_t *p; + int i, isread, n; + + /* XXX, must be an easier way to detect reads... */ + isread = ((xfer->rqflags & URQ_REQUEST) && + (xfer->request.bmRequestType & UT_READ)) || + (xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN); + + /* Copy back from any auxillary buffers after a read operation. */ + if (xfer->nframes == 0) { + for (p = ii->stdstart; p != NULL; p = p->link.std) { + if (p->aux_data != NULL) + uhci_aux_dma_complete(p, isread); + } + } else { + if (xfer->nframes != 0) { + /* Isoc transfer, do things differently. */ + n = UXFER(xfer)->curframe; + for (i = 0; i < xfer->nframes; i++) { + p = upipe->u.iso.stds[n]; + if (p->aux_data != NULL) + uhci_aux_dma_complete(p, isread); + if (++n >= UHCI_VFRAMELIST_COUNT) + n = 0; + } + } + } + + usb_transfer_complete(xfer); +} + /* Close a device bulk pipe. */ void uhci_device_bulk_close(usbd_pipe_handle pipe) @@ -2123,9 +2266,8 @@ upipe->u.intr.isread = isread; - err = uhci_alloc_std_chain(upipe, sc, xfer->length, isread, - xfer->flags, &xfer->dmabuf, &data, - &dataend); + err = uhci_alloc_std_chain(upipe, sc, xfer->length, isread, xfer->flags, + xfer, &data, &dataend); if (err) return (err); dataend->td.td_status |= htole32(UHCI_TD_IOC); @@ -2263,7 +2405,7 @@ if (len != 0) { upipe->nexttoggle = 1; err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, - &xfer->dmabuf, &data, &dataend); + xfer, &data, &dataend); if (err) return (err); next = data; @@ -2390,8 +2532,9 @@ uhci_softc_t *sc = (uhci_softc_t *)dev->bus; struct iso *iso = &upipe->u.iso; uhci_soft_td_t *std; - u_int32_t buf, len, status; - int s, i, next, nframes; + void *dataptr; + u_int32_t len, status; + int err, s, i, isread, next, nframes, seg, segoff; DPRINTFN(5,("uhci_device_isoc_enter: used=%d next=%d xfer=%p " "nframes=%d\n", @@ -2421,7 +2564,10 @@ xfer->status = USBD_IN_PROGRESS; UXFER(xfer)->curframe = next; - buf = DMAADDR(&xfer->dmabuf, 0); + seg = 0; + segoff = 0; + dataptr = xfer->allocbuf; /* Normal buffers not possible for isoc? */ + isread = xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS); @@ -2432,7 +2578,35 @@ if (++next >= UHCI_VFRAMELIST_COUNT) next = 0; len = xfer->frlengths[i]; - std->td.td_buffer = htole32(buf); + KASSERT(seg < xfer->dmamap.nsegs, + ("uhci_device_isoc_enter: too few segments")); + if (len + segoff > xfer->dmamap.segs[seg].ds_len) { + /* UHCI can't handle non-contiguous data. */ + err = uhci_aux_dma_alloc(sc, std, dataptr, len); + /* XXX */ + if (err) + printf("uhci_device_isoc_enter: aux alloc\n"); + std->td.td_buffer = htole32(uhci_aux_dma_prepare(std, + isread)); + segoff += len; + while (segoff >= xfer->dmamap.segs[seg].ds_len) { + KASSERT(seg < xfer->dmamap.nsegs - 1 || + segoff == xfer->dmamap.segs[seg].ds_len, + ("uhci_device_isoc_enter: overlap2")); + segoff -= xfer->dmamap.segs[seg].ds_len; + seg++; + } + } else { + std->td.td_buffer = + htole32(xfer->dmamap.segs[seg].ds_addr + segoff); + segoff += len; + if (segoff >= xfer->dmamap.segs[seg].ds_len) { + KASSERT(segoff == xfer->dmamap.segs[seg].ds_len, + ("uhci_device_isoc_enter: overlap")); + segoff = 0; + seg++; + } + } if (i == nframes - 1) status |= UHCI_TD_IOC; std->td.td_status = htole32(status); @@ -2444,7 +2618,7 @@ uhci_dump_td(std); } #endif - buf += len; + dataptr = (char *)dataptr + len; } iso->next = next; iso->inuse += xfer->nframes; @@ -2543,7 +2717,7 @@ UXFER(xfer)->iinfo.isdone = 1; #endif /* Run callback and remove from interrupt list. */ - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); } @@ -2691,6 +2865,8 @@ return; } #endif + ii->stdstart = NULL; + ii->stdend = NULL; } void @@ -2720,8 +2896,8 @@ /* This alloc cannot fail since we freed the chain above. */ uhci_alloc_std_chain(upipe, sc, xfer->length, - upipe->u.intr.isread, xfer->flags, - &xfer->dmabuf, &data, &dataend); + upipe->u.intr.isread, xfer->flags, xfer, + &data, &dataend); dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef USB_DEBUG @@ -2749,8 +2925,11 @@ /* The ii is already on the examined list, just leave it. */ } else { DPRINTFN(5,("uhci_device_intr_done: removing\n")); - if (uhci_active_intr_info(ii)) + if (uhci_active_intr_info(ii)) { uhci_del_intr_info(ii); + ii->stdstart = NULL; + ii->stdend = NULL; + } } } @@ -2779,6 +2958,8 @@ if (upipe->u.ctl.length != 0) uhci_free_std_chain(sc, ii->stdstart->link.std, ii->stdend); + ii->stdstart = NULL; + ii->stdend = NULL; DPRINTFN(5, ("uhci_device_ctrl_done: length=%d\n", xfer->actlen)); } @@ -2802,6 +2983,8 @@ uhci_remove_bulk(sc, upipe->u.bulk.sqh); uhci_free_std_chain(sc, ii->stdstart, NULL); + ii->stdstart = NULL; + ii->stdend = NULL; DPRINTFN(5, ("uhci_device_bulk_done: length=%d\n", xfer->actlen)); } @@ -3202,7 +3385,7 @@ index = UGETW(req->wIndex); if (len != 0) - buf = KERNADDR(&xfer->dmabuf, 0); + buf = xfer->buffer; #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { @@ -3492,7 +3675,7 @@ ret: xfer->status = err; s = splusb(); - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); return (USBD_IN_PROGRESS); } @@ -3528,7 +3711,7 @@ #ifdef DIAGNOSTIC UXFER(xfer)->iinfo.isdone = 1; #endif - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); } usbd_status Index: uhci_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhci_pci.c,v retrieving revision 1.57 diff -u -r1.57 uhci_pci.c --- uhci_pci.c 1 Mar 2005 07:50:11 -0000 1.57 +++ uhci_pci.c 18 Mar 2006 19:32:00 -0000 @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #include #if defined(__FreeBSD__) @@ -348,6 +350,29 @@ #endif pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, + USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, + &sc->sc_bus.parent_dmatag); + if (err) { + device_printf(self, "Could not allocate parent DMA tag (%d)\n", + err); + uhci_pci_detach(self); + return ENXIO; + } + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag); + if (err) { + device_printf(self, "Could not allocate transfer tag (%d)\n", + err); + uhci_pci_detach(self); + return ENXIO; + } + err = uhci_init(sc); if (!err) { sc->sc_flags |= UHCI_SCFLG_DONEINIT; @@ -372,6 +397,10 @@ sc->sc_flags &= ~UHCI_SCFLG_DONEINIT; } + if (sc->sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.parent_dmatag); + if (sc->sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag); if (sc->irq_res && sc->ih) { int err = bus_teardown_intr(self, sc->irq_res, sc->ih); Index: uhcireg.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhcireg.h,v retrieving revision 1.22 diff -u -r1.22 uhcireg.h --- uhcireg.h 6 Jan 2005 01:43:28 -0000 1.22 +++ uhcireg.h 18 Mar 2006 19:32:00 -0000 @@ -140,8 +140,8 @@ */ typedef struct { - uhci_physaddr_t td_link; - u_int32_t td_status; + volatile uhci_physaddr_t td_link; + volatile u_int32_t td_status; #define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff) #define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff) #define UHCI_TD_BITSTUFF 0x00020000 @@ -157,7 +157,7 @@ #define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3) #define UHCI_TD_SET_ERRCNT(n) ((n) << 27) #define UHCI_TD_SPD 0x20000000 - u_int32_t td_token; + volatile u_int32_t td_token; #define UHCI_TD_PID_IN 0x00000069 #define UHCI_TD_PID_OUT 0x000000e1 #define UHCI_TD_PID_SETUP 0x0000002d @@ -171,7 +171,7 @@ #define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21) #define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff) #define UHCI_TD_MAXLEN_MASK 0xffe00000 - u_int32_t td_buffer; + volatile u_int32_t td_buffer; } uhci_td_t; #define UHCI_TD_ERROR (UHCI_TD_BITSTUFF|UHCI_TD_CRCTO|UHCI_TD_BABBLE|UHCI_TD_DBUFFER|UHCI_TD_STALLED) @@ -186,8 +186,8 @@ UHCI_TD_SET_DT(dt)) typedef struct { - uhci_physaddr_t qh_hlink; - uhci_physaddr_t qh_elink; + volatile uhci_physaddr_t qh_hlink; + volatile uhci_physaddr_t qh_elink; } uhci_qh_t; #endif /* _DEV_PCI_UHCIREG_H_ */ Index: uhcivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhcivar.h,v retrieving revision 1.40 diff -u -r1.40 uhcivar.h --- uhcivar.h 19 Mar 2005 19:08:46 -0000 1.40 +++ uhcivar.h 18 Mar 2006 20:40:56 -0000 @@ -100,6 +100,9 @@ uhci_td_t td; /* The real TD, must be first */ uhci_soft_td_qh_t link; /* soft version of the td_link field */ uhci_physaddr_t physaddr; /* TD's physical address. */ + usb_dma_t aux_dma; /* Auxillary storage if needed. */ + void *aux_data; /* Original aux data virtual address. */ + int aux_len; /* Auxillary storage size. */ }; /* * Make the size such that it is a multiple of UHCI_TD_ALIGN. This way Index: usb_mem.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb_mem.c,v retrieving revision 1.8 diff -u -r1.8 usb_mem.c --- usb_mem.c 10 Jan 2006 22:55:35 -0000 1.8 +++ usb_mem.c 18 Mar 2006 19:32:00 -0000 @@ -230,7 +230,7 @@ usbd_status usb_allocmem(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p) { - bus_dma_tag_t tag = bus->dmatag; + bus_dma_tag_t tag = bus->parent_dmatag; usbd_status err; struct usb_frag_dma *f; usb_dma_block_t *b; Index: usb_port.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb_port.h,v retrieving revision 1.78 diff -u -r1.78 usb_port.h --- usb_port.h 1 Mar 2006 06:31:24 -0000 1.78 +++ usb_port.h 18 Mar 2006 19:32:00 -0000 @@ -47,6 +47,8 @@ #ifndef _USB_PORT_H #define _USB_PORT_H +#define USB_DEBUG + /* * Macro's to cope with the differences between operating systems. */ Index: usbdi.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdi.c,v retrieving revision 1.92 diff -u -r1.92 usbdi.c --- usbdi.c 8 Dec 2005 03:08:17 -0000 1.92 +++ usbdi.c 19 Mar 2006 18:36:18 -0000 @@ -87,6 +87,10 @@ Static usbd_status usbd_open_pipe_ival (usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int); Static int usbd_xfer_isread(usbd_xfer_handle xfer); +Static void usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg, + int error); +Static void usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, + int error); Static int usbd_nbuses = 0; @@ -283,7 +287,7 @@ usbd_transfer(usbd_xfer_handle xfer) { usbd_pipe_handle pipe = xfer->pipe; - usb_dma_t *dmap = &xfer->dmabuf; + struct usb_dma_mapping *dmap = &xfer->dmamap; usbd_status err; u_int size; int s; @@ -302,43 +306,36 @@ size = xfer->length; /* If there is no buffer, allocate one. */ if (!(xfer->rqflags & URQ_DEV_DMABUF) && size != 0) { - struct usbd_bus *bus = pipe->device->bus; + bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; #ifdef DIAGNOSTIC if (xfer->rqflags & URQ_AUTO_DMABUF) printf("usbd_transfer: has old buffer!\n"); #endif - err = bus->methods->allocm(bus, dmap, size); + err = bus_dmamap_create(tag, 0, &dmap->map); if (err) - return (err); - xfer->rqflags |= URQ_AUTO_DMABUF; - } - - /* Copy data if going out. */ - if (!(xfer->flags & USBD_NO_COPY) && size != 0 && - !usbd_xfer_isread(xfer)) - memcpy(KERNADDR(dmap, 0), xfer->buffer, size); - - err = pipe->methods->transfer(xfer); - - if (err != USBD_IN_PROGRESS && err) { - /* The transfer has not been queued, so free buffer. */ - if (xfer->rqflags & URQ_AUTO_DMABUF) { - struct usbd_bus *bus = pipe->device->bus; + return (USBD_NOMEM); - bus->methods->freem(bus, &xfer->dmabuf); + xfer->rqflags |= URQ_AUTO_DMABUF; + err = bus_dmamap_load(tag, dmap->map, xfer->buffer, size, + usbd_start_transfer, xfer, 0); + if (err != 0 && err != EINPROGRESS) { xfer->rqflags &= ~URQ_AUTO_DMABUF; + bus_dmamap_destroy(tag, dmap->map); + return (USBD_INVAL); } + } else if (size != 0) { + usbd_start_transfer(xfer, dmap->segs, dmap->nsegs, 0); + } else { + usbd_start_transfer(xfer, NULL, 0, 0); } if (!(xfer->flags & USBD_SYNCHRONOUS)) - return (err); + return (xfer->done ? 0 : USBD_IN_PROGRESS); /* Sync transfer, wait for completion. */ - if (err != USBD_IN_PROGRESS) - return (err); s = splusb(); - if (!xfer->done) { + while (!xfer->done) { if (pipe->device->bus->use_polling) panic("usbd_transfer: not done"); tsleep(xfer, PRIBIO, "usbsyn", 0); @@ -347,6 +344,51 @@ return (xfer->status); } +Static void +usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + usbd_xfer_handle xfer = arg; + usbd_pipe_handle pipe = xfer->pipe; + struct usb_dma_mapping *dmap = &xfer->dmamap; + bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; + int err, i; + + if (error != 0) { + KASSERT(xfer->rqflags & URQ_AUTO_DMABUF, + ("usbd_start_transfer: error with non-auto buf")); + if (nseg > 0) + bus_dmamap_unload(tag, dmap->map); + bus_dmamap_destroy(tag, dmap->map); + /* XXX */ + usb_insert_transfer(xfer); + xfer->status = USBD_IOERROR; + usb_transfer_complete(xfer); + return; + } + + if (segs != dmap->segs) { + for (i = 0; i < nseg; i++) + dmap->segs[i] = segs[i]; + } + dmap->nsegs = nseg; + + if (segs > 0 && !usbd_xfer_isread(xfer)) + bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREWRITE); + err = pipe->methods->transfer(xfer); + if (err != USBD_IN_PROGRESS && err) { + if (xfer->rqflags & URQ_AUTO_DMABUF) { + bus_dmamap_unload(tag, dmap->map); + bus_dmamap_destroy(tag, dmap->map); + xfer->rqflags &= ~URQ_AUTO_DMABUF; + } + /* XXX */ + usb_insert_transfer(xfer); + xfer->status = err; + usb_transfer_complete(xfer); + return; + } +} + /* Like usbd_transfer(), but waits for completion. */ usbd_status usbd_sync_transfer(usbd_xfer_handle xfer) @@ -355,42 +397,103 @@ return (usbd_transfer(xfer)); } +struct usbd_allocstate { + usbd_xfer_handle xfer; + int done; + int error; + int waiting; +}; + void * usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size) { - struct usbd_bus *bus = xfer->device->bus; + struct usbd_allocstate allocstate; + struct usb_dma_mapping *dmap = &xfer->dmamap; + bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag; + void *buf; usbd_status err; + int error, s; -#ifdef DIAGNOSTIC - if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) - printf("usbd_alloc_buffer: xfer already has a buffer\n"); -#endif - err = bus->methods->allocm(bus, &xfer->dmabuf, size); + KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) == 0, + ("usbd_alloc_buffer: xfer already has a buffer")); + err = bus_dmamap_create(tag, 0, &dmap->map); if (err) return (NULL); + buf = malloc(size, M_USB, M_WAITOK); + + allocstate.xfer = xfer; + allocstate.done = 0; + allocstate.error = 0; + allocstate.waiting = 0; + error = bus_dmamap_load(tag, dmap->map, buf, size, usbd_alloc_callback, + &allocstate, 0); + if (error && error != EINPROGRESS) { + bus_dmamap_destroy(tag, dmap->map); + free(buf, M_USB); + return (NULL); + } + if (error == EINPROGRESS) { + /* Wait for completion. */ + s = splusb(); + allocstate.waiting = 1; + while (!allocstate.done) + tsleep(&allocstate, PRIBIO, "usbdab", 0); + splx(s); + error = allocstate.error; + } + if (error) { + bus_dmamap_unload(tag, dmap->map); + bus_dmamap_destroy(tag, dmap->map); + free(buf, M_USB); + return (NULL); + } + + xfer->allocbuf = buf; xfer->rqflags |= URQ_DEV_DMABUF; - return (KERNADDR(&xfer->dmabuf, 0)); + return (buf); } void usbd_free_buffer(usbd_xfer_handle xfer) { -#ifdef DIAGNOSTIC - if (!(xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))) { - printf("usbd_free_buffer: no buffer\n"); - return; - } -#endif - xfer->rqflags &= ~(URQ_DEV_DMABUF | URQ_AUTO_DMABUF); - xfer->device->bus->methods->freem(xfer->device->bus, &xfer->dmabuf); + struct usb_dma_mapping *dmap = &xfer->dmamap; + bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag; + + KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) == + URQ_DEV_DMABUF, ("usbd_free_buffer: no/auto buffer")); + + xfer->rqflags &= ~URQ_DEV_DMABUF; + bus_dmamap_unload(tag, dmap->map); + bus_dmamap_destroy(tag, dmap->map); + free(xfer->allocbuf, M_USB); + xfer->allocbuf = NULL; } void * usbd_get_buffer(usbd_xfer_handle xfer) { if (!(xfer->rqflags & URQ_DEV_DMABUF)) - return (0); - return (KERNADDR(&xfer->dmabuf, 0)); + return (NULL); + return (xfer->allocbuf); +} + +Static void +usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct usbd_allocstate *allocstate = arg; + usbd_xfer_handle xfer = allocstate->xfer; + struct usb_dma_mapping *dmap = &xfer->dmamap; + int i; + + allocstate->error = error; + if (error == 0) { + for (i = 0; i < nseg; i++) + dmap->segs[i] = segs[i]; + dmap->nsegs = nseg; + } + allocstate->done = 1; + if (allocstate->waiting) + wakeup(&allocstate); } usbd_xfer_handle @@ -411,7 +514,7 @@ usbd_free_xfer(usbd_xfer_handle xfer) { DPRINTFN(5,("usbd_free_xfer: %p\n", xfer)); - if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) + if (xfer->rqflags & URQ_DEV_DMABUF) usbd_free_buffer(xfer); #if defined(__NetBSD__) && defined(DIAGNOSTIC) if (callout_pending(&xfer->timeout_handle)) { @@ -468,10 +571,14 @@ usbd_private_handle priv, u_int16_t *frlengths, u_int32_t nframes, u_int16_t flags, usbd_callback callback) { + int i; + xfer->pipe = pipe; xfer->priv = priv; xfer->buffer = 0; xfer->length = 0; + for (i = 0; i < nframes; i++) + xfer->length += frlengths[i]; xfer->actlen = 0; xfer->flags = flags; xfer->timeout = USBD_NO_TIMEOUT; @@ -760,6 +867,7 @@ pipe, xfer, pipe->methods)); /* Make the HC abort it (and invoke the callback). */ pipe->methods->abort(xfer); + KASSERT(SIMPLEQ_FIRST(&pipe->queue) != xfer, ("usbd_ar_pipe")); /* XXX only for non-0 usbd_clear_endpoint_stall(pipe); */ } pipe->aborting = 0; @@ -771,7 +879,8 @@ usb_transfer_complete(usbd_xfer_handle xfer) { usbd_pipe_handle pipe = xfer->pipe; - usb_dma_t *dmap = &xfer->dmabuf; + struct usb_dma_mapping *dmap = &xfer->dmamap; + bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; int sync = xfer->flags & USBD_SYNCHRONOUS; int erred = xfer->status == USBD_CANCELLED || xfer->status == USBD_TIMEOUT; @@ -801,23 +910,14 @@ if (polling) pipe->running = 0; - if (!(xfer->flags & USBD_NO_COPY) && xfer->actlen != 0 && - usbd_xfer_isread(xfer)) { -#ifdef DIAGNOSTIC - if (xfer->actlen > xfer->length) { - printf("usb_transfer_complete: actlen > len %d > %d\n", - xfer->actlen, xfer->length); - xfer->actlen = xfer->length; - } -#endif - memcpy(xfer->buffer, KERNADDR(dmap, 0), xfer->actlen); - } + if (xfer->actlen != 0 && usbd_xfer_isread(xfer)) + bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_POSTREAD); - /* if we allocated the buffer in usbd_transfer() we free it here. */ + /* if we mapped the buffer in usbd_transfer() we unmap it here. */ if (xfer->rqflags & URQ_AUTO_DMABUF) { if (!repeat) { - struct usbd_bus *bus = pipe->device->bus; - bus->methods->freem(bus, dmap); + bus_dmamap_unload(tag, dmap->map); + bus_dmamap_destroy(tag, dmap->map); xfer->rqflags &= ~URQ_AUTO_DMABUF; } } @@ -825,11 +925,10 @@ if (!repeat) { /* Remove request from queue. */ #ifdef DIAGNOSTIC - if (xfer != SIMPLEQ_FIRST(&pipe->queue)) - printf("usb_transfer_complete: bad dequeue %p != %p\n", - xfer, SIMPLEQ_FIRST(&pipe->queue)); xfer->busy_free = XFER_BUSY; #endif + KASSERT(SIMPLEQ_FIRST(&pipe->queue) == xfer, + ("usb_transfer_complete: bad dequeue")); SIMPLEQ_REMOVE_HEAD(&pipe->queue, next); } DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n", @@ -893,6 +992,7 @@ xfer->busy_free = XFER_ONQU; #endif s = splusb(); + KASSERT(SIMPLEQ_FIRST(&pipe->queue) != xfer, ("usb_insert_transfer")); SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next); if (pipe->running) err = USBD_IN_PROGRESS; Index: usbdivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.44 diff -u -r1.44 usbdivar.h --- usbdivar.h 26 Feb 2006 02:57:57 -0000 1.44 +++ usbdivar.h 18 Mar 2006 19:32:00 -0000 @@ -130,7 +130,8 @@ #endif #endif - bus_dma_tag_t dmatag; /* DMA tag */ + bus_dma_tag_t parent_dmatag; /* Base DMA tag */ + bus_dma_tag_t buffer_dmatag; /* Tag for transfer buffers */ }; struct usbd_device { @@ -187,6 +188,15 @@ struct usbd_pipe_methods *methods; }; +#define USB_DMA_NSEG (btoc(MAXPHYS) + 1) + +/* DMA-capable memory buffer. */ +struct usb_dma_mapping { + bus_dma_segment_t segs[USB_DMA_NSEG]; /* The physical segments. */ + int nsegs; /* Number of segments. */ + bus_dmamap_t map; /* DMA mapping. */ +}; + struct usbd_xfer { struct usbd_pipe *pipe; void *priv; @@ -214,7 +224,8 @@ /* For memory allocation */ struct usbd_device *device; - usb_dma_t dmabuf; + struct usb_dma_mapping dmamap; + void *allocbuf; int rqflags; #define URQ_REQUEST 0x01