? hist ? uaudio.c ? uaudioreg.h Index: ehci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ehci.c,v retrieving revision 1.1 diff -u -r1.1 ehci.c --- ehci.c 2003/04/14 14:04:07 1.1 +++ ehci.c 2003/07/09 23:27:04 @@ -55,8 +55,8 @@ #include #include #include -#if defined(__NetBSD__) || defined(__OpenBSD__) #include +#if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) @@ -65,6 +65,7 @@ #include #include #include +#include #if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__) #include #endif @@ -985,12 +986,9 @@ usbd_status ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ehci_softc *sc = (struct ehci_softc *)bus; -#endif usbd_status err; - err = usb_allocmem(&sc->sc_bus, size, 0, dma); + err = usb_allocmem(bus, size, 0, dma); #ifdef USB_DEBUG if (err) printf("ehci_allocm: usb_allocmem()=%d\n", err); @@ -1001,11 +999,7 @@ void ehci_freem(struct usbd_bus *bus, usb_dma_t *dma) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ehci_softc *sc = (struct ehci_softc *)bus; -#endif - - usb_freemem(&sc->sc_bus, dma); + usb_freemem(bus, dma); } usbd_xfer_handle Index: ehci_pci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ehci_pci.c,v retrieving revision 1.3 diff -u -r1.3 ehci_pci.c --- ehci_pci.c 2003/07/04 01:50:38 1.3 +++ ehci_pci.c 2003/07/09 23:27:04 @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include Index: ohci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohci.c,v retrieving revision 1.119 diff -u -r1.119 ohci.c --- ohci.c 2003/07/04 23:11:13 1.119 +++ ohci.c 2003/07/09 23:27:06 @@ -46,14 +46,14 @@ * USB Open Host Controller driver. * * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html - * USB spec: http://www.usb.org/developers/data/usbspec.zip + * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include -#if defined(__NetBSD__) || defined(__OpenBSD__) #include +#if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) @@ -255,6 +255,7 @@ struct ohci_pipe { struct usbd_pipe pipe; ohci_soft_ed_t *sed; + u_int32_t aborting; union { ohci_soft_td_t *td; ohci_soft_itd_t *itd; @@ -489,8 +490,8 @@ ohci_soft_td_t *sp, ohci_soft_td_t **ep) { ohci_soft_td_t *next, *cur; - ohci_physaddr_t dataphys, dataphysend; - u_int32_t intr, tdflags; + ohci_physaddr_t dataphys; + u_int32_t tdflags; int offset = 0; int len, curlen; usb_dma_t *dma = &xfer->dmabuf; @@ -501,12 +502,10 @@ len = alen; cur = sp; - dataphys = DMAADDR(dma, 0); - dataphysend = OHCI_PAGE(DMAADDR(dma, len - 1)); 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_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR); for (;;) { next = ohci_alloc_std(sc); @@ -515,20 +514,27 @@ dataphys = DMAADDR(dma, offset); - /* The OHCI hardware can handle at most one page crossing. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) - if (OHCI_PAGE(dataphys) == dataphysend || - OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) -#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. + /* + * 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 + * 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 + * CurrentBufferPointer causing the next buffer address to + * 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. + * + * We can describe the above using maxsegsz = 4k and nsegs = 2 + * in the future. */ - if (OHCI_PAGE(dataphys) == dataphysend) -#endif - { + if (OHCI_PAGE(dataphys) == OHCI_PAGE(DMAADDR(dma, offset + + len - 1)) || len - (OHCI_PAGE_SIZE - + OHCI_PAGE_OFFSET(dataphys)) <= OHCI_PAGE_SIZE) { /* we can handle it in this TD */ curlen = len; } else { @@ -539,38 +545,36 @@ * the case of an mbuf cluster). You'll get an early * short packet. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) /* must use multiple TDs, fill as much as possible. */ curlen = 2 * OHCI_PAGE_SIZE - - OHCI_PAGE_MASK(dataphys); - if (curlen > len) /* may have fit in one page */ - curlen = len; -#elif defined(__FreeBSD__) - /* See comment above (XXX) */ - curlen = OHCI_PAGE_SIZE - - OHCI_PAGE_MASK(dataphys); + OHCI_PAGE_OFFSET(dataphys); + /* the length must be a multiple of the max size */ + curlen -= curlen % + UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize); +#ifdef DIAGNOSTIC + if (curlen == 0) + panic("ohci_alloc_std: curlen == 0"); #endif } DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x " - "dataphysend=0x%08x len=%d curlen=%d\n", - dataphys, dataphysend, - len, curlen)); + "len=%d curlen=%d\n", + dataphys, len, curlen)); len -= curlen; - intr = len == 0 ? OHCI_TD_SET_DI(1) : OHCI_TD_NOINTR; - cur->td.td_flags = htole32(tdflags | intr); + cur->td.td_flags = tdflags; cur->td.td_cbp = htole32(dataphys); cur->nexttd = next; cur->td.td_nexttd = htole32(next->physaddr); - cur->td.td_be = htole32(dataphys + curlen - 1); + cur->td.td_be = htole32(DMAADDR(dma, curlen - 1)); cur->len = curlen; cur->flags = OHCI_ADD_LEN; + cur->xfer = xfer; DPRINTFN(10,("ohci_alloc_std_chain: cbp=0x%08x be=0x%08x\n", dataphys, dataphys + curlen - 1)); if (len == 0) break; if (len < 0) - panic("Length went negative: %d curlen %d dma %p offset %08x", len, curlen, *dma, (int)offset); + panic("Length went negative: %d curlen %d dma %p offset %08x", len, curlen, dma, (int)0); DPRINTFN(10,("ohci_alloc_std_chain: extend chain\n")); offset += curlen; @@ -580,14 +584,13 @@ alen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) { /* Force a 0 length transfer at the end. */ - cur->td.td_flags = htole32(tdflags | OHCI_TD_NOINTR); cur = next; next = ohci_alloc_std(sc); if (next == NULL) goto nomem; - cur->td.td_flags = htole32(tdflags | OHCI_TD_SET_DI(1)); + cur->td.td_flags = tdflags; cur->td.td_cbp = 0; /* indicate 0 length packet */ cur->nexttd = next; cur->td.td_nexttd = htole32(next->physaddr); @@ -597,8 +600,7 @@ cur->xfer = xfer; DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n")); } - cur->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; - *ep = next; + *ep = cur; return (USBD_NORMAL_COMPLETION); @@ -635,6 +637,7 @@ OHCI_ITD_ALIGN, &dma); if (err) return (NULL); + s = splusb(); for(i = 0; i < OHCI_SITD_CHUNK; i++) { offs = i * OHCI_SITD_SIZE; sitd = KERNADDR(&dma, offs); @@ -642,6 +645,7 @@ sitd->nextitd = sc->sc_freeitds; sc->sc_freeitds = sitd; } + splx(s); } s = splusb(); @@ -708,6 +712,8 @@ sc->sc_bus.usbrev = USBREV_1_0; for (i = 0; i < OHCI_HASH_SIZE; i++) + LIST_INIT(&sc->sc_hash_tds[i]); + for (i = 0; i < OHCI_HASH_SIZE; i++) LIST_INIT(&sc->sc_hash_itds[i]); SIMPLEQ_INIT(&sc->sc_free_xfers); @@ -926,21 +932,13 @@ usbd_status ohci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ohci_softc *sc = (struct ohci_softc *)bus; -#endif - - return (usb_allocmem(&sc->sc_bus, size, 0, dma)); + return (usb_allocmem(bus, size, 0, dma)); } void ohci_freem(struct usbd_bus *bus, usb_dma_t *dma) { -#if defined(__NetBSD__) || defined(__OpenBSD__) - struct ohci_softc *sc = (struct ohci_softc *)bus; -#endif - - usb_freemem(&sc->sc_bus, dma); + usb_freemem(bus, dma); } usbd_xfer_handle @@ -974,7 +972,15 @@ 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) { printf("ohci_freex: xfer=%p not busy, 0x%08x\n", xfer, @@ -1146,7 +1152,7 @@ return (0); } - intrs = 0; + intrs = 0; done = le32toh(sc->sc_hcca->hcca_done_head); /* The LSb of done is used to inform the HC Driver that an interrupt @@ -1324,7 +1330,7 @@ usbd_xfer_handle xfer; int len, cc, s; - DPRINTFN(10,("ohci_softintr: enter\n:")); + DPRINTFN(10,("ohci_softintr: enter\n")); sc->sc_bus.intr_context++; @@ -1380,7 +1386,9 @@ xfer->actlen += len; if (std->flags & OHCI_CALL_DONE) { xfer->status = USBD_NORMAL_COMPLETION; + s = splusb(); usb_transfer_complete(xfer); + splx(s); } ohci_free_std(sc, std); } else { @@ -1420,7 +1428,10 @@ xfer->status = USBD_STALLED; else xfer->status = USBD_IOERROR; + + s = splusb(); usb_transfer_complete(xfer); + splx(s); } } @@ -1434,6 +1445,7 @@ for (sitd = sidone; sitd != NULL; sitd = sitdnext) { xfer = sitd->xfer; sitdnext = sitd->dnext; + sitd->flags |= OHCI_ITD_INTFIN; DPRINTFN(1, ("ohci_process_done: sitd=%p xfer=%p hcpriv=%p\n", sitd, xfer, xfer ? xfer->hcpriv : 0)); if (xfer == NULL) @@ -1445,27 +1457,36 @@ /* Handled by abort routine. */ continue; } + if (xfer->pipe) + if (xfer->pipe->aborting) + continue; /*Ignore.*/ #ifdef DIAGNOSTIC if (sitd->isdone) printf("ohci_softintr: sitd=%p is done\n", sitd); sitd->isdone = 1; #endif + struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; + if (opipe->aborting) + continue; + cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)); if (cc == OHCI_CC_NO_ERROR) { /* XXX compute length for input */ - struct ohci_pipe *opipe = - (struct ohci_pipe *)xfer->pipe; if (sitd->flags & OHCI_CALL_DONE) { opipe->u.iso.inuse -= xfer->nframes; /* XXX update frlengths with actual length */ /* XXX xfer->actlen = actlen; */ xfer->status = USBD_NORMAL_COMPLETION; + s = splusb(); usb_transfer_complete(xfer); + splx(s); } } else { /* XXX Do more */ xfer->status = USBD_IOERROR; + s = splusb(); usb_transfer_complete(xfer); + splx(s); } } @@ -1657,7 +1678,6 @@ usbd_device_handle dev = opipe->pipe.device; ohci_softc_t *sc = (ohci_softc_t *)dev->bus; int addr = dev->address; - ohci_soft_td_t *data = 0; ohci_soft_td_t *setup, *stat, *next, *tail; ohci_soft_ed_t *sed; int isread; @@ -1698,31 +1718,20 @@ OHCI_ED_SET_FA(addr) | OHCI_ED_SET_MAXP(UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize))); + next = stat; + /* Set up data transaction */ if (len != 0) { - data = ohci_alloc_std(sc); - if (data == NULL) { - err = USBD_NOMEM; - goto bad3; - } - data->td.td_flags = htole32( - (isread ? OHCI_TD_IN : OHCI_TD_OUT) | OHCI_TD_NOCC | - OHCI_TD_TOGGLE_1 | OHCI_TD_NOINTR | - (xfer->flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0)); - data->td.td_cbp = htole32(DMAADDR(&xfer->dmabuf, 0)); - data->nexttd = stat; - data->td.td_nexttd = htole32(stat->physaddr); - data->td.td_be = htole32(le32toh(data->td.td_cbp) + len - 1); - data->len = len; - data->xfer = xfer; - data->flags = OHCI_ADD_LEN; + ohci_soft_td_t *std = stat; - next = data; - stat->flags = OHCI_CALL_DONE; - } else { - next = stat; - /* XXX ADD_LEN? */ - stat->flags = OHCI_CALL_DONE | OHCI_ADD_LEN; + err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, + std, &stat); + stat = stat->nexttd; /* point at free TD */ + if (err) + goto bad3; + /* Start toggle at 1 and then use the carried toggle. */ + std->td.td_flags &= htole32(~OHCI_TD_TOGGLE_MASK); + std->td.td_flags |= htole32(OHCI_TD_TOGGLE_1); } memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req); @@ -1745,6 +1754,7 @@ stat->nexttd = tail; stat->td.td_nexttd = htole32(tail->physaddr); stat->td.td_be = 0; + stat->flags = OHCI_CALL_DONE; stat->len = 0; stat->xfer = xfer; @@ -2079,6 +2089,7 @@ if (sitd == NULL) goto bad1; opipe->tail.itd = sitd; + opipe->aborting = 0; tdphys = sitd->physaddr; fmt = OHCI_ED_FORMAT_ISO; if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) @@ -2906,6 +2917,11 @@ data = opipe->tail.td; err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer, data, &tail); + /* We want interrupt at the end of the transfer. */ + tail->td.td_flags &= htole32(~OHCI_TD_INTR_MASK); + tail->td.td_flags |= htole32(OHCI_TD_SET_DI(1)); + tail->flags |= OHCI_CALL_DONE; + tail = tail->nexttd; /* point at sentinel */ if (err) return (err); @@ -3223,8 +3239,9 @@ 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; ohci_soft_itd_t *sitd, *nsitd; - ohci_physaddr_t buf, offs, noffs, bp0; + ohci_physaddr_t buf, offs, noffs, bp0, tdphys; int i, ncur, nframes; int s; @@ -3242,6 +3259,24 @@ 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); @@ -3273,7 +3308,7 @@ sitd->itd.itd_nextitd = htole32(nsitd->physaddr); sitd->itd.itd_be = htole32(bp0 + offs - 1); sitd->xfer = xfer; - sitd->flags = 0; + sitd->flags = OHCI_ITD_ACTIVE; sitd = nsitd; iso->next = iso->next + ncur; @@ -3301,7 +3336,7 @@ sitd->itd.itd_nextitd = htole32(nsitd->physaddr); sitd->itd.itd_be = htole32(bp0 + offs - 1); sitd->xfer = xfer; - sitd->flags = OHCI_CALL_DONE; + sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE; iso->next = iso->next + ncur; iso->inuse += nframes; @@ -3310,6 +3345,8 @@ 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", @@ -3321,6 +3358,7 @@ s = splusb(); opipe->tail.itd = nsitd; + sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); sed->ed.ed_tailp = htole32(nsitd->physaddr); splx(s); @@ -3340,6 +3378,8 @@ { 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; + int s; DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer)); @@ -3353,6 +3393,11 @@ /* XXX anything to do? */ + s = splusb(); + sed = opipe->sed; /* Turn off ED skip-bit to start processing */ + sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* ED's ITD list.*/ + splx(s); + return (USBD_IN_PROGRESS); } @@ -3362,10 +3407,11 @@ 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; - int s; + ohci_soft_itd_t *sitd, *tmp_sitd; + int s,undone,num_sitds; s = splusb(); + opipe->aborting = 1; DPRINTFN(1,("ohci_device_isoc_abort: xfer=%p\n", xfer)); @@ -3383,6 +3429,7 @@ sed = opipe->sed; sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */ + num_sitds = 0; sitd = xfer->hcpriv; #ifdef DIAGNOSTIC if (sitd == NULL) { @@ -3391,7 +3438,8 @@ return; } #endif - for (; sitd->xfer == xfer; sitd = sitd->nextitd) { + for (; sitd != NULL && sitd->xfer == xfer; sitd = sitd->nextitd) { + num_sitds++; #ifdef DIAGNOSTIC DPRINTFN(1,("abort sets done sitd=%p\n", sitd)); sitd->isdone = 1; @@ -3399,15 +3447,44 @@ } splx(s); + + /* + * Each sitd has up to OHCI_ITD_NOFFSET transfers, each can + * take a usb 1ms cycle. Conservatively wait for it to drain. + * Even with DMA done, it can take awhile for the "batch" + * delivery of completion interrupts to occur thru the controller. + */ + + do { + usb_delay_ms(&sc->sc_bus, 2*(num_sitds*OHCI_ITD_NOFFSET)); + + undone = 0; + tmp_sitd = xfer->hcpriv; + for (; tmp_sitd != NULL && tmp_sitd->xfer == xfer; + tmp_sitd = tmp_sitd->nextitd) { + if (OHCI_CC_NO_ERROR == + OHCI_ITD_GET_CC(le32toh(tmp_sitd->itd.itd_flags)) && + tmp_sitd->flags & OHCI_ITD_ACTIVE && + tmp_sitd->flags & OHCI_ITD_INTFIN == 0) + undone++; + } + } while( undone != 0 ); - usb_delay_ms(&sc->sc_bus, OHCI_ITD_NOFFSET); 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; - sed->ed.ed_headp = htole32(sitd->physaddr); /* unlink TDs */ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */ splx(s); @@ -3416,21 +3493,23 @@ void ohci_device_isoc_done(usbd_xfer_handle xfer) { - struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; - ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; - ohci_soft_itd_t *sitd, *nsitd; - - DPRINTFN(1,("ohci_device_isoc_done: xfer=%p\n", xfer)); - - for (sitd = xfer->hcpriv; - !(sitd->flags & OHCI_CALL_DONE); - sitd = nsitd) { - nsitd = sitd->nextitd; - DPRINTFN(1,("ohci_device_isoc_done: free sitd=%p\n", sitd)); - ohci_free_sitd(sc, sitd); - } - ohci_free_sitd(sc, sitd); - xfer->hcpriv = NULL; + /* This null routine corresponds to non-isoc "done()" routines + * that free the stds associated with an xfer after a completed + * xfer interrupt. However, in the case of isoc transfers, the + * sitds associated with the transfer have already been processed + * and reallocated for the next iteration by + * "ohci_device_isoc_transfer()". + * + * Routine "usb_transfer_complete()" is called at the end of every + * relevant usb interrupt. "usb_transfer_complete()" indirectly + * calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the + * pipeline by setting up the next transfer iteration) and 2) then + * calls "ohci_device_isoc_done()". Isoc transfers have not been + * working for the ohci usb because this routine was trashing the + * xfer set up for the next iteration (thus, only the first + * UGEN_NISOREQS xfers outstanding on an open would work). Perhaps + * this could all be re-factored, but that's another pass... + */ } usbd_status @@ -3456,11 +3535,19 @@ { struct ohci_pipe *opipe = (struct ohci_pipe *)pipe; ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus; + ohci_soft_ed_t *sed; DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe)); - ohci_close_pipe(pipe, sc->sc_isoc_head); + + sed = opipe->sed; + sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop device. */ + + ohci_close_pipe(pipe, sc->sc_isoc_head); /* Stop isoc list, free ED.*/ + + /* up to NISOREQs xfers still outstanding. */ + #ifdef DIAGNOSTIC opipe->tail.itd->isdone = 1; #endif - ohci_free_sitd(sc, opipe->tail.itd); + ohci_free_sitd(sc, opipe->tail.itd); /* Next `avail free' sitd.*/ } Index: ohcireg.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohcireg.h,v retrieving revision 1.19 diff -u -r1.19 ohcireg.h --- ohcireg.h 2003/07/04 01:50:38 1.19 +++ ohcireg.h 2003/07/09 23:27:07 @@ -187,9 +187,11 @@ #define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */ #define OHCI_TD_SET_DI(x) ((x) << 21) #define OHCI_TD_NOINTR 0x00e00000 +#define OHCI_TD_INTR_MASK 0x00e00000 #define OHCI_TD_TOGGLE_CARRY 0x00000000 #define OHCI_TD_TOGGLE_0 0x02000000 #define OHCI_TD_TOGGLE_1 0x03000000 +#define OHCI_TD_TOGGLE_MASK 0x03000000 #define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */ #define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */ #define OHCI_TD_NOCC 0xf0000000 Index: ohcivar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.33 diff -u -r1.33 ohcivar.h --- ohcivar.h 2002/09/30 17:50:16 1.33 +++ ohcivar.h 2003/07/09 23:27:07 @@ -44,7 +44,7 @@ ohci_physaddr_t physaddr; } ohci_soft_ed_t; #define OHCI_SED_SIZE ((sizeof (struct ohci_soft_ed) + OHCI_ED_ALIGN - 1) / OHCI_ED_ALIGN * OHCI_ED_ALIGN) -#define OHCI_SED_CHUNK 128 +#define OHCI_SED_CHUNK (PAGE_SIZE / OHCI_SED_SIZE) typedef struct ohci_soft_td { ohci_td_t td; @@ -60,7 +60,7 @@ #define OHCI_TD_HANDLED 0x0004 /* signal process_done has seen it */ } ohci_soft_td_t; #define OHCI_STD_SIZE ((sizeof (struct ohci_soft_td) + OHCI_TD_ALIGN - 1) / OHCI_TD_ALIGN * OHCI_TD_ALIGN) -#define OHCI_STD_CHUNK 128 +#define OHCI_STD_CHUNK (PAGE_SIZE / OHCI_STD_SIZE) typedef struct ohci_soft_itd { ohci_itd_t itd; @@ -70,12 +70,14 @@ LIST_ENTRY(ohci_soft_itd) hnext; usbd_xfer_handle xfer; u_int16_t flags; +#define OHCI_ITD_ACTIVE 0x0010 /* Hardware op in progress */ +#define OHCI_ITD_INTFIN 0x0020 /* Hw completion interrupt seen.*/ #ifdef DIAGNOSTIC char isdone; #endif } ohci_soft_itd_t; #define OHCI_SITD_SIZE ((sizeof (struct ohci_soft_itd) + OHCI_ITD_ALIGN - 1) / OHCI_ITD_ALIGN * OHCI_ITD_ALIGN) -#define OHCI_SITD_CHUNK 64 +#define OHCI_SITD_CHUNK (PAGE_SIZE / OHCI_SITD_SIZE) #define OHCI_NO_EDS (2*OHCI_NO_INTRS-1) @@ -149,9 +151,11 @@ struct ohci_xfer { struct usbd_xfer xfer; struct usb_task abort_task; + u_int32_t ohci_xfer_flags; }; +#define OHCI_ISOC_DIRTY 0x01 -#define OXFER(xfer) ((struct ehci_xfer *)(xfer)) +#define OXFER(xfer) ((struct ohci_xfer *)(xfer)) usbd_status ohci_init(ohci_softc_t *); int ohci_intr(void *); Index: uhci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/uhci.c,v retrieving revision 1.135 diff -u -r1.135 uhci.c --- uhci.c 2003/07/04 23:11:13 1.135 +++ uhci.c 2003/07/09 23:27:09 @@ -588,14 +588,13 @@ usbd_status uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) { - return (usb_allocmem(&((struct uhci_softc *)bus)->sc_bus, size, 0, - dma)); + return (usb_allocmem(bus, size, 0, dma)); } void uhci_freem(struct usbd_bus *bus, usb_dma_t *dma) { - usb_freemem(&((struct uhci_softc *)bus)->sc_bus, dma); + usb_freemem(bus, dma); } usbd_xfer_handle Index: uhcivar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/uhcivar.h,v retrieving revision 1.35 diff -u -r1.35 uhcivar.h --- uhcivar.h 2003/07/04 01:50:38 1.35 +++ uhcivar.h 2003/07/09 23:27:09 @@ -104,7 +104,7 @@ * NOTE: Minimum size is 32 bytes. */ #define UHCI_STD_SIZE ((sizeof (struct uhci_soft_td) + UHCI_TD_ALIGN - 1) / UHCI_TD_ALIGN * UHCI_TD_ALIGN) -#define UHCI_STD_CHUNK 128 /*(PAGE_SIZE / UHCI_TD_SIZE)*/ +#define UHCI_STD_CHUNK (PAGE_SIZE / UHCI_STD_SIZE) /* * Extra information that we need for a QH. @@ -118,7 +118,7 @@ }; /* See comment about UHCI_STD_SIZE. */ #define UHCI_SQH_SIZE ((sizeof (struct uhci_soft_qh) + UHCI_QH_ALIGN - 1) / UHCI_QH_ALIGN * UHCI_QH_ALIGN) -#define UHCI_SQH_CHUNK 128 /*(PAGE_SIZE / UHCI_QH_SIZE)*/ +#define UHCI_SQH_CHUNK (PAGE_SIZE / UHCI_SQH_SIZE) /* * Information about an entry in the virtual frame list. Index: usb_mem.c =================================================================== RCS file: usb_mem.c diff -N usb_mem.c --- /dev/null Wed Jul 9 16:26:49 2003 +++ usb_mem.c Wed Jul 9 16:27:09 2003 @@ -0,0 +1,294 @@ +/* $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * USB DMA memory allocation. + * We need to allocate a lot of small (many 8 byte, some larger) + * memory blocks that can be used for DMA. Using the bus_dma + * routines directly would incur large overheads in space and time. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include /* for usbdivar.h */ +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#endif +#include + +#include +#include + +#ifdef DIAGNOSTIC +#include +#endif + +#include +#include +#include /* just for usb_dma_t */ +#include + +#ifdef USB_DEBUG +#define DPRINTF(x) if (usbdebug) logprintf x +#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x +extern int usbdebug; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define USB_MEM_SMALL 64 +#define USB_MEM_CHUNKS (PAGE_SIZE / 64) +#define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS) + +/* This struct is overlayed on free fragments. */ +struct usb_frag_dma { + usb_dma_block_t *block; + u_int offs; + LIST_ENTRY(usb_frag_dma) next; +}; + +Static bus_dmamap_callback_t usbmem_callback; +Static usbd_status usb_block_allocmem(bus_dma_tag_t, size_t, size_t, + usb_dma_block_t **); +Static void usb_block_freemem(usb_dma_block_t *); + +Static LIST_HEAD(, usb_dma_block) usb_blk_freelist = + LIST_HEAD_INITIALIZER(usb_blk_freelist); +Static int usb_blk_nfree = 0; +/* XXX should have different free list for different tags (for speed) */ +Static LIST_HEAD(, usb_frag_dma) usb_frag_freelist = + LIST_HEAD_INITIALIZER(usb_frag_freelist); + +Static void +usbmem_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + int i; + usb_dma_block_t *p = arg; + + if (error == EFBIG) { + printf("usb: mapping to large\n"); + return; + } + + p->nsegs = nseg; + for (i = 0; i < nseg && i < sizeof p->segs / sizeof *p->segs; i++) + p->segs[i] = segs[i]; +} + +Static usbd_status +usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align, + usb_dma_block_t **dmap) +{ + usb_dma_block_t *p; + int s; + + DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n", + (u_long)size, (u_long)align)); + +#ifdef DIAGNOSTIC + if (!curproc) { + printf("usb_block_allocmem: in interrupt context, size=%lu\n", + (unsigned long) size); + } +#endif + + s = splusb(); + /* First check the free list. */ + for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) { + if (p->tag == tag && p->size >= size && p->align >= align) { + LIST_REMOVE(p, next); + usb_blk_nfree--; + splx(s); + *dmap = p; + DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n", + (u_long)p->size)); + return (USBD_NORMAL_COMPLETION); + } + } + splx(s); + +#ifdef DIAGNOSTIC + if (!curproc) { + printf("usb_block_allocmem: in interrupt context, failed\n"); + return (USBD_NOMEM); + } +#endif + + DPRINTFN(6, ("usb_block_allocmem: no free\n")); + p = malloc(sizeof *p, M_USB, M_NOWAIT); + if (p == NULL) + return (USBD_NOMEM); + + if (bus_dma_tag_create(tag, align, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, sizeof(p->segs) / sizeof(p->segs[0]), size, + BUS_DMA_ALLOCNOW, NULL, NULL, &p->tag) == ENOMEM) { + goto free; + } + + p->size = size; + p->align = align; + if (bus_dmamem_alloc(p->tag, &p->kaddr, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT, &p->map)) + goto tagfree; + + if (bus_dmamap_load(p->tag, p->map, p->kaddr, p->size, + usbmem_callback, p, 0)) + goto memfree; + + *dmap = p; + return (USBD_NORMAL_COMPLETION); + + /* + * XXX - do we need to _unload? is the order of _free and _destroy + * correct? + */ +memfree: + bus_dmamem_free(p->tag, p->kaddr, p->map); +tagfree: + bus_dma_tag_destroy(p->tag); +free: + free(p, M_USB); + return (USBD_NOMEM); +} + +/* + * Do not free the memory unconditionally since we might be called + * from an interrupt context and that is BAD. + * XXX when should we really free? + */ +Static void +usb_block_freemem(usb_dma_block_t *p) +{ + int s; + + DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long)p->size)); + s = splusb(); + LIST_INSERT_HEAD(&usb_blk_freelist, p, next); + usb_blk_nfree++; + splx(s); +} + +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; + usbd_status err; + struct usb_frag_dma *f; + usb_dma_block_t *b; + int i; + int s; + + /* compat w/ Net/OpenBSD */ + if (align == 0) + align = 1; + + /* If the request is large then just use a full block. */ + if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) { + DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size)); + size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1); + err = usb_block_allocmem(tag, size, align, &p->block); + if (!err) { + p->block->fullblock = 1; + p->offs = 0; + p->len = size; + } + return (err); + } + + s = splusb(); + /* Check for free fragments. */ + for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next)) + if (f->block->tag == tag) + break; + if (f == NULL) { + DPRINTFN(1, ("usb_allocmem: adding fragments\n")); + err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL,&b); + if (err) { + splx(s); + return (err); + } + b->fullblock = 0; + for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) { + f = (struct usb_frag_dma *)((char *)b->kaddr + i); + f->block = b; + f->offs = i; + LIST_INSERT_HEAD(&usb_frag_freelist, f, next); + } + f = LIST_FIRST(&usb_frag_freelist); + } + p->block = f->block; + p->offs = f->offs; + p->len = USB_MEM_SMALL; + LIST_REMOVE(f, next); + splx(s); + DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size)); + return (USBD_NORMAL_COMPLETION); +} + +void +usb_freemem(usbd_bus_handle bus, usb_dma_t *p) +{ + struct usb_frag_dma *f; + int s; + + if (p->block->fullblock) { + DPRINTFN(1, ("usb_freemem: large free\n")); + usb_block_freemem(p->block); + return; + } + f = KERNADDR(p, 0); + f->block = p->block; + f->offs = p->offs; + s = splusb(); + LIST_INSERT_HEAD(&usb_frag_freelist, f, next); + splx(s); + DPRINTFN(5, ("usb_freemem: frag=%p\n", f)); +} Index: usb_mem.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usb_mem.h,v retrieving revision 1.18 diff -u -r1.18 usb_mem.h --- usb_mem.h 2003/07/04 01:50:39 1.18 +++ usb_mem.h 2003/07/09 23:27:09 @@ -38,11 +38,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#if defined(__NetBSD__) || defined(__OpenBSD__) typedef struct usb_dma_block { bus_dma_tag_t tag; bus_dmamap_t map; +#ifdef __FreeBSD__ + void *kaddr; +#else caddr_t kaddr; +#endif bus_dma_segment_t segs[1]; int nsegs; size_t size; @@ -51,41 +54,13 @@ LIST_ENTRY(usb_dma_block) next; } usb_dma_block_t; -#define DMAADDR(dma, o) ((dma)->block->map->dm_segs[0].ds_addr + (dma)->offs + (o)) +#ifdef __FreeBSD__ +#define DMAADDR(dma, o) ((uint32_t)(uintptr_t)(((char *)(dma)->block->segs[0].ds_addr) + (dma)->offs + (o))) +#else +#define DMAADDR(dma, o) (((char *)(dma)->block->map->dm_segs[0].ds_addr) + (dma)->offs + (o)) +#endif #define KERNADDR(dma, o) \ - ((void *)((char *)((dma)->block->kaddr + (dma)->offs) + (o))) + ((void *)((char *)((dma)->block->kaddr) + (dma)->offs + (o))) usbd_status usb_allocmem(usbd_bus_handle,size_t,size_t, usb_dma_t *); void usb_freemem(usbd_bus_handle, usb_dma_t *); - -#elif defined(__FreeBSD__) - -/* - * FreeBSD does not have special functions for dma memory, so let's keep it - * simple for now. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* for vtophys */ - -#define usb_allocmem(t,s,a,p) (*(p) = malloc(s, M_USB, M_NOWAIT), (*(p) == NULL? USBD_NOMEM: USBD_NORMAL_COMPLETION)) -#define usb_freemem(t,p) (free(*(p), M_USB)) - -#ifdef __alpha__ -#define DMAADDR(dma, o) (alpha_XXX_dmamap((vm_offset_t) *(dma) + (o))) -#else -#define DMAADDR(dma, o) (vtophys(*(dma) + (o))) -#endif -#define KERNADDR(dma, o) ((void *) ((char *)*(dma) + (o))) -#endif /* __FreeBSD__ */ - Index: usb_port.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usb_port.h,v retrieving revision 1.60 diff -u -r1.60 usb_port.h --- usb_port.h 2003/07/04 01:50:39 1.60 +++ usb_port.h 2003/07/09 23:27:09 @@ -355,7 +355,13 @@ #define USBDEVUNIT(bdev) device_get_unit(bdev) #define USBGETSOFTC(bdev) (device_get_softc(bdev)) -#define DECLARE_USB_DMA_T typedef char * usb_dma_t +#define DECLARE_USB_DMA_T \ + struct usb_dma_block; \ + typedef struct { \ + struct usb_dma_block *block; \ + u_int offs; \ + u_int len; \ + } usb_dma_t typedef struct thread *usb_proc_ptr; @@ -468,6 +474,8 @@ #define SIMPLEQ_NEXT STAILQ_NEXT #define SIMPLEQ_FIRST STAILQ_FIRST #define SIMPLEQ_HEAD STAILQ_HEAD +#define SIMPLEQ_EMPTY STAILQ_EMPTY +#define SIMPLEQ_FOREACH STAILQ_FOREACH #define SIMPLEQ_INIT STAILQ_INIT #define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER #define SIMPLEQ_ENTRY STAILQ_ENTRY Index: usbdi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usbdi.c,v retrieving revision 1.78 diff -u -r1.78 usbdi.c --- usbdi.c 2003/07/04 23:11:13 1.78 +++ usbdi.c 2003/07/09 23:27:10 @@ -150,9 +150,7 @@ usbd_xfer_handle xfer; printf("usbd_dump_queue: pipe=%p\n", pipe); - for (xfer = SIMPLEQ_FIRST(&pipe->queue); - xfer; - xfer = SIMPLEQ_NEXT(xfer, next)) { + SIMPLEQ_FOREACH(xfer, &pipe->queue, next) { printf(" xfer=%p\n", xfer); } } @@ -265,7 +263,7 @@ if (--pipe->refcnt != 0) return (USBD_NORMAL_COMPLETION); - if (SIMPLEQ_FIRST(&pipe->queue) != 0) + if (! SIMPLEQ_EMPTY(&pipe->queue)) return (USBD_PENDING_REQUESTS); LIST_REMOVE(pipe, next); pipe->endpoint->refcnt--; Index: usbdivar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.38 diff -u -r1.38 usbdivar.h --- usbdivar.h 2003/07/04 01:50:39 1.38 +++ usbdivar.h 2003/07/09 23:27:10 @@ -124,9 +124,7 @@ #endif #endif -#if defined(__NetBSD__) || defined(__OpenBSD__) bus_dma_tag_t dmatag; /* DMA tag */ -#endif }; struct usbd_device {