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