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

Reply via email to