From 61b95b459d41c0a03c8e62c7a96a236ddacb9059 Mon Sep 17 00:00:00 2001 From: skrll Date: Thu, 6 Aug 2009 07:07:30 +0000 Subject: [PATCH] Make ucycomstart use usbd_setup_xfer and a callback so that it doesn't (attempt to) sleep in interrupt context. From David Howland in PR 36276 with changes from me. XXX Still losing data on input. --- sys/dev/usb/ucycom.c | 77 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/sys/dev/usb/ucycom.c b/sys/dev/usb/ucycom.c index cbd298663480..385f68d8ab56 100644 --- a/sys/dev/usb/ucycom.c +++ b/sys/dev/usb/ucycom.c @@ -1,4 +1,4 @@ -/* $NetBSD: ucycom.c,v 1.28 2009/07/24 06:58:24 skrll Exp $ */ +/* $NetBSD: ucycom.c,v 1.29 2009/08/06 07:07:30 skrll Exp $ */ /* * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -38,7 +38,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.28 2009/07/24 06:58:24 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.29 2009/08/06 07:07:30 skrll Exp $"); #include #include @@ -66,7 +66,7 @@ __KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.28 2009/07/24 06:58:24 skrll Exp $"); #ifdef UCYCOM_DEBUG #define DPRINTF(x) if (ucycomdebug) logprintf x #define DPRINTFN(n, x) if (ucycomdebug > (n)) logprintf x -int ucycomdebug = 0; +int ucycomdebug = 20; #else #define DPRINTF(x) #define DPRINTFN(n,x) @@ -122,6 +122,7 @@ struct ucycom_softc { size_t sc_olen; /* output report length */ uint8_t *sc_obuf; + int sc_wlen; /* settings */ uint32_t sc_baud; @@ -150,6 +151,7 @@ const struct cdevsw ucycom_cdevsw = { Static int ucycomparam(struct tty *, struct termios *); Static void ucycomstart(struct tty *); +Static void ucycomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status); Static void ucycom_intr(struct uhidev *, void *, u_int); Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t); Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int); @@ -444,8 +446,9 @@ ucycomstart(struct tty *tp) { struct ucycom_softc *sc = device_lookup_private(&ucycom_cd, UCYCOMUNIT(tp->t_dev)); + usbd_status err; u_char *data; - int cnt, len, err, s; + int cnt, len, s; if (sc->sc_dying) return; @@ -541,6 +544,7 @@ ucycomstart(struct tty *tp) goto out; } splx(s); + sc->sc_wlen = len; #ifdef UCYCOM_DEBUG if (ucycomdebug > 5) { @@ -555,26 +559,65 @@ ucycomstart(struct tty *tp) } } #endif - err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); + DPRINTFN(4,("ucycomstart: %d chars\n", len)); + usbd_setup_xfer(sc->sc_hdev.sc_parent->sc_oxfer, + sc->sc_hdev.sc_parent->sc_opipe, (usbd_private_handle)sc, + sc->sc_obuf, sc->sc_olen, 0 /* USBD_NO_COPY */, USBD_NO_TIMEOUT, + ucycomwritecb); - if (err) { - DPRINTF(("ucycomstart: error doing uhidev_write = %d\n", err)); - } + /* What can we do on error? */ + err = usbd_transfer(sc->sc_hdev.sc_parent->sc_oxfer); #ifdef UCYCOM_DEBUG - ucycom_get_cfg(sc); + if (err != USBD_IN_PROGRESS) + DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err))); #endif - DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", cnt, len)); + return; - s = spltty(); +out: + splx(s); +} + +Static void +ucycomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) +{ + struct ucycom_softc *sc = (struct ucycom_softc *)p; + struct tty *tp = sc->sc_tty; + usbd_status stat; + int len, s; + + if (status == USBD_CANCELLED || sc->sc_dying) + goto error; + + if (status) { + DPRINTF(("ucycomwritecb: status=%d\n", status)); + usbd_clear_endpoint_stall(sc->sc_hdev.sc_parent->sc_opipe); + /* XXX we should restart after some delay. */ + goto error; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &len, &stat); + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTFN(4,("ucycomwritecb: status = %d\n", status)); + goto error; + } + + DPRINTFN(4,("ucycomwritecb: did %d/%d chars\n", sc->sc_wlen, len)); + + 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, len); + ndflush(&tp->t_outq, sc->sc_wlen); (*tp->t_linesw->l_start)(tp); + splx(s); + return; -out: +error: + s = spltty(); + CLR(tp->t_state, TS_BUSY); splx(s); } @@ -859,6 +902,10 @@ ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) sc->sc_baud = baud; sc->sc_cfg = cfg; +#ifdef UCYCOM_DEBUG + ucycom_get_cfg(sc); +#endif + return 0; } @@ -902,9 +949,9 @@ ucycom_intr(struct uhidev *addr, void *ibuf, u_int len) } } #endif - s = spltty(); /* Give characters to tty layer. */ + s = spltty(); while (n-- > 0) { DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp)); if ((*rint)(*cp++, tp) == -1) { @@ -1046,7 +1093,7 @@ ucycom_get_cfg(struct ucycom_softc *sc) cfg = report[4]; baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + report[0]; - DPRINTF(("ucycom_configure: device reports %d baud, %d-%c-%d (%d)\n", + DPRINTF(("ucycom_get_cfg: device reports %d baud, %d-%c-%d (%d)\n", baud, 5 + (cfg & UCYCOM_DATA_MASK), (cfg & UCYCOM_PARITY_MASK) ? ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N',