Index: ehci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci.c,v retrieving revision 1.6 diff -u -r1.6 ehci.c --- ehci.c 19 Mar 2004 07:14:23 -0000 1.6 +++ ehci.c 23 May 2004 02:53:03 -0000 @@ -622,7 +622,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++) { @@ -877,32 +877,38 @@ ehci_intr1(sc); } -#if defined(__NetBSD__) || defined(__OpenBSD__) int ehci_detach(struct ehci_softc *sc, int flags) { int rv = 0; +#if defined(__NetBSD__) || defined(__OpenBSD__) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); if (rv != 0) return (rv); +#endif + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + EOWRITE4(sc, EHCI_USBCMD, 0); + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc); +#if defined(__NetBSD__) || defined(__OpenBSD__) if (sc->sc_powerhook != NULL) powerhook_disestablish(sc->sc_powerhook); if (sc->sc_shutdownhook != NULL) shutdownhook_disestablish(sc->sc_shutdownhook); +#endif usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ + usb_freemem(&sc->sc_bus, &sc->sc_fldma); /* XXX free other data structures XXX */ return (rv); } -#endif #if defined(__NetBSD__) || defined(__OpenBSD__) int @@ -1571,7 +1577,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)) { @@ -2136,18 +2142,17 @@ 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, offset; + int adj, len, curlen, maxp, offset, pagelen, seg, segoff; int i; - usb_dma_t *dma = &xfer->dmabuf; + struct usb_dma_mapping *dma = &xfer->dmamap; DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); offset = 0; len = alen; - dataphys = DMAADDR(dma, 0); - dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1)); + maxp = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); qtdstatus = htole32( EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | @@ -2161,60 +2166,60 @@ *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; -#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; + 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; } -#endif -#elif defined(__FreeBSD__) - /* See comment above (XXX) */ - curlen = EHCI_PAGE_SIZE - - EHCI_PAGE_MASK(dataphys); -#endif - /* XXX true for EHCI? */ - /* the length must be a multiple of the max size */ - curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); - DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " - "curlen=%d\n", curlen)); -#ifdef DIAGNOSTIC - if (curlen == 0) - panic("ehci_alloc_std: curlen == 0"); -#endif + 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; + } + /* Adjust down to a multiple of maxp if not at the end. */ + if (curlen < len && curlen % maxp != 0) { + adj = curlen % maxp; + curlen -= adj; + KASSERT(curlen > 0, + ("ehci_alloc_sqtd_chain: need to copy")); + segoff -= adj; + if (segoff < 0) { + seg--; + segoff += dma->segs[seg].ds_len; + } + KASSERT(seg >= 0 && segoff >= 0, + ("ehci_alloc_sqtd_chain: adjust to maxp")); } - DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " - "dataphyslastpage=0x%08x len=%d curlen=%d\n", - dataphys, dataphyslastpage, - len, curlen)); + len -= curlen; if (len != 0) { @@ -2227,19 +2232,6 @@ 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 = htole32(nextphys); cur->qtd.qtd_status = @@ -2252,7 +2244,6 @@ break; 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); Index: ehci_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci_pci.c,v retrieving revision 1.11 diff -u -r1.11 ehci_pci.c --- ehci_pci.c 22 May 2004 14:18:05 -0000 1.11 +++ ehci_pci.c 23 May 2004 14:58:20 -0000 @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include #include @@ -267,15 +269,39 @@ } 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; + } + err = ehci_init(sc); - if (!err) + if (!err) { + sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); + } if (err) { device_printf(self, "USB init failed err=%d\n", err); -#if 0 /* TODO */ ehci_pci_detach(self); -#endif return EIO; } return 0; @@ -286,16 +312,20 @@ { ehci_softc_t *sc = device_get_softc(self); + if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { + ehci_detach(sc, 0); + sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; + } + /* * XXX this code is not yet fit to be used as detach for the EHCI * controller */ - /* - * disable interrupts that might have been switched on in ehci_init - */ - 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); @@ -327,6 +357,7 @@ /* Device interface */ DEVMETHOD(device_probe, ehci_pci_probe), DEVMETHOD(device_attach, ehci_pci_attach), + DEVMETHOD(device_detach, ehci_pci_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ Index: ehcivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehcivar.h,v retrieving revision 1.1 diff -u -r1.1 ehcivar.h --- ehcivar.h 14 Apr 2003 14:04:07 -0000 1.1 +++ ehcivar.h 23 May 2004 02:52:26 -0000 @@ -73,8 +73,11 @@ #define EHCI_HASH_SIZE 128 #define EHCI_COMPANION_MAX 8 +#define EHCI_SCFLG_DONEINIT 0x0001 /* ehci_init() has been called. */ + typedef struct ehci_softc { struct usbd_bus sc_bus; /* base device */ + int sc_flags; bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t sc_size; @@ -144,8 +147,8 @@ usbd_status ehci_init(ehci_softc_t *); int ehci_intr(void *); -#if defined(__NetBSD__) || defined(__OpenBSD__) int ehci_detach(ehci_softc_t *, int); +#if defined(__NetBSD__) || defined(__OpenBSD__) int ehci_activate(device_ptr_t, enum devact); #endif Index: ohci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohci.c,v retrieving revision 1.142 diff -u -r1.142 ohci.c --- ohci.c 26 Mar 2004 18:56:58 -0000 1.142 +++ ohci.c 23 May 2004 19:15:09 -0000 @@ -380,17 +380,20 @@ } return (rv); } +#endif int ohci_detach(struct ohci_softc *sc, int flags) { - int rv = 0; + int i, rv = 0; +#if defined(__NetBSD__) || defined(__OpenBSD__) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); if (rv != 0) return (rv); +#endif usb_uncallout(sc->sc_tmo_rhsc, ohci_rhsc_enable, sc); @@ -399,13 +402,20 @@ shutdownhook_disestablish(sc->sc_shutdownhook); #endif + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ - /* free data structures XXX */ + for (i = 0; i < OHCI_NO_EDS; i++) + ohci_free_sed(sc, sc->sc_eds[i]); + ohci_free_sed(sc, sc->sc_isoc_head); + ohci_free_sed(sc, sc->sc_bulk_head); + ohci_free_sed(sc, sc->sc_ctrl_head); + usb_freemem(&sc->sc_bus, &sc->sc_hccadma); return (rv); } -#endif ohci_soft_ed_t * ohci_alloc_sed(ohci_softc_t *sc) @@ -499,11 +509,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)); @@ -511,22 +521,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_NOINTR); + 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 @@ -534,36 +544,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. - * - * We can describe the above using maxsegsz = 4k and nsegs = 2 - * in the future. */ - 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 */ + 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 { - /* XXX The calculation below is wrong and could - * result in a packet that is not a multiple of the - * MaxPacketSize in the case where the buffer does not - * start on an appropriate address (like for example in - * the case of an mbuf cluster). You'll get an early - * short packet. + /* 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. */ - /* 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); -#ifdef DIAGNOSTIC - if (curlen == 0) - panic("ohci_alloc_std: curlen == 0"); -#endif + 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", @@ -574,7 +615,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; @@ -1561,7 +1602,8 @@ 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)); + /* XXX */ + data->td.td_cbp = htole32(xfer->dmamap.segs[0].ds_addr); data->nexttd = tail; data->td.td_nexttd = htole32(tail->physaddr); data->td.td_be = htole32(le32toh(data->td.td_cbp) + @@ -1605,7 +1647,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++) { @@ -2453,7 +2495,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)) { @@ -3062,7 +3104,8 @@ 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)); + /* XXX */ + data->td.td_cbp = htole32(xfer->dmamap.segs[0].ds_addr); data->nexttd = tail; data->td.td_nexttd = htole32(tail->physaddr); data->td.td_be = htole32(le32toh(data->td.td_cbp) + len - 1); @@ -3295,13 +3338,14 @@ } sitd = opipe->tail.itd; - buf = DMAADDR(&xfer->dmabuf, 0); + buf = xfer->dmamap.segs[0].ds_addr; 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]; + /* XXX, fixme. */ if (ncur == OHCI_ITD_NOFFSET || /* all offsets used */ OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */ Index: ohci_pci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohci_pci.c,v retrieving revision 1.39 diff -u -r1.39 ohci_pci.c --- ohci_pci.c 17 Mar 2004 17:50:47 -0000 1.39 +++ ohci_pci.c 23 May 2004 14:58:34 -0000 @@ -55,6 +55,8 @@ #include #include #include +#include +#include #include #include #include @@ -289,9 +291,35 @@ 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) + if (!err) { + sc->sc_flags |= OHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); + } if (err) { device_printf(self, "USB init failed\n"); @@ -306,17 +334,15 @@ { ohci_softc_t *sc = device_get_softc(self); - /* - * XXX this code is not yet fit to be used as detach for the OHCI - * controller - */ - - /* - * disable interrupts that might have been switched on in ohci_init - */ - if (sc->iot && sc->ioh) - bus_space_write_4(sc->iot, sc->ioh, - OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + if (sc->sc_flags & OHCI_SCFLG_DONEINIT) { + ohci_detach(sc, 0); + 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); @@ -336,7 +362,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; @@ -348,6 +375,7 @@ /* Device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), + DEVMETHOD(device_detach, ohci_pci_detach), DEVMETHOD(device_suspend, ohci_pci_suspend), DEVMETHOD(device_resume, ohci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), Index: ohcivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.36 diff -u -r1.36 ohcivar.h --- ohcivar.h 22 Dec 2003 15:18:46 -0000 1.36 +++ ohcivar.h 23 May 2004 02:50:19 -0000 @@ -83,8 +83,11 @@ #define OHCI_HASH_SIZE 128 +#define OHCI_SCFLG_DONEINIT 0x0001 /* ohci_init() done. */ + typedef struct ohci_softc { struct usbd_bus sc_bus; /* base device */ + int sc_flags; bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t sc_size; @@ -159,8 +162,8 @@ usbd_status ohci_init(ohci_softc_t *); int ohci_intr(void *); +int ohci_detach(ohci_softc_t *, int); #if defined(__NetBSD__) || defined(__OpenBSD__) -int ohci_detach(ohci_softc_t *, int); int ohci_activate(device_ptr_t, enum devact); #endif Index: uhci.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhci.c,v retrieving revision 1.151 diff -u -r1.151 uhci.c --- uhci.c 23 May 2004 11:43:34 -0000 1.151 +++ uhci.c 23 May 2004 19:25:04 -0000 @@ -71,6 +71,7 @@ #include #include #include +#include #include #if defined(DIAGNOSTIC) && defined(__i386__) #include @@ -93,7 +94,7 @@ #include /* Use bandwidth reclamation for control transfers. Some devices choke on it. */ -/*#define UHCI_CTL_LOOP */ +#define UHCI_CTL_LOOP #if defined(__FreeBSD__) #include @@ -142,6 +143,10 @@ #endif #endif +Static int timedloop = 0; +SYSCTL_INT(_debug, OID_AUTO, uhci_timedloop, CTLFLAG_RW, &timedloop, 0, + "enable UHCI timed bandwidth reclaimation"); + struct uhci_pipe { struct usbd_pipe pipe; int nexttoggle; @@ -199,7 +204,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 +213,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 *); @@ -219,6 +226,7 @@ Static int uhci_str(usb_string_descriptor_t *, int, char *); Static void uhci_add_loop(uhci_softc_t *sc); Static void uhci_rem_loop(uhci_softc_t *sc); +Static void uhci_looptimeout(void *); Static usbd_status uhci_setup_isoc(usbd_pipe_handle pipe); Static void uhci_device_isoc_enter(usbd_xfer_handle); @@ -382,7 +390,7 @@ }; #define uhci_add_intr_info(sc, ii) \ - LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list) + LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list); #define uhci_del_intr_info(ii) \ do { \ LIST_REMOVE((ii), list); \ @@ -493,7 +501,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); @@ -533,6 +541,7 @@ SIMPLEQ_INIT(&sc->sc_free_xfers); usb_callout_init(sc->sc_poll_handle); + usb_callout_init(sc->sc_loop_callout); /* Set up the bus struct. */ sc->sc_bus.methods = &uhci_bus_methods; @@ -571,6 +580,7 @@ } return (rv); } +#endif int uhci_detach(struct uhci_softc *sc, int flags) @@ -578,11 +588,16 @@ usbd_xfer_handle xfer; int rv = 0; +#if defined(__NetBSD__) || defined(__OpenBSD__) if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); if (rv != 0) return (rv); +#endif + + UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ + uhci_run(sc, 0); #if defined(__NetBSD__) || defined(__OpenBSD__) powerhook_disestablish(sc->sc_powerhook); @@ -599,10 +614,10 @@ } /* XXX free other data structures XXX */ + usb_freemem(&sc->sc_bus, &sc->sc_dma); return (rv); } -#endif usbd_status uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) @@ -637,6 +652,8 @@ if (xfer != NULL) { memset(xfer, 0, sizeof (struct uhci_xfer)); UXFER(xfer)->iinfo.sc = sc; + usb_init_task(&UXFER(xfer)->abort_task, uhci_timeout_task, + xfer); #ifdef DIAGNOSTIC UXFER(xfer)->iinfo.isdone = 1; xfer->busy_free = XFER_BUSY; @@ -732,6 +749,9 @@ if (cmd & UHCI_CMD_RS) uhci_run(sc, 0); /* in case BIOS has started it */ + uhci_globalreset(sc); + uhci_reset(sc); + /* restore saved state */ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum); @@ -955,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; @@ -963,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; @@ -976,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); } @@ -1006,6 +1027,7 @@ #endif if (++sc->sc_loops == 1) { DPRINTFN(5,("uhci_start_loop: add\n")); + usb_uncallout(sc->sc_loop_callout, uhci_looptimeout, sc); /* Note, we don't loop back the soft pointer. */ sc->sc_last_qh->qh.qh_hlink = htole32(sc->sc_hctl_start->physaddr | UHCI_PTR_QH); @@ -1020,10 +1042,25 @@ #endif if (--sc->sc_loops == 0) { DPRINTFN(5,("uhci_end_loop: remove\n")); - sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T); + if (timedloop) + usb_callout(sc->sc_loop_callout, + MS_TO_TICKS(UHCI_LOOP_DELAY_MSEC), uhci_looptimeout, + sc); + else + sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T); } } +/* Remove the bandwidth reclamation loop after a timeout has expired */ +Static void +uhci_looptimeout(void *addr) +{ + uhci_softc_t *sc = addr; + + DPRINTFN(5,("uhci_looptimeout: remove loop\n")); + sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T); +} + /* Add high speed control QH, called at splusb(). */ void uhci_add_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh) @@ -1064,7 +1101,7 @@ * In this case we set the T bit and wait a little for the HC * to stop looking at the TD. */ - if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + while (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { sqh->qh.qh_elink = htole32(UHCI_PTR_T); delay(UHCI_QH_REMOVE_DELAY); } @@ -1104,7 +1141,7 @@ DPRINTFN(10, ("uhci_remove_ls_ctrl: sqh=%p\n", sqh)); /* See comment in uhci_remove_hs_ctrl() */ - if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + while (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { sqh->qh.qh_elink = htole32(UHCI_PTR_T); delay(UHCI_QH_REMOVE_DELAY); } @@ -1145,7 +1182,7 @@ DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); uhci_rem_loop(sc); /* See comment in uhci_remove_hs_ctrl() */ - if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + while (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { sqh->qh.qh_elink = htole32(UHCI_PTR_T); delay(UHCI_QH_REMOVE_DELAY); } @@ -1483,7 +1520,7 @@ } end: - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii)); } @@ -1506,7 +1543,6 @@ } /* Execute the abort in a process context. */ - usb_init_task(&uxfer->abort_task, uhci_timeout_task, ii->xfer); usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task); } @@ -1645,6 +1681,7 @@ std = KERNADDR(&dma, offs); std->physaddr = DMAADDR(&dma, offs); std->link.std = sc->sc_freetds; + std->aux_dma.block = NULL; /* XXX */ sc->sc_freetds = std; } } @@ -1665,6 +1702,11 @@ } std->td.td_token = htole32(TD_IS_FREE); #endif + /* XXX */ + if (std->aux_dma.block != NULL) { + usb_freemem(&sc->sc_bus, &std->aux_dma); + std->aux_dma.block = NULL; + } std->link.std = sc->sc_freetds; sc->sc_freetds = std; } @@ -1718,12 +1760,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; @@ -1746,29 +1788,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)) @@ -1779,10 +1823,44 @@ 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 = usb_allocmem(&sc->sc_bus, l, maxp, &p->aux_dma); + if (err) { + uhci_free_std_chain(sc, startp, NULL); + return (err); + } + if (!rd) + bcopy((char *)xfer->buffer + i * maxp, + KERNADDR(&p->aux_dma, 0), l); + p->td.td_buffer = htole32(DMAADDR(&p->aux_dma, 0)); + 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); @@ -1849,8 +1927,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); @@ -1934,7 +2012,8 @@ s = splusb(); xfer->status = status; /* make software ignore it */ usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer); - usb_transfer_complete(xfer); + usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task); + uhci_transfer_complete(xfer); splx(s); return; } @@ -1948,6 +2027,7 @@ s = splusb(); xfer->status = status; /* make software ignore it */ usb_uncallout(xfer->timeout_handle, uhci_timeout, ii); + usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task); DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii)); for (std = ii->stdstart; std != NULL; std = std->link.std) std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); @@ -1980,10 +2060,48 @@ #ifdef DIAGNOSTIC ii->isdone = 1; #endif - 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 l, maxp, ofs; + + /* Copy back from any auxillary buffers after a read operation. */ + /* XXX, must be an easier way to detect reads... */ + if (((xfer->rqflags & URQ_REQUEST) && + (xfer->request.bmRequestType & UT_READ)) || + (xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN)) { + maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); + ofs = 0; + for (p = ii->stdstart; p != NULL && ofs < xfer->actlen; + p = p->link.std) { + if (p->aux_dma.block != NULL) { + bus_dmamap_sync(p->aux_dma.block->tag, + p->aux_dma.block->map, + BUS_DMASYNC_POSTWRITE); + l = xfer->actlen - ofs; + if (l > maxp) + l = maxp; + bcopy(KERNADDR(&p->aux_dma, 0), + (char *)xfer->buffer + ofs, l); + } + ofs += maxp; + } + } + + usb_transfer_complete(xfer); +} + /* Close a device bulk pipe. */ void uhci_device_bulk_close(usbd_pipe_handle pipe) @@ -2082,9 +2200,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); @@ -2222,7 +2339,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; @@ -2350,7 +2467,7 @@ struct iso *iso = &upipe->u.iso; uhci_soft_td_t *std; u_int32_t buf, len, status; - int s, i, next, nframes; + int s, i, next, nframes, seg, segoff; DPRINTFN(5,("uhci_device_isoc_enter: used=%d next=%d xfer=%p " "nframes=%d\n", @@ -2380,7 +2497,8 @@ xfer->status = USBD_IN_PROGRESS; UXFER(xfer)->curframe = next; - buf = DMAADDR(&xfer->dmabuf, 0); + seg = 0; + segoff = 0; status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS); @@ -2391,7 +2509,17 @@ 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")); + 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); @@ -2502,7 +2630,7 @@ UXFER(xfer)->iinfo.isdone = 1; #endif /* Run callback and remove from interrupt list. */ - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); } @@ -2650,6 +2778,8 @@ return; } #endif + ii->stdstart = NULL; + ii->stdend = NULL; } void @@ -2679,8 +2809,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 @@ -2708,8 +2838,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; + } } } @@ -2738,6 +2871,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)); } @@ -2761,6 +2896,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)); } @@ -2793,7 +2930,7 @@ DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", sqh->pos, sqh)); /* See comment in uhci_remove_ctrl() */ - if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { + while (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) { sqh->qh.qh_elink = htole32(UHCI_PTR_T); delay(UHCI_QH_REMOVE_DELAY); } @@ -3161,7 +3298,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)) { @@ -3451,7 +3588,7 @@ ret: xfer->status = err; s = splusb(); - usb_transfer_complete(xfer); + uhci_transfer_complete(xfer); splx(s); return (USBD_IN_PROGRESS); } @@ -3487,7 +3624,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.53 diff -u -r1.53 uhci_pci.c --- uhci_pci.c 17 Mar 2004 17:50:47 -0000 1.53 +++ uhci_pci.c 23 May 2004 14:58:47 -0000 @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #include #if defined(__FreeBSD__) @@ -328,9 +330,34 @@ #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) + if (!err) { + sc->sc_flags |= UHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); + } if (err) { device_printf(self, "USB init failed\n"); @@ -345,22 +372,15 @@ { uhci_softc_t *sc = device_get_softc(self); - /* - * XXX This function is not yet complete and should not be added - * method list. - */ -#if 0 - if uhci_init - was successful - we should call something like uhci_deinit -#endif + if (sc->sc_flags & UHCI_SCFLG_DONEINIT) { + uhci_detach(sc, 0); + sc->sc_flags &= ~UHCI_SCFLG_DONEINIT; + } - /* - * disable interrupts that might have been switched on in - * uhci_init. - */ - if (sc->iot && sc->ioh) - bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 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); @@ -394,6 +414,7 @@ /* Device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), + DEVMETHOD(device_detach, uhci_pci_detach), DEVMETHOD(device_suspend, uhci_pci_suspend), DEVMETHOD(device_resume, uhci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), Index: uhcireg.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhcireg.h,v retrieving revision 1.21 diff -u -r1.21 uhcireg.h --- uhcireg.h 4 Jul 2003 01:50:38 -0000 1.21 +++ uhcireg.h 19 Jul 2003 00:20:00 -0000 @@ -128,6 +128,13 @@ #define UHCI_QH_REMOVE_DELAY 5 /* + * Keep the bandwidth reclaimation loop in place for this long after + * all transfers have completed. This avoids waiting until the start + * of the next 1ms frame if another transfer arrives soon. + */ +#define UHCI_LOOP_DELAY_MSEC 5 + +/* * The Queue Heads and Transfer Descriptors are accessed * by both the CPU and the USB controller which run * concurrently. This means that they have to be accessed @@ -140,8 +147,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 +164,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 +178,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 +193,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.36 diff -u -r1.36 uhcivar.h --- uhcivar.h 15 Jul 2003 23:19:49 -0000 1.36 +++ uhcivar.h 23 May 2004 19:24:09 -0000 @@ -96,6 +96,7 @@ 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. */ }; /* * Make the size such that it is a multiple of UHCI_TD_ALIGN. This way @@ -131,8 +132,11 @@ u_int bandwidth; /* max bandwidth used by this frame */ }; +#define UHCI_SCFLG_DONEINIT 0x0001 /* uhci_init() done */ + typedef struct uhci_softc { struct usbd_bus sc_bus; /* base device */ + int sc_flags; bus_space_tag_t iot; bus_space_handle_t ioh; bus_size_t sc_size; @@ -155,6 +159,7 @@ uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */ uhci_soft_qh_t *sc_last_qh; /* dummy QH at the end */ u_int32_t sc_loops; /* number of QHs that wants looping */ + usb_callout_t sc_loop_callout; /* timer for delayed de-looping */ uhci_soft_td_t *sc_freetds; /* TD free list */ uhci_soft_qh_t *sc_freeqhs; /* QH free list */ @@ -195,8 +200,8 @@ usbd_status uhci_init(uhci_softc_t *); int uhci_intr(void *); -#if defined(__NetBSD__) || defined(__OpenBSD__) int uhci_detach(uhci_softc_t *, int); +#if defined(__NetBSD__) || defined(__OpenBSD__) int uhci_activate(device_ptr_t, enum devact); #endif Index: uhub.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/uhub.c,v retrieving revision 1.54 diff -u -r1.54 uhub.c --- uhub.c 24 Aug 2003 17:55:55 -0000 1.54 +++ uhub.c 23 May 2004 18:32:09 -0000 @@ -121,10 +121,11 @@ devclass_t uhubroot_devclass; Static device_method_t uhubroot_methods[] = { + DEVMETHOD(bus_child_detached, uhub_child_detached), DEVMETHOD(device_probe, uhub_match), DEVMETHOD(device_attach, uhub_attach), - /* detach is not allowed for a root hub */ + DEVMETHOD(device_detach, uhub_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), Index: umass.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/umass.c,v retrieving revision 1.106 diff -u -r1.106 umass.c --- umass.c 22 May 2004 09:29:52 -0000 1.106 +++ umass.c 23 May 2004 12:42:27 -0000 @@ -437,6 +437,10 @@ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I, FORCE_SHORT_INQUIRY }, + { 0x090a, 0x1100, RID_WILDCARD, + UMASS_PROTO_UFI | UMASS_PROTO_CBI, + NO_QUIRKS + }, { VID_EOT, PID_EOT, RID_EOT, 0, 0 } }; Index: usb.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb.c,v retrieving revision 1.97 diff -u -r1.97 usb.c --- usb.c 21 Feb 2004 21:10:49 -0000 1.97 +++ usb.c 23 May 2004 18:51:21 -0000 @@ -126,6 +126,9 @@ struct usb_softc { USBBASEDEVICE sc_dev; /* base device */ +#ifdef __FreeBSD__ + dev_t sc_usbdev; /* /dev/usbN device */ +#endif usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ @@ -161,11 +164,19 @@ #endif Static void usb_discover(void *); +#ifdef __FreeBSD__ +Static bus_child_detached_t usb_child_detached; +#endif Static void usb_create_event_thread(void *); Static void usb_event_thread(void *); Static void usb_task_thread(void *); Static struct proc *usb_task_thread_proc = NULL; +#ifdef __FreeBSD__ +Static dev_t usb_dev; /* The /dev/usb device. */ +Static int usb_ndevs; /* Number of /dev/usbN devices. */ +#endif + #define USB_MAX_EVENTS 100 struct usb_event_q { struct usb_event ue; @@ -184,6 +195,7 @@ Static const char *usbrev_str[] = USBREV_STR; USB_DECLARE_DRIVER_INIT(usb, + DEVMETHOD(bus_child_detached, usb_child_detached), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown) @@ -206,7 +218,6 @@ #elif defined(__FreeBSD__) struct usb_softc *sc = device_get_softc(self); void *aux = device_get_ivars(self); - static int global_init_done = 0; #endif usbd_device_handle dev; usbd_status err; @@ -306,13 +317,12 @@ usb_create_event_thread(sc); /* The per controller devices (used for usb_discover) */ /* XXX This is redundant now, but old usbd's will want it */ - make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, - 0660, "usb%d", device_get_unit(self)); - if (!global_init_done) { + sc->sc_usbdev = make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, + GID_OPERATOR, 0660, "usb%d", device_get_unit(self)); + if (usb_ndevs++ == 0) { /* The device spitting out events */ - make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR, - 0660, "usb"); - global_init_done = 1; + usb_dev = make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, + GID_OPERATOR, 0660, "usb"); } #endif @@ -864,11 +874,11 @@ } return (rv); } +#endif -int -usb_detach(device_ptr_t self, int flags) +USB_DETACH(usb) { - struct usb_softc *sc = (struct usb_softc *)self; + USB_DETACH_START(usb, sc); struct usb_event ue; DPRINTF(("usb_detach: start\n")); @@ -888,6 +898,14 @@ DPRINTF(("usb_detach: event thread dead\n")); } +#ifdef __FreeBSD__ + destroy_dev(sc->sc_usbdev); + if (--usb_ndevs == 0) { + destroy_dev(usb_dev); + usb_dev = NULL; + } +#endif + usbd_finish(); #ifdef USB_USE_SOFTINTR @@ -906,18 +924,17 @@ return (0); } -#elif defined(__FreeBSD__) -int -usb_detach(device_t self) + +#if defined(__FreeBSD__) +Static void +usb_child_detached(device_t self, device_t child) { - DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self))); + struct usb_softc *sc = device_get_softc(self); - return (EINVAL); + /* XXX, should check it is the right device. */ + sc->sc_port.device = NULL; } -#endif - -#if defined(__FreeBSD__) DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0); Index: usb_mem.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb_mem.c,v retrieving revision 1.5 diff -u -r1.5 usb_mem.c --- usb_mem.c 4 Oct 2003 22:13:21 -0000 1.5 +++ usb_mem.c 5 Apr 2004 23:35:31 -0000 @@ -142,7 +142,8 @@ 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) { + if (p->tag == tag && p->size >= size && p->size < size * 2 && + p->align >= align) { LIST_REMOVE(p, next); usb_blk_nfree--; splx(s); @@ -229,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: usbdi.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdi.c,v retrieving revision 1.85 diff -u -r1.85 usbdi.c --- usbdi.c 4 Mar 2004 20:49:03 -0000 1.85 +++ usbdi.c 23 May 2004 19:18:16 -0000 @@ -85,6 +85,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; @@ -281,7 +285,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; @@ -300,43 +304,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); + return (USBD_NOMEM); - 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; - - 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); @@ -345,6 +342,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_PREREAD); + 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) @@ -353,42 +395,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 @@ -746,6 +849,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; @@ -757,9 +861,11 @@ 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; + usbd_status status; int repeat = pipe->repeat; - int polling; + int polling, xfer_flags; SPLUSBCHECK; @@ -784,23 +890,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_POSTWRITE); - /* 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; } } @@ -830,30 +927,33 @@ xfer->status = USBD_SHORT_XFER; } - if (xfer->callback) - xfer->callback(xfer, xfer->priv, xfer->status); - -#ifdef DIAGNOSTIC - if (pipe->methods->done != NULL) + /* Copy any xfer fields in case the xfer goes away in the callback. */ + status = xfer->status; + xfer_flags = xfer->flags; + /* + * For repeat operations, call the callback first, as the xfer + * will not go away and the "done" method may modify it. Otherwise + * reverse the order in case the callback wants to free or reuse + * the xfer. + */ + if (repeat) { + if (xfer->callback) + xfer->callback(xfer, xfer->priv, status); pipe->methods->done(xfer); - else - printf("usb_transfer_complete: pipe->methods->done == NULL\n"); -#else - pipe->methods->done(xfer); -#endif - - if ((xfer->flags & USBD_SYNCHRONOUS) && !polling) - wakeup(xfer); + } else { + pipe->methods->done(xfer); + if (xfer->callback) + xfer->callback(xfer, xfer->priv, status); - if (!repeat) { /* XXX should we stop the queue on all errors? */ - if ((xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) && + if ((status == USBD_CANCELLED || status == USBD_TIMEOUT) && pipe->iface != NULL) /* not control pipe */ pipe->running = 0; else usbd_start_next(pipe); } + if ((xfer_flags & USBD_SYNCHRONOUS) && !polling) + wakeup(xfer); } usbd_status @@ -874,6 +974,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: usbdi_util.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdi_util.c,v retrieving revision 1.31 diff -u -r1.31 usbdi_util.c --- usbdi_util.c 24 Aug 2003 17:55:55 -0000 1.31 +++ usbdi_util.c 21 Sep 2003 15:28:30 -0000 @@ -440,7 +440,7 @@ } usbd_get_xfer_status(xfer, NULL, NULL, size, &err); DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size)); - if (err) { + if (err == USBD_STALLED) { DPRINTF(("usbd_bulk_transfer: error=%d\n", err)); usbd_clear_endpoint_stall(pipe); } Index: usbdivar.h =================================================================== RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.40 diff -u -r1.40 usbdivar.h --- usbdivar.h 15 Jul 2003 22:42:37 -0000 1.40 +++ usbdivar.h 5 Apr 2004 23:20:49 -0000 @@ -124,7 +124,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 { @@ -180,6 +181,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; @@ -207,7 +217,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