diff options
-rw-r--r-- | sys/dev/usb/ugen.c | 50 | ||||
-rw-r--r-- | sys/dev/usb/uhidev.c | 44 |
2 files changed, 92 insertions, 2 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index 6a27d3f6410..39997668bcd 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ugen.c,v 1.109 2020/12/25 12:59:52 visa Exp $ */ +/* $OpenBSD: ugen.c,v 1.110 2021/01/25 14:14:42 mglocker Exp $ */ /* $NetBSD: ugen.c,v 1.63 2002/11/26 18:49:48 christos Exp $ */ /* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */ @@ -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 *, int, int); 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, struct proc *p) 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, struct proc *p) 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, struct proc *p) 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) @@ -1418,3 +1428,41 @@ ugenkqfilter(dev_t dev, struct knote *kn) 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__); +} diff --git a/sys/dev/usb/uhidev.c b/sys/dev/usb/uhidev.c index 5f2bc07cc96..3549129cbfc 100644 --- a/sys/dev/usb/uhidev.c +++ b/sys/dev/usb/uhidev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uhidev.c,v 1.83 2020/08/31 12:26:49 patrick Exp $ */ +/* $OpenBSD: uhidev.c,v 1.84 2021/01/25 14:14:42 mglocker Exp $ */ /* $NetBSD: uhidev.c,v 1.14 2003/03/11 16:44:00 augustss Exp $ */ /* @@ -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)); @@ -950,3 +958,37 @@ uhidev_ioctl(struct uhidev *sc, u_long cmd, caddr_t addr, int flag, } 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__); +} |