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

Reply via email to