Module Name: src Committed By: mrg Date: Sat May 12 21:28:59 UTC 2012
Modified Files: src/sys/dev/usb [jmcneill-usbmp]: ugen.c Log Message: port this to usbmp and also make it largely MPSAFE, but not yet enough for D_MPSAFE to be added. should be merge-ready now, but needs testing. To generate a diff of this commit: cvs rdiff -u -r1.114.2.4 -r1.114.2.5 src/sys/dev/usb/ugen.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/ugen.c diff -u src/sys/dev/usb/ugen.c:1.114.2.4 src/sys/dev/usb/ugen.c:1.114.2.5 --- src/sys/dev/usb/ugen.c:1.114.2.4 Sun Apr 29 23:05:01 2012 +++ src/sys/dev/usb/ugen.c Sat May 12 21:28:58 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: ugen.c,v 1.114.2.4 2012/04/29 23:05:01 mrg Exp $ */ +/* $NetBSD: ugen.c,v 1.114.2.5 2012/05/12 21:28:58 mrg Exp $ */ /* * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.114.2.4 2012/04/29 23:05:01 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.114.2.5 2012/05/12 21:28:58 mrg Exp $"); #include "opt_compat_netbsd.h" @@ -108,12 +108,17 @@ struct ugen_endpoint { void *dmabuf; u_int16_t sizes[UGEN_NISORFRMS]; } isoreqs[UGEN_NISOREQS]; + /* Keep this last; we don't overwrite it in ugen_set_config() */ + kcondvar_t cv; }; struct ugen_softc { device_t sc_dev; /* base device */ usbd_device_handle sc_udev; + kmutex_t sc_lock; + kcondvar_t sc_detach_cv; + char sc_is_open[USB_MAX_ENDPOINTS]; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; #define OUT 0 @@ -201,6 +206,9 @@ ugen_attach(device_t parent, device_t se aprint_naive("\n"); aprint_normal("\n"); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_USB); + cv_init(&sc->sc_detach_cv, "ugendet"); + devinfop = usbd_devinfo_alloc(uaa->device, 0); aprint_normal_dev(self, "%s\n", devinfop); usbd_devinfo_free(devinfop); @@ -233,6 +241,7 @@ ugen_attach(device_t parent, device_t se sce = &sc->sc_endpoints[i][dir]; selinit(&sce->rsel); + cv_init(&sce->cv, "ugensce"); } } @@ -256,7 +265,7 @@ ugen_set_config(struct ugen_softc *sc, i u_int8_t niface, nendpt; int ifaceno, endptno, endpt; usbd_status err; - int dir; + int dir, i; DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n", device_xname(sc->sc_dev), configno, sc)); @@ -284,7 +293,15 @@ ugen_set_config(struct ugen_softc *sc, i err = usbd_interface_count(dev, &niface); if (err) return (err); - memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints); + + /* Clear out the old info, but leave the cv initialised. */ + for (i = 0; i < USB_MAX_ENDPOINTS; i++) { + for (dir = OUT; dir <= IN; dir++) { + sce = &sc->sc_endpoints[i][dir]; + memset(sce, 0, offsetof(struct ugen_endpoint, cv)); + } + } + for (ifaceno = 0; ifaceno < niface; ifaceno++) { DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno)); err = usbd_device2interface_handle(dev, ifaceno, &iface); @@ -537,7 +554,6 @@ ugen_do_read(struct ugen_softc *sc, int u_int32_t n, tn; usbd_xfer_handle xfer; usbd_status err; - int s; int error = 0; DPRINTFN(5, ("%s: ugenread: %d\n", device_xname(sc->sc_dev), endpt)); @@ -562,15 +578,17 @@ ugen_do_read(struct ugen_softc *sc, int switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: /* Block until activity occurred. */ - s = splusb(); + mutex_enter(&sc->sc_lock); while (sce->q.c_cc == 0) { if (flag & IO_NDELAY) { - splx(s); + mutex_exit(&sc->sc_lock); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); - error = tsleep(sce, PZERO | PCATCH, "ugenri", mstohz(sce->timeout)); + /* "ugenri" */ + error = cv_timedwait_sig(&sce->cv, &sc->sc_lock, + mstohz(sce->timeout)); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; @@ -579,7 +597,7 @@ ugen_do_read(struct ugen_softc *sc, int break; } } - splx(s); + mutex_exit(&sc->sc_lock); /* Transfer as many chunks as possible. */ while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) { @@ -603,9 +621,9 @@ ugen_do_read(struct ugen_softc *sc, int uio->uio_resid, sce->ra_wb_used)); xfer = sce->ra_wb_xfer; - s = splusb(); + mutex_enter(&sc->sc_lock); if (sce->ra_wb_used == 0 && flag & IO_NDELAY) { - splx(s); + mutex_exit(&sc->sc_lock); return (EWOULDBLOCK); } while (uio->uio_resid > 0 && !error) { @@ -614,8 +632,9 @@ ugen_do_read(struct ugen_softc *sc, int DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); - error = tsleep(sce, PZERO | PCATCH, - "ugenrb", mstohz(sce->timeout)); + /* "ugenrb" */ + error = cv_timedwait_sig(&sce->cv, + &sc->sc_lock, mstohz(sce->timeout)); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); @@ -667,7 +686,7 @@ ugen_do_read(struct ugen_softc *sc, int sce->state |= UGEN_RA_WB_STOP; } } - splx(s); + mutex_exit(&sc->sc_lock); break; } xfer = usbd_alloc_xfer(sc->sc_udev); @@ -698,15 +717,17 @@ ugen_do_read(struct ugen_softc *sc, int usbd_free_xfer(xfer); break; case UE_ISOCHRONOUS: - s = splusb(); + mutex_enter(&sc->sc_lock); while (sce->cur == sce->fill) { if (flag & IO_NDELAY) { - splx(s); + mutex_exit(&sc->sc_lock); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; + /* "ugenri" */ DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); - error = tsleep(sce, PZERO | PCATCH, "ugenri", mstohz(sce->timeout)); + error = cv_timedwait_sig(&sce->cv, &sc->sc_lock, + mstohz(sce->timeout)); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; @@ -729,10 +750,10 @@ ugen_do_read(struct ugen_softc *sc, int if (error) break; sce->cur += n; - if(sce->cur >= sce->limit) + if (sce->cur >= sce->limit) sce->cur = sce->ibuf; } - splx(s); + mutex_exit(&sc->sc_lock); break; @@ -753,10 +774,17 @@ ugenread(dev_t dev, struct uio *uio, int if (sc == NULL) return ENXIO; + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + error = ugen_do_read(sc, endpt, uio, flag); + + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeupold(sc->sc_dev); + usb_detach_broadcast(sc->sc_dev, &sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); + return (error); } @@ -767,7 +795,6 @@ ugen_do_write(struct ugen_softc *sc, int struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT]; u_int32_t n; int error = 0; - int s; u_int32_t tn; char *dbuf; usbd_xfer_handle xfer; @@ -799,10 +826,10 @@ ugen_do_write(struct ugen_softc *sc, int uio->uio_resid, sce->ra_wb_used)); xfer = sce->ra_wb_xfer; - s = splusb(); + mutex_enter(&sc->sc_lock); if (sce->ra_wb_used == sce->limit - sce->ibuf && flag & IO_NDELAY) { - splx(s); + mutex_exit(&sc->sc_lock); return (EWOULDBLOCK); } while (uio->uio_resid > 0 && !error) { @@ -812,8 +839,9 @@ ugen_do_write(struct ugen_softc *sc, int DPRINTFN(5, ("ugenwrite: sleep on %p\n", sce)); - error = tsleep(sce, PZERO | PCATCH, - "ugenwb", mstohz(sce->timeout)); + /* "ugenwb" */ + error = cv_timedwait_sig(&sce->cv, + &sc->sc_lock, mstohz(sce->timeout)); DPRINTFN(5, ("ugenwrite: woke, error=%d\n", error)); @@ -872,7 +900,7 @@ ugen_do_write(struct ugen_softc *sc, int sce->state |= UGEN_RA_WB_STOP; } } - splx(s); + mutex_exit(&sc->sc_lock); break; } xfer = usbd_alloc_xfer(sc->sc_udev); @@ -938,10 +966,17 @@ ugenwrite(dev_t dev, struct uio *uio, in if (sc == NULL) return ENXIO; + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + error = ugen_do_write(sc, endpt, uio, flag); + + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeupold(sc->sc_dev); + usb_detach_broadcast(sc->sc_dev, &sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); + return (error); } @@ -965,7 +1000,6 @@ ugen_detach(device_t self, int flags) struct ugen_softc *sc = device_private(self); struct ugen_endpoint *sce; int i, dir; - int s; int maj, mn; DPRINTF(("ugen_detach: sc=%p flags=%d\n", sc, flags)); @@ -981,15 +1015,15 @@ ugen_detach(device_t self, int flags) } } - s = splusb(); + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ for (i = 0; i < USB_MAX_ENDPOINTS; i++) - wakeup(&sc->sc_endpoints[i][IN]); + cv_signal(&sc->sc_endpoints[i][IN].cv); /* Wait for processes to go away. */ - usb_detach_waitold(sc->sc_dev); + usb_detach_wait(sc->sc_dev, &sc->sc_detach_cv, &sc->sc_lock); } - splx(s); + mutex_exit(&sc->sc_lock); /* locate the major number */ maj = cdevsw_lookup_major(&ugen_cdevsw); @@ -1005,9 +1039,13 @@ ugen_detach(device_t self, int flags) for (dir = OUT; dir <= IN; dir++) { sce = &sc->sc_endpoints[i][dir]; seldestroy(&sce->rsel); + cv_destroy(&sce->cv); } } + cv_destroy(&sc->sc_detach_cv); + mutex_destroy(&sc->sc_lock); + return (0); } @@ -1015,7 +1053,7 @@ Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct ugen_endpoint *sce = addr; - /*struct ugen_softc *sc = sce->sc;*/ + struct ugen_softc *sc = sce->sc; u_int32_t count; u_char *ibuf; @@ -1039,11 +1077,13 @@ ugenintr(usbd_xfer_handle xfer, usbd_pri (void)b_to_q(ibuf, count, &sce->q); + mutex_enter(&sc->sc_lock); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_intr: waking %p\n", sce)); - wakeup(sce); + cv_signal(&sce->cv); } + mutex_exit(&sc->sc_lock); selnotify(&sce->rsel, 0, 0); } @@ -1053,6 +1093,7 @@ ugen_isoc_rintr(usbd_xfer_handle xfer, u { struct isoreq *req = addr; struct ugen_endpoint *sce = req->sce; + struct ugen_softc *sc = sce->sc; u_int32_t count, n; int i, isize; @@ -1098,11 +1139,13 @@ ugen_isoc_rintr(usbd_xfer_handle xfer, u USBD_NO_COPY, ugen_isoc_rintr); (void)usbd_transfer(xfer); + mutex_enter(&sc->sc_lock); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce)); - wakeup(sce); + cv_signal(&sce->cv); } + mutex_exit(&sc->sc_lock); selnotify(&sce->rsel, 0, 0); } @@ -1111,6 +1154,7 @@ ugen_bulkra_intr(usbd_xfer_handle xfer, usbd_status status) { struct ugen_endpoint *sce = addr; + struct ugen_softc *sc = sce->sc; u_int32_t count, n; char const *tbuf; usbd_status err; @@ -1165,11 +1209,13 @@ ugen_bulkra_intr(usbd_xfer_handle xfer, else sce->state |= UGEN_RA_WB_STOP; + mutex_enter(&sc->sc_lock); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_bulkra_intr: waking %p\n", sce)); - wakeup(sce); + cv_signal(&sce->cv); } + mutex_exit(&sc->sc_lock); selnotify(&sce->rsel, 0, 0); } @@ -1178,6 +1224,7 @@ ugen_bulkwb_intr(usbd_xfer_handle xfer, usbd_status status) { struct ugen_endpoint *sce = addr; + struct ugen_softc *sc = sce->sc; u_int32_t count, n; char *tbuf; usbd_status err; @@ -1230,11 +1277,13 @@ ugen_bulkwb_intr(usbd_xfer_handle xfer, else sce->state |= UGEN_RA_WB_STOP; + mutex_enter(&sc->sc_lock); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_bulkwb_intr: waking %p\n", sce)); - wakeup(sce); + cv_signal(&sce->cv); } + mutex_exit(&sc->sc_lock); selnotify(&sce->rsel, 0, 0); } @@ -1782,7 +1831,7 @@ ugenioctl(dev_t dev, u_long cmd, void *a sc->sc_refcnt++; error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, l); if (--sc->sc_refcnt < 0) - usb_detach_wakeupold(sc->sc_dev); + usb_detach_broadcast(sc->sc_dev, &sc->sc_detach_cv); return (error); } @@ -1792,7 +1841,6 @@ ugenpoll(dev_t dev, int events, struct l struct ugen_softc *sc; struct ugen_endpoint *sce_in, *sce_out; int revents = 0; - int s; sc = device_lookup_private(&ugen_cd, UGENUNIT(dev)); if (sc == NULL) @@ -1819,7 +1867,8 @@ ugenpoll(dev_t dev, int events, struct l return (POLLERR); } #endif - s = splusb(); + + mutex_enter(&sc->sc_lock); if (sce_in && sce_in->pipeh && (events & (POLLIN | POLLRDNORM))) switch (sce_in->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: @@ -1880,8 +1929,8 @@ ugenpoll(dev_t dev, int events, struct l break; } + mutex_exit(&sc->sc_lock); - splx(s); return (revents); } @@ -1889,11 +1938,11 @@ static void filt_ugenrdetach(struct knote *kn) { struct ugen_endpoint *sce = kn->kn_hook; - int s; + struct ugen_softc *sc = sce->sc; - s = splusb(); + mutex_enter(&sc->sc_lock); SLIST_REMOVE(&sce->rsel.sel_klist, kn, knote, kn_selnext); - splx(s); + mutex_exit(&sc->sc_lock); } static int @@ -1982,7 +2031,6 @@ ugenkqfilter(dev_t dev, struct knote *kn struct ugen_softc *sc; struct ugen_endpoint *sce; struct klist *klist; - int s; sc = device_lookup_private(&ugen_cd, UGENUNIT(dev)); if (sc == NULL) @@ -2042,9 +2090,9 @@ ugenkqfilter(dev_t dev, struct knote *kn kn->kn_hook = sce; - s = splusb(); + mutex_enter(&sc->sc_lock); SLIST_INSERT_HEAD(klist, kn, kn_selnext); - splx(s); + mutex_exit(&sc->sc_lock); return (0); }