# 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