Many apologies for the delay in responding; somehow I missed this
message.

> Is there a consolidated patch file that would make it easier to apply against 
> the current NetBSD source tree?

Attached is a patch file for the top level of the NetBSD source,
including all the files changed. config(8) will automatically create
the enigmatic opt_ugen_bulk_ra_wb.h file. To actually include the
changes in your kernel compile (they are not enabled by default), add
"options UGEN_BULK_RA_WB" to your kernel config.

Joanne
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 ([EMAIL PROTECTED]) 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 <sys/cdefs.h>
 __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 <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -63,10 +70,12 @@
 #include <sys/proc.h>
 #include <sys/vnode.h>
 #include <sys/poll.h>
+#include <machine/bus.h>       /* for usbdivar.h */
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>  /* 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)
_______________________________________________
Discuss-gnuradio mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/discuss-gnuradio

Reply via email to