Index: share/man/man4/ugen.4 =================================================================== RCS file: /cvs/netbsd/netbsd/src/share/man/man4/ugen.4,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- share/man/man4/ugen.4 23 Nov 2005 08:56:08 -0000 1.1.1.1 +++ share/man/man4/ugen.4 26 Jun 2006 21:02:02 -0000 1.2 @@ -77,14 +77,14 @@ If an endpoint address is used both for input and output the device can be opened for both read or write. .Pp -To find out what endpoints that exist there are a series of +To find out what endpoints exist there are a series of .Xr ioctl 2 -operation on the control endpoint that returns the USB descriptors +operations on the control endpoint that return the USB descriptors of the device, configurations, interfaces, and endpoints. .Pp The control transfer mode can only happen on the control endpoint -which is always endpoint 0. The control endpoint accepts request -and may respond with an answer to such request. Control request +which is always endpoint 0. The control endpoint accepts requests +and may respond with an answer to such requests. Control requests are issued by .Xr ioctl 2 calls. @@ -108,7 +108,15 @@ and .Xr write 2 should be used. -All IO operations on a bulk endpoint are unbuffered. +All IO operations on a bulk endpoint are normally unbuffered. +Buffering can be enabled using a +.Dv USB_SET_BULK_RA +or +.Dv USB_SET_BULK_WB +.Xr ioctl 2 +call, to enable read-ahead and write-behind respectively. When +read-ahead or write-behind are enabled, the file descriptor may +be set to use non-blocking IO. .Pp The interrupt transfer mode can be in or out depending on the endpoint. @@ -272,6 +280,57 @@ issue any USB transactions. .El .Pp +Bulk endpoints handle the following +.Xr ioctl 2 +calls: +.Pp +.Bl -tag -width indent -compact +.It Dv USB_SET_BULK_RA (int) +Enable or disable bulk read-ahead. When enabled, the driver will +begin to read data from the device into a buffer. The +.Xr read 2 +call will read data from this buffer, blocking if necessary until +there is enough data to read the length of data requested. The +buffer size and the read request length can be set by the +.Dv USB_SET_BULK_RA_OPT +.Xr ioctl 2 +call. +.It Dv USB_SET_BULK_WB (int) +Enable or disable bulk write-behind. When enabled, the driver will +buffer data from the +.Xr write 2 +call before writing it to the device. +.Xr write 2 +will block if there is not enough room in the buffer for all +the data. The buffer size and the write request length can be set +by the +.Dv USB_SET_BULK_WB_OPT +.Xr ioctl 2 +call. +.It Dv USB_SET_BULK_RA_OPT (struct usb_bulk_ra_wb_opt) +Set the size of the buffer and the length of the read requests used by +the driver when bulk read-ahead is enabled. The changes do not take +effect until the next time bulk read-ahead is enabled. Read requests +are made for the length specified, and the host controller driver +(i.e., +.Xr ehci 4 , +.Xr ohci 4 , and +.Xr uhci 4 ) will perform as many bus transfers as required. If +transfers from the device should be smaller than the maximum length, +.Dv ra_wb_request_size +must be set to the required length. +.Bd -literal +struct usb_bulk_ra_wb_opt { + int ra_wb_buffer_size; + int ra_wb_request_size; +}; +.Ed +.It Dv USB_SET_BULK_WB_OPT (struct usb_bulk_ra_wb_opt) +Set the size of the buffer and the length of the write requests used +by the driver when bulk write-behind is enabled. The changes do not +take effect until the next time bulk write-behind is enabled. +.El +.Pp Note that there are two different ways of addressing configurations, interfaces, alternatives, and endpoints: by index or by number. The index is the ordinal number (starting from 0) of the descriptor Index: sys/dev/usb/files.usb =================================================================== RCS file: /cvs/netbsd/netbsd/src/sys/dev/usb/files.usb,v retrieving revision 1.1.1.2 retrieving revision 1.3 diff -u -r1.1.1.2 -r1.3 --- sys/dev/usb/files.usb 19 Jun 2006 15:44:45 -0000 1.1.1.2 +++ sys/dev/usb/files.usb 26 Jun 2006 20:17:32 -0000 1.3 @@ -48,6 +48,7 @@ # Generic devices +defflag UGEN_BULK_RA_WB device ugen attach ugen at uhub file dev/usb/ugen.c ugen needs-flag Index: sys/dev/usb/ugen.c =================================================================== RCS file: /cvs/netbsd/netbsd/src/sys/dev/usb/ugen.c,v retrieving revision 1.1.1.2 retrieving revision 1.9 diff -u -r1.1.1.2 -r1.9 --- sys/dev/usb/ugen.c 9 Jun 2006 21:34:19 -0000 1.1.1.2 +++ sys/dev/usb/ugen.c 16 Jul 2006 16:49:00 -0000 1.9 @@ -8,6 +8,11 @@ * by Lennart Augustsson (address@hidden) at * Carlstedt Research & Technology. * + * Copyright (c) 2006 BBN Technologies Corp. All rights reserved. + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and the Department of the Interior National Business + * Center under agreement number NBCHC050166. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -41,6 +46,8 @@ #include __KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.82 2006/04/14 16:41:53 christos Exp $"); +#include "opt_ugen_bulk_ra_wb.h" + #include #include #include @@ -63,10 +70,12 @@ #include #include #include +#include /* for usbdivar.h */ #include #include #include +#include /* for usbd_xfer_handle */ #ifdef UGEN_DEBUG #define DPRINTF(x) if (ugendebug) logprintf x @@ -85,6 +94,8 @@ #define UGEN_NISOREQS 6 /* number of outstanding xfer requests */ #define UGEN_NISORFRMS 4 /* number of frames (miliseconds) per req */ +#define UGEN_BULK_RA_WB_BUFSIZE 16384 /* default buffer size: XXX add this */ + struct ugen_endpoint { struct ugen_softc *sc; usb_endpoint_descriptor_t *edesc; @@ -92,6 +103,8 @@ int state; #define UGEN_ASLP 0x02 /* waiting for data */ #define UGEN_SHORT_OK 0x04 /* short xfers are OK */ +#define UGEN_BULK_RA 0x08 /* in bulk read-ahead mode */ +#define UGEN_BULK_WB 0x10 /* in bulk write-behind mode */ usbd_pipe_handle pipeh; struct clist q; struct selinfo rsel; @@ -100,6 +113,13 @@ u_char *limit; /* end of circular buffer (isoc) */ u_char *cur; /* current read location (isoc) */ u_int32_t timeout; +#ifdef UGEN_BULK_RA_WB + u_int32_t ra_wb_bufsize; /* requested size for RA/WB buffer */ + u_int32_t ra_wb_reqsize; /* requested xfer length for RA/WB */ + u_int32_t ra_wb_used; /* how much is in buffer */ + u_int32_t ra_wb_xferlen; /* current xfer length for RA/WB */ + usbd_xfer_handle ra_wb_xfer; +#endif struct isoreq { struct ugen_endpoint *sce; usbd_xfer_handle xfer; @@ -169,6 +189,12 @@ usbd_status status); Static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); +#ifdef UGEN_BULK_RA_WB +Static void ugen_bulkra_intr(usbd_xfer_handle xfer, usbd_private_handle addr, + usbd_status status); +Static void ugen_bulkwb_intr(usbd_xfer_handle xfer, usbd_private_handle addr, + usbd_status status); +#endif Static int ugen_do_read(struct ugen_softc *, int, struct uio *, int); Static int ugen_do_write(struct ugen_softc *, int, struct uio *, int); Static int ugen_do_ioctl(struct ugen_softc *, int, u_long, @@ -396,6 +422,14 @@ edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); +#ifdef UGEN_BULK_RA_WB + sce->ra_wb_bufsize = UGEN_BULK_RA_WB_BUFSIZE; + /* + * Use request size for non-RA/WB transfers + * as the default. + */ + sce->ra_wb_reqsize = UGEN_BBSIZE; +#endif break; case UE_ISOCHRONOUS: if (dir == OUT) @@ -442,6 +476,9 @@ bad: while (--i >= 0) /* implicit buffer free */ usbd_free_xfer(sce->isoreqs[i].xfer); + /* Except it doesn't free the big buffer sce->ibuf; + relying on ugenclose to do it? Does ugenclose + even get called when there's an error in open? */ return (ENOMEM); case UE_CONTROL: sce->timeout = USBD_DEFAULT_TIMEOUT; @@ -500,6 +537,14 @@ case UE_ISOCHRONOUS: for (i = 0; i < UGEN_NISOREQS; ++i) usbd_free_xfer(sce->isoreqs[i].xfer); + break; +#ifdef UGEN_BULK_RA_WB + case UE_BULK: + if (sce->state & (UGEN_BULK_RA | UGEN_BULK_WB)) + /* ibuf freed below */ + usbd_free_xfer(sce->ra_wb_xfer); + break; +#endif default: break; @@ -508,6 +553,11 @@ if (sce->ibuf != NULL) { free(sce->ibuf, M_USBDEV); sce->ibuf = NULL; + /* I don't think this next line is right, besides + it's already done in the UE_INTERRUPT case + above. Or else, I just made it not right but + it's still a duplicate. It doesn't hurt to + call it twice. */ clfree(&sce->q); } } @@ -525,6 +575,9 @@ usbd_status err; int s; int error = 0; +#ifdef UGEN_BULK_RA_WB + int first; +#endif DPRINTFN(5, ("%s: ugenread: %d\n", USBDEVNAME(sc->sc_dev), endpt)); @@ -584,6 +637,85 @@ } break; case UE_BULK: +#ifdef UGEN_BULK_RA_WB + if (sce->state & UGEN_BULK_RA) { + DPRINTFN(5, ("ugenread: BULK_RA req: %d used: %d\n", + uio->uio_resid, sce->ra_wb_used)); + /* XXX should we do this outer loop only if + !UGEN_SHORT_OK ? */ + first = 1; + while (uio->uio_resid > 0 && !error) { + s = splusb(); + while (sce->ra_wb_used == 0) { + if (first && flag & IO_NDELAY) { + splx(s); + return (EWOULDBLOCK); + } + sce->state |= UGEN_ASLP; + DPRINTFN(5, + ("ugenread: sleep on %p\n", + sce)); + /* XXX should we pay attention to + sce->timeout here to make this + closer to original bulk reads? */ + error = tsleep(sce, PZERO | PCATCH, + "ugenri", 0); + DPRINTFN(5, + ("ugenread: woke, error=%d\n", + error)); + if (sc->sc_dying) + error = EIO; + if (error) { + sce->state &= ~UGEN_ASLP; + break; + } + } + first = 0; + + /* Copy data to the process. */ + while (uio->uio_resid > 0 + && sce->ra_wb_used > 0) { + n = min(uio->uio_resid, + sce->ra_wb_used); + n = min(n, sce->limit - sce->cur); + error = uiomove(sce->cur, n, uio); + if (error) + break; + sce->cur += n; + sce->ra_wb_used -= n; + if (sce->cur == sce->limit) + sce->cur = sce->ibuf; + } + + /* + * If the transfers stopped because the + * buffer was full, restart them. + */ + if (sce->ra_wb_xfer->done && + sce->ra_wb_used < sce->limit - sce->ibuf) { + n = (sce->limit - sce->ibuf) + - sce->ra_wb_used; + usbd_setup_xfer(sce->ra_wb_xfer, + sce->pipeh, sce, NULL, + min(n, sce->ra_wb_xferlen), + USBD_NO_COPY, USBD_NO_TIMEOUT, + ugen_bulkra_intr); + err = usbd_transfer(sce->ra_wb_xfer); + if (err != USBD_IN_PROGRESS) + /* + * The transfer has not been + * queued. Setting done + * should make us try + * again at the next read. + */ + sce->ra_wb_xfer->done = 1; + } + + splx(s); + } + break; + } +#endif xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (ENOMEM); @@ -678,6 +810,11 @@ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT]; u_int32_t n; int error = 0; +#ifdef UGEN_BULK_RA_WB + int s, first; + u_int32_t tn; + char *dbuf; +#endif usbd_xfer_handle xfer; usbd_status err; @@ -702,6 +839,92 @@ switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_BULK: +#ifdef UGEN_BULK_RA_WB + if (sce->state & UGEN_BULK_WB) { + DPRINTFN(5, ("ugenwrite: BULK_WB req: %d used: %d\n", + uio->uio_resid, sce->ra_wb_used)); + first = 1; + while (uio->uio_resid > 0 && !error) { + s = splusb(); + while (sce->ra_wb_used == + sce->limit - sce->ibuf) { + if (first && flag & IO_NDELAY) { + splx(s); + return (EWOULDBLOCK); + } + sce->state |= UGEN_ASLP; + DPRINTFN(5, + ("ugenwrite: sleep on %p\n", + sce)); + /* XXX should we pay attention to + sce->timeout here to make this + closer to original bulk writes? */ + error = tsleep(sce, PZERO | PCATCH, + "ugenwb", 0); + DPRINTFN(5, + ("ugenwrite: woke, error=%d\n", + error)); + if (sc->sc_dying) + error = EIO; + if (error) { + sce->state &= ~UGEN_ASLP; + break; + } + } + first = 0; + + /* Copy data from the process. */ + while (uio->uio_resid > 0 && + sce->ra_wb_used < sce->limit - sce->ibuf) { + n = min(uio->uio_resid, + (sce->limit - sce->ibuf) + - sce->ra_wb_used); + n = min(n, sce->limit - sce->fill); + error = uiomove(sce->fill, n, uio); + if (error) + break; + sce->fill += n; + sce->ra_wb_used += n; + if (sce->fill == sce->limit) + sce->fill = sce->ibuf; + } + + /* + * If the transfers stopped because the + * buffer was empty, restart them. + */ + if (sce->ra_wb_xfer->done && + sce->ra_wb_used > 0) { + dbuf = (char *)usbd_get_buffer(sce->ra_wb_xfer); + n = min(sce->ra_wb_used, + sce->ra_wb_xferlen); + tn = min(n, sce->limit - sce->cur); + memcpy(dbuf, sce->cur, tn); + dbuf += tn; + if (n - tn > 0) + memcpy(dbuf, sce->ibuf, + n - tn); + usbd_setup_xfer(sce->ra_wb_xfer, + sce->pipeh, sce, NULL, n, + USBD_NO_COPY, USBD_NO_TIMEOUT, + ugen_bulkwb_intr); + err = usbd_transfer(sce->ra_wb_xfer); + if (err != USBD_IN_PROGRESS) + /* + * The transfer has not been + * queued. Setting done + * should make us try again + * at the next read. + */ + sce->ra_wb_xfer->done = 1; + } + + splx(s); + } + + break; + } +#endif xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (EIO); @@ -940,6 +1163,135 @@ selnotify(&sce->rsel, 0); } +#ifdef UGEN_BULK_RA_WB +Static void +ugen_bulkra_intr(usbd_xfer_handle xfer, usbd_private_handle addr, + usbd_status status) +{ + struct ugen_endpoint *sce = addr; + u_int32_t count, n; + char const *tbuf; + usbd_status err; + + /* Return if we are aborting. */ + if (status == USBD_CANCELLED) + return; + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTF(("ugen_bulkra_intr: status=%d\n", status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(sce->pipeh); + } else { + usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); + + /* Keep track of how much is in the buffer. */ + sce->ra_wb_used += count; + + /* Copy data to buffer. */ + tbuf = (char const *)usbd_get_buffer(sce->ra_wb_xfer); + n = min(count, sce->limit - sce->fill); + memcpy(sce->fill, tbuf, n); + tbuf += n; + count -= n; + sce->fill += n; + if (sce->fill == sce->limit) + sce->fill = sce->ibuf; + if (count > 0) { + memcpy(sce->fill, tbuf, count); + sce->fill += count; + } + } + + /* Set up the next request if necessary. */ + n = (sce->limit - sce->ibuf) - sce->ra_wb_used; + if (n > 0) { + usbd_setup_xfer(xfer, sce->pipeh, sce, NULL, + min(n, sce->ra_wb_xferlen), USBD_NO_COPY, USBD_NO_TIMEOUT, + ugen_bulkra_intr); + err = usbd_transfer(xfer); + if (err != USBD_IN_PROGRESS) { + printf("usbd_bulkra_intr: error=%d\n", err); + /* + * The transfer has not been queued. Setting done + * should make us try again at the next read. + */ + xfer->done = 1; + /* pipe->running set = 0 in usbd_start_next */ + } + } + + if (sce->state & UGEN_ASLP) { + sce->state &= ~UGEN_ASLP; + DPRINTFN(5, ("ugen_bulkra_intr: waking %p\n", sce)); + wakeup(sce); + } + selnotify(&sce->rsel, 0); +} + +Static void +ugen_bulkwb_intr(usbd_xfer_handle xfer, usbd_private_handle addr, + usbd_status status) +{ + struct ugen_endpoint *sce = addr; + u_int32_t count, n; + char *tbuf; + usbd_status err; + + /* Return if we are aborting. */ + if (status == USBD_CANCELLED) + return; + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTF(("ugen_bulkwb_intr: status=%d\n", status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(sce->pipeh); + } else { + usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); + + /* Keep track of how much is in the buffer. */ + sce->ra_wb_used -= count; + + /* Update buffer pointers. */ + sce->cur += count; + if (sce->cur >= sce->limit) + sce->cur = sce->ibuf + (sce->cur - sce->limit); + } + + /* Set up next request if necessary. */ + if (sce->ra_wb_used > 0) { + /* copy data from buffer */ + tbuf = (char *)usbd_get_buffer(sce->ra_wb_xfer); + count = min(sce->ra_wb_used, sce->ra_wb_xferlen); + n = min(count, sce->limit - sce->cur); + memcpy(tbuf, sce->cur, n); + tbuf += n; + if (count - n > 0) + memcpy(tbuf, sce->ibuf, count - n); + + usbd_setup_xfer(xfer, sce->pipeh, sce, NULL, + count, USBD_NO_COPY, USBD_NO_TIMEOUT, + ugen_bulkwb_intr); + err = usbd_transfer(xfer); + if (err != USBD_IN_PROGRESS) { + printf("usbd_bulkwb_intr: error=%d\n", err); + /* + * The transfer has not been queued. Setting done + * should make us try again at the next write. + */ + xfer->done = 1; + /* pipe->running set = 0 in usbd_start_next */ + } + } + + if (sce->state & UGEN_ASLP) { + sce->state &= ~UGEN_ASLP; + DPRINTFN(5, ("ugen_bulkwb_intr: waking %p\n", sce)); + wakeup(sce); + } + selnotify(&sce->rsel, 0); +} +#endif + Static usbd_status ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) { @@ -1089,6 +1441,166 @@ return (EINVAL); sce->timeout = *(int *)addr; return (0); + case USB_SET_BULK_RA: +#ifdef UGEN_BULK_RA_WB + if (endpt == USB_CONTROL_ENDPOINT) + return (EINVAL); + sce = &sc->sc_endpoints[endpt][IN]; + if (sce == NULL || sce->pipeh == NULL) + return (EINVAL); + edesc = sce->edesc; + if ((edesc->bmAttributes & UE_XFERTYPE) != UE_BULK) + return (EINVAL); + + if (*(int *)addr) { + /* Only turn RA on if it's currently off. */ + if (sce->state & UGEN_BULK_RA) + return (0); + + if (sce->ra_wb_bufsize == 0 || sce->ra_wb_reqsize == 0) + /* shouldn't happen */ + return (EINVAL); + sce->ra_wb_xfer = usbd_alloc_xfer(sc->sc_udev); + if (sce->ra_wb_xfer == NULL) + return (ENOMEM); + sce->ra_wb_xferlen = sce->ra_wb_reqsize; + /* + * Set up a dmabuf because we reuse the xfer with + * the same (max) request length like isoc. + */ + if (usbd_alloc_buffer(sce->ra_wb_xfer, + sce->ra_wb_xferlen) == 0) { + usbd_free_xfer(sce->ra_wb_xfer); + return (ENOMEM); + } + sce->ibuf = malloc(sce->ra_wb_bufsize, + M_USBDEV, M_WAITOK); + sce->fill = sce->cur = sce->ibuf; + sce->limit = sce->ibuf + sce->ra_wb_bufsize; + sce->ra_wb_used = 0; + sce->state |= UGEN_BULK_RA; + /* Now start reading. */ + usbd_setup_xfer(sce->ra_wb_xfer, sce->pipeh, sce, + /* XXX I don't think we need a separate buffer + to then copy into ibuf */ + NULL, + min(sce->ra_wb_xferlen, sce->ra_wb_bufsize), + /* ignoring UGEN_SHORT_OK and timeout */ + USBD_NO_COPY, USBD_NO_TIMEOUT, + ugen_bulkra_intr); + err = usbd_transfer(sce->ra_wb_xfer); + if (err != USBD_IN_PROGRESS) { + /* if it's no memory, usb_transfer_complete + has already been called + XXX so make sure to deal properly there */ + sce->state &= ~UGEN_BULK_RA; + free(sce->ibuf, M_USBDEV); + sce->ibuf = NULL; + usbd_free_xfer(sce->ra_wb_xfer); + return (EIO); + } + } else { + /* Only turn RA off if it's currently on. */ + if (!(sce->state & UGEN_BULK_RA)) + return (0); + + sce->state &= ~UGEN_BULK_RA; + usbd_abort_pipe(sce->pipeh); + usbd_free_xfer(sce->ra_wb_xfer); + /* XXX discard whatever's in the buffer, but we + should keep it around and drain the buffer + instead */ + free(sce->ibuf, M_USBDEV); + sce->ibuf = NULL; + } + return (0); +#else + return (EINVAL); +#endif + case USB_SET_BULK_WB: +#ifdef UGEN_BULK_RA_WB + if (endpt == USB_CONTROL_ENDPOINT) + return (EINVAL); + sce = &sc->sc_endpoints[endpt][OUT]; + if (sce == NULL || sce->pipeh == NULL) + return (EINVAL); + edesc = sce->edesc; + if ((edesc->bmAttributes & UE_XFERTYPE) != UE_BULK) + return (EINVAL); + + if (*(int *)addr) { + /* Only turn WB on if it's currently off. */ + if (sce->state & UGEN_BULK_WB) + return (0); + + if (sce->ra_wb_bufsize == 0 || sce->ra_wb_reqsize == 0) + /* shouldn't happen */ + return (EINVAL); + sce->ra_wb_xfer = usbd_alloc_xfer(sc->sc_udev); + if (sce->ra_wb_xfer == NULL) { + return (ENOMEM); + } + sce->ra_wb_xferlen = sce->ra_wb_reqsize; + /* + * Set up a dmabuf because we reuse the xfer with + * the same (max) request length like isoc. + */ + if (usbd_alloc_buffer(sce->ra_wb_xfer, sce->ra_wb_xferlen) == 0) { + usbd_free_xfer(sce->ra_wb_xfer); + return (ENOMEM); + } + sce->ra_wb_xfer->done = 1; /* nothing pending */ + sce->ibuf = malloc(sce->ra_wb_bufsize, + M_USBDEV, M_WAITOK); + sce->fill = sce->cur = sce->ibuf; + sce->limit = sce->ibuf + sce->ra_wb_bufsize; + sce->ra_wb_used = 0; + sce->state |= UGEN_BULK_WB; + } else { + /* Only turn WB off if it's currently on. */ + if (!(sce->state & UGEN_BULK_WB)) + return (0); + + sce->state &= ~UGEN_BULK_WB; + /* XXX discard whatever's in the buffer, but we + should keep it around and keep writing to + drain the buffer instead */ + usbd_abort_pipe(sce->pipeh); + usbd_free_xfer(sce->ra_wb_xfer); + free(sce->ibuf, M_USBDEV); + sce->ibuf = NULL; + } + return (0); +#else + return (EINVAL); +#endif + case USB_SET_BULK_RA_OPT: + case USB_SET_BULK_WB_OPT: +#ifdef UGEN_BULK_RA_WB + { + struct usb_bulk_ra_wb_opt *opt; + + if (endpt == USB_CONTROL_ENDPOINT) + return (EINVAL); + opt = (struct usb_bulk_ra_wb_opt *)addr; + if (cmd == USB_SET_BULK_RA_OPT) + sce = &sc->sc_endpoints[endpt][IN]; + else + sce = &sc->sc_endpoints[endpt][OUT]; + if (sce == NULL || sce->pipeh == NULL) + return (EINVAL); + if (opt->ra_wb_buffer_size < 1 || opt->ra_wb_request_size < 1) + return (EINVAL); + /* XXX these changes do not take effect until the + next time RA/WB mode is enabled but they ought to + take effect immediately */ + sce->ra_wb_bufsize = opt->ra_wb_buffer_size; + sce->ra_wb_reqsize = opt->ra_wb_request_size; + return (0); + } +#else + return (EINVAL); +#endif default: break; } @@ -1330,7 +1842,7 @@ ugenpoll(dev_t dev, int events, struct lwp *l) { struct ugen_softc *sc; - struct ugen_endpoint *sce; + struct ugen_endpoint *sce_in, *sce_out; int revents = 0; int s; @@ -1339,50 +1851,87 @@ if (sc->sc_dying) return (POLLHUP); - /* XXX always IN */ - sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; - if (sce == NULL) + sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; + sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT]; + if (sce_in == NULL && sce_out == NULL) /* XXX || ? */ return (POLLERR); #ifdef DIAGNOSTIC - if (!sce->edesc) { + if (!sce_in->edesc && !sce_out->edesc) { /* XXX || ? */ printf("ugenpoll: no edesc\n"); return (POLLERR); } - if (!sce->pipeh) { + /* It's possible to have only one pipe open. */ + if (!sce_in->pipeh && !sce_out->pipeh) { printf("ugenpoll: no pipe\n"); return (POLLERR); } #endif s = splusb(); - switch (sce->edesc->bmAttributes & UE_XFERTYPE) { - case UE_INTERRUPT: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->q.c_cc > 0) + if (sce_in && sce_in->pipeh && (events & (POLLIN | POLLRDNORM))) + switch (sce_in->edesc->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + if (sce_in->q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else - selrecord(l, &sce->rsel); - } - break; - case UE_ISOCHRONOUS: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->cur != sce->fill) + selrecord(l, &sce_in->rsel); + break; + case UE_ISOCHRONOUS: + if (sce_in->cur != sce_in->fill) revents |= events & (POLLIN | POLLRDNORM); else - selrecord(l, &sce->rsel); + selrecord(l, &sce_in->rsel); + break; + case UE_BULK: +#ifdef UGEN_BULK_RA_WB + if (sce_in->state & UGEN_BULK_RA) { + if (sce_in->ra_wb_used > 0) + revents |= events & + (POLLIN | POLLRDNORM); + else + selrecord(l, &sce_in->rsel); + break; + } +#endif + /* + * We have no easy way of determining if a read will + * yield any data or a write will happen. + * Pretend they will. + */ + revents |= events & (POLLIN | POLLRDNORM); + break; + default: + break; } - break; - case UE_BULK: - /* - * We have no easy way of determining if a read will - * yield any data or a write will happen. - * Pretend they will. - */ - revents |= events & - (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); - break; - default: - break; - } + if (sce_out && sce_out->pipeh && (events & (POLLOUT | POLLWRNORM))) + switch (sce_out->edesc->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + case UE_ISOCHRONOUS: + /* XXX unimplemented */ + break; + case UE_BULK: +#ifdef UGEN_BULK_RA_WB + if (sce_out->state & UGEN_BULK_WB) { + if (sce_out->ra_wb_used < + sce_out->limit - sce_out->ibuf) + revents |= events & + (POLLOUT | POLLWRNORM); + else + selrecord(l, &sce_out->rsel); + break; + } +#endif + /* + * We have no easy way of determining if a read will + * yield any data or a write will happen. + * Pretend they will. + */ + revents |= events & (POLLOUT | POLLWRNORM); + break; + default: + break; + } + + splx(s); return (revents); } @@ -1424,14 +1973,66 @@ return (1); } +#ifdef UGEN_BULK_RA_WB +static int +filt_ugenread_bulk(struct knote *kn, long hint) +{ + struct ugen_endpoint *sce = kn->kn_hook; + + if (!(sce->state & UGEN_BULK_RA)) + /* + * We have no easy way of determining if a read will + * yield any data or a write will happen. + * So, emulate "seltrue". + */ + return (filt_seltrue(kn, hint)); + + if (sce->ra_wb_used == 0) + return (0); + + kn->kn_data = sce->ra_wb_used; + + return (1); +} + +static int +filt_ugenwrite_bulk(struct knote *kn, long hint) +{ + struct ugen_endpoint *sce = kn->kn_hook; + + if (!(sce->state & UGEN_BULK_WB)) + /* + * We have no easy way of determining if a read will + * yield any data or a write will happen. + * So, emulate "seltrue". + */ + return (filt_seltrue(kn, hint)); + + if (sce->ra_wb_used == sce->limit - sce->ibuf) + return (0); + + kn->kn_data = (sce->limit - sce->ibuf) - sce->ra_wb_used; + + return (1); +} +#endif + static const struct filterops ugenread_intr_filtops = { 1, NULL, filt_ugenrdetach, filt_ugenread_intr }; static const struct filterops ugenread_isoc_filtops = { 1, NULL, filt_ugenrdetach, filt_ugenread_isoc }; +#ifdef UGEN_BULK_RA_WB +static const struct filterops ugenread_bulk_filtops = + { 1, NULL, filt_ugenrdetach, filt_ugenread_bulk }; + +static const struct filterops ugenwrite_bulk_filtops = + { 1, NULL, filt_ugenrdetach, filt_ugenwrite_bulk }; +#else static const struct filterops ugen_seltrue_filtops = { 1, NULL, filt_ugenrdetach, filt_seltrue }; +#endif int ugenkqfilter(dev_t dev, struct knote *kn) @@ -1446,13 +2047,12 @@ if (sc->sc_dying) return (1); - /* XXX always IN */ - sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; - if (sce == NULL) - return (1); - switch (kn->kn_filter) { case EVFILT_READ: + sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; + if (sce == NULL) + return (1); + klist = &sce->rsel.sel_klist; switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: @@ -1462,12 +2062,17 @@ kn->kn_fop = &ugenread_isoc_filtops; break; case UE_BULK: +#ifdef UGEN_BULK_RA_WB + kn->kn_fop = &ugenread_bulk_filtops; + break; +#else /* * We have no easy way of determining if a read will * yield any data or a write will happen. * So, emulate "seltrue". */ kn->kn_fop = &ugen_seltrue_filtops; +#endif break; default: return (1); @@ -1475,6 +2080,10 @@ break; case EVFILT_WRITE: + sce = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT]; + if (sce == NULL) + return (1); + klist = &sce->rsel.sel_klist; switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: @@ -1483,12 +2092,16 @@ return (1); case UE_BULK: +#ifdef UGEN_BULK_RA_WB + kn->kn_fop = &ugenwrite_bulk_filtops; +#else /* * We have no easy way of determining if a read will * yield any data or a write will happen. * So, emulate "seltrue". */ kn->kn_fop = &ugen_seltrue_filtops; +#endif break; default: return (1); Index: sys/dev/usb/usb.h =================================================================== RCS file: /cvs/netbsd/netbsd/src/sys/dev/usb/usb.h,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -u -r1.1.1.1 -r1.3 --- sys/dev/usb/usb.h 11 Dec 2005 12:24:01 -0000 1.1.1.1 +++ sys/dev/usb/usb.h 6 Jun 2006 18:15:40 -0000 1.3 @@ -630,6 +630,11 @@ u_long uds_requests[4]; /* indexed by transfer type UE_* */ }; +struct usb_bulk_ra_wb_opt { + int ra_wb_buffer_size; + int ra_wb_request_size; +}; + /* Events that can be read from /dev/usb */ struct usb_event { int ue_type; @@ -684,6 +689,10 @@ #define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info) #define USB_SET_SHORT_XFER _IOW ('U', 113, int) #define USB_SET_TIMEOUT _IOW ('U', 114, int) +#define USB_SET_BULK_RA _IOW ('U', 115, int) +#define USB_SET_BULK_WB _IOW ('U', 116, int) +#define USB_SET_BULK_RA_OPT _IOW ('U', 117, struct usb_bulk_ra_wb_opt) +#define USB_SET_BULK_WB_OPT _IOW ('U', 118, struct usb_bulk_ra_wb_opt) /* Modem device */ #define USB_GET_CM_OVER_DATA _IOR ('U', 130, int)