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.

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)
 {

Reply via email to