On Tue, Jul 20, 2004 at 12:45:08AM +0100, Ian Dowse wrote: > In message <20040719.170132.52458790.imp_at_bsdimp.com>, "M. Warner Losh" writes: > >In message: <200407191950.aa12733_at_salmon.maths.tcd.ie> > > Ian Dowse <iedowse_at_maths.tcd.ie> writes: > >: http://people.freebsd.org/~iedowse/usb.diff This patch doesn't solve my EHCI problems ( see: http://lists.freebsd.org/pipermail/freebsd-current/2004-July/031838.html ) but it seems like a step in the right direction. For one thing, I don't panic in EHCI when I reboot my system after using my Creative Nomad. I had to change your patch a bit so that it would work against version 1.86 of usbdi.c: Index: usbdi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usbdi.c,v retrieving revision 1.86 diff -u -r1.86 usbdi.c --- usbdi.c 19 Jul 2004 20:49:02 -0000 1.86 +++ usbdi.c 20 Jul 2004 03:16:46 -0000 _at__at_ -85,6 +85,10 _at__at_ 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; _at__at_ -281,7 +285,7 _at__at_ 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; _at__at_ -300,43 +304,36 _at__at_ 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); _at__at_ -345,6 +342,51 _at__at_ 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) _at__at_ -353,42 +395,103 _at__at_ 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 _at__at_ -746,6 +849,7 _at__at_ 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; _at__at_ -757,12 +861,14 _at__at_ 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 sync = xfer->flags & USBD_SYNCHRONOUS; int erred = xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT; + xfer->status == USBD_TIMEOUT; int repeat = pipe->repeat; - int polling; + int polling, xfer_flags; SPLUSBCHECK; _at__at_ -787,23 +893,14 _at__at_ 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; } } _at__at_ -833,28 +930,32 _at__at_ 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 (sync && !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 (erred && pipe->iface != NULL) /* not control pipe */ pipe->running = 0; else usbd_start_next(pipe); } + if (sync && !polling) + wakeup(xfer); } usbd_status _at__at_ -875,6 +976,7 _at__at_ 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; -- Craig Rodrigues http://crodrigues.org rodrigc_at_crodrigues.orgReceived on Tue Jul 20 2004 - 01:19:30 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:02 UTC