tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: asynchronous ugen(4) bulk transfers
Hello. Below are two difs. The first contains the changes to the
libusb-1.0.19 library in the pkgsrc/devel/libusb1-1 package. I only
needed to change the NetBSD specific backend code to get the behavior I
desired.
The second dif contains the changes I made to ugen.c in
src/sys/dev/usb/ugen.c, relative to NetBSD-5. The default NetBSD-5 ugen
driver doesn't have conditional variables in it. In the course of
developing this code, I ran into all sorts of race conditions using the
tsleep/wakep code. I found it easier just to convert to using conditional
variables and mutexes, especially since That's what -current is doing.
I've been running this code for a couple of months on my workstations and
exercising it pretty regularly.
-thanks
-Brian
--- netbsd_usb.c.fcs 2014-04-22 05:31:42.000000000 -0700
+++ netbsd_usb.c 2015-11-06 18:08:54.000000000 -0800
@@ -47,6 +47,7 @@
/*
* Backend functions
*/
+static int netbsd_init(struct libusb_context *);
static int netbsd_get_device_list(struct libusb_context *,
struct discovered_devs **);
static int netbsd_open(struct libusb_device_handle *);
@@ -86,11 +87,14 @@
static int _sync_control_transfer(struct usbi_transfer *);
static int _sync_gen_transfer(struct usbi_transfer *);
static int _access_endpoint(struct libusb_transfer *);
+static void netbsd_bulk_read_poll(struct usbi_transfer *itransfer);
+
+#define POLL_TIMEOUT 200 /* 200 ms between checks for bulkreads */
const struct usbi_os_backend netbsd_backend = {
"Synchronous NetBSD backend",
0,
- NULL, /* init() */
+ netbsd_init, /* init() */
NULL, /* exit() */
netbsd_get_device_list,
NULL, /* hotplug_poll */
@@ -134,6 +138,22 @@
0, /* add_iso_packet_size */
};
+
+static pthread_attr_t ta; /* For read thread attributes */
+static pthread_mutex_t read_poll_lock;
+
+/*
+ * Initialize the NetBSD back end
+ */
+int
+netbsd_init(struct libusb_context *ctx)
+{
+
+ pthread_attr_init(&ta);
+ pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
+ return(0);
+}
+
int
netbsd_get_device_list(struct libusb_context * ctx,
struct discovered_devs **discdevs)
@@ -469,8 +489,10 @@
if (err)
return (err);
- if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0)
- return _errno_to_libusb(errno);
+ if (IS_XFEROUT(transfer)) {
+ if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0)
+ return _errno_to_libusb(errno);
+ }
return (LIBUSB_SUCCESS);
}
@@ -577,6 +599,8 @@
return (LIBUSB_ERROR_NO_DEVICE);
case ENOMEM:
return (LIBUSB_ERROR_NO_MEM);
+ case EAGAIN:
+return (LIBUSB_ERROR_BUSY);
}
usbi_dbg("error: %s", strerror(err));
@@ -706,6 +730,8 @@
{
struct libusb_transfer *transfer;
int fd, nr = 1;
+ int res;
+ pthread_t tid;
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@@ -719,20 +745,100 @@
if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
return _errno_to_libusb(errno);
+ /*
+ * Since the NetBSD ugen(4) driver doesn't honor timeouts, use
+ * non-blocking i/o via the fcntl(2) system call.
+ */
+ if (IS_XFERIN(transfer)) {
+ res = fcntl(fd, F_GETFL, 0);
+ if (res < 0) {
+ return _errno_to_libusb(errno);
+ }
+
+ res |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, res);
+ }
+
+
+ /*
+ * In order for non-blocking reads/writes to work, USB_SET_BULK_RA
+ * and USB_SET_BULK_WB must be enabled.
+ */
+ res = 1;
+ /* If not an end point or already enabled, nothing happens */
+ ioctl(fd, USB_SET_BULK_WB, &res);
+
if (IS_XFERIN(transfer)) {
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0)
return _errno_to_libusb(errno);
- nr = read(fd, transfer->buffer, transfer->length);
+ /* Must come after answering the short read question */
+ res = 1;
+ ioctl(fd, USB_SET_BULK_RA, &res);
+ /* Start the reading thread */
+ nr = pthread_create(&tid, &ta, netbsd_bulk_read_poll, itransfer);
+ if (nr == 0) return(nr);
} else {
nr = write(fd, transfer->buffer, transfer->length);
}
- if (nr < 0)
+ if (nr < 0) {
return _errno_to_libusb(errno);
+ }
itransfer->transferred = nr;
return (0);
}
+
+
+/*
+ * netbsd_bulk_read_poll(struct usbi_transfer *itransfer)
+ * Wait for a bulk endpoint to become ready for reading and, once ready,
+ * perform the read and notify the upper layers.
+ */
+
+static void
+netbsd_bulk_read_poll(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct handle_priv *hpriv;
+ int nfd, fd, nr = 1;
+ int res;
+ struct pollfd readpolls;
+
+ pthread_mutex_lock(&read_poll_lock);
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+
+ /*
+ * Bulk, Interrupt or Isochronous transfer depends on the
+ * endpoint and thus the node to open.
+ */
+ if ((fd = _access_endpoint(transfer)) < 0) {
+ pthread_mutex_unlock(&read_poll_lock);
+ return;
+ }
+
+ res = 0;
+ while (!res) {
+ memset(&readpolls, 0, sizeof(struct pollfd));
+
+ readpolls.fd = fd;
+ readpolls.events = POLLIN|POLLRDNORM|POLLPRI;
+ nfd = 1;
+ res = poll(&readpolls, nfd, POLL_TIMEOUT);
+ }
+
+ if (res < 0) {
+ pthread_mutex_unlock(&read_poll_lock);
+ return; /* An error, abort */
+ }
+
+ nr = read(fd, transfer->buffer, transfer->length);
+ itransfer->transferred = nr;
+ write(hpriv->pipe[1], &itransfer, sizeof(itransfer));
+ pthread_mutex_unlock(&read_poll_lock);
+ return;
+}
<ugen.c diffs below this line.>
Index: ugen.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ugen.c,v
retrieving revision 1.99.8.4
diff -u -r1.99.8.4 ugen.c
--- ugen.c 25 Jan 2012 17:33:17 -0000 1.99.8.4
+++ ugen.c 4 Apr 2016 22:50:04 -0000
@@ -1,4 +1,4 @@
-/* $NetBSD$ */
+/* $NetBSD: ugen.c,v 1.99.8.4 2012/01/25 17:33:17 riz Exp $ */
/*
* Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD$");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.99.8.4 2012/01/25 17:33:17 riz Exp $");
#include "opt_ugen_bulk_ra_wb.h"
#include "opt_compat_netbsd.h"
@@ -49,6 +49,8 @@
#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/device.h>
#include <sys/ioctl.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
#elif defined(__FreeBSD__)
#include <sys/module.h>
#include <sys/bus.h>
@@ -81,6 +83,7 @@
#define UGEN_CHUNK 128 /* chunk size for read */
#define UGEN_IBSIZE 1020 /* buffer size */
#define UGEN_BBSIZE 1024
+#define BUFFCOUNT 4 /* How many read ahead buffers do we use */
#define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */
#define UGEN_NISOREQS 6 /* number of outstanding xfer requests */
@@ -99,15 +102,21 @@
#define UGEN_BULK_RA 0x08 /* in bulk read-ahead mode */
#define UGEN_BULK_WB 0x10 /* in bulk write-behind mode */
#define UGEN_RA_WB_STOP 0x20 /* RA/WB xfer is stopped (buffer full/empty) */
+#define UGEN_RA_WRAP 0x40 /* RA overrun is in effect */
+#define UGEN_RA_COMPLETE 0x80 /* RA Read ahead has read an entire block */
+#define UGEN_RA_ZPENDING 0x100 /* A Zero Length Packet is pending */
usbd_pipe_handle pipeh;
struct clist q;
- struct selinfo rsel;
u_char *ibuf; /* start of buffer (circular for isoc) */
u_char *fill; /* location for input (isoc) */
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_char *ra_buflist[BUFFCOUNT];
+ int ra_buflen[BUFFCOUNT];
+ u_int32_t ra_load; /* The buffer we're loading from a USB device */
+ u_int32_t ra_read; /* The buffer ready to go to user space */
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 */
@@ -120,12 +129,17 @@
void *dmabuf;
u_int16_t sizes[UGEN_NISORFRMS];
} isoreqs[UGEN_NISOREQS];
+#define UGEN_ENDPOINT_NONZERO_CRUFT offsetof(struct ugen_endpoint, rsel)
+ struct selinfo rsel;
+ kcondvar_t cv;
};
struct ugen_softc {
USBBASEDEVICE sc_dev; /* base device */
usbd_device_handle sc_udev;
+ kmutex_t sc_lock;
+
char sc_is_open[USB_MAX_ENDPOINTS];
struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
#define OUT 0
@@ -230,9 +244,13 @@
aprint_normal_dev(self, "%s\n", devinfop);
usbd_devinfo_free(devinfop);
+ //mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+
sc->sc_dev = self;
sc->sc_udev = udev = uaa->device;
+
/* First set configuration index 0, the default one for ugen. */
err = usbd_set_config_index(udev, 0, 0);
if (err) {
@@ -243,6 +261,17 @@
}
conf = usbd_get_config_descriptor(udev)->bConfigurationValue;
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ for (dir = OUT; dir <= IN; dir++) {
+ struct ugen_endpoint *sce;
+
+ sce = &sc->sc_endpoints[i][dir];
+ memset(sce, 0, UGEN_ENDPOINT_NONZERO_CRUFT);
+ selinit(&sce->rsel);
+ cv_init(&sce->cv, "ugensce");
+ }
+ }
+
/* Set up all the local state for this configuration. */
err = ugen_set_config(sc, conf);
if (err) {
@@ -261,14 +290,7 @@
}
}
#endif
- for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
- for (dir = OUT; dir <= IN; dir++) {
- struct ugen_endpoint *sce;
- sce = &sc->sc_endpoints[i][dir];
- selinit(&sce->rsel);
- }
- }
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
USBDEV(sc->sc_dev));
@@ -290,7 +312,7 @@
u_int8_t niface, nendpt;
int ifaceno, endptno, endpt;
usbd_status err;
- int dir;
+ int i, dir;
DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
USBDEVNAME(sc->sc_dev), configno, sc));
@@ -318,7 +340,14 @@
err = usbd_interface_count(dev, &niface);
if (err)
return (err);
- memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+ /* Clear out the old info, but leave the selinfo and cv initialised. */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ for (dir = OUT; dir <= IN; dir++) {
+ sce = &sc->sc_endpoints[i][dir];
+ memset(sce, 0, UGEN_ENDPOINT_NONZERO_CRUFT);
+ }
+ }
+
for (ifaceno = 0; ifaceno < niface; ifaceno++) {
DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
err = usbd_device2interface_handle(dev, ifaceno, &iface);
@@ -530,9 +559,16 @@
DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
endpt, dir, sce));
- usbd_abort_pipe(sce->pipeh);
- usbd_close_pipe(sce->pipeh);
- sce->pipeh = NULL;
+ /* If we're dying, the abort is handled by ugen_detach. */
+ mutex_enter(&sc->sc_lock);
+ if (!sc->sc_dying) {
+ mutex_exit(&sc->sc_lock);
+ usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ sce->pipeh = NULL;
+ } else {
+ mutex_exit(&sc->sc_lock);
+ }
switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
case UE_INTERRUPT:
@@ -547,6 +583,13 @@
case UE_BULK:
if (sce->state & (UGEN_BULK_RA | UGEN_BULK_WB))
/* ibuf freed below */
+ for (i = 0; i < BUFFCOUNT;i ++) {
+ if (sce->ra_buflist[i] != NULL) {
+ free(sce->ra_buflist[i], M_USBDEV);
+ sce->ra_buflist[i] = NULL;
+ }
+ sce->ra_buflen[i] = 0;
+ }
usbd_free_xfer(sce->ra_wb_xfer);
break;
#endif
@@ -572,8 +615,10 @@
u_int32_t n, tn;
usbd_xfer_handle xfer;
usbd_status err;
- int s;
+ uint16_t xfer_flags;
int error = 0;
+ int have_data;
+ u_char *readptr;
DPRINTFN(5, ("%s: ugenread: %d\n", USBDEVNAME(sc->sc_dev), endpt));
@@ -597,15 +642,17 @@
switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
case UE_INTERRUPT:
/* Block until activity occurred. */
- s = splusb();
+ mutex_enter(&sc->sc_lock);
while (sce->q.c_cc == 0) {
if (flag & IO_NDELAY) {
- splx(s);
+ mutex_exit(&sc->sc_lock);
return (EWOULDBLOCK);
}
sce->state |= UGEN_ASLP;
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
- error = tsleep(sce, PZERO | PCATCH, "ugenri", mstohz(sce->timeout));
+ /* "ugenri" */
+ error = cv_timedwait_sig(&sce->cv, &sc->sc_lock,
+ mstohz(sce->timeout));
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
if (sc->sc_dying)
error = EIO;
@@ -614,7 +661,7 @@
break;
}
}
- splx(s);
+ mutex_exit(&sc->sc_lock);
/* Transfer as many chunks as possible. */
while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) {
@@ -635,23 +682,27 @@
case UE_BULK:
#ifdef UGEN_BULK_RA_WB
if (sce->state & UGEN_BULK_RA) {
- DPRINTFN(5, ("ugenread: BULK_RA req: %zd used: %d\n",
- uio->uio_resid, sce->ra_wb_used));
+ DPRINTFN(5, ("ugenread: BULK_RA req: %zd used: %d(%d)\n",
+ uio->uio_resid, sce->ra_buflen[sce->ra_read],
+ sce->ra_read));
xfer = sce->ra_wb_xfer;
- s = splusb();
- if (sce->ra_wb_used == 0 && flag & IO_NDELAY) {
- splx(s);
+ have_data = 0;
+ mutex_enter(&sc->sc_lock);
+ if (((sce->state & UGEN_RA_COMPLETE) == 0) &&
+ (flag & IO_NDELAY)) {
+ mutex_exit(&sc->sc_lock);
return (EWOULDBLOCK);
}
while (uio->uio_resid > 0 && !error) {
- while (sce->ra_wb_used == 0) {
+ while (((sce->state & UGEN_RA_COMPLETE) == 0) && (sce->ra_read == sce->ra_load)) {
sce->state |= UGEN_ASLP;
DPRINTFN(5,
("ugenread: sleep on %p\n",
sce));
- error = tsleep(sce, PZERO | PCATCH,
- "ugenrb", mstohz(sce->timeout));
+ /* "ugenrb" */
+ error = cv_timedwait_sig(&sce->cv,
+ &sc->sc_lock, mstohz(sce->timeout));
DPRINTFN(5,
("ugenread: woke, error=%d\n",
error));
@@ -664,46 +715,77 @@
}
/* Copy data to the process. */
+ if (uio->uio_resid > 0) {
+ readptr = sce->ra_buflist[sce->ra_read];
+ }
while (uio->uio_resid > 0
- && sce->ra_wb_used > 0) {
+ && sce->ra_buflen[sce->ra_read] > 0) {
+ have_data = 1;
n = min(uio->uio_resid,
- sce->ra_wb_used);
- n = min(n, sce->limit - sce->cur);
- error = uiomove(sce->cur, n, uio);
+ sce->ra_buflen[sce->ra_read]);
+ error = uiomove(readptr, n, uio);
if (error)
break;
- sce->cur += n;
- sce->ra_wb_used -= n;
- if (sce->cur == sce->limit)
- sce->cur = sce->ibuf;
+ readptr += n;
+ sce->ra_buflen[sce->ra_read] -= n;
+ }
+ DPRINTFN(5, ("ugenread: BULK_RA_POST_UIO req: %zd used: %d(%d)\n",
+ uio->uio_resid, sce->ra_buflen[sce->ra_read],
+ sce->ra_read));
+
+ if (sce->ra_buflen[sce->ra_read] == 0) {
+ sce->ra_read ++;
+ if (sce->ra_read == BUFFCOUNT) {
+ sce->ra_read = 0;
+ }
+ sce->state &= ~UGEN_RA_WRAP;
+ }
+
+ if (sce->ra_read == sce->ra_load) {
+ sce->state &= ~UGEN_RA_COMPLETE;
}
+
/*
- * If the transfers stopped because the
- * buffer was full, restart them.
+ * If not in non-blocking mode or not in a read
+ * overrun, set up for a new read transfer.
*/
- if (sce->state & UGEN_RA_WB_STOP &&
- sce->ra_wb_used < sce->limit - sce->ibuf) {
- n = (sce->limit - sce->ibuf)
- - sce->ra_wb_used;
- usbd_setup_xfer(xfer,
- sce->pipeh, sce, NULL,
- min(n, sce->ra_wb_xferlen),
- USBD_NO_COPY, USBD_NO_TIMEOUT,
- ugen_bulkra_intr);
- sce->state &= ~UGEN_RA_WB_STOP;
- err = usbd_transfer(xfer);
- if (err != USBD_IN_PROGRESS)
- /*
- * The transfer has not been
- * queued. Setting STOP
- * will make us try
- * again at the next read.
- */
- sce->state |= UGEN_RA_WB_STOP;
+ if (!flag & IO_NDELAY) {
+ xfer_flags = USBD_NO_COPY |
+ (sce->state &UGEN_SHORT_OK ? USBD_SHORT_XFER_OK : 0);
+ n = min(sce->ra_wb_reqsize,
+ (sce->ra_wb_bufsize - sce->ra_buflen[sce->ra_load]));
+ DPRINTFN(5, ("ugenread: calling bulkra_intr, req = %d(%d)\n",
+ n, sce->ra_load));
+ if (n > 0) {
+ usbd_setup_xfer(xfer,
+ sce->pipeh, sce, NULL, n,
+ xfer_flags, USBD_NO_TIMEOUT,
+ ugen_bulkra_intr);
+ sce->state &= ~UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
+ err = usbd_transfer(xfer);
+ mutex_enter(&sc->sc_lock);
+ if (err != USBD_IN_PROGRESS)
+ /*
+ * The transfer has not been
+ * queued. Setting STOP
+ * will make us try
+ * again at the next read.
+ */
+ sce->state |= UGEN_RA_WB_STOP;
+ } else {
+ sce->state |= UGEN_RA_WB_STOP;
+ }
+ }
+
+ if ((have_data) && (flag & IO_NDELAY)) {
+ /* Data already copied to user, now return */
+ error = 0;
+ break;
}
}
- splx(s);
+ mutex_exit(&sc->sc_lock);
break;
}
#endif
@@ -735,15 +817,17 @@
usbd_free_xfer(xfer);
break;
case UE_ISOCHRONOUS:
- s = splusb();
+ mutex_enter(&sc->sc_lock);
while (sce->cur == sce->fill) {
if (flag & IO_NDELAY) {
- splx(s);
+ mutex_exit(&sc->sc_lock);
return (EWOULDBLOCK);
}
sce->state |= UGEN_ASLP;
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
- error = tsleep(sce, PZERO | PCATCH, "ugenri", mstohz(sce->timeout));
+ /* "ugenri" */
+ error = cv_timedwait_sig(&sce->cv,
+ &sc->sc_lock, mstohz(sce->timeout));
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
if (sc->sc_dying)
error = EIO;
@@ -769,7 +853,7 @@
if(sce->cur >= sce->limit)
sce->cur = sce->ibuf;
}
- splx(s);
+ mutex_exit(&sc->sc_lock);
break;
@@ -784,14 +868,28 @@
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
- int error;
+ int error, s;
USB_GET_SC_OPEN(ugen, UGENUNIT(dev), sc);
+ if (sc->sc_dying)
+ return (EIO);
+
+ mutex_enter(&sc->sc_lock);
sc->sc_refcnt++;
+ mutex_exit(&sc->sc_lock);
error = ugen_do_read(sc, endpt, uio, flag);
- if (--sc->sc_refcnt < 0)
+
+ mutex_enter(&sc->sc_lock);
+ if (--sc->sc_refcnt < 0) {
+ mutex_exit(&sc->sc_lock);
+ s = splusb();
usb_detach_wakeup(USBDEV(sc->sc_dev));
+ splx(s);
+ } else {
+ mutex_exit(&sc->sc_lock);
+ }
+
return (error);
}
@@ -803,7 +901,7 @@
u_int32_t n;
int error = 0;
#ifdef UGEN_BULK_RA_WB
- int s;
+ int send_zero;
u_int32_t tn;
char *dbuf;
#endif
@@ -837,21 +935,61 @@
uio->uio_resid, sce->ra_wb_used));
xfer = sce->ra_wb_xfer;
- s = splusb();
- if (sce->ra_wb_used == sce->limit - sce->ibuf &&
- flag & IO_NDELAY) {
- splx(s);
+ mutex_enter(&sc->sc_lock);
+ if ((sce->ra_wb_used == sce->limit - sce->ibuf) &&
+ (flag & IO_NDELAY)) {
+ DPRINTFN(5, ("ugenwrite: BULK_WB: Would block!\n"));
+ mutex_exit(&sc->sc_lock);
return (EWOULDBLOCK);
}
- while (uio->uio_resid > 0 && !error) {
- while (sce->ra_wb_used ==
- sce->limit - sce->ibuf) {
+
+ /* Allow zero length packets to generate a USB transaction */
+ if (uio->uio_resid == 0) {
+ send_zero = 1;
+ sce->state |= UGEN_RA_ZPENDING;
+ } else {
+ send_zero = 0;
+ }
+
+ DPRINTFN(2, ("ugenwrite: BULK_WB: before write state = 0x%x\n",
+ sce->state));
+
+ while (!(sce->state & UGEN_RA_COMPLETE)) {
+ if ((sce->state & UGEN_RA_WB_STOP) &&
+ (sce->ra_wb_used == 0)) {
+ break;
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5,
+ ("ugenwrite: (complete) sleep on %p\n",
+ sce));
+ /* "ugenwb" */
+ error = cv_timedwait_sig(&sce->cv,
+ &sc->sc_lock, mstohz(sce->timeout));
+ DPRINTFN(5,
+ ("ugenwrite: woke, error=%d\n",
+ error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error) {
+ sce->state &= ~UGEN_ASLP;
+ mutex_exit(&sc->sc_lock);
+ return(error);
+ }
+ }
+
+ while ((uio->uio_resid > 0 && !error)
+ || (send_zero == 1)) {
+ DPRINTFN(2, ("ugenwrite: BULK_WB: state = 0x%x\n",
+ sce->state));
+ while (!(sce->state & UGEN_RA_WB_STOP)) {
sce->state |= UGEN_ASLP;
DPRINTFN(5,
("ugenwrite: sleep on %p\n",
sce));
- error = tsleep(sce, PZERO | PCATCH,
- "ugenwb", mstohz(sce->timeout));
+ /* "ugenwb" */
+ error = cv_timedwait_sig(&sce->cv,
+ &sc->sc_lock, mstohz(sce->timeout));
DPRINTFN(5,
("ugenwrite: woke, error=%d\n",
error));
@@ -862,7 +1000,31 @@
break;
}
}
+
+ if (sce->state & UGEN_RA_ZPENDING) {
+ usbd_setup_xfer(xfer,
+ sce->pipeh, sce, NULL, 0,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ ugen_bulkwb_intr);
+ sce->state &= ~(UGEN_RA_WB_STOP | UGEN_RA_COMPLETE
+ | UGEN_RA_ZPENDING);
+ mutex_exit(&sc->sc_lock);
+ err = usbd_transfer(xfer);
+ mutex_enter(&sc->sc_lock);
+ if (err != USBD_IN_PROGRESS) {
+ /*
+ * The transfer has not been
+ * queued. Setting STOP
+ * will make us try again
+ * at the next read.
+ */
+ sce->state |= UGEN_RA_WB_STOP;
+ printf("ugenwrite: ZLP error: %d\n",err);
+ }
+ send_zero = 0;
+ }
+
/* Copy data from the process. */
while (uio->uio_resid > 0 &&
sce->ra_wb_used < sce->limit - sce->ibuf) {
@@ -877,6 +1039,8 @@
sce->ra_wb_used += n;
if (sce->fill == sce->limit)
sce->fill = sce->ibuf;
+ DPRINTFN(5, ("ugenwrite: BULK_WB: after uiomove: req: %zd used: %d\n",
+ uio->uio_resid, sce->ra_wb_used));
}
/*
@@ -885,6 +1049,7 @@
*/
if (sce->state & UGEN_RA_WB_STOP &&
sce->ra_wb_used > 0) {
+ DPRINTFN(5, ("ugenwrite: BULK_WB: starting transfer\n"));
dbuf = (char *)usbd_get_buffer(xfer);
n = min(sce->ra_wb_used,
sce->ra_wb_xferlen);
@@ -894,12 +1059,15 @@
if (n - tn > 0)
memcpy(dbuf, sce->ibuf,
n - tn);
+ DPRINTFN(5, ("ugenwrite: BULK_WB: Transfer length: %d\n",n));
usbd_setup_xfer(xfer,
sce->pipeh, sce, NULL, n,
USBD_NO_COPY, USBD_NO_TIMEOUT,
ugen_bulkwb_intr);
- sce->state &= ~UGEN_RA_WB_STOP;
+ sce->state &= ~(UGEN_RA_WB_STOP | UGEN_RA_COMPLETE);
+ mutex_exit(&sc->sc_lock);
err = usbd_transfer(xfer);
+ mutex_enter(&sc->sc_lock);
if (err != USBD_IN_PROGRESS)
/*
* The transfer has not been
@@ -910,7 +1078,7 @@
sce->state |= UGEN_RA_WB_STOP;
}
}
- splx(s);
+ mutex_exit(&sc->sc_lock);
break;
}
#endif
@@ -971,14 +1139,27 @@
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
- int error;
+ int error, s;
USB_GET_SC_OPEN(ugen, UGENUNIT(dev), sc);
+ if (sc->sc_dying)
+ return (EIO);
+
+ mutex_enter(&sc->sc_lock);
sc->sc_refcnt++;
+ mutex_exit(&sc->sc_lock);
+
error = ugen_do_write(sc, endpt, uio, flag);
- if (--sc->sc_refcnt < 0)
+ mutex_enter(&sc->sc_lock);
+ if (--sc->sc_refcnt < 0) {
+ mutex_exit(&sc->sc_lock);
+ s = splusb();
usb_detach_wakeup(USBDEV(sc->sc_dev));
+ splx(s);
+ } else {
+ mutex_exit(&sc->sc_lock);
+ }
return (error);
}
@@ -1004,8 +1185,7 @@
{
USB_DETACH_START(ugen, sc);
struct ugen_endpoint *sce;
- int i, dir;
- int s;
+ int i, dir, s;
#if defined(__NetBSD__) || defined(__OpenBSD__)
int maj, mn;
@@ -1017,23 +1197,34 @@
sc->sc_dying = 1;
pmf_device_deregister(self);
/* Abort all pipes. Causes processes waiting for transfer to wake. */
+ mutex_enter(&sc->sc_lock);
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
for (dir = OUT; dir <= IN; dir++) {
sce = &sc->sc_endpoints[i][dir];
- if (sce && sce->pipeh)
+ if (sce && sce->pipeh) {
+ mutex_exit(&sc->sc_lock);
usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ mutex_enter(&sc->sc_lock);
+ sce->pipeh = NULL;
+ }
}
}
+ mutex_exit(&sc->sc_lock);
- s = splusb();
+ mutex_enter(&sc->sc_lock);
if (--sc->sc_refcnt >= 0) {
+ mutex_exit(&sc->sc_lock);
+ s = splusb();
/* Wake everyone */
for (i = 0; i < USB_MAX_ENDPOINTS; i++)
wakeup(&sc->sc_endpoints[i][IN]);
/* Wait for processes to go away. */
usb_detach_wait(USBDEV(sc->sc_dev));
+ splx(s);
+ } else {
+ mutex_exit(&sc->sc_lock);
}
- splx(s);
#if defined(__NetBSD__) || defined(__OpenBSD__)
/* locate the major number */
@@ -1059,9 +1250,12 @@
for (dir = OUT; dir <= IN; dir++) {
sce = &sc->sc_endpoints[i][dir];
seldestroy(&sce->rsel);
+ cv_destroy(&sce->cv);
}
}
+ mutex_destroy(&sc->sc_lock);
+
return (0);
}
@@ -1069,7 +1263,7 @@
ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
{
struct ugen_endpoint *sce = addr;
- /*struct ugen_softc *sc = sce->sc;*/
+ struct ugen_softc *sc = sce->sc;
u_int32_t count;
u_char *ibuf;
@@ -1093,11 +1287,13 @@
(void)b_to_q(ibuf, count, &sce->q);
+ mutex_enter(&sc->sc_lock);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_intr: waking %p\n", sce));
- wakeup(sce);
+ cv_signal(&sce->cv);
}
+ mutex_exit(&sc->sc_lock);
selnotify(&sce->rsel, 0, 0);
}
@@ -1107,6 +1303,7 @@
{
struct isoreq *req = addr;
struct ugen_endpoint *sce = req->sce;
+ struct ugen_softc *sc = sce->sc;
u_int32_t count, n;
int i, isize;
@@ -1152,11 +1349,13 @@
USBD_NO_COPY, ugen_isoc_rintr);
(void)usbd_transfer(xfer);
+ mutex_enter(&sc->sc_lock);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce));
- wakeup(sce);
+ cv_signal(&sce->cv);
}
+ mutex_exit(&sc->sc_lock);
selnotify(&sce->rsel, 0, 0);
}
@@ -1166,65 +1365,135 @@
usbd_status status)
{
struct ugen_endpoint *sce = addr;
+ struct ugen_softc *sc = sce->sc;
u_int32_t count, n;
char const *tbuf;
usbd_status err;
+ uint16_t xfer_flags;
+ u_char *loadptr;
+ int xfer_done; /* Is the USB transaction complete ? */
/* Return if we are aborting. */
- if (status == USBD_CANCELLED)
+ if (sc->sc_dying) {
return;
+ }
+ if (status == USBD_CANCELLED) {
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
+ return;
+ }
if (status != USBD_NORMAL_COMPLETION) {
DPRINTF(("ugen_bulkra_intr: status=%d\n", status));
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sce->pipeh);
return;
}
+ if (sce->state & UGEN_RA_WRAP) {
+ printf ("ugen_bulkra_intr: USB read overrun!\n");
+ return;
+ }
+
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
- /* Keep track of how much is in the buffer. */
- sce->ra_wb_used += count;
+ DPRINTFN(5, ("ugen_bulkra_intr: count = %d(%d)\n", count,
+ sce->ra_load));
+ if ((count > 0) && (count < UGEN_BBSIZE)) {
+ xfer_done = 1;
+ } else {
+ if (count == 0) {
+ xfer_done = 1;
+ } else {
+ xfer_done = 0;
+ }
+ }
+
+ loadptr = sce->ra_buflist[sce->ra_load] +
+ sce->ra_buflen[sce->ra_load];
/* 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);
+ n = min(count,
+ (sce->ra_wb_bufsize - sce->ra_buflen[sce->ra_load]));
+
+ /* Keep track of how much is in the buffer. */
+ sce->ra_buflen[sce->ra_load] += count;
+ if (sce->ra_buflen[sce->ra_load] >= sce->ra_wb_bufsize) {
+ xfer_done = 1;
+ }
+ memcpy(loadptr, 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;
+ loadptr += n;
+
+ if (count != 0) {
+ DPRINTFN(5, ("ugen_bulkra_intr: count = %d, should be 0\n", count));
+ }
+
+ /* If we're done with this USB transaction, move to the next buffer */
+ if (xfer_done) {
+ sce->ra_load ++;
+ if (sce->ra_load == BUFFCOUNT) {
+ sce->ra_load = 0;
+ }
+ if (sce->ra_load != sce->ra_read) {
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_COMPLETE;
+ mutex_exit(&sc->sc_lock);
+ } else {
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_WRAP;
+ mutex_exit(&sc->sc_lock);
+ }
}
/* 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 STOP
- * will make us try again at the next read.
- */
+ if ((xfer_done == 0) && ((sce->state &UGEN_RA_WRAP) == 0)) {
+ xfer_flags = USBD_NO_COPY |
+ (sce->state &UGEN_SHORT_OK ? USBD_SHORT_XFER_OK : 0);
+ n = min(sce->ra_wb_reqsize,
+ (sce->ra_wb_bufsize - sce->ra_buflen[sce->ra_load]));
+ DPRINTFN(5, ("bulkra_intr: calling bulkra_intr, req = %d(%d)\n",
+ n, sce->ra_load));
+ if (n > 0) {
+ usbd_setup_xfer(xfer, sce->pipeh, sce, NULL, n, xfer_flags,
+ 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 STOP
+ * will make us try again at the next read.
+ */
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
+ }
+ } else {
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
}
}
else
+ {
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
+ }
+ mutex_enter(&sc->sc_lock);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_bulkra_intr: waking %p\n", sce));
- wakeup(sce);
+ cv_signal(&sce->cv);
}
+ mutex_exit(&sc->sc_lock);
selnotify(&sce->rsel, 0, 0);
}
@@ -1233,17 +1502,28 @@
usbd_status status)
{
struct ugen_endpoint *sce = addr;
+ struct ugen_softc *sc = sce->sc;
u_int32_t count, n;
char *tbuf;
usbd_status err;
/* Return if we are aborting. */
- if (status == USBD_CANCELLED)
+ if (sc->sc_dying) {
+ return;
+ }
+ if (status == USBD_CANCELLED) {
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
return;
+ }
- if (status != USBD_NORMAL_COMPLETION) {
+ if ((status != USBD_NORMAL_COMPLETION) &&
+ (status != USBD_SHORT_XFER)) {
DPRINTF(("ugen_bulkwb_intr: status=%d\n", status));
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sce->pipeh);
return;
@@ -1270,6 +1550,7 @@
if (count - n > 0)
memcpy(tbuf, sce->ibuf, count - n);
+ DPRINTF(("ugen_bulkwb_intr: transfer length: %d\n", count));
usbd_setup_xfer(xfer, sce->pipeh, sce, NULL,
count, USBD_NO_COPY, USBD_NO_TIMEOUT, ugen_bulkwb_intr);
err = usbd_transfer(xfer);
@@ -1279,17 +1560,25 @@
* The transfer has not been queued. Setting STOP
* will make us try again at the next write.
*/
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
}
}
else
- sce->state |= UGEN_RA_WB_STOP;
+ {
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_RA_WB_STOP | UGEN_RA_COMPLETE;
+ mutex_exit(&sc->sc_lock);
+ }
+ mutex_enter(&sc->sc_lock);
if (sce->state & UGEN_ASLP) {
sce->state &= ~UGEN_ASLP;
DPRINTFN(5, ("ugen_bulkwb_intr: waking %p\n", sce));
- wakeup(sce);
+ cv_signal(&sce->cv);
}
+ mutex_exit(&sc->sc_lock);
selnotify(&sce->rsel, 0, 0);
}
#endif
@@ -1412,6 +1701,8 @@
struct usb_alt_interface *ai;
struct usb_string_desc *si;
u_int8_t conf, alt;
+ uint16_t xfer_flags;
+ int i, xfersize;
DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
if (sc->sc_dying)
@@ -1475,23 +1766,38 @@
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;
+ for (i = 0; i < BUFFCOUNT; i ++) {
+ sce->ra_buflen[i] = 0;
+ sce->ra_buflist[i] = malloc(sce->ra_wb_bufsize,
+ M_USBDEV, M_WAITOK);
+ }
+ sce->ra_load = sce->ra_read = 0;
+ mutex_enter(&sc->sc_lock);
sce->state |= UGEN_BULK_RA;
- sce->state &= ~UGEN_RA_WB_STOP;
- /* Now start reading. */
+ sce->state &= ~(UGEN_RA_COMPLETE | UGEN_RA_WRAP);
+ mutex_exit(&sc->sc_lock);
+ xfer_flags = USBD_NO_COPY |
+ (sce->state &UGEN_SHORT_OK ? USBD_SHORT_XFER_OK : 0);
+ xfersize = min(sce->ra_wb_reqsize,
+ (sce->ra_wb_bufsize - sce->ra_buflen[sce->ra_load]));
+ DPRINTFN(5, ("ugenioctl: calling bulkra_intr, req = %d(%d)\n",
+ xfersize, sce->ra_load));
usbd_setup_xfer(sce->ra_wb_xfer, sce->pipeh, sce,
- NULL,
- min(sce->ra_wb_xferlen, sce->ra_wb_bufsize),
- USBD_NO_COPY, USBD_NO_TIMEOUT,
+ NULL, xfersize,
+ xfer_flags, USBD_NO_TIMEOUT,
ugen_bulkra_intr);
err = usbd_transfer(sce->ra_wb_xfer);
if (err != USBD_IN_PROGRESS) {
+ mutex_enter(&sc->sc_lock);
sce->state &= ~UGEN_BULK_RA;
- free(sce->ibuf, M_USBDEV);
+ mutex_exit(&sc->sc_lock);
+ for (i = 0; i < BUFFCOUNT;i ++) {
+ if (sce->ra_buflist[i] != NULL) {
+ free(sce->ra_buflist[i], M_USBDEV);
+ sce->ra_buflist[i] = NULL;
+ }
+ sce->ra_buflen[i] = 0;
+ }
sce->ibuf = NULL;
usbd_free_xfer(sce->ra_wb_xfer);
return (EIO);
@@ -1501,15 +1807,25 @@
if (!(sce->state & UGEN_BULK_RA))
return (0);
+ mutex_enter(&sc->sc_lock);
sce->state &= ~UGEN_BULK_RA;
+ mutex_exit(&sc->sc_lock);
usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ sce->pipeh = NULL;
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);
+ for (i = 0; i < BUFFCOUNT;i ++) {
+ if (sce->ra_buflist[i] != NULL) {
+ free(sce->ra_buflist[i], M_USBDEV);
+ sce->ra_buflist[i] = NULL;
+ }
+ sce->ra_buflen[i] = 0;
+ }
sce->ibuf = NULL;
}
return (0);
@@ -1553,19 +1869,25 @@
sce->fill = sce->cur = sce->ibuf;
sce->limit = sce->ibuf + sce->ra_wb_bufsize;
sce->ra_wb_used = 0;
- sce->state |= UGEN_BULK_WB | UGEN_RA_WB_STOP;
+ mutex_enter(&sc->sc_lock);
+ sce->state |= UGEN_BULK_WB | UGEN_RA_WB_STOP | UGEN_RA_COMPLETE;
+ mutex_exit(&sc->sc_lock);
} else {
/* Only turn WB off if it's currently on. */
if (!(sce->state & UGEN_BULK_WB))
return (0);
+ mutex_enter(&sc->sc_lock);
sce->state &= ~UGEN_BULK_WB;
+ mutex_exit(&sc->sc_lock);
/*
* 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_close_pipe(sce->pipeh);
+ sce->pipeh = NULL;
usbd_free_xfer(sce->ra_wb_xfer);
free(sce->ibuf, M_USBDEV);
sce->ibuf = NULL;
@@ -1841,14 +2163,29 @@
{
int endpt = UGENENDPOINT(dev);
struct ugen_softc *sc;
- int error;
+ int error, s;
USB_GET_SC_OPEN(ugen, UGENUNIT(dev), sc);
+ if (sc->sc_dying)
+ return (EIO);
+
+ mutex_enter(&sc->sc_lock);
sc->sc_refcnt++;
+ mutex_exit(&sc->sc_lock);
+
error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, l);
- if (--sc->sc_refcnt < 0)
+
+ mutex_enter(&sc->sc_lock);
+ if (--sc->sc_refcnt < 0) {
+ mutex_exit(&sc->sc_lock);
+ s = splusb();
usb_detach_wakeup(USBDEV(sc->sc_dev));
+ splx(s);
+ } else {
+ mutex_exit(&sc->sc_lock);
+ }
+
return (error);
}
@@ -1858,10 +2195,15 @@
struct ugen_softc *sc;
struct ugen_endpoint *sce_in, *sce_out;
int revents = 0;
- int s;
+ int xfersize, err;
+ uint16_t xfer_flags;
USB_GET_SC_OPEN(ugen, UGENUNIT(dev), sc);
+ DPRINTFN(5,
+ ("%s: ugenpoll: unit: %d, endpoint: %d\n", USBDEVNAME(sc->sc_dev),
+ UGENUNIT(dev), UGENENDPOINT(dev)));
+
if (sc->sc_dying)
return (POLLHUP);
@@ -1880,7 +2222,7 @@
return (POLLERR);
}
#endif
- s = splusb();
+ mutex_enter(&sc->sc_lock);
if (sce_in && sce_in->pipeh && (events & (POLLIN | POLLRDNORM)))
switch (sce_in->edesc->bmAttributes & UE_XFERTYPE) {
case UE_INTERRUPT:
@@ -1898,11 +2240,41 @@
case UE_BULK:
#ifdef UGEN_BULK_RA_WB
if (sce_in->state & UGEN_BULK_RA) {
- if (sce_in->ra_wb_used > 0)
+ DPRINTFN(5,
+ ("%s: ra_wb_used: %d(%d)\n",USBDEVNAME(sc->sc_dev),
+ sce_in->ra_buflen[sce_in->ra_read], sce_in->ra_read));
+ DPRINTFN(5,
+ ("%s: ra_wb_state: 0x%x\n",USBDEVNAME(sc->sc_dev),
+ sce_in->state));
+ if (sce_in->state & UGEN_RA_COMPLETE) {
revents |= events &
(POLLIN | POLLRDNORM);
+ }
else
+ {
selrecord(l, &sce_in->rsel);
+ xfer_flags = USBD_NO_COPY |
+ (sce_in->state &UGEN_SHORT_OK ? USBD_SHORT_XFER_OK : 0);
+ xfersize = min(sce_in->ra_wb_reqsize,
+ (sce_in->ra_wb_bufsize - sce_in->ra_buflen[sce_in->ra_load]));
+ if ((xfersize) &&
+ ((sce_in->state & UGEN_RA_WRAP) == 0) &&
+ (sce_in->state & UGEN_RA_WB_STOP)) {
+ DPRINTFN(5, ("ugenpoll: calling bulkra_intr, req = %d(%d)\n",
+ xfersize, sce_in->ra_load));
+ usbd_setup_xfer(sce_in->ra_wb_xfer, sce_in->pipeh, sce_in,
+ NULL, xfersize,
+ xfer_flags, USBD_NO_TIMEOUT,
+ ugen_bulkra_intr);
+ sce_in->state &= ~UGEN_RA_WB_STOP;
+ mutex_exit(&sc->sc_lock);
+ err = usbd_transfer(sce_in->ra_wb_xfer);
+ mutex_enter(&sc->sc_lock);
+ if (err != USBD_IN_PROGRESS) {
+ sce_in->state |= UGEN_RA_WB_STOP; /* Try again */
+ }
+ }
+ }
break;
}
#endif
@@ -1946,7 +2318,7 @@
}
- splx(s);
+ mutex_exit(&sc->sc_lock);
return (revents);
}
@@ -1954,11 +2326,11 @@
filt_ugenrdetach(struct knote *kn)
{
struct ugen_endpoint *sce = kn->kn_hook;
- int s;
+ struct ugen_softc *sc = sce->sc;
- s = splusb();
+ mutex_enter(&sc->sc_lock);
SLIST_REMOVE(&sce->rsel.sel_klist, kn, knote, kn_selnext);
- splx(s);
+ mutex_exit(&sc->sc_lock);
}
static int
@@ -2054,7 +2426,6 @@
struct ugen_softc *sc;
struct ugen_endpoint *sce;
struct klist *klist;
- int s;
USB_GET_SC_OPEN(ugen, UGENUNIT(dev), sc);
@@ -2128,9 +2499,9 @@
kn->kn_hook = sce;
- s = splusb();
+ mutex_enter(&sc->sc_lock);
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
- splx(s);
+ mutex_exit(&sc->sc_lock);
return (0);
}
Home |
Main Index |
Thread Index |
Old Index