There are a few threads going on related to problems with ugen(4) and uhidev(4) devices on xhci(4). This is related to the issue patrick@ already explained; while ehci(4) can save the last data toggle state, xhci(4) resets it on every open/close cycle, getting out of sync with the device.
This diff addresses a possible solution for ugen(4) and uhidev(4). For ugen(4) we already did some positive testing related to scanner devices, see e.g.: https://marc.info/?l=openbsd-bugs&m=161056827312265&w=2 I would appreciate some regression testing and feedback. On xhci(4) it might resolve issues related to that, and on ehci(4) it shouldn't break your existing setup. Index: dev/usb/ugen.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ugen.c,v retrieving revision 1.109 diff -u -p -u -p -r1.109 ugen.c --- dev/usb/ugen.c 25 Dec 2020 12:59:52 -0000 1.109 +++ dev/usb/ugen.c 13 Jan 2021 21:38:30 -0000 @@ -46,9 +46,12 @@ #include <sys/vnode.h> #include <sys/poll.h> +#include <machine/bus.h> + #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> #include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> #ifdef UGEN_DEBUG #define DPRINTF(x) do { if (ugendebug) printf x; } while (0) @@ -114,6 +117,7 @@ int ugen_do_close(struct ugen_softc *, i int ugen_set_config(struct ugen_softc *sc, int configno); int ugen_set_interface(struct ugen_softc *, int, int); int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx); +void ugen_clear_iface_eps(struct ugen_softc *, struct usbd_interface *); #define UGENUNIT(n) ((minor(n) >> 4) & 0xf) #define UGENENDPOINT(n) (minor(n) & 0xf) @@ -302,6 +306,8 @@ ugenopen(dev_t dev, int flag, int mode, DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", sc, endpt, dir, sce)); edesc = sce->edesc; + /* Clear device endpoint toggle. */ + ugen_clear_iface_eps(sc, sce->iface); switch (UE_GET_XFERTYPE(edesc->bmAttributes)) { case UE_INTERRUPT: if (dir == OUT) { @@ -329,6 +335,8 @@ ugenopen(dev_t dev, int flag, int mode, clfree(&sce->q); return (EIO); } + /* Clear HC endpoint toggle. */ + usbd_clear_endpoint_toggle(sce->pipeh); DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: @@ -336,6 +344,8 @@ ugenopen(dev_t dev, int flag, int mode, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); + /* Clear HC endpoint toggle. */ + usbd_clear_endpoint_toggle(sce->pipeh); break; case UE_ISOCHRONOUS: if (dir == OUT) @@ -1417,4 +1427,42 @@ ugenkqfilter(dev_t dev, struct knote *kn splx(s); return (0); +} + +void +ugen_clear_iface_eps(struct ugen_softc *sc, struct usbd_interface *iface) +{ + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + uint8_t xfertype; + int i; + + /* Only clear interface endpoints when none are in use. */ + for (i = 0; i < USB_MAX_ENDPOINTS; i++) { + if (i == USB_CONTROL_ENDPOINT) + continue; + if (sc->sc_is_open[i] != 0) + return; + } + DPRINTFN(1,("%s: clear interface eps\n", __func__)); + + id = usbd_get_interface_descriptor(iface); + if (id == NULL) + goto bad; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) + goto bad; + + xfertype = UE_GET_XFERTYPE(ed->bmAttributes); + if (xfertype == UE_BULK || xfertype == UE_INTERRUPT) { + if (usbd_clear_endpoint_feature(sc->sc_udev, + ed->bEndpointAddress, UF_ENDPOINT_HALT)) + goto bad; + } + } + return; +bad: + printf("%s: clear endpoints failed!\n", __func__); } Index: dev/usb/uhidev.c =================================================================== RCS file: /cvs/src/sys/dev/usb/uhidev.c,v retrieving revision 1.83 diff -u -p -u -p -r1.83 uhidev.c --- dev/usb/uhidev.c 31 Aug 2020 12:26:49 -0000 1.83 +++ dev/usb/uhidev.c 13 Jan 2021 21:38:31 -0000 @@ -98,6 +98,7 @@ int uhidev_activate(struct device *, int void uhidev_get_report_async_cb(struct usbd_xfer *, void *, usbd_status); void uhidev_set_report_async_cb(struct usbd_xfer *, void *, usbd_status); +void uhidev_clear_iface_eps(struct uhidev_softc *, struct usbd_interface *); struct cfdriver uhidev_cd = { NULL, "uhidev", DV_DULL @@ -508,6 +509,9 @@ uhidev_open(struct uhidev *scd) DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize, sc->sc_iep_addr)); + /* Clear device endpoint toggle. */ + uhidev_clear_iface_eps(sc, sc->sc_iface); + err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr, USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf, sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL); @@ -517,6 +521,8 @@ uhidev_open(struct uhidev *scd) error = EIO; goto out1; } + /* Clear HC endpoint toggle. */ + usbd_clear_endpoint_toggle(sc->sc_ipipe); DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe)); @@ -542,6 +548,8 @@ uhidev_open(struct uhidev *scd) error = EIO; goto out2; } + /* Clear HC endpoint toggle. */ + usbd_clear_endpoint_toggle(sc->sc_opipe); DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe)); @@ -949,4 +957,38 @@ uhidev_ioctl(struct uhidev *sc, u_long c return -1; } return 0; +} + +void +uhidev_clear_iface_eps(struct uhidev_softc *sc, struct usbd_interface *iface) +{ + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + uint8_t xfertype; + int i; + + /* Only clear interface endpoints when none are in use. */ + if (sc->sc_ipipe || sc->sc_opipe) + return; + DPRINTFN(1,("%s: clear interface eps\n", __func__)); + + id = usbd_get_interface_descriptor(iface); + if (id == NULL) + goto bad; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) + goto bad; + + xfertype = UE_GET_XFERTYPE(ed->bmAttributes); + if (xfertype == UE_BULK || xfertype == UE_INTERRUPT) { + if (usbd_clear_endpoint_feature(sc->sc_udev, + ed->bEndpointAddress, UF_ENDPOINT_HALT)) + goto bad; + } + } + return; +bad: + printf("%s: clear endpoints failed!\n", __func__); } Index: dev/usb/usbdi_util.c =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdi_util.c,v retrieving revision 1.44 diff -u -p -u -p -r1.44 usbdi_util.c --- dev/usb/usbdi_util.c 6 Oct 2019 17:11:51 -0000 1.44 +++ dev/usb/usbdi_util.c 13 Jan 2021 21:38:31 -0000 @@ -192,6 +192,19 @@ usbd_clear_port_feature(struct usbd_devi } usbd_status +usbd_clear_endpoint_feature(struct usbd_device *dev, int epaddr, int sel) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, sel); + USETW(req.wIndex, epaddr); + USETW(req.wLength, 0); + return (usbd_do_request(dev, &req, 0)); +} + +usbd_status usbd_set_port_feature(struct usbd_device *dev, int port, int sel) { usb_device_request_t req; Index: dev/usb/usbdi_util.h =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdi_util.h,v retrieving revision 1.29 diff -u -p -u -p -r1.29 usbdi_util.h --- dev/usb/usbdi_util.h 8 Dec 2014 22:00:11 -0000 1.29 +++ dev/usb/usbdi_util.h 13 Jan 2021 21:38:31 -0000 @@ -41,6 +41,7 @@ usbd_status usbd_set_hub_feature(struct usbd_status usbd_clear_hub_feature(struct usbd_device *, int); usbd_status usbd_set_port_feature(struct usbd_device *dev, int, int); usbd_status usbd_clear_port_feature(struct usbd_device *, int, int); +usbd_status usbd_clear_endpoint_feature(struct usbd_device *, int, int); usbd_status usbd_get_device_status(struct usbd_device *, usb_status_t *); usbd_status usbd_get_hub_status(struct usbd_device *, usb_hub_status_t *); usbd_status usbd_get_hub_descriptor(struct usbd_device *,