On Fri, Oct 25, 2019 at 03:59:25PM +0200, Patrick Wildt wrote:
> On Fri, Oct 25, 2019 at 03:10:48PM +1100, Damien Miller wrote:
> > Hi,
> > 
> > Some HID devices do not list any report IDs for communication, e.g. my
> > Yubikey5:
> > 
> > > uhidev0: iclass 3/0
> > > uhid0 at uhidev0: input=64, output=64, feature=0
> > > ugen0 at uhub0 port 2 configuration 1 "Yubico YubiKey FIDO+CCID" rev 
> > > 2.00/5.12 addr 2
> > 
> > on Linux, these can be talked to by sending to report ID 0. This AFAIK
> > causes the write(2)'d data to be sent to the interrupt pipe.
> > 
> > Our uhid(4) devices already consider write(2) operations to be
> > equivalent to USB_SET_REPORT to the UHID_OUTPUT_REPORT, so we can't
> > use that. So this patch allows USB_SET_REPORT with ucr_report=0
> > to do something similar to what Linux does.
> > 
> > Caveat: this is literally my first attempt at writing USB code, and
> > I ended up futzing with the kernel.
> 
> So from what I understood the Yubikey expects the transfer to happen
> on the Interrupt OUT pipe instead of doing a control transfer.  Read-
> ing some code and documentation, it looks like that we should by de-
> fault send our reports on the interrupt pipe, and only if it does not
> exist fall back to control transfers.  Linux seems to do exactly that.
> 
> I tried to come up with the following diff, which appeard to make a
> test program work for me.  Though I'm not sure I got all the specifics
> right.
> 
> Can you give this a go with your test progam?
> 
> Patrick

Oops, obvious error.  Though I still cannot write/read multiple times
in a row, so there is probably still something wrong (unless my test
is as well).

diff --git a/sys/dev/usb/uhidev.c b/sys/dev/usb/uhidev.c
index 7c02c82f7d5..73de7aeaf56 100644
--- a/sys/dev/usb/uhidev.c
+++ b/sys/dev/usb/uhidev.c
@@ -97,6 +97,7 @@ int uhidev_detach(struct device *, int);
 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);
 
 struct cfdriver uhidev_cd = {
        NULL, "uhidev", DV_DULL
@@ -670,14 +671,23 @@ uhidev_set_report(struct uhidev_softc *sc, int type, int 
id, void *data,
                memcpy(buf + 1, data, len - 1);
        }
 
-       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-       req.bRequest = UR_SET_REPORT;
-       USETW2(req.wValue, type, id);
-       USETW(req.wIndex, sc->sc_ifaceno);
-       USETW(req.wLength, len);
+       if (sc->sc_opipe != NULL) {
+               usbd_setup_xfer(sc->sc_owxfer, sc->sc_opipe, 0, buf, len,
+                   USBD_SYNCHRONOUS | USBD_CATCH, 0, NULL);
+               if (usbd_transfer(sc->sc_owxfer)) {
+                       usbd_clear_endpoint_stall(sc->sc_opipe);
+                       actlen = -1;
+               }
+       } else {
+               req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+               req.bRequest = UR_SET_REPORT;
+               USETW2(req.wValue, type, id);
+               USETW(req.wIndex, sc->sc_ifaceno);
+               USETW(req.wLength, len);
 
-       if (usbd_do_request(sc->sc_udev, &req, buf))
-               actlen = -1;
+               if (usbd_do_request(sc->sc_udev, &req, buf))
+                       actlen = -1;
+       }
 
        if (id > 0)
                free(buf, M_TEMP, len);
@@ -685,6 +695,16 @@ uhidev_set_report(struct uhidev_softc *sc, int type, int 
id, void *data,
        return (actlen);
 }
 
+void
+uhidev_set_report_async_cb(struct usbd_xfer *xfer, void *priv, usbd_status err)
+{
+       struct uhidev_softc *sc = priv;
+
+       if (err == USBD_STALLED)
+               usbd_clear_endpoint_stall_async(sc->sc_opipe);
+       usbd_free_xfer(xfer);
+}
+
 int
 uhidev_set_report_async(struct uhidev_softc *sc, int type, int id, void *data,
     int len)
@@ -715,14 +735,23 @@ uhidev_set_report_async(struct uhidev_softc *sc, int 
type, int id, void *data,
                memcpy(buf, data, len);
        }
 
-       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-       req.bRequest = UR_SET_REPORT;
-       USETW2(req.wValue, type, id);
-       USETW(req.wIndex, sc->sc_ifaceno);
-       USETW(req.wLength, len);
-
-       if (usbd_request_async(xfer, &req, NULL, NULL))
-               actlen = -1;
+       if (sc->sc_opipe != NULL) {
+               usbd_setup_xfer(xfer, sc->sc_opipe, sc, buf, len,
+                   USBD_NO_COPY, USBD_DEFAULT_TIMEOUT,
+                   uhidev_set_report_async_cb);
+               if (usbd_transfer(xfer)) {
+                       usbd_clear_endpoint_stall_async(sc->sc_opipe);
+                       actlen = -1;
+               }
+       } else {
+               req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+               req.bRequest = UR_SET_REPORT;
+               USETW2(req.wValue, type, id);
+               USETW(req.wIndex, sc->sc_ifaceno);
+               USETW(req.wLength, len);
+               if (usbd_request_async(xfer, &req, NULL, NULL))
+                       actlen = -1;
+       }
 
        return (actlen);
 }

Reply via email to