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

Reply via email to