Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb Share unit numbering for ugen and ugenif.



details:   https://anonhg.NetBSD.org/src/rev/94ad15a6ecbf
branches:  trunk
changeset: 974892:94ad15a6ecbf
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Aug 16 02:37:19 2020 +0000

description:
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.

Not a fully fleshed out solution to the ugen problem -- there's no
way for a userland driver to kick out a kernel driver and take over,
but this will let us, e.g., use uhidev(4) for Yubikey OTP/U2F/FIDO2
but ugen(4), with pcscd(8), for Yubikey CCID.

Fix various MP-safety issues while here (still not MPSAFE, but more
progress).

diffstat:

 sys/dev/usb/ugen.c |  295 ++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 213 insertions(+), 82 deletions(-)

diffs (truncated from 550 to 300 lines):

diff -r 1278c8bb64f1 -r 94ad15a6ecbf sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c        Sun Aug 16 02:34:54 2020 +0000
+++ b/sys/dev/usb/ugen.c        Sun Aug 16 02:37:19 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ugen.c,v 1.154 2020/08/16 02:34:54 riastradh Exp $     */
+/*     $NetBSD: ugen.c,v 1.155 2020/08/16 02:37:19 riastradh 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.154 2020/08/16 02:34:54 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.155 2020/08/16 02:37:19 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -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_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 @@
        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 @@
                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 @@
        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 @@
        /* 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 @@
                        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 @@
                                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 @@
                        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 @@
                        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 @@
                        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 @@
        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;
 



Home | Main Index | Thread Index | Old Index