On Sat, Nov 26, 2022 at 07:46:08AM +0100, Anton Lindqvist wrote: > Hi, > This diff adds supports for the following to uhidpp: > > 1. Bolt receivers, they use a different set of registers for querying > paired devices. > > 2. The Unified Battery feature, this is a more competent feature > function used to report battery status compared to the Battery > feature which is already supported by uhidpp. > > Makes battery sensors appear for my Logitech Lift mouse and Paul de > Weerd's MX Anywhere 3 mouse. > > If you have a Logitech HID++ device currently lacking sensors under > sysctl hw.sensors, please give this diff a try. Send me your dmesg and > output of sysctl hw.sensors after moving the mouse to ensure a connect > interrupt has fired off. Note that the diff must be applied on top of > revision 1.37 of sys/dev/usb/uhidpp.c.
No idea what Bolt or Logitech HID++ is, but my MX Ergo doesn't report any new sensors with this diff. Should it do so? The trackball keeps working, though. Here's the dmesg from when I attach the USB receiver with your diff: uhidev0 at uhub3 port 2 configuration 1 interface 0 "Logitech USB Receiver" rev 2.00/12.03 addr 3 uhidev0: iclass 3/1 ukbd0 at uhidev0: 8 variable keys, 6 key codes wskbd1 at ukbd0 mux 1 wskbd1: connecting to wsdisplay0 uhidev1 at uhub3 port 2 configuration 1 interface 1 "Logitech USB Receiver" rev 2.00/12.03 addr 3 uhidev1: iclass 3/1, 8 report ids ums0 at uhidev1 reportid 2: 16 buttons, Z and W dir wsmouse2 at ums0 mux 0 ucc0 at uhidev1 reportid 3: 767 usages, 20 keys, array wskbd2 at ucc0 mux 1 wskbd2: connecting to wsdisplay0 uhid0 at uhidev1 reportid 4: input=1, output=0, feature=0 uhid1 at uhidev1 reportid 8: input=1, output=0, feature=0 uhidev2 at uhub3 port 2 configuration 1 interface 2 "Logitech USB Receiver" rev 2.00/12.03 addr 3 uhidev2: iclass 3/0, 33 report ids uhidpp0 at uhidev2hidpp_send_report: 10 ff 83 b5 [20 00 00] uhidpp_intr: 11 ff 83 b5 [20 0b 08 40 6f 04 02 08 07 00 00 00 00 00 00 00] hidpp_send_report: 10 ff 83 b5 [40 00 00] uhidpp_intr: 11 ff 83 b5 [40 07 4d 58 20 45 72 67 6f 00 00 00 00 00 00 00] device 1 trackball "MX Ergo"hidpp_send_report: 10 ff 83 b5 [21 00 00] uhidpp_intr: 10 ff 8f 83 [b5 03 00] hidpp_send_report: 10 ff 83 b5 [22 00 00] uhidpp_intr: 10 ff 8f 83 [b5 03 00] hidpp_send_report: 10 ff 83 b5 [23 00 00] uhidpp_intr: 10 ff 8f 83 [b5 03 00] hidpp_send_report: 10 ff 83 b5 [24 00 00] uhidpp_intr: 10 ff 8f 83 [b5 03 00] hidpp_send_report: 10 ff 83 b5 [25 00 00] uhidpp_intr: 10 ff 8f 83 [b5 03 00] hidpp_send_report: 10 ff 80 00 [10 09 00] uhidpp_intr: 10 ff 80 00 [00 00 00] uhidpp_intr: 10 ff 80 00 [00 00 00] uhid2 at uhidev2 reportid 32: input=14, output=14, feature=0 uhid3 at uhidev2 reportid 33: input=31, output=31, feature=0 > > Comments? OK? > > diff --git sys/dev/usb/uhidpp.c sys/dev/usb/uhidpp.c > index 83e58d56fc9..5b9ef198948 100644 > --- sys/dev/usb/uhidpp.c > +++ sys/dev/usb/uhidpp.c > @@ -29,7 +29,7 @@ > #include <dev/usb/usbdevs.h> > #include <dev/usb/uhidev.h> > > -/* #define UHIDPP_DEBUG */ > +#define UHIDPP_DEBUG > #ifdef UHIDPP_DEBUG > > #define DPRINTF(x...) do { \ > @@ -125,6 +125,15 @@ int uhidpp_debug = 1; > #define HIDPP20_FEAT_BATTERY_CAPABILITY_FUNC 0x0001 > #define HIDPP20_CAPABILITY_RECHARGEABLE 0x0004 > > +#define HIDPP20_FEAT_UNIFIED_BATTERY_ID 0x1004 > +#define HIDPP20_FEAT_UNIFIED_BATTERY_CAPABILITIES_FUNC 0x0000 > +#define HIDPP20_CAPABILITES_RECHARGEABLE 0x0002 > +#define HIDPP20_FEAT_UNIFIED_BATTERY_STATUS_FUNC 0x0001 > +#define HIDPP20_BATTERY_STATUS_CRITICAL 0x0001 > +#define HIDPP20_BATTERY_STATUS_LOW 0x0002 > +#define HIDPP20_BATTERY_STATUS_GOOD 0x0004 > +#define HIDPP20_BATTERY_STATUS_FULL 0x0008 > + > /* HID++ 2.0 error codes. */ > #define HIDPP20_ERROR 0xff > #define HIDPP20_ERROR_NO_ERROR 0x00 > @@ -193,17 +202,20 @@ struct uhidpp_device { > uint8_t d_features; > #define UHIDPP_DEVICE_FEATURE_ROOT 0x01 > #define UHIDPP_DEVICE_FEATURE_BATTERY 0x02 > +#define UHIDPP_DEVICE_FEATURE_UNIFIIED_BATTERY 0x04 > > struct { > struct ksensor sens[UHIDPP_NSENSORS]; > uint8_t feature_idx; > uint8_t nlevels; > + uint8_t unified_level_mask; > uint8_t rechargeable; > } d_battery; > }; > > /* > * Locking: > + * [I] immutable > * [m] sc_mtx > */ > struct uhidpp_softc { > @@ -226,6 +238,11 @@ struct uhidpp_softc { > struct uhidpp_report *sc_req; /* [m] synchronous request buffer */ > struct uhidpp_report *sc_resp; /* [m] synchronous response buffer */ > u_int sc_resp_state; /* [m] synchronous response state */ > + > + enum { > + UHIDPP_RECEIVER_UNIFYING, > + UHIDPP_RECEIVER_BOLT, > + } sc_receiver; /* [I] */ > }; > > int uhidpp_match(struct device *, void *, void *); > @@ -264,6 +281,11 @@ int hidpp20_battery_get_level_status(struct uhidpp_softc > *, > int hidpp20_battery_get_capability(struct uhidpp_softc *, > struct uhidpp_device *); > int hidpp20_battery_status_is_charging(uint8_t); > +int hidpp20_unified_battery_get_capabilities(struct uhidpp_softc *, > + struct uhidpp_device *); > +int hidpp20_unified_battery_get_status(struct uhidpp_softc *, > + struct uhidpp_device *); > +int hidpp20_unified_battery_status_is_charging(uint8_t); > > int hidpp_send_validate(uint8_t, int); > int hidpp_send_rap_report(struct uhidpp_softc *, uint8_t, uint8_t, uint8_t, > @@ -273,6 +295,18 @@ int hidpp_send_fap_report(struct uhidpp_softc *, > uint8_t, uint8_t, uint8_t, > int hidpp_send_report(struct uhidpp_softc *, uint8_t, struct uhidpp_report *, > struct uhidpp_report *); > > +static uint8_t > +nlevels(uint8_t mask) > +{ > + uint8_t nbits = 0; > + > + for (; mask > 0; mask >>= 1) { > + if (mask & 1) > + nbits++; > + } > + return nbits; > +} > + > struct cfdriver uhidpp_cd = { > NULL, "uhidpp", DV_DULL > }; > @@ -369,6 +403,11 @@ uhidpp_attach(struct device *parent, struct device > *self, void *aux) > return; > } > > + if (uaa->product == 0xc548) > + sc->sc_receiver = UHIDPP_RECEIVER_BOLT; > + else > + sc->sc_receiver = UHIDPP_RECEIVER_UNIFYING; > + > /* Probe paired devices. */ > for (i = 0; i < UHIDPP_NDEVICES; i++) { > char name[16]; > @@ -573,7 +612,10 @@ uhidpp_device_connect(struct uhidpp_softc *sc, struct > uhidpp_device *dev) > return; > } > > - error = hidpp20_battery_get_capability(sc, dev); > + if (dev->d_features & UHIDPP_DEVICE_FEATURE_BATTERY) > + error = hidpp20_battery_get_capability(sc, dev); > + else if (dev->d_features & UHIDPP_DEVICE_FEATURE_UNIFIIED_BATTERY) > + error = hidpp20_unified_battery_get_capabilities(sc, dev); > if (error) { > DPRINTF("%s: battery capability failure: device_id=%d, " > "error=%d\n", __func__, dev->d_id, error); > @@ -635,7 +677,12 @@ uhidpp_device_refresh(struct uhidpp_softc *sc, struct > uhidpp_device *dev) > if (dev->d_major <= 1) > return; > > - error = hidpp20_battery_get_level_status(sc, dev); > + if (dev->d_features & UHIDPP_DEVICE_FEATURE_BATTERY) > + error = hidpp20_battery_get_level_status(sc, dev); > + else if (dev->d_features & UHIDPP_DEVICE_FEATURE_UNIFIIED_BATTERY) > + error = hidpp20_unified_battery_get_status(sc, dev); > + else > + error = -ENOTSUP; > if (error) { > DPRINTF("%s: battery status failure: device_id=%d, error=%d\n", > __func__, dev->d_id, error); > @@ -682,6 +729,9 @@ uhidpp_device_features(struct uhidpp_softc *sc, struct > uhidpp_device *dev) > if (id == HIDPP20_FEAT_BATTERY_ID) { > dev->d_features |= UHIDPP_DEVICE_FEATURE_BATTERY; > dev->d_battery.feature_idx = i; > + } else if (id == HIDPP20_FEAT_UNIFIED_BATTERY_ID) { > + dev->d_features |= > UHIDPP_DEVICE_FEATURE_UNIFIIED_BATTERY; > + dev->d_battery.feature_idx = i; > } > > DPRINTF("%s: idx=%d, id=%x, type=%x device_id=%d\n", > @@ -815,24 +865,40 @@ hidpp10_get_name(struct uhidpp_softc *sc, uint8_t > device_id, > { > struct uhidpp_report resp; > int error; > - uint8_t params[1] = { 0x40 + (device_id - 1) }; > + const uint8_t *name; > 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; > + if (sc->sc_receiver == UHIDPP_RECEIVER_BOLT) { > + uint8_t params[2] = { 0x60 + device_id, 0x01 }; > + > + 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[2]; > + name = &resp.rap.params[3]; > + } else { > + uint8_t params[1] = { 0x40 + (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; > + len = resp.rap.params[1]; > + name = &resp.rap.params[2]; > + } > > - 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); > + memcpy(buf, name, len); > buf[len] = '\0'; > return 0; > } > @@ -842,18 +908,35 @@ hidpp10_get_type(struct uhidpp_softc *sc, uint8_t > device_id, const char **buf) > { > struct uhidpp_report resp; > int error; > - uint8_t params[1] = { 0x20 + (device_id - 1) }; > + uint8_t type; > > - 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; > + if (sc->sc_receiver == UHIDPP_RECEIVER_BOLT) { > + uint8_t params[1] = { 0x50 + device_id }; > > - switch (resp.rap.params[7]) { > + 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; > + type = resp.rap.params[1] & 0xf; > + } else { > + 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; > + type = resp.rap.params[7]; > + } > + > + switch (type) { > case 0x00: > *buf = "unknown"; > return 0; > @@ -1059,6 +1142,81 @@ hidpp20_battery_get_capability(struct uhidpp_softc *sc, > return 0; > } > > +int > +hidpp20_unified_battery_get_capabilities(struct uhidpp_softc *sc, > + struct uhidpp_device *dev) > +{ > + struct uhidpp_report resp; > + int error; > + > + error = hidpp_send_fap_report(sc, > + HIDPP_REPORT_ID_LONG, > + dev->d_id, > + dev->d_battery.feature_idx, > + HIDPP20_FEAT_UNIFIED_BATTERY_CAPABILITIES_FUNC, > + NULL, 0, &resp); > + if (error) > + return error; > + dev->d_battery.nlevels = nlevels(resp.fap.params[0]); > + dev->d_battery.unified_level_mask = resp.fap.params[0]; > + dev->d_battery.rechargeable = resp.fap.params[1] & > + HIDPP20_CAPABILITES_RECHARGEABLE; > + return 0; > +} > + > +int > +hidpp20_unified_battery_get_status(struct uhidpp_softc *sc, > + struct uhidpp_device *dev) > +{ > + struct uhidpp_report resp; > + int charging, error; > + uint8_t level, percentage, status; > + > + error = hidpp_send_fap_report(sc, > + HIDPP_REPORT_ID_LONG, > + dev->d_id, > + dev->d_battery.feature_idx, > + HIDPP20_FEAT_UNIFIED_BATTERY_STATUS_FUNC, > + NULL, 0, &resp); > + if (error) > + return error; > + percentage = resp.fap.params[0]; > + level = resp.fap.params[1] & dev->d_battery.unified_level_mask; > + status = resp.fap.params[2]; > + /* external_power_status = resp.fap.params[3]; */ > + > + charging = hidpp20_unified_battery_status_is_charging(status); > + dev->d_battery.sens[0].value = percentage * 1000; > + dev->d_battery.sens[0].flags &= ~SENSOR_FUNKNOWN; > + dev->d_battery.sens[0].status = SENSOR_S_UNKNOWN; > + /* Do not trust the level while charging. */ > + if (!charging) { > + if (level & HIDPP20_BATTERY_STATUS_CRITICAL) > + dev->d_battery.sens[0].status = SENSOR_S_CRIT; > + else if (level & HIDPP20_BATTERY_STATUS_LOW) > + dev->d_battery.sens[0].status = SENSOR_S_WARN; > + else if (level & HIDPP20_BATTERY_STATUS_GOOD) > + dev->d_battery.sens[0].status = SENSOR_S_OK; > + else if (level & HIDPP20_BATTERY_STATUS_FULL) > + dev->d_battery.sens[0].status = SENSOR_S_OK; > + } > + if (dev->d_battery.rechargeable) > + dev->d_battery.sens[2].value = charging; > + return 0; > +} > + > +int > +hidpp20_unified_battery_status_is_charging(uint8_t status) > +{ > + switch (status) { > + case 1: /* charging */ > + case 2: /* charging slow */ > + return 1; > + default: > + return 0; > + } > +} > + > int > hidpp20_battery_status_is_charging(uint8_t status) > { >