From d30d96ea57cf8ca4dcc821588caf4c06e3e78cd8 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 5 Nov 2012 17:50:40 +0000 Subject: [PATCH] Add a jitter buffer in the common USB serial driver code which temporarily stores characters if the TTY buffer is full when used a as a console. This can happen when a console is suspended. Also properly do the flow stop signalling when this happens and flow start when the condition changes back to normal again. Bump __FreeBSD_version to force external kernel modules to be recompiled. No kernel API changes. MFC after: 1 week Suggested by: ed @ --- sys/dev/usb/serial/usb_serial.c | 79 ++++++++++++++++++++++++++++++++- sys/dev/usb/serial/usb_serial.h | 4 ++ sys/sys/param.h | 2 +- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c index 242b6437108b..02f75fb1cb90 100644 --- a/sys/dev/usb/serial/usb_serial.c +++ b/sys/dev/usb/serial/usb_serial.c @@ -162,6 +162,7 @@ static tsw_ioctl_t ucom_ioctl; static tsw_modem_t ucom_modem; static tsw_param_t ucom_param; static tsw_outwakeup_t ucom_outwakeup; +static tsw_inwakeup_t ucom_inwakeup; static tsw_free_t ucom_free; static struct ttydevsw ucom_class = { @@ -169,6 +170,7 @@ static struct ttydevsw ucom_class = { .tsw_open = ucom_open, .tsw_close = ucom_close, .tsw_outwakeup = ucom_outwakeup, + .tsw_inwakeup = ucom_inwakeup, .tsw_ioctl = ucom_ioctl, .tsw_param = ucom_param, .tsw_modem = ucom_modem, @@ -716,6 +718,10 @@ ucom_open(struct tty *tp) sc->sc_pls_set = 0; sc->sc_pls_clr = 0; + /* reset jitter buffer */ + sc->sc_jitterbuf_in = 0; + sc->sc_jitterbuf_out = 0; + ucom_queue_command(sc, ucom_cfg_open, NULL, &sc->sc_open_task[0].hdr, &sc->sc_open_task[1].hdr); @@ -780,6 +786,47 @@ ucom_close(struct tty *tp) } } +static void +ucom_inwakeup(struct tty *tp) +{ + struct ucom_softc *sc = tty_softc(tp); + uint16_t pos; + + if (sc == NULL) + return; + + tty_lock(tp); + + if (ttydisc_can_bypass(tp) != 0 || + (sc->sc_flag & UCOM_FLAG_HL_READY) == 0) { + tty_unlock(tp); + return; + } + + pos = sc->sc_jitterbuf_out; + + while (sc->sc_jitterbuf_in != pos) { + int c; + + c = (char)sc->sc_jitterbuf[pos]; + + if (ttydisc_rint(tp, c, 0) == -1) + break; + pos++; + if (pos >= UCOM_JITTERBUF_SIZE) + pos -= UCOM_JITTERBUF_SIZE; + } + + sc->sc_jitterbuf_out = pos; + + /* clear RTS in async fashion */ + if ((sc->sc_jitterbuf_in == pos) && + (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)) + ucom_rts(sc, 0); + + tty_unlock(tp); +} + static int ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { @@ -1360,6 +1407,11 @@ ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, /* first check if we can pass the buffer directly */ if (ttydisc_can_bypass(tp)) { + + /* clear any jitter buffer */ + sc->sc_jitterbuf_in = 0; + sc->sc_jitterbuf_out = 0; + if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { DPRINTF("tp=%p, data lost\n", tp); } @@ -1368,8 +1420,31 @@ ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, /* need to loop */ for (cnt = 0; cnt != res.length; cnt++) { - if (ttydisc_rint(tp, buf[cnt], 0) == -1) { - /* XXX what should we do? */ + if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out || + ttydisc_rint(tp, buf[cnt], 0) == -1) { + uint16_t end; + uint16_t pos; + + pos = sc->sc_jitterbuf_in; + end = sc->sc_jitterbuf_out + + UCOM_JITTERBUF_SIZE - 1; + if (end >= UCOM_JITTERBUF_SIZE) + end -= UCOM_JITTERBUF_SIZE; + + for (; cnt != res.length; cnt++) { + if (pos == end) + break; + sc->sc_jitterbuf[pos] = buf[cnt]; + pos++; + if (pos >= UCOM_JITTERBUF_SIZE) + pos -= UCOM_JITTERBUF_SIZE; + } + + sc->sc_jitterbuf_in = pos; + + /* set RTS in async fashion */ + if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) + ucom_rts(sc, 1); DPRINTF("tp=%p, lost %d " "chars\n", tp, res.length - cnt); diff --git a/sys/dev/usb/serial/usb_serial.h b/sys/dev/usb/serial/usb_serial.h index 7d72adfe944a..d045fbe8d31f 100644 --- a/sys/dev/usb/serial/usb_serial.h +++ b/sys/dev/usb/serial/usb_serial.h @@ -78,6 +78,7 @@ #define UCOM_MINVER 1 #define UCOM_PREFVER UCOM_MODVER #define UCOM_MAXVER 1 +#define UCOM_JITTERBUF_SIZE 128 /* bytes */ struct usb_device; struct ucom_softc; @@ -169,6 +170,8 @@ struct ucom_softc { struct mtx *sc_mtx; void *sc_parent; int sc_subunit; + uint16_t sc_jitterbuf_in; + uint16_t sc_jitterbuf_out; uint16_t sc_portno; uint16_t sc_flag; #define UCOM_FLAG_RTS_IFLOW 0x01 /* use RTS input flow control */ @@ -191,6 +194,7 @@ struct ucom_softc { #define UCOM_LS_RTS 0x02 #define UCOM_LS_BREAK 0x04 #define UCOM_LS_RING 0x08 + uint8_t sc_jitterbuf[UCOM_JITTERBUF_SIZE]; }; #define UCOM_MTX_ASSERT(sc, what) mtx_assert((sc)->sc_mtx, what) diff --git a/sys/sys/param.h b/sys/sys/param.h index 470e369f98cd..161f9ad0e357 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1000022 /* Master, propagated to newvers */ +#define __FreeBSD_version 1000023 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,