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; + } } }