For devices with rechargeable batteries, expose a new boolean sensor
authoranton <anton@openbsd.org>
Tue, 17 Aug 2021 11:30:45 +0000 (11:30 +0000)
committeranton <anton@openbsd.org>
Tue, 17 Aug 2021 11:30:45 +0000 (11:30 +0000)
indicating the charging state.

Thanks to Laurence Tratt <laurie at tratt dot net> for testing.

sys/dev/usb/uhidpp.c

index ab14e92..23d7062 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uhidpp.c,v 1.17 2021/08/17 05:56:24 anton Exp $       */
+/*     $OpenBSD: uhidpp.c,v 1.18 2021/08/17 11:30:45 anton Exp $       */
 
 /*
  * Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org>
@@ -127,6 +127,8 @@ int uhidpp_debug = 1;
 
 #define HIDPP20_BATTERY_STATUS_CHARGING_DONE   0x0003
 
+#define HIDPP20_BATTERY_CAPABILITY_RECHARGEABLE        0x0004
+
 /* HID++ 2.0 error codes. */
 #define HIDPP20_ERROR                          0xff
 #define HIDPP20_ERROR_NO_ERROR                 0x00
@@ -155,7 +157,7 @@ int uhidpp_debug = 1;
 #define UHIDPP_NNOTIFICATIONS                  4
 
 /* Number of sensors per paired device. */
-#define UHIDPP_NSENSORS                                2
+#define UHIDPP_NSENSORS                                3
 
 /* Feature access report used by the HID++ 2.0 (and greater) protocol. */
 struct fap {
@@ -203,6 +205,7 @@ struct uhidpp_device {
                uint8_t b_next_level;
                uint8_t b_status;
                uint8_t b_nlevels;
+               uint8_t b_rechargeable;
        } d_battery;
 };
 
@@ -268,7 +271,7 @@ int hidpp20_feature_get_id(struct uhidpp_softc *, uint8_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 *);
+    uint8_t *, uint8_t *);
 int hidpp20_battery_status_is_charging(uint8_t);
 
 int hidpp_send_validate(uint8_t, int);
@@ -413,7 +416,7 @@ int
 uhidpp_detach(struct device *self, int flags)
 {
        struct uhidpp_softc *sc = (struct uhidpp_softc *)self;
-       int i, j;
+       int i;
 
        usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
 
@@ -427,12 +430,15 @@ uhidpp_detach(struct device *self, int flags)
 
        for (i = 0; i < UHIDPP_NDEVICES; i++) {
                struct uhidpp_device *dev = &sc->sc_devices[i];
+               struct ksensor *sens = dev->d_battery.b_sens;
 
                if (!dev->d_connected)
                        continue;
 
-               for (j = 0; j < UHIDPP_NSENSORS; j++)
-                       sensor_detach(&sc->sc_sensdev, &dev->d_battery.b_sens[j]);
+               sensor_detach(&sc->sc_sensdev, &sens[0]);
+               sensor_detach(&sc->sc_sensdev, &sens[1]);
+               if (dev->d_battery.b_rechargeable)
+                       sensor_detach(&sc->sc_sensdev, &sens[2]);
        }
 
        uhidev_close(&sc->sc_hdev);
@@ -594,8 +600,9 @@ uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
                        return;
                }
 
-               error = hidpp20_battery_get_capability(sc, dev->d_id,
-                   dev->d_battery.b_feature_idx, &dev->d_battery.b_nlevels);
+               error = hidpp20_battery_get_capability(sc,
+                   dev->d_id, dev->d_battery.b_feature_idx,
+                   &dev->d_battery.b_nlevels, &dev->d_battery.b_rechargeable);
                if (error) {
                        DPRINTF("%s: battery capability failure: device_id=%d, "
                            "error=%d\n", __func__, dev->d_id, error);
@@ -630,6 +637,14 @@ uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
        sens->value = dev->d_battery.b_nlevels;
        sensor_attach(&sc->sc_sensdev, sens);
 
+       if (dev->d_battery.b_rechargeable) {
+               sens = &dev->d_battery.b_sens[2];
+               strlcpy(sens->desc, "charger", sizeof(sens->desc));
+               sens->type = SENSOR_INDICATOR;
+               sens->value = 0;
+               sensor_attach(&sc->sc_sensdev, sens);
+       }
+
        if (sc->sc_senstsk == NULL) {
                /*
                 * The mutex must be temporarily released while calling
@@ -646,11 +661,11 @@ uhidpp_device_connect(struct uhidpp_softc *sc, struct uhidpp_device *dev)
 void
 uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
 {
-       int error;
-
        MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
 
        if (dev->d_major >= 2) {
+               int charging, error;
+
                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,
@@ -662,6 +677,9 @@ uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
                        return;
                }
 
+               charging = hidpp20_battery_status_is_charging(
+                   dev->d_battery.b_status);
+
                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) {
@@ -678,8 +696,7 @@ uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
                         * it even further. Unless the battery is charging in
                         * which the level cannot be trusted.
                         */
-                       if (hidpp20_battery_status_is_charging(
-                           dev->d_battery.b_status))
+                       if (charging)
                                dev->d_battery.b_sens[0].status = SENSOR_S_UNKNOWN;
                        else if (dev->d_battery.b_level <= 10)
                                dev->d_battery.b_sens[0].status = SENSOR_S_CRIT;
@@ -695,6 +712,9 @@ uhidpp_device_refresh(struct uhidpp_softc *sc, struct uhidpp_device *dev)
                         */
                        dev->d_battery.b_sens[0].status = SENSOR_S_UNKNOWN;
                }
+
+               if (dev->d_battery.b_rechargeable)
+                       dev->d_battery.b_sens[2].value = charging;
        }
 }
 
@@ -1084,7 +1104,7 @@ hidpp20_battery_get_level_status(struct uhidpp_softc *sc, uint8_t device_id,
 
 int
 hidpp20_battery_get_capability(struct uhidpp_softc *sc, uint8_t device_id,
-    uint8_t feature_idx, uint8_t *nlevels)
+    uint8_t feature_idx, uint8_t *nlevels, uint8_t *rechargeable)
 {
        struct uhidpp_report resp;
        int error;
@@ -1098,6 +1118,8 @@ hidpp20_battery_get_capability(struct uhidpp_softc *sc, uint8_t device_id,
        if (error)
                return error;
        *nlevels = resp.fap.params[0];
+       *rechargeable = resp.fap.params[1] &
+           HIDPP20_BATTERY_CAPABILITY_RECHARGEABLE;
        return 0;
 }