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 *,

Reply via email to