Module Name: src
Committed By: martin
Date: Thu Aug 27 14:13:18 UTC 2020
Modified Files:
src/sys/dev/usb [netbsd-9]: ugen.c usbdevices.config
Log Message:
Pull up following revision(s) (requested by riastradh in ticket #1065):
sys/dev/usb/usbdevices.config: revision 1.41 (patch)
sys/dev/usb/ugen.c: revision 1.152
sys/dev/usb/ugen.c: revision 1.153
sys/dev/usb/ugen.c: revision 1.154
sys/dev/usb/ugen.c: revision 1.155 (patch)
sys/dev/usb/ugen.c: revision 1.156
sys/dev/usb/ugen.c: revision 1.157
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 -- progress toward real MP-safety.
Hold the lock over access to the data structures it covers.
Still not MPSAFE, but progress.
Convert DIAGNOSTIC prints to KASSERTs.
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).
Expose Yubikey CCID interface to userland via ugenif.
Fix sloppy mistakes in previous.
1. Give the offset of the rbnode, not some other random members to
overwrite with garbage.
2. Don't try to unlock a mutex at NULL.
3. Make sure all paths out after ugenif_acquire go via
ugenif_release.
Fix ugen detach after partial attach.
While here, register null pmf handler even for partially attached
devices so they don't needlessly interfere with suspend.
To generate a diff of this commit:
cvs rdiff -u -r1.146.2.1 -r1.146.2.2 src/sys/dev/usb/ugen.c
cvs rdiff -u -r1.35.4.1 -r1.35.4.2 src/sys/dev/usb/usbdevices.config
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.146.2.1 src/sys/dev/usb/ugen.c:1.146.2.2
--- src/sys/dev/usb/ugen.c:1.146.2.1 Wed Dec 11 14:56:36 2019
+++ src/sys/dev/usb/ugen.c Thu Aug 27 14:13:18 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: ugen.c,v 1.146.2.1 2019/12/11 14:56:36 martin Exp $ */
+/* $NetBSD: ugen.c,v 1.146.2.2 2020/08/27 14:13:18 martin 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.146.2.1 2019/12/11 14:56:36 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.146.2.2 2020/08/27 14:13:18 martin Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -58,6 +58,8 @@ __KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.1
#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>
@@ -97,7 +99,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 */
@@ -124,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;
@@ -136,8 +139,109 @@ struct ugen_softc {
int sc_refcnt;
char sc_buffer[UGEN_BBSIZE];
u_char sc_dying;
+ u_char sc_attached;
};
+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_node),
+};
+
+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 == NULL)
+ goto out;
+ mutex_enter(&sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(&sc->sc_lock);
+ sc = NULL;
+ goto out;
+ }
+ KASSERT(sc->sc_refcnt < INT_MAX);
+ sc->sc_refcnt++;
+ mutex_exit(&sc->sc_lock);
+out: 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);
+}
+
dev_type_open(ugenopen);
dev_type_close(ugenclose);
dev_type_read(ugenread);
@@ -276,6 +380,9 @@ ugenif_attach(device_t parent, device_t
}
}
+ if (!pmf_device_register(self, NULL, NULL))
+ aprint_error_dev(self, "couldn't establish power handler\n");
+
if (uiaa->uiaa_ifaceno < 0) {
/*
* If we attach the whole device,
@@ -285,7 +392,6 @@ ugenif_attach(device_t parent, device_t
if (err) {
aprint_error_dev(self,
"setting configuration index 0 failed\n");
- sc->sc_dying = 1;
return;
}
}
@@ -298,15 +404,12 @@ ugenif_attach(device_t parent, device_t
if (err) {
aprint_error_dev(self, "setting configuration %d failed\n",
conf);
- sc->sc_dying = 1;
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))
- aprint_error_dev(self, "couldn't establish power handler\n");
-
+ sc->sc_attached = 1;
}
Static void
@@ -404,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",
@@ -415,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;
+ }
}
}
@@ -446,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,
@@ -470,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
@@ -487,11 +603,15 @@ 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;
+ if (isize == 0) { /* shouldn't happen */
+ error = EINVAL;
+ goto out;
+ }
sce->ibuf = kmem_alloc(isize * UGEN_NISOFRAMES,
KM_SLEEP);
sce->cur = sce->fill = sce->ibuf;
@@ -503,7 +623,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;
@@ -530,14 +651,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;
}
int
@@ -548,25 +673,21 @@ 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",
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"));
sc->sc_is_open[endpt] = 0;
- return 0;
+ error = 0;
+ goto out;
}
for (dir = OUT; dir <= IN; dir++) {
@@ -611,8 +732,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
@@ -629,16 +752,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:
@@ -649,7 +764,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 +771,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 +805,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 +816,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 +895,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 +902,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) {
@@ -833,20 +939,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;
}
@@ -868,16 +964,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:
@@ -895,7 +983,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 +994,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. */
@@ -1023,20 +1108,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;
}
@@ -1067,6 +1142,10 @@ ugen_detach(device_t self, int flags)
sc->sc_dying = 1;
pmf_device_deregister(self);
+
+ if (!sc->sc_attached)
+ goto out;
+
/* Abort all pipes. Causes processes waiting for transfer to wake. */
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
for (dir = OUT; dir <= IN; dir++) {
@@ -1091,12 +1170,13 @@ 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++) {
+out: for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
for (dir = OUT; dir <= IN; dir++) {
sce = &sc->sc_endpoints[i][dir];
seldestroy(&sce->rsel);
@@ -1136,14 +1216,9 @@ ugenintr(struct usbd_xfer *xfer, void *a
DPRINTFN(5, (" data = %02x %02x %02x\n",
ibuf[0], ibuf[1], ibuf[2]));
- (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);
- }
+ (void)b_to_q(ibuf, count, &sce->q);
+ cv_signal(&sce->cv);
mutex_exit(&sc->sc_lock);
selnotify(&sce->rsel, 0, 0);
}
@@ -1166,10 +1241,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 +1265,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 +1277,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 +1306,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 +1343,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 +1372,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 +1408,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);
}
@@ -1465,8 +1531,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:
@@ -1865,14 +1929,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;
-
- sc->sc_refcnt++;
error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, l);
- if (--sc->sc_refcnt < 0)
- cv_broadcast(&sc->sc_detach_cv);
+ ugenif_release(sc);
+
return error;
}
@@ -1883,31 +1944,18 @@ 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];
- 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)))
@@ -1938,8 +1986,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;
}
@@ -1972,6 +2020,7 @@ ugenpoll(dev_t dev, int events, struct l
mutex_exit(&sc->sc_lock);
+out: ugenif_release(sc);
return revents;
}
@@ -1991,12 +2040,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
@@ -2004,20 +2059,24 @@ filt_ugenread_isoc(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->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
@@ -2025,24 +2084,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);
-
- if (sce->ra_wb_used == 0)
- return 0;
-
- kn->kn_data = sce->ra_wb_used;
+ 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);
- return 1;
+ return ret;
}
static int
@@ -2050,24 +2112,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);
-
- if (sce->ra_wb_used == sce->limit - sce->ibuf)
- return 0;
-
- kn->kn_data = (sce->limit - sce->ibuf) - sce->ra_wb_used;
+ 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);
- return 1;
+ return ret;
}
static const struct filterops ugenread_intr_filtops = {
@@ -2104,19 +2169,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) {
@@ -2130,32 +2199,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;
@@ -2164,5 +2239,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;
+ }
}
Index: src/sys/dev/usb/usbdevices.config
diff -u src/sys/dev/usb/usbdevices.config:1.35.4.1 src/sys/dev/usb/usbdevices.config:1.35.4.2
--- src/sys/dev/usb/usbdevices.config:1.35.4.1 Sun Apr 12 08:44:42 2020
+++ src/sys/dev/usb/usbdevices.config Thu Aug 27 14:13:18 2020
@@ -1,4 +1,4 @@
-# $NetBSD: usbdevices.config,v 1.35.4.1 2020/04/12 08:44:42 martin Exp $
+# $NetBSD: usbdevices.config,v 1.35.4.2 2020/08/27 14:13:18 martin Exp $
#
# This file contains all USB related configuration.
# It is suitable for inclusion in a kernel config(5) file.
@@ -259,3 +259,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