Module Name: src
Committed By: martin
Date: Wed Jan 6 20:37:56 UTC 2010
Modified Files:
src/sys/dev/usb: ucom.c
Log Message:
Optimize for higher speeds, e.g. when used as part of a 3G modem.
Contributed anonymously.
To generate a diff of this commit:
cvs rdiff -u -r1.81 -r1.82 src/sys/dev/usb/ucom.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/ucom.c
diff -u src/sys/dev/usb/ucom.c:1.81 src/sys/dev/usb/ucom.c:1.82
--- src/sys/dev/usb/ucom.c:1.81 Sun Dec 6 21:40:31 2009
+++ src/sys/dev/usb/ucom.c Wed Jan 6 20:37:56 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: ucom.c,v 1.81 2009/12/06 21:40:31 dyoung Exp $ */
+/* $NetBSD: ucom.c,v 1.82 2010/01/06 20:37:56 martin Exp $ */
/*
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.81 2009/12/06 21:40:31 dyoung Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.82 2010/01/06 20:37:56 martin Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -48,6 +48,7 @@
#include <sys/vnode.h>
#include <sys/device.h>
#include <sys/poll.h>
+#include <sys/queue.h>
#include <sys/kauth.h>
#if defined(__NetBSD__)
#include "rnd.h"
@@ -87,6 +88,22 @@
#define UCOMDIALOUT(x) (minor(x) & UCOMDIALOUT_MASK)
#define UCOMCALLUNIT(x) (minor(x) & UCOMCALLUNIT_MASK)
+/*
+ * XXX: We can submit multiple input/output buffers to the usb stack
+ * to improve throughput, but the usb stack is too lame to deal with this
+ * in a number of places.
+ */
+#define UCOM_IN_BUFFS 1
+#define UCOM_OUT_BUFFS 1
+
+struct ucom_buffer {
+ SIMPLEQ_ENTRY(ucom_buffer) ub_link;
+ usbd_xfer_handle ub_xfer;
+ u_char *ub_data;
+ u_int ub_len;
+ u_int ub_index;
+};
+
struct ucom_softc {
USBBASEDEVICE sc_dev; /* base device */
@@ -96,18 +113,21 @@
int sc_bulkin_no; /* bulk in endpoint address */
usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */
- usbd_xfer_handle sc_ixfer; /* read request */
- u_char *sc_ibuf; /* read buffer */
u_int sc_ibufsize; /* read buffer size */
u_int sc_ibufsizepad; /* read buffer size padded */
+ struct ucom_buffer sc_ibuff[UCOM_IN_BUFFS];
+ SIMPLEQ_HEAD(, ucom_buffer) sc_ibuff_empty;
+ SIMPLEQ_HEAD(, ucom_buffer) sc_ibuff_full;
int sc_bulkout_no; /* bulk out endpoint address */
usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */
- usbd_xfer_handle sc_oxfer; /* write request */
- u_char *sc_obuf; /* write buffer */
u_int sc_obufsize; /* write buffer size */
- u_int sc_opkthdrlen; /* header length of
- * output packet */
+ u_int sc_opkthdrlen; /* header length of */
+ struct ucom_buffer sc_obuff[UCOM_OUT_BUFFS];
+ SIMPLEQ_HEAD(, ucom_buffer) sc_obuff_free;
+ SIMPLEQ_HEAD(, ucom_buffer) sc_obuff_full;
+
+ void *sc_si;
struct ucom_methods *sc_methods;
void *sc_parent;
@@ -117,6 +137,8 @@
u_char sc_lsr;
u_char sc_msr;
u_char sc_mcr;
+ volatile u_char sc_rx_stopped;
+ u_char sc_rx_unblock;
u_char sc_tx_stopped;
int sc_swflags;
@@ -143,21 +165,28 @@
ucomstop, ucomtty, ucompoll, nommap, ttykqfilter, D_TTY
};
-Static void ucom_cleanup(struct ucom_softc *);
-Static void ucom_hwiflow(struct ucom_softc *);
-Static int ucomparam(struct tty *, struct termios *);
-Static void ucomstart(struct tty *);
-Static void ucom_shutdown(struct ucom_softc *);
-Static int ucom_do_ioctl(struct ucom_softc *, u_long, void *,
+static void ucom_cleanup(struct ucom_softc *);
+static int ucomparam(struct tty *, struct termios *);
+static int ucomhwiflow(struct tty *, int);
+static void ucomstart(struct tty *);
+static void ucom_shutdown(struct ucom_softc *);
+static int ucom_do_ioctl(struct ucom_softc *, u_long, void *,
int, struct lwp *);
-Static void ucom_dtr(struct ucom_softc *, int);
-Static void ucom_rts(struct ucom_softc *, int);
-Static void ucom_break(struct ucom_softc *, int);
-Static usbd_status ucomstartread(struct ucom_softc *);
-Static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
-Static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
-Static void tiocm_to_ucom(struct ucom_softc *, u_long, int);
-Static int ucom_to_tiocm(struct ucom_softc *);
+static void ucom_dtr(struct ucom_softc *, int);
+static void ucom_rts(struct ucom_softc *, int);
+static void ucom_break(struct ucom_softc *, int);
+static void tiocm_to_ucom(struct ucom_softc *, u_long, int);
+static int ucom_to_tiocm(struct ucom_softc *);
+
+static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ucom_submit_write(struct ucom_softc *, struct ucom_buffer *);
+static void ucom_write_status(struct ucom_softc *, struct ucom_buffer *,
+ usbd_status);
+
+static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ucom_read_complete(struct ucom_softc *);
+static usbd_status ucomsubmitread(struct ucom_softc *, struct ucom_buffer *);
+static void ucom_softintr(void *);
USB_DECLARE_DRIVER(ucom);
@@ -189,9 +218,21 @@
sc->sc_parent = uca->arg;
sc->sc_portno = uca->portno;
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+ sc->sc_mcr = 0;
+ sc->sc_tx_stopped = 0;
+ sc->sc_swflags = 0;
+ sc->sc_opening = 0;
+ sc->sc_refcnt = 0;
+ sc->sc_dying = 0;
+
+ sc->sc_si = softint_establish(SOFTINT_NET, ucom_softintr, sc);
+
tp = ttymalloc();
tp->t_oproc = ucomstart;
tp->t_param = ucomparam;
+ tp->t_hwiflow = ucomhwiflow;
sc->sc_tty = tp;
DPRINTF(("ucom_attach: tty_attach %p\n", tp));
@@ -212,7 +253,7 @@
struct ucom_softc *sc = device_private(self);
struct tty *tp = sc->sc_tty;
int maj, mn;
- int s;
+ int s, i;
DPRINTF(("ucom_detach: sc=%p flags=%d tp=%p, pipe=%d,%d\n",
sc, flags, tp, sc->sc_bulkin_no, sc->sc_bulkout_no));
@@ -238,6 +279,8 @@
/* Wait for processes to go away. */
usb_detach_wait(USBDEV(sc->sc_dev));
}
+
+ softint_disestablish(sc->sc_si);
splx(s);
/* locate the major number */
@@ -257,6 +300,16 @@
sc->sc_tty = NULL;
}
+ for (i = 0; i < UCOM_IN_BUFFS; i++) {
+ if (sc->sc_ibuff[i].ub_xfer != NULL)
+ usbd_free_xfer(sc->sc_ibuff[i].ub_xfer);
+ }
+
+ for (i = 0; i < UCOM_OUT_BUFFS; i++) {
+ if (sc->sc_obuff[i].ub_xfer != NULL)
+ usbd_free_xfer(sc->sc_obuff[i].ub_xfer);
+ }
+
/* Detach the random source */
#if defined(__NetBSD__) && NRND > 0
rnd_detach_source(&sc->sc_rndsource);
@@ -303,8 +356,9 @@
int unit = UCOMUNIT(dev);
usbd_status err;
struct ucom_softc *sc = device_lookup_private(&ucom_cd, unit);
+ struct ucom_buffer *ub;
struct tty *tp;
- int s;
+ int s, i;
int error;
if (sc == NULL)
@@ -388,15 +442,12 @@
ucom_dtr(sc, 1);
ucom_rts(sc, 1);
- /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/
- ucom_hwiflow(sc);
-
DPRINTF(("ucomopen: open pipes in=%d out=%d\n",
sc->sc_bulkin_no, sc->sc_bulkout_no));
/* Open the bulk pipes */
- err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
- &sc->sc_bulkin_pipe);
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
if (err) {
DPRINTF(("%s: open bulk in error (addr %d), err=%s\n",
USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no,
@@ -414,35 +465,56 @@
goto fail_1;
}
- /* Allocate a request and an input buffer and start reading. */
- sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
- if (sc->sc_ixfer == NULL) {
- error = ENOMEM;
- goto fail_2;
- }
+ sc->sc_rx_unblock = 0;
+ sc->sc_rx_stopped = 0;
+ sc->sc_tx_stopped = 0;
+
+ memset(sc->sc_ibuff, 0, sizeof(sc->sc_ibuff));
+ memset(sc->sc_obuff, 0, sizeof(sc->sc_obuff));
+
+ SIMPLEQ_INIT(&sc->sc_ibuff_empty);
+ SIMPLEQ_INIT(&sc->sc_ibuff_full);
+ SIMPLEQ_INIT(&sc->sc_obuff_free);
+ SIMPLEQ_INIT(&sc->sc_obuff_full);
+
+ /* Allocate input buffers */
+ for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS];
+ ub++) {
+ ub->ub_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (ub->ub_xfer == NULL) {
+ error = ENOMEM;
+ goto fail_2;
+ }
+ ub->ub_data = usbd_alloc_buffer(ub->ub_xfer,
+ sc->sc_ibufsizepad);
+ if (ub->ub_data == NULL) {
+ error = ENOMEM;
+ goto fail_2;
+ }
- sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
- sc->sc_ibufsizepad);
- if (sc->sc_ibuf == NULL) {
- error = ENOMEM;
- goto fail_3;
+ if (ucomsubmitread(sc, ub) != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ goto fail_2;
+ }
}
- sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
- if (sc->sc_oxfer == NULL) {
- error = ENOMEM;
- goto fail_3;
- }
+ for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS];
+ ub++) {
+ ub->ub_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (ub->ub_xfer == NULL) {
+ error = ENOMEM;
+ goto fail_2;
+ }
+ ub->ub_data = usbd_alloc_buffer(ub->ub_xfer,
+ sc->sc_obufsize);
+ if (ub->ub_data == NULL) {
+ error = ENOMEM;
+ goto fail_2;
+ }
- sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
- sc->sc_obufsize +
- sc->sc_opkthdrlen);
- if (sc->sc_obuf == NULL) {
- error = ENOMEM;
- goto fail_4;
+ SIMPLEQ_INSERT_TAIL(&sc->sc_obuff_free, ub, ub_link);
}
- ucomstartread(sc);
}
sc->sc_opening = 0;
wakeup(&sc->sc_opening);
@@ -458,13 +530,24 @@
return (0);
-fail_4:
- usbd_free_xfer(sc->sc_oxfer);
- sc->sc_oxfer = NULL;
-fail_3:
- usbd_free_xfer(sc->sc_ixfer);
- sc->sc_ixfer = NULL;
fail_2:
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ for (i = 0; i < UCOM_IN_BUFFS; i++) {
+ if (sc->sc_ibuff[i].ub_xfer != NULL) {
+ usbd_free_xfer(sc->sc_ibuff[i].ub_xfer);
+ sc->sc_ibuff[i].ub_xfer = NULL;
+ sc->sc_ibuff[i].ub_data = NULL;
+ }
+ }
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ for (i = 0; i < UCOM_OUT_BUFFS; i++) {
+ if (sc->sc_obuff[i].ub_xfer != NULL) {
+ usbd_free_xfer(sc->sc_obuff[i].ub_xfer);
+ sc->sc_obuff[i].ub_xfer = NULL;
+ sc->sc_obuff[i].ub_data = NULL;
+ }
+ }
+
usbd_close_pipe(sc->sc_bulkout_pipe);
sc->sc_bulkout_pipe = NULL;
fail_1:
@@ -477,6 +560,8 @@
return (error);
bad:
+ s = spltty();
+ CLR(tp->t_state, TS_BUSY);
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
/*
* We failed to open the device, and nobody else had it opened.
@@ -484,6 +569,7 @@
*/
ucom_cleanup(sc);
}
+ splx(s);
return (error);
}
@@ -493,12 +579,15 @@
{
struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev));
struct tty *tp = sc->sc_tty;
+ int s;
DPRINTF(("ucomclose: unit=%d\n", UCOMUNIT(dev)));
if (!ISSET(tp->t_state, TS_ISOPEN))
return (0);
+ s = spltty();
sc->sc_refcnt++;
+ CLR(tp->t_state, TS_BUSY);
(*tp->t_linesw->l_close)(tp, flag);
ttyclose(tp);
@@ -517,6 +606,7 @@
if (--sc->sc_refcnt < 0)
usb_detach_wakeup(USBDEV(sc->sc_dev));
+ splx(s);
return (0);
}
@@ -594,7 +684,7 @@
return (error);
}
-Static int
+static int
ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, void *data,
int flag, struct lwp *l)
{
@@ -676,7 +766,7 @@
return (error);
}
-Static void
+static void
tiocm_to_ucom(struct ucom_softc *sc, u_long how, int ttybits)
{
u_char combits;
@@ -708,7 +798,7 @@
ucom_rts(sc, (sc->sc_mcr & UMCR_RTS) != 0);
}
-Static int
+static int
ucom_to_tiocm(struct ucom_softc *sc)
{
u_char combits;
@@ -739,7 +829,7 @@
return (ttybits);
}
-Static void
+static void
ucom_break(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_break: onoff=%d\n", onoff));
@@ -749,7 +839,7 @@
UCOM_SET_BREAK, onoff);
}
-Static void
+static void
ucom_dtr(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_dtr: onoff=%d\n", onoff));
@@ -759,7 +849,7 @@
UCOM_SET_DTR, onoff);
}
-Static void
+static void
ucom_rts(struct ucom_softc *sc, int onoff)
{
DPRINTF(("ucom_rts: onoff=%d\n", onoff));
@@ -789,7 +879,7 @@
}
}
-Static int
+static int
ucomparam(struct tty *tp, struct termios *t)
{
struct ucom_softc *sc = device_lookup_private(&ucom_cd,
@@ -858,38 +948,32 @@
return (0);
}
-/*
- * (un)block input via hw flowcontrol
- */
-Static void
-ucom_hwiflow(struct ucom_softc *sc)
+static int
+ucomhwiflow(struct tty *tp, int block)
{
- DPRINTF(("ucom_hwiflow:\n"));
-#if 0
-XXX
- bus_space_tag_t iot = sc->sc_iot;
- bus_space_handle_t ioh = sc->sc_ioh;
+ struct ucom_softc *sc = device_lookup_private(&ucom_cd,
+ UCOMUNIT(tp->t_dev));
+ int old;
- if (sc->sc_mcr_rts == 0)
- return;
+ old = sc->sc_rx_stopped;
+ sc->sc_rx_stopped = (u_char)block;
- if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
- CLR(sc->sc_mcr, sc->sc_mcr_rts);
- CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
- } else {
- SET(sc->sc_mcr, sc->sc_mcr_rts);
- SET(sc->sc_mcr_active, sc->sc_mcr_rts);
+ if (old && !block) {
+ int s = splusb();
+ sc->sc_rx_unblock = 1;
+ softint_schedule(sc->sc_si);
+ splx(s);
}
- bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active);
-#endif
+
+ return (1);
}
-Static void
+static void
ucomstart(struct tty *tp)
{
struct ucom_softc *sc = device_lookup_private(&ucom_cd,
UCOMUNIT(tp->t_dev));
- usbd_status err;
+ struct ucom_buffer *ub;
int s;
u_char *data;
int cnt;
@@ -905,7 +989,7 @@
if (sc->sc_tx_stopped)
goto out;
- if (!ttypull(tp))
+ if (!ttypull(tp))
goto out;
/* Grab the first contiguous region of buffer space. */
@@ -917,30 +1001,30 @@
goto out;
}
- SET(tp->t_state, TS_BUSY);
+ ub = SIMPLEQ_FIRST(&sc->sc_obuff_free);
+ KASSERT(ub != NULL);
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_obuff_free, ub_link);
- if (cnt > sc->sc_obufsize) {
- DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
+ if (SIMPLEQ_FIRST(&sc->sc_obuff_free) == NULL)
+ SET(tp->t_state, TS_BUSY);
+
+ if (cnt > sc->sc_obufsize)
cnt = sc->sc_obufsize;
- }
+
if (sc->sc_methods->ucom_write != NULL)
sc->sc_methods->ucom_write(sc->sc_parent, sc->sc_portno,
- sc->sc_obuf, data, &cnt);
+ ub->ub_data, data, &cnt);
else
- memcpy(sc->sc_obuf, data, cnt);
+ memcpy(ub->ub_data, data, cnt);
- DPRINTFN(4,("ucomstart: %d chars\n", cnt));
- usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
- (usbd_private_handle)sc, sc->sc_obuf, cnt,
- USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
- /* What can we do on error? */
- err = usbd_transfer(sc->sc_oxfer);
-#ifdef DIAGNOSTIC
- if (err != USBD_IN_PROGRESS)
- printf("ucomstart: err=%s\n", usbd_errstr(err));
-#endif
+ ub->ub_len = cnt;
+ ub->ub_index = 0;
+
+ SIMPLEQ_INSERT_TAIL(&sc->sc_obuff_full, ub, ub_link);
+
+ softint_schedule(sc->sc_si);
-out:
+ out:
splx(s);
}
@@ -964,133 +1048,234 @@
#endif
}
-Static void
-ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+static void
+ucom_write_status(struct ucom_softc *sc, struct ucom_buffer *ub,
+ usbd_status err)
{
- struct ucom_softc *sc = (struct ucom_softc *)p;
struct tty *tp = sc->sc_tty;
- u_int32_t cc;
- int s;
-
- DPRINTFN(5,("ucomwritecb: status=%d\n", status));
+ uint32_t cc = ub->ub_len;
- if (status == USBD_CANCELLED || sc->sc_dying)
- goto error;
+ switch (err) {
+ case USBD_IN_PROGRESS:
+ ub->ub_index = ub->ub_len;
+ break;
+ case USBD_STALLED:
+ ub->ub_index = 0;
+ softint_schedule(sc->sc_si);
+ break;
+ case USBD_NORMAL_COMPLETION:
+ usbd_get_xfer_status(ub->ub_xfer, NULL, NULL, &cc, NULL);
+#if defined(__NetBSD__) && NRND > 0
+ rnd_add_uint32(&sc->sc_rndsource, cc);
+#endif
+ /*FALLTHROUGH*/
+ default:
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_obuff_full, ub_link);
+ SIMPLEQ_INSERT_TAIL(&sc->sc_obuff_free, ub, ub_link);
+ cc -= sc->sc_opkthdrlen;
+
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq, cc);
+
+ if (err != USBD_CANCELLED && err != USBD_IOERROR &&
+ !sc->sc_dying) {
+ if ((ub = SIMPLEQ_FIRST(&sc->sc_obuff_full)) != NULL)
+ ucom_submit_write(sc, ub);
- if (status) {
- DPRINTF(("ucomwritecb: status=%d\n", status));
- usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
- /* XXX we should restart after some delay. */
- goto error;
+ (*tp->t_linesw->l_start)(tp);
+ }
+ break;
}
+}
- usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
-#if defined(__NetBSD__) && NRND > 0
- rnd_add_uint32(&sc->sc_rndsource, cc);
-#endif
- DPRINTFN(5,("ucomwritecb: cc=%d\n", cc));
- /* convert from USB bytes to tty bytes */
- cc -= sc->sc_opkthdrlen;
+/* Call at spltty() */
+static void
+ucom_submit_write(struct ucom_softc *sc, struct ucom_buffer *ub)
+{
+
+ usbd_setup_xfer(ub->ub_xfer, sc->sc_bulkout_pipe,
+ (usbd_private_handle)sc, ub->ub_data, ub->ub_len,
+ USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
+
+ ucom_write_status(sc, ub, usbd_transfer(ub->ub_xfer));
+}
+
+static void
+ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ucom_softc *sc = (struct ucom_softc *)p;
+ int s;
s = spltty();
- CLR(tp->t_state, TS_BUSY);
- if (ISSET(tp->t_state, TS_FLUSH))
- CLR(tp->t_state, TS_FLUSH);
- else
- ndflush(&tp->t_outq, cc);
- (*tp->t_linesw->l_start)(tp);
+
+ ucom_write_status(sc, SIMPLEQ_FIRST(&sc->sc_obuff_full), status);
+
splx(s);
- return;
+}
+
+static void
+ucom_softintr(void *arg)
+{
+ struct ucom_softc *sc = arg;
+ struct tty *tp = sc->sc_tty;
+ struct ucom_buffer *ub;
+ int s;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN))
+ return;
-error:
s = spltty();
- CLR(tp->t_state, TS_BUSY);
+
+ ub = SIMPLEQ_FIRST(&sc->sc_obuff_full);
+
+ if (ub != NULL && ub->ub_index == 0)
+ ucom_submit_write(sc, ub);
+
+ if (sc->sc_rx_unblock)
+ ucom_read_complete(sc);
+
splx(s);
}
-Static usbd_status
-ucomstartread(struct ucom_softc *sc)
+static void
+ucom_read_complete(struct ucom_softc *sc)
+{
+ int (*rint)(int, struct tty *);
+ struct ucom_buffer *ub;
+ struct tty *tp;
+ int s;
+
+ tp = sc->sc_tty;
+ rint = tp->t_linesw->l_rint;
+ ub = SIMPLEQ_FIRST(&sc->sc_ibuff_full);
+
+ while (ub != NULL && !sc->sc_rx_stopped) {
+
+ s = spltty();
+
+ while (ub->ub_index < ub->ub_len && !sc->sc_rx_stopped) {
+ /* Give characters to tty layer. */
+ if ((*rint)(ub->ub_data[ub->ub_index], tp) == -1) {
+ /* Overflow: drop remainder */
+ ub->ub_index = ub->ub_len;
+ } else
+ ub->ub_index++;
+ }
+
+ splx(s);
+
+ if (ub->ub_index == ub->ub_len) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_full, ub_link);
+
+ ucomsubmitread(sc, ub);
+
+ ub = SIMPLEQ_FIRST(&sc->sc_ibuff_full);
+ }
+ }
+
+ sc->sc_rx_unblock = (ub != NULL);
+}
+
+static usbd_status
+ucomsubmitread(struct ucom_softc *sc, struct ucom_buffer *ub)
{
usbd_status err;
- DPRINTFN(5,("ucomstartread: start\n"));
- usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
- (usbd_private_handle)sc,
- sc->sc_ibuf, sc->sc_ibufsize,
- USBD_SHORT_XFER_OK | USBD_NO_COPY,
- USBD_NO_TIMEOUT, ucomreadcb);
- err = usbd_transfer(sc->sc_ixfer);
- if (err != USBD_IN_PROGRESS) {
- DPRINTF(("ucomstartread: err=%s\n", usbd_errstr(err)));
+ usbd_setup_xfer(ub->ub_xfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc, ub->ub_data, sc->sc_ibufsize,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, ucomreadcb);
+
+ if ((err = usbd_transfer(ub->ub_xfer)) != USBD_IN_PROGRESS) {
+ /* XXX: Recover from this, please! */
+ printf("ucomsubmitread: err=%s\n", usbd_errstr(err));
return (err);
}
+
+ SIMPLEQ_INSERT_TAIL(&sc->sc_ibuff_empty, ub, ub_link);
+
return (USBD_NORMAL_COMPLETION);
}
-Static void
+static void
ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
{
struct ucom_softc *sc = (struct ucom_softc *)p;
struct tty *tp = sc->sc_tty;
- int (*rint)(int, struct tty *) = tp->t_linesw->l_rint;
- usbd_status err;
+ struct ucom_buffer *ub;
u_int32_t cc;
u_char *cp;
int s;
- DPRINTFN(5,("ucomreadcb: status=%d\n", status));
+ ub = SIMPLEQ_FIRST(&sc->sc_ibuff_empty);
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_empty, ub_link);
if (status == USBD_CANCELLED || status == USBD_IOERROR ||
sc->sc_dying) {
DPRINTF(("ucomreadcb: dying\n"));
+ ub->ub_index = ub->ub_len = 0;
/* Send something to wake upper layer */
s = spltty();
- (*rint)('\n', tp);
- mutex_spin_enter(&tty_lock); /* XXX */
- ttwakeup(tp);
- mutex_spin_exit(&tty_lock); /* XXX */
+ if (status != USBD_CANCELLED) {
+ (tp->t_linesw->l_rint)('\n', tp);
+ mutex_spin_enter(&tty_lock); /* XXX */
+ ttwakeup(tp);
+ mutex_spin_exit(&tty_lock); /* XXX */
+ }
splx(s);
return;
}
- if (status) {
+ if (status == USBD_STALLED) {
usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
- /* XXX we should restart after some delay. */
+ ucomsubmitread(sc, ub);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("ucomreadcb: wonky status=%s\n", usbd_errstr(status));
return;
}
usbd_get_xfer_status(xfer, NULL, (void *)&cp, &cc, NULL);
+
+ if (cc == 0) {
+ aprint_normal_dev(sc->sc_dev,
+ "ucomreadcb: zero length xfer!\n");
+ }
+
+ KDASSERT(cp == ub->ub_data);
+
#if defined(__NetBSD__) && NRND > 0
rnd_add_uint32(&sc->sc_rndsource, cc);
#endif
- DPRINTFN(5,("ucomreadcb: got %d chars, tp=%p\n", cc, tp));
- if (sc->sc_methods->ucom_read != NULL)
- sc->sc_methods->ucom_read(sc->sc_parent, sc->sc_portno,
- &cp, &cc);
- s = spltty();
- /* Give characters to tty layer. */
- while (cc-- > 0) {
- DPRINTFN(7,("ucomreadcb: char=0x%02x\n", *cp));
- if ((*rint)(*cp++, tp) == -1) {
- /* XXX what should we do? */
- aprint_error_dev(sc->sc_dev, "lost %d chars\n",
- cc);
- break;
- }
+ if (sc->sc_opening) {
+ ucomsubmitread(sc, ub);
+ return;
}
- splx(s);
- err = ucomstartread(sc);
- if (err) {
- aprint_error_dev(sc->sc_dev, "read start failed\n");
- /* XXX what should we do now? */
- }
+ if (sc->sc_methods->ucom_read != NULL) {
+ sc->sc_methods->ucom_read(sc->sc_parent, sc->sc_portno,
+ &cp, &cc);
+ ub->ub_index = (u_int)(cp - ub->ub_data);
+ } else
+ ub->ub_index = 0;
+
+ ub->ub_len = cc;
+
+ SIMPLEQ_INSERT_TAIL(&sc->sc_ibuff_full, ub, ub_link);
+
+ ucom_read_complete(sc);
}
-Static void
+static void
ucom_cleanup(struct ucom_softc *sc)
{
+ struct ucom_buffer *ub;
+
DPRINTF(("ucom_cleanup: closing pipes\n"));
ucom_shutdown(sc);
@@ -1104,13 +1289,19 @@
usbd_close_pipe(sc->sc_bulkout_pipe);
sc->sc_bulkout_pipe = NULL;
}
- if (sc->sc_ixfer != NULL) {
- usbd_free_xfer(sc->sc_ixfer);
- sc->sc_ixfer = NULL;
- }
- if (sc->sc_oxfer != NULL) {
- usbd_free_xfer(sc->sc_oxfer);
- sc->sc_oxfer = NULL;
+ for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; ub++) {
+ if (ub->ub_xfer != NULL) {
+ usbd_free_xfer(ub->ub_xfer);
+ ub->ub_xfer = NULL;
+ ub->ub_data = NULL;
+ }
+ }
+ for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS]; ub++){
+ if (ub->ub_xfer != NULL) {
+ usbd_free_xfer(ub->ub_xfer);
+ ub->ub_xfer = NULL;
+ ub->ub_data = NULL;
+ }
}
}