# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1597537866 0
#      Sun Aug 16 00:31:06 2020 +0000
# Branch trunk
# Node ID ddb21176686b76b7c9fc6bb0c111f9a434758599
# Parent  d40f878f19f7a16ff6ada2d6f1e3f2e4359f1458
# EXP-Topic riastradh-ugenif
Remove UGEN_ASLP microoptimization.

cv_signal already has this microoptimization.

While here, make the lock cover the relevant things we're issuing
cv_signal about.

diff -r d40f878f19f7 -r ddb21176686b sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c	Sat Aug 15 13:49:09 2020 +0000
+++ b/sys/dev/usb/ugen.c	Sun Aug 16 00:31:06 2020 +0000
@@ -97,7 +97,6 @@ struct ugen_endpoint {
 	usb_endpoint_descriptor_t *edesc;
 	struct usbd_interface *iface;
 	int state;
-#define	UGEN_ASLP	0x02	/* waiting for data */
 #define UGEN_SHORT_OK	0x04	/* short xfers are OK */
 #define UGEN_BULK_RA	0x08	/* in bulk read-ahead mode */
 #define UGEN_BULK_WB	0x10	/* in bulk write-behind mode */
@@ -649,7 +648,6 @@ ugen_do_read(struct ugen_softc *sc, int 
 				mutex_exit(&sc->sc_lock);
 				return EWOULDBLOCK;
 			}
-			sce->state |= UGEN_ASLP;
 			DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
 			/* "ugenri" */
 			error = cv_timedwait_sig(&sce->cv, &sc->sc_lock,
@@ -657,10 +655,8 @@ ugen_do_read(struct ugen_softc *sc, int 
 			DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
 			if (sc->sc_dying)
 				error = EIO;
-			if (error) {
-				sce->state &= ~UGEN_ASLP;
+			if (error)
 				break;
-			}
 		}
 		mutex_exit(&sc->sc_lock);
 
@@ -693,7 +689,6 @@ ugen_do_read(struct ugen_softc *sc, int 
 			}
 			while (uio->uio_resid > 0 && !error) {
 				while (sce->ra_wb_used == 0) {
-					sce->state |= UGEN_ASLP;
 					DPRINTFN(5,
 						 ("ugenread: sleep on %p\n",
 						  sce));
@@ -705,10 +700,8 @@ ugen_do_read(struct ugen_softc *sc, int 
 						  error));
 					if (sc->sc_dying)
 						error = EIO;
-					if (error) {
-						sce->state &= ~UGEN_ASLP;
+					if (error)
 						break;
-					}
 				}
 
 				/* Copy data to the process. */
@@ -786,7 +779,6 @@ ugen_do_read(struct ugen_softc *sc, int 
 				mutex_exit(&sc->sc_lock);
 				return EWOULDBLOCK;
 			}
-			sce->state |= UGEN_ASLP;
 			/* "ugenri" */
 			DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
 			error = cv_timedwait_sig(&sce->cv, &sc->sc_lock,
@@ -794,10 +786,8 @@ ugen_do_read(struct ugen_softc *sc, int 
 			DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
 			if (sc->sc_dying)
 				error = EIO;
-			if (error) {
-				sce->state &= ~UGEN_ASLP;
+			if (error)
 				break;
-			}
 		}
 
 		while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
@@ -895,7 +885,6 @@ ugen_do_write(struct ugen_softc *sc, int
 			while (uio->uio_resid > 0 && !error) {
 				while (sce->ra_wb_used ==
 				       sce->limit - sce->ibuf) {
-					sce->state |= UGEN_ASLP;
 					DPRINTFN(5,
 						 ("ugenwrite: sleep on %p\n",
 						  sce));
@@ -907,10 +896,8 @@ ugen_do_write(struct ugen_softc *sc, int
 						  error));
 					if (sc->sc_dying)
 						error = EIO;
-					if (error) {
-						sce->state &= ~UGEN_ASLP;
+					if (error)
 						break;
-					}
 				}
 
 				/* Copy data from the process. */
@@ -1136,14 +1123,9 @@ ugenintr(struct usbd_xfer *xfer, void *a
 	DPRINTFN(5, ("          data = %02x %02x %02x\n",
 		     ibuf[0], ibuf[1], ibuf[2]));
 
+	mutex_enter(&sc->sc_lock);
 	(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));
-		cv_signal(&sce->cv);
-	}
+	cv_signal(&sce->cv);
 	mutex_exit(&sc->sc_lock);
 	selnotify(&sce->rsel, 0, 0);
 }
@@ -1166,10 +1148,12 @@ ugen_isoc_rintr(struct usbd_xfer *xfer, 
 	DPRINTFN(5,("ugen_isoc_rintr: xfer %ld, count=%d\n",
 	    (long)(req - sce->isoreqs), count));
 
+	mutex_enter(&sc->sc_lock);
+
 	/* throw away oldest input if the buffer is full */
-	if(sce->fill < sce->cur && sce->cur <= sce->fill + count) {
+	if (sce->fill < sce->cur && sce->cur <= sce->fill + count) {
 		sce->cur += count;
-		if(sce->cur >= sce->limit)
+		if (sce->cur >= sce->limit)
 			sce->cur = sce->ibuf + (sce->limit - sce->cur);
 		DPRINTFN(5, ("ugen_isoc_rintr: throwing away %d bytes\n",
 			     count));
@@ -1188,7 +1172,7 @@ ugen_isoc_rintr(struct usbd_xfer *xfer, 
 			tbuf += n;
 			actlen -= n;
 			sce->fill += n;
-			if(sce->fill == sce->limit)
+			if (sce->fill == sce->limit)
 				sce->fill = sce->ibuf;
 		}
 
@@ -1200,12 +1184,7 @@ ugen_isoc_rintr(struct usbd_xfer *xfer, 
 	    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));
-		cv_signal(&sce->cv);
-	}
+	cv_signal(&sce->cv);
 	mutex_exit(&sc->sc_lock);
 	selnotify(&sce->rsel, 0, 0);
 }
@@ -1234,6 +1213,8 @@ ugen_bulkra_intr(struct usbd_xfer *xfer,
 
 	usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
 
+	mutex_enter(&sc->sc_lock);
+
 	/* Keep track of how much is in the buffer. */
 	sce->ra_wb_used += count;
 
@@ -1269,12 +1250,7 @@ ugen_bulkra_intr(struct usbd_xfer *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));
-		cv_signal(&sce->cv);
-	}
+	cv_signal(&sce->cv);
 	mutex_exit(&sc->sc_lock);
 	selnotify(&sce->rsel, 0, 0);
 }
@@ -1303,6 +1279,8 @@ ugen_bulkwb_intr(struct usbd_xfer *xfer,
 
 	usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
 
+	mutex_enter(&sc->sc_lock);
+
 	/* Keep track of how much is in the buffer. */
 	sce->ra_wb_used -= count;
 
@@ -1337,12 +1315,7 @@ ugen_bulkwb_intr(struct usbd_xfer *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));
-		cv_signal(&sce->cv);
-	}
+	cv_signal(&sce->cv);
 	mutex_exit(&sc->sc_lock);
 	selnotify(&sce->rsel, 0, 0);
 }
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1597538184 0
#      Sun Aug 16 00:36:24 2020 +0000
# Branch trunk
# Node ID 4a92a330489cb04844ab7c557329609217c67f63
# Parent  ddb21176686b76b7c9fc6bb0c111f9a434758599
# EXP-Topic riastradh-ugenif
Hold the lock over access to the data structures it covers.

Still not MPSAFE, but progress.

diff -r ddb21176686b -r 4a92a330489c sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c	Sun Aug 16 00:31:06 2020 +0000
+++ b/sys/dev/usb/ugen.c	Sun Aug 16 00:36:24 2020 +0000
@@ -1964,12 +1964,18 @@ filt_ugenread_intr(struct knote *kn, lon
 {
 	struct ugen_endpoint *sce = kn->kn_hook;
 	struct ugen_softc *sc = sce->sc;
+	int ret;
 
-	if (sc->sc_dying)
-		return 0;
+	mutex_enter(&sc->sc_lock);
+	if (sc->sc_dying) {
+		ret = 0;
+	} else {
+		kn->kn_data = sce->q.c_cc;
+		ret = kn->kn_data > 0;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	kn->kn_data = sce->q.c_cc;
-	return kn->kn_data > 0;
+	return ret;
 }
 
 static int
@@ -1977,20 +1983,24 @@ filt_ugenread_isoc(struct knote *kn, lon
 {
 	struct ugen_endpoint *sce = kn->kn_hook;
 	struct ugen_softc *sc = sce->sc;
-
-	if (sc->sc_dying)
-		return 0;
+	int ret;
 
-	if (sce->cur == sce->fill)
-		return 0;
-
-	if (sce->cur < sce->fill)
+	mutex_enter(&sc->sc_lock);
+	if (sc->sc_dying) {
+		ret = 0;
+	} else if (sce->cur == sce->fill) {
+		ret = 0;
+	} else if (sce->cur < sce->fill) {
 		kn->kn_data = sce->fill - sce->cur;
-	else
+		ret = 1;
+	} else {
 		kn->kn_data = (sce->limit - sce->cur) +
 		    (sce->fill - sce->ibuf);
+		ret = 1;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	return 1;
+	return ret;
 }
 
 static int
@@ -1998,24 +2008,27 @@ filt_ugenread_bulk(struct knote *kn, lon
 {
 	struct ugen_endpoint *sce = kn->kn_hook;
 	struct ugen_softc *sc = sce->sc;
+	int ret;
 
-	if (sc->sc_dying)
-		return 0;
-
-	if (!(sce->state & UGEN_BULK_RA))
+	mutex_enter(&sc->sc_lock);
+	if (sc->sc_dying) {
+		ret = 0;
+	} else if (!(sce->state & UGEN_BULK_RA)) {
 		/*
 		 * We have no easy way of determining if a read will
 		 * yield any data or a write will happen.
 		 * So, emulate "seltrue".
 		 */
-		return filt_seltrue(kn, hint);
+		ret = filt_seltrue(kn, hint);
+	} else if (sce->ra_wb_used == 0) {
+		ret = 0;
+	} else {
+		kn->kn_data = sce->ra_wb_used;
+		ret = 1;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	if (sce->ra_wb_used == 0)
-		return 0;
-
-	kn->kn_data = sce->ra_wb_used;
-
-	return 1;
+	return ret;
 }
 
 static int
@@ -2023,24 +2036,27 @@ filt_ugenwrite_bulk(struct knote *kn, lo
 {
 	struct ugen_endpoint *sce = kn->kn_hook;
 	struct ugen_softc *sc = sce->sc;
+	int ret;
 
-	if (sc->sc_dying)
-		return 0;
-
-	if (!(sce->state & UGEN_BULK_WB))
+	mutex_enter(&sc->sc_lock);
+	if (sc->sc_dying) {
+		ret = 0;
+	} else if (!(sce->state & UGEN_BULK_WB)) {
 		/*
 		 * We have no easy way of determining if a read will
 		 * yield any data or a write will happen.
 		 * So, emulate "seltrue".
 		 */
-		return filt_seltrue(kn, hint);
+		ret = filt_seltrue(kn, hint);
+	} else if (sce->ra_wb_used == sce->limit - sce->ibuf) {
+		ret = 0;
+	} else {
+		kn->kn_data = (sce->limit - sce->ibuf) - sce->ra_wb_used;
+		ret = 1;
+	}
+	mutex_exit(&sc->sc_lock);
 
-	if (sce->ra_wb_used == sce->limit - sce->ibuf)
-		return 0;
-
-	kn->kn_data = (sce->limit - sce->ibuf) - sce->ra_wb_used;
-
-	return 1;
+	return ret;
 }
 
 static const struct filterops ugenread_intr_filtops = {
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1597538414 0
#      Sun Aug 16 00:40:14 2020 +0000
# Branch trunk
# Node ID 50d21eaa12da9fe4fc9986b141ef1f43df2675c0
# Parent  4a92a330489cb04844ab7c557329609217c67f63
# EXP-Topic riastradh-ugenif
Convert DIAGNOSTIC prints to KASSERTs.

diff -r 4a92a330489c -r 50d21eaa12da sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c	Sun Aug 16 00:36:24 2020 +0000
+++ b/sys/dev/usb/ugen.c	Sun Aug 16 00:40:14 2020 +0000
@@ -555,12 +555,7 @@ ugenclose(dev_t dev, int flag, int mode,
 	DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
 		     flag, mode, UGENUNIT(dev), endpt));
 
-#ifdef DIAGNOSTIC
-	if (!sc->sc_is_open[endpt]) {
-		printf("ugenclose: not open\n");
-		return EINVAL;
-	}
-#endif
+	KASSERT(sc->sc_is_open[endpt]);
 
 	if (endpt == USB_CONTROL_ENDPOINT) {
 		DPRINTFN(5, ("ugenclose: close control\n"));
@@ -628,16 +623,8 @@ ugen_do_read(struct ugen_softc *sc, int 
 	if (endpt == USB_CONTROL_ENDPOINT)
 		return ENODEV;
 
-#ifdef DIAGNOSTIC
-	if (sce->edesc == NULL) {
-		printf("ugenread: no edesc\n");
-		return EIO;
-	}
-	if (sce->pipeh == NULL) {
-		printf("ugenread: no pipe\n");
-		return EIO;
-	}
-#endif
+	KASSERT(sce->edesc);
+	KASSERT(sce->pipeh);
 
 	switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
 	case UE_INTERRUPT:
@@ -858,16 +845,8 @@ ugen_do_write(struct ugen_softc *sc, int
 	if (endpt == USB_CONTROL_ENDPOINT)
 		return ENODEV;
 
-#ifdef DIAGNOSTIC
-	if (sce->edesc == NULL) {
-		printf("ugenwrite: no edesc\n");
-		return EIO;
-	}
-	if (sce->pipeh == NULL) {
-		printf("ugenwrite: no pipe\n");
-		return EIO;
-	}
-#endif
+	KASSERT(sce->edesc);
+	KASSERT(sce->pipeh);
 
 	switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
 	case UE_BULK:
@@ -1868,19 +1847,8 @@ ugenpoll(dev_t dev, int events, struct l
 
 	sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
 	sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
-	if (sce_in == NULL && sce_out == NULL)
-		return POLLERR;
-#ifdef DIAGNOSTIC
-	if (!sce_in->edesc && !sce_out->edesc) {
-		printf("ugenpoll: no edesc\n");
-		return POLLERR;
-	}
-	/* It's possible to have only one pipe open. */
-	if (!sce_in->pipeh && !sce_out->pipeh) {
-		printf("ugenpoll: no pipe\n");
-		return POLLERR;
-	}
-#endif
+	KASSERT(sce_in->edesc || sce_out->edesc);
+	KASSERT(sce_in->pipeh || sce_out->pipeh);
 
 	mutex_enter(&sc->sc_lock);
 	if (sce_in && sce_in->pipeh && (events & (POLLIN | POLLRDNORM)))
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1597538943 0
#      Sun Aug 16 00:49:03 2020 +0000
# Branch trunk
# Node ID ffdb1963bf76e5cae83bf66bd3cc02fd99e40102
# Parent  50d21eaa12da9fe4fc9986b141ef1f43df2675c0
# EXP-Topic riastradh-ugenif
Share unit numbering for ugen and ugenif.

This way putting ugenif in kernel config actually works to wire it to
the /dev/ugenN.MM device nodes in userland.

Fix various MP-safety issues (still not MPSAFE but progress).

diff -r 50d21eaa12da -r ffdb1963bf76 sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c	Sun Aug 16 00:40:14 2020 +0000
+++ b/sys/dev/usb/ugen.c	Sun Aug 16 00:49:03 2020 +0000
@@ -58,6 +58,8 @@
 #include <sys/vnode.h>
 #include <sys/poll.h>
 #include <sys/compat_stub.h>
+#include <sys/module.h>
+#include <sys/rbtree.h>
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
@@ -123,6 +125,8 @@ struct ugen_endpoint {
 struct ugen_softc {
 	device_t sc_dev;		/* base device */
 	struct usbd_device *sc_udev;
+	struct rb_node sc_node;
+	unsigned sc_unit;
 
 	kmutex_t		sc_lock;
 	kcondvar_t		sc_detach_cv;
@@ -137,6 +141,105 @@ struct ugen_softc {
 	u_char sc_dying;
 };
 
+static struct {
+	kmutex_t	lock;
+	rb_tree_t	tree;
+} ugenif __cacheline_aligned;
+
+static int
+compare_ugen(void *cookie, const void *vsca, const void *vscb)
+{
+	const struct ugen_softc *sca = vsca;
+	const struct ugen_softc *scb = vscb;
+
+	if (sca->sc_unit < scb->sc_unit)
+		return -1;
+	if (sca->sc_unit > scb->sc_unit)
+		return +1;
+	return 0;
+}
+
+static int
+compare_ugen_key(void *cookie, const void *vsc, const void *vk)
+{
+	const struct ugen_softc *sc = vsc;
+	const unsigned *k = vk;
+
+	if (sc->sc_unit < *k)
+		return -1;
+	if (sc->sc_unit > *k)
+		return +1;
+	return 0;
+}
+
+static const rb_tree_ops_t ugenif_tree_ops = {
+	.rbto_compare_nodes = compare_ugen,
+	.rbto_compare_key = compare_ugen_key,
+	.rbto_node_offset = offsetof(struct ugen_softc, sc_unit),
+};
+
+static void
+ugenif_get_unit(struct ugen_softc *sc)
+{
+	struct ugen_softc *sc0;
+	unsigned i;
+
+	mutex_enter(&ugenif.lock);
+	for (i = 0, sc0 = RB_TREE_MIN(&ugenif.tree);
+	     sc0 != NULL && i == sc0->sc_unit;
+	     i++, sc0 = RB_TREE_NEXT(&ugenif.tree, sc0))
+		KASSERT(i < UINT_MAX);
+	KASSERT(rb_tree_find_node(&ugenif.tree, &i) == NULL);
+	sc->sc_unit = i;
+	sc0 = rb_tree_insert_node(&ugenif.tree, sc);
+	KASSERT(sc0 == sc);
+	KASSERT(rb_tree_find_node(&ugenif.tree, &i) == sc);
+	mutex_exit(&ugenif.lock);
+}
+
+static void
+ugenif_put_unit(struct ugen_softc *sc)
+{
+
+	mutex_enter(&ugenif.lock);
+	KASSERT(rb_tree_find_node(&ugenif.tree, &sc->sc_unit) == sc);
+	rb_tree_remove_node(&ugenif.tree, sc);
+	sc->sc_unit = -1;
+	mutex_exit(&ugenif.lock);
+}
+
+static struct ugen_softc *
+ugenif_acquire(unsigned unit)
+{
+	struct ugen_softc *sc;
+
+	mutex_enter(&ugenif.lock);
+	sc = rb_tree_find_node(&ugenif.tree, &unit);
+	if (sc) {
+		mutex_enter(&sc->sc_lock);
+		if (sc->sc_dying) {
+			sc = NULL;
+		} else {
+			KASSERT(sc->sc_refcnt < INT_MAX);
+			sc->sc_refcnt++;
+		}
+		mutex_exit(&sc->sc_lock);
+	}
+	mutex_exit(&ugenif.lock);
+
+	return sc;
+}
+
+static void
+ugenif_release(struct ugen_softc *sc)
+{
+
+	mutex_enter(&sc->sc_lock);
+	if (--sc->sc_refcnt < 0)
+		cv_broadcast(&sc->sc_detach_cv);
+	mutex_exit(&sc->sc_lock);
+}
+
 static dev_type_open(ugenopen);
 static dev_type_close(ugenclose);
 static dev_type_read(ugenread);
@@ -301,6 +404,7 @@ ugenif_attach(device_t parent, device_t 
 		return;
 	}
 
+	ugenif_get_unit(sc);
 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
 
 	if (!pmf_device_register(self, NULL, NULL))
@@ -403,9 +507,9 @@ ugenopen(dev_t dev, int flag, int mode, 
 	usbd_status err;
 	struct usbd_xfer *xfer;
 	int i, j;
+	int error;
 
-	sc = device_lookup_private(&ugen_cd, unit);
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(unit)) == NULL)
 		return ENXIO;
 
 	DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
@@ -414,18 +518,23 @@ ugenopen(dev_t dev, int flag, int mode, 
 	/* The control endpoint allows multiple opens. */
 	if (endpt == USB_CONTROL_ENDPOINT) {
 		sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
-		return 0;
+		error = 0;
+		goto out;
 	}
 
-	if (sc->sc_is_open[endpt])
-		return EBUSY;
+	if (sc->sc_is_open[endpt]) {
+		error = EBUSY;
+		goto out;
+	}
 
 	/* Make sure there are pipes for all directions. */
 	for (dir = OUT; dir <= IN; dir++) {
 		if (flag & (dir == OUT ? FWRITE : FREAD)) {
 			sce = &sc->sc_endpoints[endpt][dir];
-			if (sce->edesc == NULL)
-				return ENXIO;
+			if (sce->edesc == NULL) {
+				error = ENXIO;
+				goto out;
+			}
 		}
 	}
 
@@ -445,20 +554,25 @@ ugenopen(dev_t dev, int flag, int mode, 
 			if (dir == OUT) {
 				err = usbd_open_pipe(sce->iface,
 				    edesc->bEndpointAddress, 0, &sce->pipeh);
-				if (err)
-					return EIO;
+				if (err) {
+					error = EIO;
+					goto out;
+				}
 				break;
 			}
 			isize = UGETW(edesc->wMaxPacketSize);
-			if (isize == 0)	/* shouldn't happen */
-				return EINVAL;
+			if (isize == 0) {	/* shouldn't happen */
+				error = EINVAL;
+				goto out;
+			}
 			sce->ibuf = kmem_alloc(isize, KM_SLEEP);
 			DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
 				     endpt, isize));
 			if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) {
 				kmem_free(sce->ibuf, isize);
 				sce->ibuf = NULL;
-				return ENOMEM;
+				error = ENOMEM;
+				goto out;
 			}
 			err = usbd_open_pipe_intr(sce->iface,
 				  edesc->bEndpointAddress,
@@ -469,15 +583,18 @@ ugenopen(dev_t dev, int flag, int mode, 
 				clfree(&sce->q);
 				kmem_free(sce->ibuf, isize);
 				sce->ibuf = NULL;
-				return EIO;
+				error = EIO;
+				goto out;
 			}
 			DPRINTFN(5, ("ugenopen: interrupt open done\n"));
 			break;
 		case UE_BULK:
 			err = usbd_open_pipe(sce->iface,
 				  edesc->bEndpointAddress, 0, &sce->pipeh);
-			if (err)
-				return EIO;
+			if (err) {
+				error = EIO;
+				goto out;
+			}
 			sce->ra_wb_bufsize = UGEN_BULK_RA_WB_BUFSIZE;
 			/*
 			 * Use request size for non-RA/WB transfers
@@ -486,8 +603,10 @@ ugenopen(dev_t dev, int flag, int mode, 
 			sce->ra_wb_reqsize = UGEN_BBSIZE;
 			break;
 		case UE_ISOCHRONOUS:
-			if (dir == OUT)
-				return EINVAL;
+			if (dir == OUT) {
+				error = EINVAL;
+				goto out;
+			}
 			isize = UGETW(edesc->wMaxPacketSize);
 			if (isize == 0)	/* shouldn't happen */
 				return EINVAL;
@@ -502,7 +621,8 @@ ugenopen(dev_t dev, int flag, int mode, 
 			if (err) {
 				kmem_free(sce->ibuf, isize * UGEN_NISOFRAMES);
 				sce->ibuf = NULL;
-				return EIO;
+				error = EIO;
+				goto out;
 			}
 			for (i = 0; i < UGEN_NISOREQS; ++i) {
 				sce->isoreqs[i].sce = sce;
@@ -529,14 +649,18 @@ ugenopen(dev_t dev, int flag, int mode, 
 			sce->pipeh = NULL;
 			kmem_free(sce->ibuf, isize * UGEN_NISOFRAMES);
 			sce->ibuf = NULL;
-			return ENOMEM;
+			error = ENOMEM;
+			goto out;
 		case UE_CONTROL:
 			sce->timeout = USBD_DEFAULT_TIMEOUT;
-			return EINVAL;
+			error = EINVAL;
+			goto out;
 		}
 	}
 	sc->sc_is_open[endpt] = 1;
-	return 0;
+	error = 0;
+out:	ugenif_release(sc);
+	return error;
 }
 
 static int
@@ -547,9 +671,9 @@ ugenclose(dev_t dev, int flag, int mode,
 	struct ugen_endpoint *sce;
 	int dir;
 	int i;
+	int error;
 
-	sc = device_lookup_private(& ugen_cd, UGENUNIT(dev));
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == NULL)
 		return ENXIO;
 
 	DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
@@ -560,7 +684,8 @@ ugenclose(dev_t dev, int flag, int mode,
 	if (endpt == USB_CONTROL_ENDPOINT) {
 		DPRINTFN(5, ("ugenclose: close control\n"));
 		sc->sc_is_open[endpt] = 0;
-		return 0;
+		error = 0;
+		goto out;
 	}
 
 	for (dir = OUT; dir <= IN; dir++) {
@@ -605,8 +730,10 @@ ugenclose(dev_t dev, int flag, int mode,
 		}
 	}
 	sc->sc_is_open[endpt] = 0;
+	error = 0;
 
-	return 0;
+out:	ugenif_release(sc);
+	return error;
 }
 
 Static int
@@ -810,20 +937,10 @@ ugenread(dev_t dev, struct uio *uio, int
 	struct ugen_softc *sc;
 	int error;
 
-	sc = device_lookup_private(& ugen_cd, UGENUNIT(dev));
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == 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)
-		cv_broadcast(&sc->sc_detach_cv);
-	mutex_exit(&sc->sc_lock);
+	ugenif_release(sc);
 
 	return error;
 }
@@ -989,20 +1106,10 @@ ugenwrite(dev_t dev, struct uio *uio, in
 	struct ugen_softc *sc;
 	int error;
 
-	sc = device_lookup_private(& ugen_cd, UGENUNIT(dev));
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == 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)
-		cv_broadcast(&sc->sc_detach_cv);
-	mutex_exit(&sc->sc_lock);
+	ugenif_release(sc);
 
 	return error;
 }
@@ -1057,10 +1164,11 @@ ugen_detach(device_t self, int flags)
 	maj = cdevsw_lookup_major(&ugen_cdevsw);
 
 	/* Nuke the vnodes for any open instances (calls close). */
-	mn = device_unit(self) * USB_MAX_ENDPOINTS;
+	mn = sc->sc_unit * USB_MAX_ENDPOINTS;
 	vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+	ugenif_put_unit(sc);
 
 	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
 		for (dir = OUT; dir <= IN; dir++) {
@@ -1417,8 +1525,6 @@ ugen_do_ioctl(struct ugen_softc *sc, int
 	int dir;
 
 	DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
-	if (sc->sc_dying)
-		return EIO;
 
 	switch (cmd) {
 	case FIONBIO:
@@ -1817,14 +1923,11 @@ ugenioctl(dev_t dev, u_long cmd, void *a
 	struct ugen_softc *sc;
 	int error;
 
-	sc = device_lookup_private(& ugen_cd, UGENUNIT(dev));
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == 0)
 		return ENXIO;
+	error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, l);
+	ugenif_release(sc);
 
-	sc->sc_refcnt++;
-	error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, l);
-	if (--sc->sc_refcnt < 0)
-		cv_broadcast(&sc->sc_detach_cv);
 	return error;
 }
 
@@ -1835,15 +1938,13 @@ ugenpoll(dev_t dev, int events, struct l
 	struct ugen_endpoint *sce_in, *sce_out;
 	int revents = 0;
 
-	sc = device_lookup_private(&ugen_cd, UGENUNIT(dev));
-	if (sc == NULL)
-		return ENXIO;
-
-	if (sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == NULL)
 		return POLLHUP;
 
-	if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT)
-		return ENODEV;
+	if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT) {
+		revents |= POLLERR;
+		goto out;
+	}
 
 	sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
 	sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
@@ -1879,8 +1980,8 @@ ugenpoll(dev_t dev, int events, struct l
 			 * yield any data or a write will happen.
 			 * Pretend they will.
 			 */
-			 revents |= events & (POLLIN | POLLRDNORM);
-			 break;
+			revents |= events & (POLLIN | POLLRDNORM);
+			break;
 		default:
 			break;
 		}
@@ -1913,6 +2014,7 @@ ugenpoll(dev_t dev, int events, struct l
 
 	mutex_exit(&sc->sc_lock);
 
+out:	ugenif_release(sc);
 	return revents;
 }
 
@@ -2061,19 +2163,23 @@ ugenkqfilter(dev_t dev, struct knote *kn
 	struct ugen_softc *sc;
 	struct ugen_endpoint *sce;
 	struct klist *klist;
+	int error;
 
-	sc = device_lookup_private(&ugen_cd, UGENUNIT(dev));
-	if (sc == NULL || sc->sc_dying)
+	if ((sc = ugenif_acquire(UGENUNIT(dev))) == NULL)
 		return ENXIO;
 
-	if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT)
-		return ENODEV;
+	if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT) {
+		error = ENODEV;
+		goto out;
+	}
 
 	switch (kn->kn_filter) {
 	case EVFILT_READ:
 		sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
-		if (sce == NULL)
-			return EINVAL;
+		if (sce == NULL) {
+			error = EINVAL;
+			goto out;
+		}
 
 		klist = &sce->rsel.sel_klist;
 		switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
@@ -2087,32 +2193,38 @@ ugenkqfilter(dev_t dev, struct knote *kn
 			kn->kn_fop = &ugenread_bulk_filtops;
 			break;
 		default:
-			return EINVAL;
+			error = EINVAL;
+			goto out;
 		}
 		break;
 
 	case EVFILT_WRITE:
 		sce = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
-		if (sce == NULL)
-			return EINVAL;
+		if (sce == NULL) {
+			error = EINVAL;
+			goto out;
+		}
 
 		klist = &sce->rsel.sel_klist;
 		switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
 		case UE_INTERRUPT:
 		case UE_ISOCHRONOUS:
 			/* XXX poll doesn't support this */
-			return EINVAL;
+			error = EINVAL;
+			goto out;
 
 		case UE_BULK:
 			kn->kn_fop = &ugenwrite_bulk_filtops;
 			break;
 		default:
-			return EINVAL;
+			error = EINVAL;
+			goto out;
 		}
 		break;
 
 	default:
-		return EINVAL;
+		error = EINVAL;
+		goto out;
 	}
 
 	kn->kn_hook = sce;
@@ -2121,5 +2233,24 @@ ugenkqfilter(dev_t dev, struct knote *kn
 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
 	mutex_exit(&sc->sc_lock);
 
-	return 0;
+	error = 0;
+
+out:	ugenif_release(sc);
+	return error;
 }
+
+MODULE(MODULE_CLASS_DRIVER, ugen, NULL);
+
+static int
+ugen_modcmd(modcmd_t cmd, void *aux)
+{
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+		mutex_init(&ugenif.lock, MUTEX_DEFAULT, IPL_NONE);
+		rb_tree_init(&ugenif.tree, &ugenif_tree_ops);
+		return 0;
+	default:
+		return ENOTTY;
+	}
+}
# HG changeset patch
# User Taylor R Campbell <riastradh@NetBSD.org>
# Date 1597539227 0
#      Sun Aug 16 00:53:47 2020 +0000
# Branch trunk
# Node ID 3803bfd58b01151412f8b5431de4ba1569b43522
# Parent  ffdb1963bf76e5cae83bf66bd3cc02fd99e40102
# EXP-Topic riastradh-ugenif
Expose Yubikey CCID interface to userland via ugenif.

diff -r ffdb1963bf76 -r 3803bfd58b01 sys/dev/usb/usbdevices.config
--- a/sys/dev/usb/usbdevices.config	Sun Aug 16 00:49:03 2020 +0000
+++ b/sys/dev/usb/usbdevices.config	Sun Aug 16 00:53:47 2020 +0000
@@ -253,3 +253,12 @@ bthub* at ubt?
 
 # Araneus Alea I/II TRNG
 ualea* at uhub? port ? configuration ? interface ?
+
+# Yubikey -- CCID interface (OTP and FIDO are handled by uhidev(4))
+# https://support.yubico.com/support/solutions/articles/15000028104-yubikey-usb-id-values
+ugenif* at uhub? vendor 0x1050 product 0x0114 configuration 1 interface 1
+ugenif* at uhub? vendor 0x1050 product 0x0115 configuration 1 interface 1
+ugenif* at uhub? vendor 0x1050 product 0x0116 configuration 1 interface 2
+ugenif* at uhub? vendor 0x1050 product 0x0405 configuration 1 interface 1
+ugenif* at uhub? vendor 0x1050 product 0x0406 configuration 1 interface 1
+ugenif* at uhub? vendor 0x1050 product 0x0407 configuration 1 interface 2