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