Module Name: src Committed By: skrll Date: Sat Nov 19 09:49:20 UTC 2016
Modified Files: src/sys/dev/usb: ucom.c Log Message: Pull across various locking and reference counting fixes from nick-nhusb. To generate a diff of this commit: cvs rdiff -u -r1.114 -r1.115 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.114 src/sys/dev/usb/ucom.c:1.115 --- src/sys/dev/usb/ucom.c:1.114 Mon Oct 3 13:36:33 2016 +++ src/sys/dev/usb/ucom.c Sat Nov 19 09:49:20 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: ucom.c,v 1.114 2016/10/03 13:36:33 skrll Exp $ */ +/* $NetBSD: ucom.c,v 1.115 2016/11/19 09:49:20 skrll Exp $ */ /* * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. @@ -34,7 +34,11 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.114 2016/10/03 13:36:33 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.115 2016/11/19 09:49:20 skrll Exp $"); + +#ifdef _KERNEL_OPT +#include "opt_usb.h" +#endif #include <sys/param.h> #include <sys/systm.h> @@ -139,11 +143,10 @@ struct ucom_softc { device_t sc_dev; /* base device */ struct usbd_device * sc_udev; /* USB device */ - struct usbd_interface * sc_iface; /* data interface */ int sc_bulkin_no; /* bulk in endpoint address */ - struct usbd_pipe * sc_bulkin_pipe; /* bulk in pipe */ + struct usbd_pipe * sc_bulkin_pipe;/* bulk in pipe */ u_int sc_ibufsize; /* read buffer size */ u_int sc_ibufsizepad; /* read buffer size padded */ struct ucom_buffer sc_ibuff[UCOM_IN_BUFFS]; @@ -173,17 +176,22 @@ struct ucom_softc { u_char sc_tx_stopped; int sc_swflags; - u_char sc_opening; /* lock during open */ - u_char sc_closing; /* lock during close */ + enum ucom_state { + UCOM_DEAD, + UCOM_ATTACHED, + UCOM_OPENING, + UCOM_CLOSING, + UCOM_OPEN + } sc_state; int sc_refcnt; - u_char sc_dying; /* disconnecting */ + bool sc_dying; /* disconnecting */ struct pps_state sc_pps_state; /* pps state */ krndsource_t sc_rndsource; /* random source */ kmutex_t sc_lock; - kcondvar_t sc_opencv; + kcondvar_t sc_statecv; kcondvar_t sc_detachcv; }; @@ -227,11 +235,11 @@ static int ucom_to_tiocm(struct ucom_sof static void ucomreadcb(struct usbd_xfer *, void *, 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); + usbd_status); static void ucomwritecb(struct usbd_xfer *, void *, usbd_status); static void ucom_read_complete(struct ucom_softc *); -static usbd_status ucomsubmitread(struct ucom_softc *, struct ucom_buffer *); +static int ucomsubmitread(struct ucom_softc *, struct ucom_buffer *); static void ucom_softintr(void *); int ucom_match(device_t, cfdata_t, void *); @@ -253,7 +261,6 @@ ucom_attach(device_t parent, device_t se { struct ucom_softc *sc = device_private(self); struct ucom_attach_args *ucaa = aux; - struct tty *tp; UCOMHIST_FUNC(); UCOMHIST_CALLED(); @@ -282,14 +289,13 @@ ucom_attach(device_t parent, device_t se sc->sc_mcr = 0; sc->sc_tx_stopped = 0; sc->sc_swflags = 0; - sc->sc_opening = 0; - sc->sc_closing = 0; sc->sc_refcnt = 0; - sc->sc_dying = 0; + sc->sc_dying = false; + sc->sc_state = UCOM_DEAD; sc->sc_si = softint_establish(SOFTINT_USB, ucom_softintr, sc); mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); - cv_init(&sc->sc_opencv, "ucomopen"); + cv_init(&sc->sc_statecv, "ucomstate"); cv_init(&sc->sc_detachcv, "ucomdtch"); SIMPLEQ_INIT(&sc->sc_ibuff_empty); @@ -316,6 +322,17 @@ ucom_attach(device_t parent, device_t se error = EIO; goto fail_0; } + /* Allocate input buffers */ + for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; + ub++) { + error = usbd_create_xfer(sc->sc_bulkin_pipe, + sc->sc_ibufsizepad, USBD_SHORT_XFER_OK, 0, + &ub->ub_xfer); + if (error) + goto fail_1; + ub->ub_data = usbd_get_buffer(ub->ub_xfer); + } + err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no, USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); if (err) { @@ -324,28 +341,17 @@ ucom_attach(device_t parent, device_t se error = EIO; goto fail_1; } - - /* Allocate input buffers */ - for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; - ub++) { - error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_ibufsizepad, - USBD_SHORT_XFER_OK, 0, &ub->ub_xfer); - if (error) - goto fail_2; - ub->ub_data = usbd_get_buffer(ub->ub_xfer); - } - for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS]; ub++) { - error = usbd_create_xfer(sc->sc_bulkout_pipe, sc->sc_obufsize, - 0, 0, &ub->ub_xfer); + error = usbd_create_xfer(sc->sc_bulkout_pipe, + sc->sc_obufsize, 0, 0, &ub->ub_xfer); if (error) goto fail_2; ub->ub_data = usbd_get_buffer(ub->ub_xfer); SIMPLEQ_INSERT_TAIL(&sc->sc_obuff_free, ub, ub_link); } - tp = tty_alloc(); + struct tty *tp = tty_alloc(); tp->t_oproc = ucomstart; tp->t_param = ucomparam; tp->t_hwiflow = ucomhwiflow; @@ -359,21 +365,26 @@ ucom_attach(device_t parent, device_t se if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); + + sc->sc_state = UCOM_ATTACHED; + return; fail_2: - for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; + for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS]; ub++) { if (ub->ub_xfer) usbd_destroy_xfer(ub->ub_xfer); } - for (ub = &sc->sc_obuff[0]; ub != &sc->sc_obuff[UCOM_OUT_BUFFS]; + + usbd_close_pipe(sc->sc_bulkout_pipe); + +fail_1: + for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; ub++) { if (ub->ub_xfer) usbd_destroy_xfer(ub->ub_xfer); } - -fail_1: usbd_close_pipe(sc->sc_bulkin_pipe); fail_0: @@ -393,10 +404,10 @@ ucom_detach(device_t self, int flags) UCOMHIST_FUNC(); UCOMHIST_CALLED(); DPRINTF("sc=%p flags=%d tp=%p", sc, flags, tp, 0); - DPRINTF("... pipe=%d,%d",sc->sc_bulkin_no, sc->sc_bulkout_no, 0, 0); + DPRINTF("... pipe=%d,%d", sc->sc_bulkin_no, sc->sc_bulkout_no, 0, 0); mutex_enter(&sc->sc_lock); - sc->sc_dying = 1; + sc->sc_dying = true; mutex_exit(&sc->sc_lock); pmf_device_deregister(self); @@ -407,6 +418,18 @@ ucom_detach(device_t self, int flags) usbd_abort_pipe(sc->sc_bulkout_pipe); mutex_enter(&sc->sc_lock); + + /* wait for open/close to finish */ + while (sc->sc_state == UCOM_OPENING || sc->sc_state == UCOM_CLOSING) { + int error = cv_wait_sig(&sc->sc_statecv, &sc->sc_lock); + + if (error) { + mutex_exit(&sc->sc_lock); + return error; + } + } + + sc->sc_refcnt--; while (sc->sc_refcnt > 0) { /* Wake up anyone waiting */ if (tp != NULL) { @@ -417,7 +440,10 @@ ucom_detach(device_t self, int flags) mutex_spin_exit(&tty_lock); } /* Wait for processes to go away. */ - usb_detach_wait(sc->sc_dev, &sc->sc_detachcv, &sc->sc_lock); + if (cv_timedwait(&sc->sc_detachcv, &sc->sc_lock, hz * 60)) { + printf("%s: %s didn't detach\n", __func__, + device_xname(sc->sc_dev)); + } } softint_disestablish(sc->sc_si); @@ -428,7 +454,7 @@ ucom_detach(device_t self, int flags) /* Nuke the vnodes for any open instances. */ mn = device_unit(self); - DPRINTF("maj=%d mn=%d\n", maj, mn, 0, 0); + DPRINTF("maj=%d mn=%d", maj, mn, 0, 0); vdevgone(maj, mn, mn, VCHR); vdevgone(maj, mn | UCOMDIALOUT_MASK, mn | UCOMDIALOUT_MASK, VCHR); vdevgone(maj, mn | UCOMCALLUNIT_MASK, mn | UCOMCALLUNIT_MASK, VCHR); @@ -464,7 +490,7 @@ ucom_detach(device_t self, int flags) rnd_detach_source(&sc->sc_rndsource); mutex_destroy(&sc->sc_lock); - cv_destroy(&sc->sc_opencv); + cv_destroy(&sc->sc_statecv); cv_destroy(&sc->sc_detachcv); return 0; @@ -482,7 +508,7 @@ ucom_activate(device_t self, enum devact switch (act) { case DVACT_DEACTIVATE: mutex_enter(&sc->sc_lock); - sc->sc_dying = 1; + sc->sc_dying = true; mutex_exit(&sc->sc_lock); return 0; default: @@ -512,11 +538,9 @@ ucom_shutdown(struct ucom_softc *sc) int ucomopen(dev_t dev, int flag, int mode, struct lwp *l) { - int unit = UCOMUNIT(dev); - struct ucom_softc *sc = device_lookup_private(&ucom_cd, unit); - struct ucom_buffer *ub; - struct tty *tp; - int error; + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); + int error = 0; UCOMHIST_FUNC(); UCOMHIST_CALLED(); @@ -525,6 +549,7 @@ ucomopen(dev_t dev, int flag, int mode, mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); mutex_exit(&sc->sc_lock); return EIO; } @@ -534,9 +559,9 @@ ucomopen(dev_t dev, int flag, int mode, return ENXIO; } - tp = sc->sc_tty; + struct tty *tp = sc->sc_tty; - DPRINTF("unit=%d, tp=%p\n", unit, tp, 0, 0); + DPRINTF("unit=%d, tp=%p", unit, tp, 0, 0); if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) { mutex_exit(&sc->sc_lock); @@ -547,31 +572,36 @@ ucomopen(dev_t dev, int flag, int mode, * Wait while the device is initialized by the * first opener or cleaned up by the last closer. */ - while (sc->sc_opening || sc->sc_closing) { - error = cv_wait_sig(&sc->sc_opencv, &sc->sc_lock); + while (sc->sc_state == UCOM_OPENING || sc->sc_state == UCOM_CLOSING) { + error = cv_wait_sig(&sc->sc_statecv, &sc->sc_lock); + + if (sc->sc_dying) + error = EIO; if (error) { mutex_exit(&sc->sc_lock); return error; } } + enum ucom_state state = sc->sc_state; - sc->sc_opening = 1; + KASSERTMSG(state == UCOM_OPEN || state == UCOM_ATTACHED, + "state is %d", state); + bool cleanup = false; + /* If this is the first open then perform the initialisation */ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { - struct termios t; - + KASSERT(state == UCOM_ATTACHED); tp->t_dev = dev; + cleanup = true; + sc->sc_state = UCOM_OPENING; + mutex_exit(&sc->sc_lock); if (sc->sc_methods->ucom_open != NULL) { error = sc->sc_methods->ucom_open(sc->sc_parent, - sc->sc_portno); + sc->sc_portno); if (error) { - ucom_cleanup(sc); - sc->sc_opening = 0; - cv_signal(&sc->sc_opencv); - mutex_exit(&sc->sc_lock); - return error; + goto fail_2; } } @@ -588,6 +618,8 @@ ucomopen(dev_t dev, int flag, int mode, * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ + struct termios t; + t.c_ispeed = 0; t.c_ospeed = TTYDEF_SPEED; t.c_cflag = TTYDEF_CFLAG; @@ -616,22 +648,32 @@ ucomopen(dev_t dev, int flag, int mode, ucom_dtr(sc, 1); ucom_rts(sc, 1); + mutex_enter(&sc->sc_lock); + if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); + error = EIO; + goto fail_1; + } + sc->sc_rx_unblock = 0; sc->sc_rx_stopped = 0; sc->sc_tx_stopped = 0; - for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; - ub++) { - if (ucomsubmitread(sc, ub) != USBD_NORMAL_COMPLETION) { - error = EIO; + for (size_t i = 0; i < UCOM_IN_BUFFS; i++) { + struct ucom_buffer *ub = &sc->sc_ibuff[i]; + error = ucomsubmitread(sc, ub); + if (error) { + mutex_exit(&sc->sc_lock); goto fail_2; } } } - sc->sc_opening = 0; - cv_signal(&sc->sc_opencv); + sc->sc_state = UCOM_OPEN; + cv_signal(&sc->sc_statecv); mutex_exit(&sc->sc_lock); + DPRINTF("unit=%d, tp=%p dialout %d nonblock %d", unit, tp, + !!UCOMDIALOUT(dev), !!ISSET(flag, O_NONBLOCK)); error = ttyopen(tp, UCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); if (error) goto bad; @@ -642,28 +684,24 @@ ucomopen(dev_t dev, int flag, int mode, return 0; -fail_2: - usbd_abort_pipe(sc->sc_bulkin_pipe); - usbd_abort_pipe(sc->sc_bulkout_pipe); - - mutex_enter(&sc->sc_lock); - sc->sc_opening = 0; - cv_signal(&sc->sc_opencv); - mutex_exit(&sc->sc_lock); - - return error; - bad: - mutex_spin_enter(&tty_lock); - CLR(tp->t_state, TS_BUSY); - if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + cleanup = !ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0; + +fail_2: + if (cleanup) { /* - * We failed to open the device, and nobody else had it opened. - * Clean up the state as appropriate. + * We failed to open the device, and nobody else had + * it opened. Clean up the state as appropriate. */ ucom_cleanup(sc); } - mutex_spin_exit(&tty_lock); + + mutex_enter(&sc->sc_lock); + +fail_1: + sc->sc_state = state; + cv_signal(&sc->sc_statecv); + mutex_exit(&sc->sc_lock); return error; } @@ -671,8 +709,9 @@ bad: int ucomclose(dev_t dev, int flag, int mode, struct lwp *l) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); - struct tty *tp; + const int unit = UCOMUNIT(dev); + struct ucom_softc *sc = device_lookup_private(&ucom_cd, unit); + int error = 0; UCOMHIST_FUNC(); UCOMHIST_CALLED(); @@ -682,21 +721,44 @@ ucomclose(dev_t dev, int flag, int mode, return 0; mutex_enter(&sc->sc_lock); - tp = sc->sc_tty; + if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + return ENXIO; + } - while (sc->sc_closing) - cv_wait(&sc->sc_opencv, &sc->sc_lock); - sc->sc_closing = 1; + /* + * Wait until any opens/closes have finished + */ + while (sc->sc_state == UCOM_OPENING || sc->sc_state == UCOM_CLOSING) { + error = cv_wait_sig(&sc->sc_statecv, &sc->sc_lock); + + if (sc->sc_dying) + error = EIO; + + if (error) { + mutex_exit(&sc->sc_lock); + return error; + } + } + + struct tty *tp = sc->sc_tty; if (!ISSET(tp->t_state, TS_ISOPEN)) { - goto out; + KASSERT(sc->sc_state == UCOM_ATTACHED); + mutex_exit(&sc->sc_lock); + return 0; } - sc->sc_refcnt++; + KASSERT(sc->sc_state == UCOM_OPEN); + sc->sc_state = UCOM_CLOSING; + mutex_exit(&sc->sc_lock); (*tp->t_linesw->l_close)(tp, flag); ttyclose(tp); + /* state when we're done - default to open */ + enum ucom_state state = UCOM_OPEN; if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { /* * Although we got a last close, the device may still be in @@ -704,28 +766,25 @@ ucomclose(dev_t dev, int flag, int mode, * processes waiting for carrier on the non-dialout node. */ ucom_cleanup(sc); + if (sc->sc_methods->ucom_close != NULL) + sc->sc_methods->ucom_close(sc->sc_parent, + sc->sc_portno); + state = UCOM_ATTACHED; } - if (sc->sc_methods->ucom_close != NULL) - sc->sc_methods->ucom_close(sc->sc_parent, sc->sc_portno); - - if (--sc->sc_refcnt < 0) - usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv); - -out: - sc->sc_closing = 0; - cv_signal(&sc->sc_opencv); - + mutex_enter(&sc->sc_lock); + sc->sc_state = state; + cv_signal(&sc->sc_statecv); mutex_exit(&sc->sc_lock); - return 0; + return error; } int ucomread(dev_t dev, struct uio *uio, int flag) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); - struct tty *tp; + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); int error; UCOMHIST_FUNC(); UCOMHIST_CALLED(); @@ -735,19 +794,22 @@ ucomread(dev_t dev, struct uio *uio, int mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); mutex_exit(&sc->sc_lock); return EIO; } - tp = sc->sc_tty; + struct tty *tp = sc->sc_tty; sc->sc_refcnt++; mutex_exit(&sc->sc_lock); + error = ((*tp->t_linesw->l_read)(tp, uio, flag)); - mutex_enter(&sc->sc_lock); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv); + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(dev), sc->sc_refcnt, 0, 0); mutex_exit(&sc->sc_lock); return error; @@ -756,27 +818,33 @@ ucomread(dev_t dev, struct uio *uio, int int ucomwrite(dev_t dev, struct uio *uio, int flag) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); - struct tty *tp; + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); int error; + UCOMHIST_FUNC(); UCOMHIST_CALLED(); + if (sc == NULL) return EIO; mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); mutex_exit(&sc->sc_lock); return EIO; } - tp = sc->sc_tty; + struct tty *tp = sc->sc_tty; sc->sc_refcnt++; mutex_exit(&sc->sc_lock); + error = ((*tp->t_linesw->l_write)(tp, uio, flag)); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv); + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(dev), sc->sc_refcnt, 0, 0); mutex_exit(&sc->sc_lock); return error; @@ -785,27 +853,31 @@ ucomwrite(dev_t dev, struct uio *uio, in int ucompoll(dev_t dev, int events, struct lwp *l) { - struct ucom_softc *sc; - struct tty *tp; - int revents; + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); + + UCOMHIST_FUNC(); UCOMHIST_CALLED(); - sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); if (sc == NULL) return POLLHUP; mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); mutex_exit(&sc->sc_lock); return POLLHUP; } - tp = sc->sc_tty; + struct tty *tp = sc->sc_tty; sc->sc_refcnt++; mutex_exit(&sc->sc_lock); - revents = ((*tp->t_linesw->l_poll)(tp, events, l)); + + int revents = ((*tp->t_linesw->l_poll)(tp, events, l)); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv); + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(dev), sc->sc_refcnt, 0, 0); mutex_exit(&sc->sc_lock); return revents; @@ -814,7 +886,8 @@ ucompoll(dev_t dev, int events, struct l struct tty * ucomtty(dev_t dev) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); return sc != NULL ? sc->sc_tty : NULL; } @@ -822,31 +895,39 @@ ucomtty(dev_t dev) int ucomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev)); + const int unit = UCOMUNIT(dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); int error; + UCOMHIST_FUNC(); UCOMHIST_CALLED(); + if (sc == NULL) return EIO; mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); mutex_exit(&sc->sc_lock); return EIO; } sc->sc_refcnt++; mutex_exit(&sc->sc_lock); + error = ucom_do_ioctl(sc, cmd, data, flag, l); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv); + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(dev), sc->sc_refcnt, 0, 0); mutex_exit(&sc->sc_lock); + return error; } static int -ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, void *data, - int flag, struct lwp *l) +ucom_do_ioctl(struct ucom_softc *sc, u_long cmd, void *data, int flag, + struct lwp *l) { struct tty *tp = sc->sc_tty; int error; @@ -865,7 +946,7 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l if (sc->sc_methods->ucom_ioctl != NULL) { error = sc->sc_methods->ucom_ioctl(sc->sc_parent, - sc->sc_portno, cmd, data, flag, l->l_proc); + sc->sc_portno, cmd, data, flag, l->l_proc); if (error != EPASSTHROUGH) return error; } @@ -873,7 +954,6 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l error = 0; DPRINTF("our cmd=0x%08lx", cmd, 0, 0, 0); - //mutex_enter(&tty_lock); switch (cmd) { case TIOCSBRK: @@ -937,8 +1017,6 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l break; } - //mutex_exit(&tty_lock); - return error; } @@ -968,12 +1046,13 @@ tiocm_to_ucom(struct ucom_softc *sc, u_l SET(sc->sc_mcr, combits); break; } + u_char mcr = sc->sc_mcr; mutex_exit(&sc->sc_lock); if (how == TIOCMSET || ISSET(combits, UMCR_DTR)) - ucom_dtr(sc, (sc->sc_mcr & UMCR_DTR) != 0); + ucom_dtr(sc, (mcr & UMCR_DTR) != 0); if (how == TIOCMSET || ISSET(combits, UMCR_RTS)) - ucom_rts(sc, (sc->sc_mcr & UMCR_RTS) != 0); + ucom_rts(sc, (mcr & UMCR_RTS) != 0); } static int @@ -1049,13 +1128,20 @@ void ucom_status_change(struct ucom_softc *sc) { struct tty *tp = sc->sc_tty; - u_char old_msr; if (sc->sc_methods->ucom_get_status != NULL) { - old_msr = sc->sc_msr; + u_char msr, lsr; + sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno, - &sc->sc_lsr, &sc->sc_msr); - if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) { + &lsr, &msr); + mutex_enter(&sc->sc_lock); + u_char old_msr = sc->sc_msr; + + sc->sc_lsr = lsr; + sc->sc_msr = msr; + mutex_exit(&sc->sc_lock); + + if (ISSET((msr ^ old_msr), UMSR_DCD)) { mutex_spin_enter(&timecounter_lock); pps_capture(&sc->sc_pps_state); pps_event(&sc->sc_pps_state, @@ -1064,31 +1150,44 @@ ucom_status_change(struct ucom_softc *sc PPS_CAPTURECLEAR); mutex_spin_exit(&timecounter_lock); - (*tp->t_linesw->l_modem)(tp, - ISSET(sc->sc_msr, UMSR_DCD)); + (*tp->t_linesw->l_modem)(tp, ISSET(msr, UMSR_DCD)); } } else { + mutex_enter(&sc->sc_lock); sc->sc_lsr = 0; /* Assume DCD is present, if we have no chance to check it. */ sc->sc_msr = UMSR_DCD; + mutex_exit(&sc->sc_lock); } } static int ucomparam(struct tty *tp, struct termios *t) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, - UCOMUNIT(tp->t_dev)); - int error; + const int unit = UCOMUNIT(tp->t_dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); + int error = 0; UCOMHIST_FUNC(); UCOMHIST_CALLED(); - if (sc == NULL || sc->sc_dying) + if (sc == NULL) return EIO; + mutex_enter(&sc->sc_lock); + if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + return EIO; + } + + sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + /* Check requested parameters. */ - if (t->c_ispeed && t->c_ispeed != t->c_ospeed) - return EINVAL; + if (t->c_ispeed && t->c_ispeed != t->c_ospeed) { + error = EINVAL; + goto out; + } /* * For the console, always force CLOCAL and !HUPCL, so that the port @@ -1105,8 +1204,9 @@ ucomparam(struct tty *tp, struct termios * VMIN and VTIME. */ if (tp->t_ospeed == t->c_ospeed && - tp->t_cflag == t->c_cflag) - return 0; + tp->t_cflag == t->c_cflag) { + goto out; + } /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */ @@ -1117,9 +1217,9 @@ ucomparam(struct tty *tp, struct termios if (sc->sc_methods->ucom_param != NULL) { error = sc->sc_methods->ucom_param(sc->sc_parent, sc->sc_portno, - t); + t); if (error) - return error; + goto out; } /* XXX worry about CHWFLOW */ @@ -1141,6 +1241,13 @@ XXX what if the hardware is not open } } #endif +out: + mutex_enter(&sc->sc_lock); + if (--sc->sc_refcnt < 0) + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(tp->t_dev), sc->sc_refcnt, 0, 0); + + mutex_exit(&sc->sc_lock); return 0; } @@ -1148,10 +1255,12 @@ XXX what if the hardware is not open static int ucomhwiflow(struct tty *tp, int block) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, - UCOMUNIT(tp->t_dev)); + const int unit = UCOMUNIT(tp->t_dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); int old; + UCOMHIST_FUNC(); UCOMHIST_CALLED(); + if (sc == NULL) return 0; @@ -1171,18 +1280,21 @@ ucomhwiflow(struct tty *tp, int block) static void ucomstart(struct tty *tp) { - struct ucom_softc *sc = device_lookup_private(&ucom_cd, - UCOMUNIT(tp->t_dev)); + const int unit = UCOMUNIT(tp->t_dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); struct ucom_buffer *ub; u_char *data; int cnt; + UCOMHIST_FUNC(); UCOMHIST_CALLED(); + if (sc == NULL) return; KASSERT(&sc->sc_lock); KASSERT(mutex_owned(&tty_lock)); if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); return; } @@ -1217,7 +1329,7 @@ ucomstart(struct tty *tp) if (sc->sc_methods->ucom_write != NULL) sc->sc_methods->ucom_write(sc->sc_parent, sc->sc_portno, - ub->ub_data, data, &cnt); + ub->ub_data, data, &cnt); else memcpy(ub->ub_data, data, cnt); @@ -1229,6 +1341,7 @@ ucomstart(struct tty *tp) softint_schedule(sc->sc_si); out: + DPRINTF("... done", 0, 0, 0, 0); return; } @@ -1236,8 +1349,8 @@ void ucomstop(struct tty *tp, int flag) { #if 0 - struct ucom_softc *sc = - device_lookup_private(&ucom_cd, UCOMUNIT(tp->t_dev)); + const int unit = UCOMUNIT(tp->t_dev); + struct ucom_softc * const sc = device_lookup_private(&ucom_cd, unit); mutex_enter(&sc->sc_lock); mutex_spin_enter(&tty_lock); @@ -1325,11 +1438,18 @@ ucomwritecb(struct usbd_xfer *xfer, void static void ucom_softintr(void *arg) { + UCOMHIST_FUNC(); UCOMHIST_CALLED(); + struct ucom_softc *sc = arg; - struct tty *tp = sc->sc_tty; - struct ucom_buffer *ub; mutex_enter(&sc->sc_lock); + if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + return; + } + + struct tty *tp = sc->sc_tty; mutex_enter(&tty_lock); if (!ISSET(tp->t_state, TS_ISOPEN)) { mutex_exit(&tty_lock); @@ -1338,7 +1458,7 @@ ucom_softintr(void *arg) } mutex_exit(&tty_lock); - ub = SIMPLEQ_FIRST(&sc->sc_obuff_full); + struct ucom_buffer *ub = SIMPLEQ_FIRST(&sc->sc_obuff_full); if (ub != NULL && ub->ub_index == 0) ucom_submit_write(sc, ub); @@ -1377,6 +1497,8 @@ ucom_read_complete(struct ucom_softc *sc if (ub->ub_index == ub->ub_len) { SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_full, ub_link); + sc->sc_refcnt--; + /* increments sc_refcnt */ ucomsubmitread(sc, ub); ub = SIMPLEQ_FIRST(&sc->sc_ibuff_full); @@ -1386,64 +1508,81 @@ ucom_read_complete(struct ucom_softc *sc sc->sc_rx_unblock = (ub != NULL); } -static usbd_status +static int ucomsubmitread(struct ucom_softc *sc, struct ucom_buffer *ub) { usbd_status err; + KASSERT(mutex_owned(&sc->sc_lock)); + usbd_setup_xfer(ub->ub_xfer, sc, ub->ub_data, sc->sc_ibufsize, USBD_SHORT_XFER_OK, 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; + printf("%s: err=%s\n", __func__, usbd_errstr(err)); + return EIO; } + sc->sc_refcnt++; + SIMPLEQ_INSERT_TAIL(&sc->sc_ibuff_empty, ub, ub_link); - return USBD_NORMAL_COMPLETION; + return 0; } static void ucomreadcb(struct usbd_xfer *xfer, void *p, usbd_status status) { struct ucom_softc *sc = (struct ucom_softc *)p; - struct tty *tp = sc->sc_tty; struct ucom_buffer *ub; uint32_t cc; u_char *cp; UCOMHIST_FUNC(); UCOMHIST_CALLED(); - if (status == USBD_CANCELLED) - return; - mutex_enter(&sc->sc_lock); - if (status == USBD_IOERROR || + + struct tty *tp = sc->sc_tty; + + if (status == USBD_CANCELLED || status == USBD_IOERROR || sc->sc_dying) { - DPRINTF("dying", 0, 0, 0, 0); - /* Send something to wake upper layer */ - (tp->t_linesw->l_rint)('\n', tp); - mutex_spin_enter(&tty_lock); /* XXX */ - ttwakeup(tp); - mutex_spin_exit(&tty_lock); /* XXX */ + + DPRINTF("... done (status %d dying %d)", status, sc->sc_dying, + 0, 0); + + if (status == USBD_IOERROR || sc->sc_dying) { + /* Send something to wake upper layer */ + (tp->t_linesw->l_rint)('\n', tp); + mutex_spin_enter(&tty_lock); /* XXX */ + ttwakeup(tp); + mutex_spin_exit(&tty_lock); /* XXX */ + } + + if (--sc->sc_refcnt < 0) + cv_broadcast(&sc->sc_detachcv); + DPRINTF("unit=%d refcnt %d", UCOMUNIT(tp->t_dev), sc->sc_refcnt, + 0, 0); mutex_exit(&sc->sc_lock); + return; } ub = SIMPLEQ_FIRST(&sc->sc_ibuff_empty); SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_empty, ub_link); - if (status == USBD_STALLED) { - usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); - ucomsubmitread(sc, ub); - mutex_exit(&sc->sc_lock); - return; - } - if (status != USBD_NORMAL_COMPLETION) { - printf("ucomreadcb: wonky status=%s\n", usbd_errstr(status)); + if (status == USBD_STALLED) { + usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); + } else { + printf("ucomreadcb: wonky status=%s\n", + usbd_errstr(status)); + } + + DPRINTF("... done (status %d)", status, 0, 0, 0); + sc->sc_refcnt--; + /* re-adds ub to sc_ibuff_empty and increments sc_refcnt */ + ucomsubmitread(sc, ub); mutex_exit(&sc->sc_lock); return; } @@ -1456,14 +1595,17 @@ ucomreadcb(struct usbd_xfer *xfer, void device_printf(sc->sc_dev, "ucomreadcb: zero length xfer!\n"); } #endif - KDASSERT(cp == ub->ub_data); rnd_add_uint32(&sc->sc_rndsource, cc); - if (sc->sc_opening) { + if (sc->sc_state != UCOM_OPEN) { + /* Go around again - we're not quite ready */ + sc->sc_refcnt--; + /* re-adds ub to sc_ibuff_empty and increments sc_refcnt */ ucomsubmitread(sc, ub); mutex_exit(&sc->sc_lock); + DPRINTF("... done (not open)", 0, 0, 0, 0); return; } @@ -1480,6 +1622,8 @@ ucomreadcb(struct usbd_xfer *xfer, void ucom_read_complete(sc); mutex_exit(&sc->sc_lock); + + DPRINTF("... done", 0, 0, 0, 0); } static void @@ -1490,9 +1634,20 @@ ucom_cleanup(struct ucom_softc *sc) DPRINTF("aborting pipes", 0, 0, 0, 0); - KASSERT(mutex_owned(&sc->sc_lock)); + mutex_enter(&sc->sc_lock); + + /* If we're dying then the detach routine will abort our pipes, etc */ + if (sc->sc_dying) { + DPRINTF("... dying", 0, 0, 0, 0); + + mutex_exit(&sc->sc_lock); + return; + } ucom_shutdown(sc); + + mutex_exit(&sc->sc_lock); + if (sc->sc_bulkin_pipe != NULL) { usbd_abort_pipe(sc->sc_bulkin_pipe); }