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 :) -- Kind regards, Ville On Fri, 29 Jan 2021 at 09:21, Anton Lindqvist <[email protected]> wrote: > > Ping > > On Fri, Jan 22, 2021 at 08:18:51AM +0100, Anton Lindqvist wrote: > > Hi, > > Here's a new driver for Logitech HID++ devices, currently limited to > > exposing battery sensors. Here's an example using a Logitech M330 mouse: > > > > $ dmesg | grep uhidpp > > uhidpp0 at uhidev1 device 1 mouse "B330/M330/M3" serial c7-2f-a8-33 > > $ sysctl hw.sensors.uhidpp0 > > hw.sensors.uhidpp0.raw0=2 (battery levels) > > hw.sensors.uhidpp0.percent0=70.00% (battery level), OK > > > > The raw0 sensor reflects number of available battery levels, the > > resolution on this device is not great... > > > > Most of the code is derived from the hid-logitech-hidpp Linux driver. > > Some assorted notes: > > > > * In order to communicate with the device inside the attach routine, I > > had to wire up the interrupt handler as this by default is done first > > once the same attach routine has returned. Hence the introduction of > > uhidev_set_intr(). If this is an acceptable approach, this can go in > > as a separate commit. > > > > * I kept using the `return -errno' convention from the Linux driver in > > order to distingush errors from the hardware, which are always > > positive. > > > > I you happen to have a Logitech HID++ device and run into any > > problem(s), please enable UHIDPP_DEBUG and send me the output. > > > > Comments? OK? > > > > diff --git share/man/man4/Makefile share/man/man4/Makefile > > index 02af7a47a44..74a4e17d7dc 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 \ > > - uk.4 ukbd.4 \ > > + uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uhidpp.4 \ > > + uipaq.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 f0a6776a27b..d264935a65c 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 "ukbd* at uhidev?" > > .Cd "ums* at uhidev?" > > .Cd "umstc* at uhidev?" > > @@ -73,6 +74,7 @@ only dispatches data to them based on the report id. > > .Xr ucycom 4 , > > .Xr ugold 4 , > > .Xr uhid 4 , > > +.Xr uhidpp 4 , > > .Xr ukbd 4 , > > .Xr ums 4 , > > .Xr umstc 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 <[email protected]> > > +.\" > > +.\" 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 [email protected] . > > diff --git share/man/man4/usb.4 share/man/man4/usb.4 > > index 520f46265e0..b58190539e3 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 ukbd 4 > > USB keyboards that follow the boot protocol > > .It Xr ums 4 > > diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC > > index 45b3a9b6e66..00ac52adcd6 100644 > > --- sys/arch/amd64/conf/GENERIC > > +++ sys/arch/amd64/conf/GENERIC > > @@ -288,6 +288,7 @@ uhid* at uhidev? # USB generic HID > > support > > fido* at uhidev? # FIDO/U2F security key 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 1d673cf635d..5c95d1c0ac5 100644 > > --- sys/dev/usb/files.usb > > +++ sys/dev/usb/files.usb > > @@ -478,3 +478,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 5f2bc07cc96..68212d50471 100644 > > --- sys/dev/usb/uhidev.c > > +++ sys/dev/usb/uhidev.c > > @@ -255,8 +255,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; > > } > > > > @@ -269,7 +272,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; > > } > > } > > > > @@ -950,3 +955,16 @@ uhidev_ioctl(struct uhidev *sc, u_long cmd, caddr_t > > addr, int flag, > > } > > return 0; > > } > > + > > +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 <[email protected]> > > + * > > + * 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 >
