On Fri, Jan 29, 2021 at 07:23:37PM +0200, Ville Valkonen wrote:
> Hello Anton,
> 
> either I failed to use git or then files have changed since the first patch:
> $ git apply -p0 --check hid_plusplus.patch
> error: patch failed: share/man/man4/Makefile:83
> error: share/man/man4/Makefile: patch does not apply
> error: patch failed: share/man/man4/uhidev.4:40
> error: share/man/man4/uhidev.4: patch does not apply
> error: patch failed: share/man/man4/usb.4:255
> error: share/man/man4/usb.4: patch does not apply
> error: patch failed: sys/arch/amd64/conf/GENERIC:288
> error: sys/arch/amd64/conf/GENERIC: patch does not apply
> error: patch failed: sys/dev/usb/uhidev.c:950
> error: sys/dev/usb/uhidev.c: patch does not apply
> error: sys/dev/usb/uhidpp.c: already exists in working directory
> 
> Running that on root of src. A quick peek on sys/dev/usb/uhidev.c file
> shows that it has been modified on 25th of Jan so I'd guess the patch
> needs to be updated.
> 
> Thanks in advance! Been thinking to have a look on that protocol but
> since I am no HW hacker I've postponed that for years :)

Rebased diff, should apply using either git-apply or patch.

diff --git share/man/man4/Makefile share/man/man4/Makefile
index 70e62135237..f893825ceb9 100644
--- share/man/man4/Makefile
+++ share/man/man4/Makefile
@@ -83,8 +83,8 @@ MAN=  aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
        txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 \
        ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
        udav.4 udcf.4 udl.4 udp.4 udsbr.4 \
-       uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uipaq.4 \
-       ujoy.4 uk.4 ukbd.4 \
+       uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uhidpp.4 \
+       uipaq.4 ujoy.4 uk.4 ukbd.4
        ukphy.4 ulpt.4 umass.4 umb.4 umbg.4 umcs.4 umct.4 umidi.4 umodem.4 \
        ums.4 umsm.4 umstc.4 umt.4 unix.4 uonerng.4 uow.4 uoaklux.4 uoakrh.4 \
        uoakv.4 upd.4 upgt.4 upl.4 uplcom.4 ural.4 ure.4 url.4 urlphy.4 \
diff --git share/man/man4/uhidev.4 share/man/man4/uhidev.4
index 06911ddef29..aa1efbea710 100644
--- share/man/man4/uhidev.4
+++ share/man/man4/uhidev.4
@@ -40,6 +40,7 @@
 .Cd "ucycom*  at uhidev?"
 .Cd "ugold*   at uhidev?"
 .Cd "uhid*    at uhidev?"
+.Cd "uhidpp*  at uhidev?"
 .Cd "ujoy*    at uhidev?"
 .Cd "ukbd*    at uhidev?"
 .Cd "ums*     at uhidev?"
@@ -74,6 +75,7 @@ only dispatches data to them based on the report id.
 .Xr ucycom 4 ,
 .Xr ugold 4 ,
 .Xr uhid 4 ,
+.Xr uhidpp 4 ,
 .Xr ujoy 4 ,
 .Xr ukbd 4 ,
 .Xr ums 4 ,
diff --git share/man/man4/uhidpp.4 share/man/man4/uhidpp.4
new file mode 100644
index 00000000000..4c78380c35b
--- /dev/null
+++ share/man/man4/uhidpp.4
@@ -0,0 +1,48 @@
+.\"    $OpenBSD$
+.\"
+.\" Copyright (c) 2021 Anton Lindqvsit <an...@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt UHIDPP 4
+.Os
+.Sh NAME
+.Nm uhidpp
+.Nd Logitech HID++ devices
+.Sh SYNOPSIS
+.Cd "uhidpp* at uhidev?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Logitech HID++ devices.
+It exposes a collection of battery sensor values which are made available
+through the
+.Xr sysctl 8
+interface.
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr uhidev 4 ,
+.Xr usb 4 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 6.9 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Anton Lindqvist Aq Mt an...@opensd.org .
diff --git share/man/man4/usb.4 share/man/man4/usb.4
index 8b9e3ffdc3c..245d01cdef4 100644
--- share/man/man4/usb.4
+++ share/man/man4/usb.4
@@ -255,6 +255,8 @@ TEMPer gold HID thermometer and hygrometer
 Generic driver for Human Interface Devices
 .It Xr uhidev 4
 Base driver for all Human Interface Devices
+.It Xr uhidpp 4
+Logitech HID++ devices
 .It Xr ujoy 4
 USB joysticks/gamecontrollers
 .It Xr ukbd 4
diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC
index ffa1b4a497c..643e6bd0ba1 100644
--- sys/arch/amd64/conf/GENERIC
+++ sys/arch/amd64/conf/GENERIC
@@ -289,6 +289,7 @@ fido*       at uhidev?              # FIDO/U2F security key 
support
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 upd*   at uhidev?              # USB Power Devices sensors
 umstc* at uhidev?              # Microsoft Surface Type Cover
+uhidpp*        at uhidev?              # Logitech HID++ Devices
 aue*   at uhub?                # ADMtek AN986 Pegasus Ethernet
 atu*   at uhub?                # Atmel AT76c50x based 802.11b
 axe*   at uhub?                # ASIX Electronics AX88172 USB Ethernet
diff --git sys/dev/usb/files.usb sys/dev/usb/files.usb
index ff94bf8765b..35e533ab290 100644
--- sys/dev/usb/files.usb
+++ sys/dev/usb/files.usb
@@ -483,3 +483,8 @@ file        dev/usb/if_bwfm_usb.c           bwfm_usb
 device umstc: hid
 attach umstc at uhidbus
 file   dev/usb/umstc.c                 umstc
+
+# Logitech HID++ Devices
+device uhidpp: hid
+attach uhidpp at uhidbus
+file   dev/usb/uhidpp.c                uhidpp
diff --git sys/dev/usb/uhidev.c sys/dev/usb/uhidev.c
index 16f440714c1..9a2f424661a 100644
--- sys/dev/usb/uhidev.c
+++ sys/dev/usb/uhidev.c
@@ -256,8 +256,11 @@ uhidev_attach(struct device *parent, struct device *self, 
void *aux)
        /* Look for a driver claiming all report IDs first. */
        dev = config_found_sm(self, &uha, NULL, uhidevsubmatch);
        if (dev != NULL) {
-               for (repid = 0; repid < nrepid; repid++)
-                       sc->sc_subdevs[repid] = (struct uhidev *)dev;
+               for (repid = 0; repid < nrepid; repid++) {
+                       /* Could already be assigned by uhidev_set_intr(). */
+                       if (sc->sc_subdevs[repid] == NULL)
+                               sc->sc_subdevs[repid] = (struct uhidev *)dev;
+               }
                return;
        }
 
@@ -270,7 +273,9 @@ uhidev_attach(struct device *parent, struct device *self, 
void *aux)
 
                uha.reportid = repid;
                dev = config_found_sm(self, &uha, uhidevprint, uhidevsubmatch);
-               sc->sc_subdevs[repid] = (struct uhidev *)dev;
+               /* Could already be assigned by uhidev_set_intr(). */
+               if (sc->sc_subdevs[repid] == NULL)
+                       sc->sc_subdevs[repid] = (struct uhidev *)dev;
        }
 }
 
@@ -992,3 +997,15 @@ uhidev_clear_iface_eps(struct uhidev_softc *sc, struct 
usbd_interface *iface)
 bad:
        printf("%s: clear endpoints failed!\n", __func__);
 }
+
+int
+uhidev_set_intr(struct uhidev_softc *sc, struct uhidev *dev, int repid)
+{
+       if ((dev->sc_state & UHIDEV_OPEN) == 0)
+               return ENODEV;
+       if (repid >= sc->sc_nrepid)
+               return EINVAL;
+
+       sc->sc_subdevs[repid] = dev;
+       return 0;
+}
diff --git sys/dev/usb/uhidev.h sys/dev/usb/uhidev.h
index 16657f1e712..791c8882769 100644
--- sys/dev/usb/uhidev.h
+++ sys/dev/usb/uhidev.h
@@ -95,3 +95,4 @@ int uhidev_get_report(struct uhidev_softc *, int, int, void 
*, int);
 int uhidev_get_report_async(struct uhidev_softc *, int, int, void *, int,
     void *, void (*)(void *, int, void *, int));
 usbd_status uhidev_write(struct uhidev_softc *, void *, int);
+int uhidev_set_intr(struct uhidev_softc *, struct uhidev *, int);
diff --git sys/dev/usb/uhidpp.c sys/dev/usb/uhidpp.c
new file mode 100644
index 00000000000..aee989e2de5
--- /dev/null
+++ sys/dev/usb/uhidpp.c
@@ -0,0 +1,1055 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 Anton Lindqvist <an...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/mutex.h>
+#include <sys/sensors.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+
+/* #define UHIDPP_DEBUG */
+#ifdef UHIDPP_DEBUG
+
+#define DPRINTF(x...) do {                                             \
+       if (uhidpp_debug)                                               \
+               printf(x);                                              \
+} while (0)
+
+#define DREPORT(prefix, repid, buf, len) do {                          \
+       if (uhidpp_debug)                                               \
+               uhidd_dump_report((prefix), (repid), (buf), (len));     \
+} while (0)
+
+void uhidd_dump_report(const char *, uint8_t, const unsigned char *, u_int);
+
+int uhidpp_debug = 1;
+
+#else
+
+#define DPRINTF(x...)
+#define DREPORT(prefix, repid, buf, len)
+
+#endif
+
+#define HIDPP_LINK_STATUS(x)   ((x) & (1 << 7))
+
+#define HIDPP_REPORT_ID_SHORT                  0x10
+#define HIDPP_REPORT_ID_LONG                   0x11
+
+/*
+ * Length of reports. Note that the effective length is always +1 as
+ * uhidev_set_report() prepends the report ID.
+ */
+#define HIDPP_REPORT_SHORT_LENGTH              (7 - 1)
+#define HIDPP_REPORT_LONG_LENGTH               (20 - 1)
+
+/*
+ * Maximum number of allowed parameters for reports. Note, the parameters 
always
+ * starts at offset 3 for both RAP and FAP reports.
+ */
+#define HIDPP_REPORT_SHORT_PARAMS_MAX          (HIDPP_REPORT_SHORT_LENGTH - 3)
+#define HIDPP_REPORT_LONG_PARAMS_MAX           (HIDPP_REPORT_LONG_LENGTH - 3)
+
+#define HIDPP_DEVICE_ID_RECEIVER               0xff
+
+#define HIDPP_FEAT_ROOT_IDX                    0x00
+#define HIDPP_FEAT_ROOT_PING_FUNC              0x01
+#define HIDPP_FEAT_ROOT_PING_DATA              0x5a
+
+#define HIDPP_SET_REGISTER                     0x80
+#define HIDPP_GET_REGISTER                     0x81
+#define HIDPP_SET_LONG_REGISTER                        0x82
+#define HIDPP_GET_LONG_REGISTER                        0x83
+
+#define HIDPP_REG_ENABLE_REPORTS               0x00
+#define HIDPP_REG_PAIRING_INFORMATION          0xb5
+
+#define HIDPP_NOTIF_DEVICE_BATTERY_STATUS      (1 << 4)
+#define HIDPP_NOTIF_RECEIVER_WIRELESS          (1 << 0)
+#define HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT  (1 << 3)
+
+/* HID++ 1.0 error codes. */
+#define HIDPP_ERROR                            0x8f
+#define HIDPP_ERROR_SUCCESS                    0x00
+#define HIDPP_ERROR_INVALID_SUBID              0x01
+#define HIDPP_ERROR_INVALID_ADRESS             0x02
+#define HIDPP_ERROR_INVALID_VALUE              0x03
+#define HIDPP_ERROR_CONNECT_FAIL               0x04
+#define HIDPP_ERROR_TOO_MANY_DEVICES           0x05
+#define HIDPP_ERROR_ALREADY_EXISTS             0x06
+#define HIDPP_ERROR_BUSY                       0x07
+#define HIDPP_ERROR_UNKNOWN_DEVICE             0x08
+#define HIDPP_ERROR_RESOURCE_ERROR             0x09
+#define HIDPP_ERROR_REQUEST_UNAVAILABLE                0x0a
+#define HIDPP_ERROR_INVALID_PARAM_VALUE                0x0b
+#define HIDPP_ERROR_WRONG_PIN_CODE             0x0c
+
+/*
+ * The software ID is added to feature access reports (FAP) and used to
+ * distinguish responses from notifications. Note, the software ID must be
+ * greater than zero which is reserved for notifications.
+ */
+#define HIDPP_SOFTWARE_ID                      0x01
+#define HIDPP_SOFTWARE_ID_MASK                 0x0f
+#define HIDPP_SOFTWARE_ID_LEN                  4
+
+#define HIDPP20_FEAT_ROOT_IDX                  0x00
+#define HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC     0x00
+
+#define HIDPP20_FEAT_BATTERY_IDX               0x1000
+#define HIDPP20_FEAT_BATTERY_LEVEL_FUNC                0x0000
+#define HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC   0x0001
+
+/* HID++ 2.0 error codes. */
+#define HIDPP20_ERROR                          0xff
+#define HIDPP20_ERROR_NO_ERROR                 0x00
+#define HIDPP20_ERROR_UNKNOWN                  0x01
+#define HIDPP20_ERROR_INVALID_ARGUMENT         0x02
+#define HIDPP20_ERROR_OUT_OF_RANGE             0x03
+#define HIDPP20_ERROR_HARDWARE_ERROR           0x04
+#define HIDPP20_ERROR_LOGITECH_INTERNAL                0x05
+#define HIDPP20_ERROR_INVALID_FEATURE_INDEX    0x06
+#define HIDPP20_ERROR_INVALID_FUNCTION_ID      0x07
+#define HIDPP20_ERROR_BUSY                     0x08
+#define HIDPP20_ERROR_UNSUPPORTED              0x09
+
+/*
+ * Sentinels used for interrupt response synchronization. The values must be
+ * disjoint from existing report IDs.
+ */
+#define UHIDPP_RESP_NONE                       0
+#define UHIDPP_RESP_WAIT                       1
+#define UHIDPP_RESP_ERROR                      2
+
+/* Maximum number of devices associated with a single receiver. */
+#define UHIDPP_NDEVICES                                6
+
+/* Maximum number of pending notifications. */
+#define UHIDPP_NNOTIFICATIONS                  4
+
+/* Number of sensors per paired device. */
+#define UHIDPP_NSENSORS                                2
+
+/* Feature access report used by the HID++ 2.0 (and greater) protocol. */
+struct fap {
+       uint8_t feature_index;
+       uint8_t funcindex_clientid;
+       uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
+};
+
+/*
+ * Register access report used by the HID++ 1.0 protocol. Receivers always uses
+ * this type of report.
+ */
+struct rap {
+       uint8_t sub_id;
+       uint8_t reg_address;
+       uint8_t params[HIDPP_REPORT_LONG_PARAMS_MAX];
+};
+
+struct uhidpp_report {
+       uint8_t device_id;
+       union {
+               struct fap fap;
+               struct rap rap;
+       };
+} __packed;
+
+struct uhidpp_notification {
+       struct uhidpp_report n_rep;
+       unsigned int n_id;
+};
+
+struct uhidpp_device {
+       uint8_t d_id;
+       uint8_t d_paired;
+       uint8_t d_connected;
+       struct {
+               struct ksensor b_sens[UHIDPP_NSENSORS];
+               uint8_t b_feature_idx;
+               uint8_t b_level;
+               uint8_t b_next_level;
+               uint8_t b_status;
+               uint8_t b_nlevels;
+       } d_battery;
+};
+
+/*
+ * Locking:
+ *     [m]     sc_mtx
+ */
+struct uhidpp_softc {
+       struct uhidev sc_hdev;
+       struct usbd_device *sc_udev;
+
+       struct mutex sc_mtx;
+
+       struct uhidpp_device sc_devices[UHIDPP_NDEVICES];
+                                       /* [m] connected devices */
+
+       struct uhidpp_notification sc_notifications[UHIDPP_NNOTIFICATIONS];
+                                       /* [m] pending notifications */
+
+       struct usb_task sc_task;        /* [m] notification task */
+
+       struct ksensordev sc_sensdev;   /* [m] */
+       struct sensor_task *sc_senstsk; /* [m] */
+
+       struct uhidpp_report *sc_resp;  /* [m] synchronous response buffer */
+       u_int sc_resp_state;            /* [m] synchronous response state */
+
+};
+
+int uhidpp_match(struct device *, void *, void *);
+void uhidpp_attach(struct device *, struct device *, void *);
+int uhidpp_detach(struct device *, int flags);
+void uhidpp_intr(struct uhidev *addr, void *ibuf, u_int len);
+void uhidpp_refresh(void *);
+void uhidpp_task(void *);
+int uhidpp_sleep(struct uhidpp_softc *, uint64_t);
+
+void uhidpp_device_pair(struct uhidpp_softc *, struct uhidpp_device *);
+void uhidpp_device_connect(struct uhidpp_softc *, struct uhidpp_device *);
+void uhidpp_device_refresh(struct uhidpp_softc *, struct uhidpp_device *);
+
+struct uhidpp_notification *uhidpp_claim_notification(struct uhidpp_softc *);
+int uhidpp_consume_notification(struct uhidpp_softc *, struct uhidpp_report *);
+int uhidpp_is_notification(struct uhidpp_report *);
+
+int hidpp_get_protocol_version(struct uhidpp_softc  *, uint8_t, int *, int *);
+
+int hidpp10_get_name(struct uhidpp_softc *, uint8_t, char *, size_t);
+int hidpp10_get_serial(struct uhidpp_softc *, uint8_t, uint8_t *, size_t);
+int hidpp10_get_type(struct uhidpp_softc *, uint8_t, const char **);
+int hidpp10_enable_notifications(struct uhidpp_softc *, uint8_t);
+
+int hidpp20_root_get_feature(struct uhidpp_softc *, uint8_t, uint16_t,
+    uint8_t *, uint8_t *);
+int hidpp20_battery_get_level_status(struct uhidpp_softc *, uint8_t, uint8_t,
+    uint8_t *, uint8_t *, uint8_t *);
+int hidpp20_battery_get_capability(struct uhidpp_softc *, uint8_t, uint8_t,
+    uint8_t *);
+
+int hidpp_send_validate(uint8_t, int);
+int hidpp_send_rap_report(struct uhidpp_softc *, uint8_t, uint8_t,
+    uint8_t, uint8_t, uint8_t *, int, struct uhidpp_report *);
+int hidpp_send_fap_report(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t,
+    uint8_t, uint8_t *, int, struct uhidpp_report *);
+int hidpp_send_report(struct uhidpp_softc *, uint8_t, void *,
+    struct uhidpp_report *);
+
+struct cfdriver uhidpp_cd = {
+       NULL, "uhidpp", DV_DULL
+};
+
+const struct cfattach uhidpp_ca = {
+       sizeof(struct uhidpp_softc),
+       uhidpp_match,
+       uhidpp_attach,
+       uhidpp_detach,
+};
+
+static const struct usb_devno uhidpp_devs[] = {
+       { USB_VENDOR_LOGITECH,  USB_PRODUCT_ANY },
+};
+
+int
+uhidpp_match(struct device *parent, void *match, void *aux)
+{
+       struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+       void *desc;
+       int descsiz, siz;
+
+       if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
+               return UMATCH_NONE;
+
+       if (usb_lookup(uhidpp_devs,
+                   uha->uaa->vendor, uha->uaa->product) == NULL)
+               return UMATCH_NONE;
+
+       uhidev_get_report_desc(uha->parent, &desc, &descsiz);
+       siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_SHORT);
+       if (siz != HIDPP_REPORT_SHORT_LENGTH)
+               return UMATCH_NONE;
+       siz = hid_report_size(desc, descsiz, hid_output, HIDPP_REPORT_ID_LONG);
+       if (siz != HIDPP_REPORT_LONG_LENGTH)
+               return UMATCH_NONE;
+
+       return UMATCH_VENDOR_PRODUCT;
+}
+
+void
+uhidpp_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
+       struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+       struct usb_attach_arg *uaa = uha->uaa;
+       int error, i;
+       int npaired = 0;
+
+       sc->sc_hdev.sc_intr = uhidpp_intr;
+       sc->sc_hdev.sc_udev = uaa->device;
+       sc->sc_hdev.sc_parent = uha->parent;
+       sc->sc_hdev.sc_report_id = uha->reportid;
+       /* The largest supported report dictates the sizes. */
+       sc->sc_hdev.sc_isize = HIDPP_REPORT_LONG_LENGTH;
+       sc->sc_hdev.sc_osize = HIDPP_REPORT_LONG_LENGTH;
+
+       sc->sc_udev = uaa->device;
+
+       mtx_init(&sc->sc_mtx, IPL_USB);
+
+       sc->sc_resp = NULL;
+       sc->sc_resp_state = UHIDPP_RESP_NONE;
+
+       error = uhidev_open(&sc->sc_hdev);
+       if (error) {
+               printf(" error %d\n", error);
+               return;
+       }
+
+       usb_init_task(&sc->sc_task, uhidpp_task, sc, USB_TASK_TYPE_GENERIC);
+
+       mtx_enter(&sc->sc_mtx);
+
+       /*
+        * Wire up interrupt handlers before issuing commands to the device in
+        * order to receive responses. Necessary as uhidev by default performs
+        * the wiring after the attach routine has returned.
+        */
+       uhidev_set_intr(sc->sc_hdev.sc_parent, &sc->sc_hdev,
+           HIDPP_REPORT_ID_SHORT);
+       uhidev_set_intr(sc->sc_hdev.sc_parent, &sc->sc_hdev,
+           HIDPP_REPORT_ID_LONG);
+
+       /* Probe paired devices. */
+       for (i = 0; i < UHIDPP_NDEVICES; i++) {
+               char name[16];
+               uint8_t serial[4];
+               struct uhidpp_device *dev = &sc->sc_devices[i];
+               const char *type;
+               uint8_t device_id = device_id + 1;
+
+               dev->d_id = device_id;
+
+               if (hidpp10_get_serial(sc, device_id, serial, sizeof(serial)) ||
+                   hidpp10_get_type(sc, device_id, &type) ||
+                   hidpp10_get_name(sc, device_id, name, sizeof(name)))
+                       continue;
+
+               uhidpp_device_pair(sc, dev);
+
+               if (npaired > 0)
+                       printf(",");
+               printf(" device %d", device_id);
+               printf(" %s", type);
+               printf(" \"%s\"", name);
+               printf(" serial %02x-%02x-%02x-%02x",
+                   serial[0], serial[1], serial[2], serial[3]);
+               npaired++;
+       }
+
+       /* Enable notifications for the receiver. */
+       error = hidpp10_enable_notifications(sc, HIDPP_DEVICE_ID_RECEIVER);
+       if (error)
+               printf(" error %d", error);
+
+       printf("\n");
+
+       strlcpy(sc->sc_sensdev.xname, sc->sc_hdev.sc_dev.dv_xname,
+           sizeof(sc->sc_sensdev.xname));
+       sensordev_install(&sc->sc_sensdev);
+       sc->sc_senstsk = sensor_task_register(sc, uhidpp_refresh, 6);
+
+       mtx_leave(&sc->sc_mtx);
+}
+
+int
+uhidpp_detach(struct device *self, int flags)
+{
+       struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
+       int i, j;
+
+       usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
+
+       if (sc->sc_senstsk != NULL)
+               sensor_task_unregister(sc->sc_senstsk);
+
+       KASSERT(sc->sc_resp_state == UHIDPP_RESP_NONE);
+
+       sensordev_deinstall(&sc->sc_sensdev);
+
+       for (i = 0; i < UHIDPP_NDEVICES; i++) {
+               struct uhidpp_device *dev = &sc->sc_devices[i];
+
+               if (!dev->d_paired)
+                       continue;
+
+               for (j = 0; j < UHIDPP_NSENSORS; j++)
+                       sensor_detach(&sc->sc_sensdev, 
&dev->d_battery.b_sens[j]);
+       }
+
+       uhidev_close(&sc->sc_hdev);
+
+       return 0;
+}
+
+void
+uhidpp_intr(struct uhidev *addr, void *buf, u_int len)
+{
+       struct uhidpp_softc *sc = (struct uhidpp_softc *)addr;
+       struct uhidpp_report *rep = buf;
+       int dowake = 0;
+       uint8_t repid;
+
+       /*
+        * Ugliness ahead as the report ID is stripped of by uhidev_intr() but
+        * needed to determine if an error occurred.
+        * Note that an error response is always a short report even if the
+        * command that caused the error is a long report.
+        */
+       repid = ((uint8_t *)buf)[-1];
+
+       DREPORT(__func__, repid, buf, len);
+
+       mtx_enter(&sc->sc_mtx);
+       if (uhidpp_is_notification(rep)) {
+               struct uhidpp_notification *ntf;
+
+               ntf = uhidpp_claim_notification(sc);
+               if (ntf != NULL) {
+                       memcpy(&ntf->n_rep, buf, len);
+                       usb_add_task(sc->sc_udev, &sc->sc_task);
+               } else {
+                       DPRINTF("%s: too many notifications", __func__);
+               }
+       } else {
+               KASSERT(sc->sc_resp_state == UHIDPP_RESP_WAIT);
+               dowake = 1;
+               sc->sc_resp_state = repid;
+               memcpy(sc->sc_resp, buf, len);
+       }
+       mtx_leave(&sc->sc_mtx);
+       if (dowake)
+               wakeup(sc);
+}
+
+void
+uhidpp_refresh(void *arg)
+{
+       struct uhidpp_softc *sc = arg;
+       int i;
+
+       mtx_enter(&sc->sc_mtx);
+       for (i = 0; i < UHIDPP_NDEVICES; i++) {
+               struct uhidpp_device *dev = &sc->sc_devices[i];
+
+               if (dev->d_connected)
+                       uhidpp_device_refresh(sc, dev);
+       }
+       mtx_leave(&sc->sc_mtx);
+}
+
+void
+uhidpp_task(void *arg)
+{
+       struct uhidpp_softc *sc = arg;
+
+       mtx_enter(&sc->sc_mtx);
+       for (;;) {
+               struct uhidpp_report rep;
+               struct uhidpp_device *dev;
+
+               if (uhidpp_consume_notification(sc, &rep))
+                       break;
+
+               DPRINTF("%s: device_id=%d, sub_id=%02x\n",
+                   __func__, rep.device_id, rep.rap.sub_id);
+
+               if (rep.device_id == 0 || rep.device_id > UHIDPP_NDEVICES) {
+                       DPRINTF("%s: invalid device\n", __func__);
+                       continue;
+               }
+               dev = &sc->sc_devices[rep.device_id - 1];
+
+               switch (rep.rap.sub_id) {
+               case 0x0e:      /* leds */
+               case 0x40:      /* disconnect */
+                       break;
+               case 0x41:      /* connect */
+                       /*
+                        * Do nothing if the link is reported to be out of
+                        * range. This happens when a device has been idle for a
+                        * while.
+                        */
+                       if (HIDPP_LINK_STATUS(rep.rap.params[0]))
+                               uhidpp_device_connect(sc, dev);
+                       break;
+               }
+       }
+       mtx_leave(&sc->sc_mtx);
+}
+
+int
+uhidpp_sleep(struct uhidpp_softc *sc, uint64_t nsecs)
+{
+       return msleep_nsec(sc, &sc->sc_mtx, PZERO, "uhidpp", nsecs);
+}
+
+void
+uhidpp_device_pair(struct uhidpp_softc *sc, struct uhidpp_device *dev)
+{
+       struct ksensor *sens;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       sens = &dev->d_battery.b_sens[0];
+       strlcpy(sens->desc, "battery level", sizeof(sens->desc));
+       sens->type = SENSOR_PERCENT;
+       sens->flags = SENSOR_FUNKNOWN;
+       sensor_attach(&sc->sc_sensdev, sens);
+
+       sens = &dev->d_battery.b_sens[1];
+       strlcpy(sens->desc, "battery levels", sizeof(sens->desc));
+       sens->type = SENSOR_INTEGER;
+       sens->flags = SENSOR_FUNKNOWN;
+       sensor_attach(&sc->sc_sensdev, sens);
+
+       dev->d_paired = 1;
+}
+
+void
+uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
+{
+       int error, major, minor;
+       uint8_t feature_type;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       /* A connected device will continously send connect events. */
+       if (dev->d_connected)
+               return;
+
+       error = hidpp_get_protocol_version(sc, dev->d_id, &major, &minor);
+       if (error) {
+               DPRINTF("%s: protocol version failure: device_id=%d, 
error=%d\n",
+                   __func__, dev->d_id, error);
+               return;
+       }
+
+       DPRINTF("%s: device_id=%d, version=%d.%d\n",
+           __func__, dev->d_id, major, minor);
+
+       error = hidpp20_root_get_feature(sc, dev->d_id,
+           HIDPP20_FEAT_BATTERY_IDX,
+           &dev->d_battery.b_feature_idx, &feature_type);
+       if (error) {
+               DPRINTF("%s: battery feature index failure: device_id=%d, "
+                   "error=%d\n", __func__, dev->d_id, error);
+               return;
+       }
+
+       error = hidpp20_battery_get_capability(sc, dev->d_id,
+           dev->d_battery.b_feature_idx, &dev->d_battery.b_nlevels);
+       if (error) {
+               DPRINTF("%s: battery capability failure: device_id=%d, "
+                   "error=%d\n", __func__, dev->d_id, error);
+               return;
+       }
+       dev->d_battery.b_sens[1].value = dev->d_battery.b_nlevels;
+       dev->d_battery.b_sens[1].flags &= ~SENSOR_FUNKNOWN;
+
+       dev->d_connected = 1;
+       uhidpp_device_refresh(sc, dev);
+}
+
+void
+uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
+{
+       int error;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       error = hidpp20_battery_get_level_status(sc, dev->d_id,
+           dev->d_battery.b_feature_idx,
+           &dev->d_battery.b_level, &dev->d_battery.b_next_level,
+           &dev->d_battery.b_status);
+       if (error) {
+               DPRINTF("%s: battery level status failure: device_id=%d, "
+                   "error=%d\n", __func__, dev->d_id, error);
+               return;
+       }
+
+       dev->d_battery.b_sens[0].value = dev->d_battery.b_level * 1000;
+       dev->d_battery.b_sens[0].flags &= ~SENSOR_FUNKNOWN;
+       if (dev->d_battery.b_nlevels < 10) {
+               /*
+                * According to the HID++ 2.0 specification, less than 10 levels
+                * should be mapped to the following 4 levels:
+                *
+                * [0, 10]   critical
+                * [11, 30]  low
+                * [31, 80]  good
+                * [81, 100] full
+                *
+                * Since sensors are limited to 3 valid statuses, clamp it even
+                * further.
+                */
+               if (dev->d_battery.b_level <= 10)
+                       dev->d_battery.b_sens[0].status = SENSOR_S_CRIT;
+               else if (dev->d_battery.b_level <= 30)
+                       dev->d_battery.b_sens[0].status = SENSOR_S_WARN;
+               else
+                       dev->d_battery.b_sens[0].status = SENSOR_S_OK;
+       } else {
+               /*
+                * XXX the device supports battery mileage. The current level
+                * must be checked against resp.fap.params[3] given by
+                * hidpp20_battery_get_capability().
+                */
+               dev->d_battery.b_sens[0].status = SENSOR_S_UNKNOWN;
+       }
+}
+
+/*
+ * Returns the next available notification slot, if available.
+ */
+struct uhidpp_notification *
+uhidpp_claim_notification(struct uhidpp_softc *sc)
+{
+       struct uhidpp_notification *ntf = NULL;
+       int nclaimed = 0;
+       int i;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
+               struct uhidpp_notification *tmp = &sc->sc_notifications[i];
+
+               if (tmp->n_id > 0)
+                       nclaimed++;
+               else if (ntf == NULL)
+                       ntf = tmp;
+       }
+
+       if (ntf == NULL)
+               return NULL;
+       ntf->n_id = nclaimed + 1;
+       return ntf;
+}
+
+/*
+ * Consume the first unhandled notification, if present.
+ */
+int
+uhidpp_consume_notification(struct uhidpp_softc *sc, struct uhidpp_report *rep)
+{
+       struct uhidpp_notification *ntf = NULL;
+       int i;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       for (i = 0; i < UHIDPP_NNOTIFICATIONS; i++) {
+               struct uhidpp_notification *tmp = &sc->sc_notifications[i];
+
+               if (tmp->n_id > 0 && (ntf == NULL || tmp->n_id < ntf->n_id))
+                       ntf = tmp;
+       }
+       if (ntf == NULL)
+               return 1;
+
+       memcpy(rep, &ntf->n_rep, sizeof(*rep));
+       ntf->n_id = 0;
+       return 0;
+}
+
+
+int
+uhidpp_is_notification(struct uhidpp_report *rep)
+{
+       uint8_t swid;
+
+       /* HID++ 1.0 response. */
+       if (rep->rap.sub_id > 0x7f)
+               return 0;
+
+       /* HID++ 2.0 response if the software ID is ours. */
+       swid = rep->fap.funcindex_clientid & HIDPP_SOFTWARE_ID_MASK;
+       if (swid == HIDPP_SOFTWARE_ID)
+               return 0;
+
+       return 1;
+}
+
+int
+hidpp_get_protocol_version(struct uhidpp_softc  *sc, uint8_t device_id,
+    int *major, int *minor)
+{
+       struct uhidpp_report resp;
+       uint8_t params[3] = { 0, 0, HIDPP_FEAT_ROOT_PING_DATA };
+       int error;
+
+       error = hidpp_send_fap_report(sc,
+           HIDPP_REPORT_ID_SHORT,
+           device_id,
+           HIDPP_FEAT_ROOT_IDX,
+           HIDPP_FEAT_ROOT_PING_FUNC,
+           params, sizeof(params), &resp);
+       if (error == HIDPP_ERROR_INVALID_SUBID) {
+               *major = 1;
+               *minor = 0;
+               return 0;
+       }
+       if (error)
+               return error;
+       if (resp.rap.params[2] != HIDPP_FEAT_ROOT_PING_DATA)
+               return -EPROTO;
+
+       *major = resp.fap.params[0];
+       *minor = resp.fap.params[1];
+       return 0;
+}
+
+int
+hidpp10_get_name(struct uhidpp_softc *sc, uint8_t device_id,
+    char *buf, size_t bufsiz)
+{
+       struct uhidpp_report resp;
+       int error;
+       uint8_t params[1] = { 0x40 + (device_id - 1) };
+       uint8_t len;
+
+       error = hidpp_send_rap_report(sc,
+           HIDPP_REPORT_ID_SHORT,
+           HIDPP_DEVICE_ID_RECEIVER,
+           HIDPP_GET_LONG_REGISTER,
+           HIDPP_REG_PAIRING_INFORMATION,
+           params, sizeof(params), &resp);
+       if (error)
+               return error;
+
+       len = resp.rap.params[1];
+       if (len + 2 > sizeof(resp.rap.params))
+               return -ENAMETOOLONG;
+       if (len > bufsiz - 1)
+               len = bufsiz - 1;
+       memcpy(buf, &resp.rap.params[2], len);
+       buf[len] = '\0';
+       return 0;
+}
+
+int
+hidpp10_get_serial(struct uhidpp_softc *sc, uint8_t device_id,
+    uint8_t *buf, size_t bufsiz)
+{
+       struct uhidpp_report resp;
+       int error;
+       uint8_t params[1] = { 0x30 + (device_id - 1) };
+       uint8_t len;
+
+       error = hidpp_send_rap_report(sc,
+           HIDPP_REPORT_ID_SHORT,
+           HIDPP_DEVICE_ID_RECEIVER,
+           HIDPP_GET_LONG_REGISTER,
+           HIDPP_REG_PAIRING_INFORMATION,
+           params, sizeof(params), &resp);
+       if (error)
+               return error;
+
+       len = 4;
+       if (bufsiz < len)
+               len = bufsiz;
+       memcpy(buf, &resp.rap.params[1], len);
+       return 0;
+}
+
+int
+hidpp10_get_type(struct uhidpp_softc *sc, uint8_t device_id, const char **type)
+{
+       struct uhidpp_report resp;
+       int error;
+       uint8_t params[1] = { 0x20 + (device_id - 1) };
+
+       error = hidpp_send_rap_report(sc,
+           HIDPP_REPORT_ID_SHORT,
+           HIDPP_DEVICE_ID_RECEIVER,
+           HIDPP_GET_LONG_REGISTER,
+           HIDPP_REG_PAIRING_INFORMATION,
+           params, sizeof(params), &resp);
+       if (error)
+               return error;
+
+       switch (resp.rap.params[7]) {
+       case 0x00:
+               *type = "unknown";
+               return 0;
+       case 0x01:
+               *type = "keyboard";
+               return 0;
+       case 0x02:
+               *type = "mouse";
+               return 0;
+       case 0x03:
+               *type = "numpad";
+               return 0;
+       case 0x04:
+               *type = "presenter";
+               return 0;
+       case 0x08:
+               *type = "trackball";
+               return 0;
+       case 0x09:
+               *type = "touchpad";
+               return 0;
+       }
+       return -ENOENT;
+}
+
+int
+hidpp10_enable_notifications(struct uhidpp_softc *sc, uint8_t device_id)
+{
+       struct uhidpp_report resp;
+       uint8_t params[3];
+
+       /* Device reporting flags. */
+       params[0] = HIDPP_NOTIF_DEVICE_BATTERY_STATUS;
+       /* Receiver reporting flags. */
+       params[1] = HIDPP_NOTIF_RECEIVER_WIRELESS |
+           HIDPP_NOTIF_RECEIVER_SOFTWARE_PRESENT;
+       /* Device reporting flags (continued). */
+       params[2] = 0;
+
+       return hidpp_send_rap_report(sc,
+           HIDPP_REPORT_ID_SHORT,
+           device_id,
+           HIDPP_SET_REGISTER,
+           HIDPP_REG_ENABLE_REPORTS,
+           params, sizeof(params), &resp);
+}
+
+int
+hidpp20_root_get_feature(struct uhidpp_softc *sc, uint8_t device_id,
+    uint16_t feature, uint8_t *feature_index, uint8_t *feature_type)
+{
+       struct uhidpp_report resp;
+       uint8_t params[2] = { feature >> 8, feature & 0xff };
+       int error;
+
+       error = hidpp_send_fap_report(sc,
+           HIDPP_REPORT_ID_LONG,
+           device_id,
+           HIDPP20_FEAT_ROOT_IDX,
+           HIDPP20_FEAT_ROOT_GET_FEATURE_FUNC,
+           params, sizeof(params), &resp);
+       if (error)
+               return error;
+
+       if (resp.fap.params[0] == 0)
+               return -ENOENT;
+
+       *feature_index = resp.fap.params[0];
+       *feature_type = resp.fap.params[1];
+       return 0;
+}
+
+int
+hidpp20_battery_get_level_status(struct uhidpp_softc *sc, uint8_t device_id,
+    uint8_t feature_index, uint8_t *level, uint8_t *next_level, uint8_t 
*status)
+{
+       struct uhidpp_report resp;
+       int error;
+
+       error = hidpp_send_fap_report(sc,
+           HIDPP_REPORT_ID_LONG,
+           device_id,
+           feature_index,
+           HIDPP20_FEAT_BATTERY_LEVEL_FUNC,
+           NULL, 0, &resp);
+       if (error)
+               return error;
+
+       *level = resp.fap.params[0];
+       *next_level = resp.fap.params[1];
+       *status = resp.fap.params[2];
+       return 0;
+}
+
+int
+hidpp20_battery_get_capability(struct uhidpp_softc *sc, uint8_t device_id,
+    uint8_t feature_index, uint8_t *nlevels)
+{
+       struct uhidpp_report resp;
+       int error;
+
+       error = hidpp_send_fap_report(sc,
+           HIDPP_REPORT_ID_LONG,
+           device_id,
+           feature_index,
+           HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC,
+           NULL, 0, &resp);
+       if (error)
+               return error;
+       *nlevels = resp.fap.params[0];
+       return 0;
+}
+
+int
+hidpp_send_validate(uint8_t report_id, int nparams)
+{
+       if (report_id == HIDPP_REPORT_ID_SHORT) {
+               if (nparams > HIDPP_REPORT_SHORT_PARAMS_MAX)
+                       return -EMSGSIZE;
+       } else if (report_id == HIDPP_REPORT_ID_LONG) {
+               if (nparams > HIDPP_REPORT_LONG_PARAMS_MAX)
+                       return -EMSGSIZE;
+       } else {
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int
+hidpp_send_fap_report(struct uhidpp_softc *sc, uint8_t report_id,
+    uint8_t device_id, uint8_t feature_index, uint8_t funcindex_clientid,
+    uint8_t *params, int nparams, struct uhidpp_report *resp)
+{
+       struct uhidpp_report req;
+       int error;
+
+       error = hidpp_send_validate(report_id, nparams);
+       if (error)
+               return error;
+
+       memset(&req, 0, sizeof(req));
+       req.device_id = device_id;
+       req.fap.feature_index = feature_index;
+       req.fap.funcindex_clientid =
+           (funcindex_clientid << HIDPP_SOFTWARE_ID_LEN) | HIDPP_SOFTWARE_ID;
+       memcpy(req.fap.params, params, nparams);
+       return hidpp_send_report(sc, report_id, &req, resp);
+}
+
+int
+hidpp_send_rap_report(struct uhidpp_softc *sc, uint8_t report_id,
+    uint8_t device_id, uint8_t sub_id, uint8_t reg_address,
+    uint8_t *params, int nparams, struct uhidpp_report *resp)
+{
+       struct uhidpp_report req;
+       int error;
+
+       error = hidpp_send_validate(report_id, nparams);
+       if (error)
+               return error;
+
+       memset(&req, 0, sizeof(req));
+       req.device_id = device_id;
+       req.rap.sub_id = sub_id;
+       req.rap.reg_address = reg_address;
+       memcpy(req.rap.params, params, nparams);
+       return hidpp_send_report(sc, report_id, &req, resp);
+}
+
+int
+hidpp_send_report(struct uhidpp_softc *sc, uint8_t report_id, void *data,
+    struct uhidpp_report *resp)
+{
+       int error, len, n;
+
+       MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
+
+       if (report_id == HIDPP_REPORT_ID_SHORT)
+               len = HIDPP_REPORT_SHORT_LENGTH;
+       else if (report_id == HIDPP_REPORT_ID_LONG)
+               len = HIDPP_REPORT_LONG_LENGTH;
+       else
+               return -EINVAL;
+
+       DREPORT(__func__, report_id, data, len);
+
+       /* Wait until any ongoing command has completed. */
+       while (sc->sc_resp_state != UHIDPP_RESP_NONE)
+               uhidpp_sleep(sc, INFSLP);
+       sc->sc_resp = resp;
+       sc->sc_resp_state = UHIDPP_RESP_WAIT;
+       /*
+        * The mutex must be temporarily released while calling
+        * uhidev_set_report() as it might end up sleeping.
+        */
+       mtx_leave(&sc->sc_mtx);
+
+       n = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
+           report_id, data, len);
+
+       mtx_enter(&sc->sc_mtx);
+       if (len != n) {
+               error = -EBUSY;
+               goto out;
+       }
+       /*
+        * The interrupt could already have been received while the mutex was
+        * released. Otherwise, wait for it.
+        */
+       if (sc->sc_resp_state == UHIDPP_RESP_WAIT) {
+               /* Timeout taken from the hid-logitech-hidpp Linux driver. */
+               error = uhidpp_sleep(sc, SEC_TO_NSEC(5));
+               if (error) {
+                       error = -error;
+                       goto out;
+               }
+       }
+
+       if (sc->sc_resp_state == UHIDPP_RESP_ERROR)
+               error = -EIO;
+       else if (sc->sc_resp_state == HIDPP_REPORT_ID_SHORT &&
+           resp->rap.sub_id == HIDPP_ERROR)
+               error = resp->rap.params[1];
+       else if (sc->sc_resp_state == HIDPP_REPORT_ID_LONG &&
+           resp->fap.feature_index == HIDPP20_ERROR)
+               error = resp->fap.params[1];
+
+out:
+       sc->sc_resp = NULL;
+       sc->sc_resp_state = UHIDPP_RESP_NONE;
+       wakeup(sc);
+       return error;
+}
+
+#ifdef UHIDPP_DEBUG
+
+void
+uhidd_dump_report(const char *prefix, uint8_t repid, const unsigned char *buf,
+    u_int buflen)
+{
+       u_int i;
+
+       printf("%s: %02x ", prefix, repid);
+       for (i = 0; i < buflen; i++) {
+               printf("%02x%s", buf[i],
+                   i == 2 ? " [" : (i + 1 < buflen ? " " : ""));
+       }
+       printf("]\n");
+}
+
+#endif

Reply via email to