summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/usb/ugen.c50
-rw-r--r--sys/dev/usb/uhidev.c44
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__);
+}