Module Name: src Committed By: mrg Date: Sat Apr 27 01:23:26 UTC 2019
Modified Files: src/sys/dev/usb: uchcom.c Log Message: fix uchcom(4) detach, like umodem(4) recently: - use static normally in umodem_common.c - add sc_refcnt, sc_lock and sc_detach_cv to softc. usage is: - sc_dying is protected by sc_lock - sc_detach_cv is matched with sc_lock for cv operations - sc_refcnt is increased in open and decreased in close, any time it is decreased, it is checked for less than zero, and a broadcast performed on sc_detach_cv. detach waits for sc_refcnt - uchcom_param() and uchcom_set() check for sc_dying this fixes pullout out an open ucom@uchcom. XXX: pullup To generate a diff of this commit: cvs rdiff -u -r1.28 -r1.29 src/sys/dev/usb/uchcom.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/uchcom.c diff -u src/sys/dev/usb/uchcom.c:1.28 src/sys/dev/usb/uchcom.c:1.29 --- src/sys/dev/usb/uchcom.c:1.28 Thu Jan 31 18:21:21 2019 +++ src/sys/dev/usb/uchcom.c Sat Apr 27 01:23:26 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: uchcom.c,v 1.28 2019/01/31 18:21:21 jakllsch Exp $ */ +/* $NetBSD: uchcom.c,v 1.29 2019/04/27 01:23:26 mrg Exp $ */ /* * Copyright (c) 2007 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uchcom.c,v 1.28 2019/01/31 18:21:21 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uchcom.c,v 1.29 2019/04/27 01:23:26 mrg Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -127,7 +127,10 @@ struct uchcom_softc struct usbd_device * sc_udev; device_t sc_subdev; struct usbd_interface * sc_iface; - int sc_dying; + bool sc_dying; + kmutex_t sc_lock; + kcondvar_t sc_detach_cv; + int sc_refcnt; /* */ int sc_intr_endpoint; int sc_intr_size; @@ -170,12 +173,12 @@ static const struct usb_devno uchcom_dev }; #define uchcom_lookup(v, p) usb_lookup(uchcom_devs, v, p) -Static void uchcom_get_status(void *, int, u_char *, u_char *); -Static void uchcom_set(void *, int, int, int); -Static int uchcom_param(void *, int, struct termios *); -Static int uchcom_open(void *, int); -Static void uchcom_close(void *, int); -Static void uchcom_intr(struct usbd_xfer *, void *, +static void uchcom_get_status(void *, int, u_char *, u_char *); +static void uchcom_set(void *, int, int, int); +static int uchcom_param(void *, int, struct termios *); +static int uchcom_open(void *, int); +static void uchcom_close(void *, int); +static void uchcom_intr(struct usbd_xfer *, void *, usbd_status); static int set_config(struct uchcom_softc *); @@ -245,10 +248,14 @@ uchcom_attach(device_t parent, device_t sc->sc_dev = self; sc->sc_udev = dev; - sc->sc_dying = 0; + sc->sc_dying = false; + sc->sc_refcnt = 0; sc->sc_dtr = sc->sc_rts = -1; sc->sc_lsr = sc->sc_msr = 0; + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); + cv_init(&sc->sc_detach_cv, "uchcomdet"); + DPRINTF(("\n\nuchcom attach: sc=%p\n", sc)); if (set_config(sc)) @@ -285,7 +292,9 @@ uchcom_attach(device_t parent, device_t return; failed: - sc->sc_dying = 1; + mutex_enter(&sc->sc_lock); + sc->sc_dying = true; + mutex_exit(&sc->sc_lock); return; } @@ -308,13 +317,24 @@ uchcom_detach(device_t self, int flags) close_intr_pipe(sc); - sc->sc_dying = 1; + mutex_enter(&sc->sc_lock); + sc->sc_dying = true; + + sc->sc_refcnt--; + while (sc->sc_refcnt > 0) { + if (cv_timedwait(&sc->sc_detach_cv, &sc->sc_lock, hz * 60)) + aprint_error_dev(sc->sc_dev, ": didn't detach\n"); + } + mutex_exit(&sc->sc_lock); if (sc->sc_subdev != NULL) rv = config_detach(sc->sc_subdev, flags); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); + mutex_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_detach_cv); + return rv; } @@ -326,7 +346,9 @@ uchcom_activate(device_t self, enum deva switch (act) { case DVACT_DEACTIVATE: close_intr_pipe(sc); - sc->sc_dying = 1; + mutex_enter(&sc->sc_lock); + sc->sc_dying = true; + mutex_exit(&sc->sc_lock); return 0; default: return EOPNOTSUPP; @@ -850,8 +872,10 @@ close_intr_pipe(struct uchcom_softc *sc) { usbd_status err; + mutex_enter(&sc->sc_lock); if (sc->sc_dying) return; + mutex_exit(&sc->sc_lock); if (sc->sc_intr_pipe != NULL) { err = usbd_abort_pipe(sc->sc_intr_pipe); @@ -873,25 +897,29 @@ close_intr_pipe(struct uchcom_softc *sc) /* ---------------------------------------------------------------------- * methods for ucom */ -void +static void uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr) { struct uchcom_softc *sc = arg; + mutex_enter(&sc->sc_lock); if (sc->sc_dying) return; + mutex_exit(&sc->sc_lock); *rlsr = sc->sc_lsr; *rmsr = sc->sc_msr; } -void +static void uchcom_set(void *arg, int portno, int reg, int onoff) { struct uchcom_softc *sc = arg; + mutex_enter(&sc->sc_lock); if (sc->sc_dying) return; + mutex_exit(&sc->sc_lock); switch (reg) { case UCOM_SET_DTR: @@ -908,14 +936,16 @@ uchcom_set(void *arg, int portno, int re } } -int +static int uchcom_param(void *arg, int portno, struct termios *t) { struct uchcom_softc *sc = arg; int ret; + mutex_enter(&sc->sc_lock); if (sc->sc_dying) return 0; + mutex_exit(&sc->sc_lock); ret = set_line_control(sc, t->c_cflag); if (ret) @@ -928,50 +958,65 @@ uchcom_param(void *arg, int portno, stru return 0; } -int +static int uchcom_open(void *arg, int portno) { int ret; struct uchcom_softc *sc = arg; - if (sc->sc_dying) + mutex_enter(&sc->sc_lock); + if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); return EIO; + } - ret = setup_intr_pipe(sc); - if (ret) - return ret; + sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); - ret = setup_comm(sc); - if (ret) - return ret; + ret = setup_intr_pipe(sc); + if (ret == 0) + ret = setup_comm(sc); - return 0; + if (ret) { + mutex_enter(&sc->sc_lock); + if (--sc->sc_refcnt < 0) + cv_broadcast(&sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); + } + return ret; } -void +static void uchcom_close(void *arg, int portno) { struct uchcom_softc *sc = arg; - if (sc->sc_dying) - return; - - close_intr_pipe(sc); + mutex_enter(&sc->sc_lock); + if (!sc->sc_dying) { + mutex_exit(&sc->sc_lock); + close_intr_pipe(sc); + mutex_enter(&sc->sc_lock); + } + if (--sc->sc_refcnt < 0) + cv_broadcast(&sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); } /* ---------------------------------------------------------------------- * callback when the modem status is changed. */ -void +static void uchcom_intr(struct usbd_xfer *xfer, void * priv, usbd_status status) { struct uchcom_softc *sc = priv; u_char *buf = sc->sc_intr_buf; + mutex_enter(&sc->sc_lock); if (sc->sc_dying) return; + mutex_exit(&sc->sc_lock); if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)