On Mar 3, 2015, at 8:44 AM, David Higgs <hig...@gmail.com> wrote:

> With much help from mpi@, I have made a first big step towards improving 
> upd(4).  I’m not sure when tree lock ends, but I’m still happy to accept 
> feedback if right now isn’t the time to commit.  There’s plenty more to do, 
> but I’d like to get this ironed out before moving on.
> 
> New behavior with this diff:
> - Leverage new USB async reporting (must have up-to-date tree)
> - Sensor dependencies ensure reports are gathered in useful order
> - Track pending reports to minimize USB queries
> - Relevant sensors immediately invalidated when battery is removed (instead 
> of waiting for the next refresh)
> 
> Things that may need sanity checks:
> - Simplified upd_attach; the old code seemed to have redundant / confusing 
> logic
> - Integer promotion/truncation with batpres and hdata/adjust variables

Below is an overhauled diff with some additional considerations I came up with.

Improvements on the previous version:
- ACPresent no longer depends on BatteryPresent
- Sensor dependencies now handled manually, so dynamic behavior can be added 
later.
- Avoid hypothetical cases where certain USB report layouts could trigger:
  - Infinite loops in sensor dependencies.
  - Updating sensor contents using stale information.
  - Unnecessary sensor invalidation.
  - Redundant USB transfers.

As before, comments, questions, and feedback are welcome.

--david


Index: upd.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/upd.c,v
retrieving revision 1.13
diff -u -p -r1.13 upd.c
--- upd.c       11 Jan 2015 03:08:38 -0000      1.13
+++ upd.c       5 Mar 2015 17:06:19 -0000
@@ -39,45 +39,17 @@
 #define DPRINTF(x)
 #endif
 
-struct upd_usage_entry {
-       uint8_t                 usage_pg;
-       uint8_t                 usage_id;
-       enum sensor_type        senstype;
-       char                    *usage_name; /* sensor string */
-};
-
-static struct upd_usage_entry upd_usage_table[] = {
-       { HUP_BATTERY,  HUB_REL_STATEOF_CHARGE,
-           SENSOR_PERCENT,      "RelativeStateOfCharge" },
-       { HUP_BATTERY,  HUB_ABS_STATEOF_CHARGE,
-           SENSOR_PERCENT,      "AbsoluteStateOfCharge" },
-       { HUP_BATTERY,  HUB_REM_CAPACITY,
-           SENSOR_PERCENT,      "RemainingCapacity" },
-       { HUP_BATTERY,  HUB_FULLCHARGE_CAPACITY,
-           SENSOR_PERCENT,      "FullChargeCapacity" },
-       { HUP_BATTERY,  HUB_CHARGING,
-           SENSOR_INDICATOR,    "Charging" },
-       { HUP_BATTERY,  HUB_DISCHARGING,
-           SENSOR_INDICATOR,    "Discharging" },
-       { HUP_BATTERY,  HUB_BATTERY_PRESENT,
-           SENSOR_INDICATOR,    "BatteryPresent" },
-       { HUP_POWER,    HUP_SHUTDOWN_IMMINENT,
-           SENSOR_INDICATOR,    "ShutdownImminent" },
-       { HUP_BATTERY,  HUB_AC_PRESENT,
-           SENSOR_INDICATOR,    "ACPresent" },
-       { HUP_BATTERY,  HUB_ATRATE_TIMETOFULL,
-           SENSOR_TIMEDELTA,    "AtRateTimeToFull" }
-};
-
 struct upd_report {
        size_t          size;
-       int             enabled;
+       int             pending;
 };
 
 struct upd_sensor {
        struct ksensor          ksensor;
        struct hid_item         hitem;
+       struct upd_usage_entry  *entry;
        int                     attached;
+       int                     pending;
 };
 
 struct upd_softc {
@@ -85,6 +57,7 @@ struct upd_softc {
        int                      sc_num_sensors;
        u_int                    sc_max_repid;
        u_int                    sc_max_sensors;
+       char                     sc_buf[256];
 
        /* sensor framework */
        struct ksensordev        sc_sensordev;
@@ -93,15 +66,66 @@ struct upd_softc {
        struct upd_sensor       *sc_sensors;
 };
 
+struct upd_usage_entry {
+       uint8_t                 usage_pg;
+       uint8_t                 usage_id;
+       enum sensor_type        senstype;
+       char                    *usage_name; /* sensor string */
+       void (*update_sensor)(struct upd_softc *, struct upd_sensor *,
+           uint8_t *, int);
+};
+
 int  upd_match(struct device *, void *, void *);
 void upd_attach(struct device *, struct device *, void *);
 int  upd_detach(struct device *, int);
 
 void upd_refresh(void *);
-void upd_update_sensors(struct upd_softc *, uint8_t *, unsigned int, int);
-void upd_intr(struct uhidev *, void *, uint);
+void upd_request_sensor_refresh(struct upd_softc *, uint8_t, uint8_t, int);
+void upd_update_sensor_cb(void *, int, void *, int);
+void upd_mark_sensor_invalid(struct upd_softc *, struct upd_sensor *, int);
 struct upd_usage_entry *upd_lookup_usage_entry(const struct hid_item *);
 struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int);
+int upd_battery_present(struct upd_softc *);
+void upd_update_batpres_sensor(struct upd_softc *, struct upd_sensor *,
+    uint8_t *, int);
+void upd_update_batdep_sensor(struct upd_softc *, struct upd_sensor *,
+    uint8_t *, int);
+void upd_update_sensor_value(struct upd_softc *, struct upd_sensor *,
+    uint8_t *, int);
+void upd_intr(struct uhidev *, void *, uint);
+
+static struct upd_usage_entry upd_usage_table[] = {
+       { HUP_BATTERY,  HUB_REL_STATEOF_CHARGE,
+           SENSOR_PERCENT,     "RelativeStateOfCharge",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_ABS_STATEOF_CHARGE,
+           SENSOR_PERCENT,     "AbsoluteStateOfCharge",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_REM_CAPACITY,
+           SENSOR_PERCENT,     "RemainingCapacity",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_FULLCHARGE_CAPACITY,
+           SENSOR_PERCENT,     "FullChargeCapacity",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_CHARGING,
+           SENSOR_INDICATOR,   "Charging",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_DISCHARGING,
+           SENSOR_INDICATOR,   "Discharging",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_BATTERY_PRESENT,
+           SENSOR_INDICATOR,   "BatteryPresent",
+           upd_update_batpres_sensor },
+       { HUP_POWER,    HUP_SHUTDOWN_IMMINENT,
+           SENSOR_INDICATOR,   "ShutdownImminent",
+           upd_update_sensor_value },
+       { HUP_BATTERY,  HUB_AC_PRESENT,
+           SENSOR_INDICATOR,   "ACPresent",
+           upd_update_batdep_sensor },
+       { HUP_BATTERY,  HUB_ATRATE_TIMETOFULL,
+           SENSOR_TIMEDELTA,   "AtRateTimeToFull",
+           upd_update_batdep_sensor }
+};
 
 struct cfdriver upd_cd = {
        NULL, "upd", DV_DULL
@@ -183,38 +207,30 @@ upd_attach(struct device *parent, struct
             sc->sc_num_sensors < sc->sc_max_sensors; ) {
                DPRINTF(("upd: repid=%d\n", item.report_ID));
                if (item.kind != hid_feature ||
-                   item.report_ID < 0)
+                   item.report_ID < 0 ||
+                   item.report_ID >= sc->sc_max_repid)
                        continue;
-
                if ((entry = upd_lookup_usage_entry(&item)) == NULL)
                        continue;
-
-               /* filter repeated usages, avoid duplicated sensors */
-               sensor = upd_lookup_sensor(sc, entry->usage_pg,
-                   entry->usage_id);
-               if (sensor && sensor->attached)
+               if (upd_lookup_sensor(sc, entry->usage_pg, entry->usage_id))
                        continue;
 
                sensor = &sc->sc_sensors[sc->sc_num_sensors];
-               /* keep our copy of hid_item */
                memcpy(&sensor->hitem, &item, sizeof(struct hid_item));
                strlcpy(sensor->ksensor.desc, entry->usage_name,
                    sizeof(sensor->ksensor.desc));
                sensor->ksensor.type = entry->senstype;
-               sensor->ksensor.flags |= SENSOR_FINVALID;
-               sensor->ksensor.status = SENSOR_S_UNKNOWN;
+               upd_mark_sensor_invalid(sc, sensor, 0);
                sensor->ksensor.value = 0;
+               sensor->entry = entry;
                sensor_attach(&sc->sc_sensordev, &sensor->ksensor);
                sensor->attached = 1;
+               sensor->pending = 0;
                sc->sc_num_sensors++;
 
-               if (item.report_ID >= sc->sc_max_repid ||
-                   sc->sc_reports[item.report_ID].enabled)
-                       continue;
-
                sc->sc_reports[item.report_ID].size = hid_report_size(desc,
                    size, item.kind, item.report_ID);
-               sc->sc_reports[item.report_ID].enabled = 1;
+               sc->sc_reports[item.report_ID].pending = 0;
        }
        hid_end_parse(hdata);
        DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
@@ -262,32 +278,124 @@ void
 upd_refresh(void *arg)
 {
        struct upd_softc        *sc = (struct upd_softc *)arg;
+       struct upd_sensor       *sensor;
        struct upd_report       *report;
-       uint8_t                 buf[256];
-       int                     repid, actlen;
+       int                     i, pending = 0;
 
-       for (repid = 0; repid < sc->sc_max_repid; repid++) {
-               report = &sc->sc_reports[repid];
-               if (!report->enabled)
+       for (i = 0; i < sc->sc_num_sensors; i++) {
+               sensor = &sc->sc_sensors[i];
+               if (!sensor->attached)
                        continue;
+               report = &sc->sc_reports[sensor->hitem.report_ID];
+               if (sensor->pending || report->pending) {
+                       pending++;
+                       upd_mark_sensor_invalid(sc, sensor, 0);
+                       DPRINTF(("%s: sensor %s (repid=%d) still pending\n",
+                           sc->sc_sensordev.xname, sensor->ksensor.desc,
+                           sensor->hitem.report_ID));
+               }
+       }
 
-               memset(buf, 0x0, sizeof(buf));
-               actlen = uhidev_get_report(sc->sc_hdev.sc_parent,
-                   UHID_FEATURE_REPORT, repid, buf, report->size);
+       if (pending > 0)
+               printf("%s: %d async sensors still pending\n",
+                   sc->sc_sensordev.xname, pending);
+       else {
+               upd_request_sensor_refresh(sc, HUP_POWER,
+                   HUP_SHUTDOWN_IMMINENT, 1);
+               upd_request_sensor_refresh(sc, HUP_BATTERY,
+                   HUB_BATTERY_PRESENT, 1);
+               upd_request_sensor_refresh(sc, HUP_BATTERY,
+                   HUB_AC_PRESENT, 1);
+       }
+}
 
-               if (actlen == -1) {
-                       DPRINTF(("upd: failed to get report id=%02x\n", repid));
-                       continue;
-               }
+void
+upd_request_sensor_refresh(struct upd_softc *sc, uint8_t usage_pg,
+    uint8_t usage_id, int valid)
+{
+       struct upd_sensor       *sensor;
+       struct upd_report       *report;
+       int                     repid;
 
-               /* Deal with buggy firmwares. */
-               if (actlen < report->size)
-                       report->size = actlen;
+       sensor = upd_lookup_sensor(sc, usage_pg, usage_id);
+       if (sensor != NULL && sensor->attached) {
+#ifdef DIAGNOSTIC
+               if (sensor->pending)
+                       printf("%s: sensor %s is already pending!\n",
+                           sc->sc_sensordev.xname, sensor->ksensor.desc);
+#endif
+               repid = sensor->hitem.report_ID;
+               report = &sc->sc_reports[repid];
 
-               upd_update_sensors(sc, buf, report->size, repid);
+               if (!valid)
+                       /* invalidate just one sensor */
+                       upd_mark_sensor_invalid(sc, sensor, 1);
+               else if (report->pending)
+                       /* already requested */
+                       sensor->pending = 1;
+               else if (uhidev_get_report_async(sc->sc_hdev.sc_parent,
+                   UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size,
+                   sc, upd_update_sensor_cb) > 0) {
+                       DPRINTF(("%s: repid=%d requested by %s\n",
+                           sc->sc_sensordev.xname, repid,
+                           sensor->ksensor.desc));
+                       report->pending = 1;
+                       sensor->pending = 1;
+               } else
+                       /* no other sensors can be pending on this repid */
+                       upd_mark_sensor_invalid(sc, sensor, 1);
        }
 }
 
+void
+upd_update_sensor_cb(void *priv, int repid, void *data, int len)
+{
+       struct upd_softc        *sc = priv;
+       struct upd_report       *report;
+       struct upd_sensor       *sensor;
+       int                     i, updated;
+
+       report = &sc->sc_reports[repid];
+       if (len > 0 && report->size != len) {
+               printf("%s: adjusting size of repid %d\n",
+                   sc->sc_sensordev.xname, repid);
+               report->size = len;
+       }
+
+       /* sc_sensors may not be in dependency order */
+       do {
+               updated = 0;
+               for (i = 0; i < sc->sc_num_sensors; i++) {
+                       sensor = &sc->sc_sensors[i];
+
+                       /* prevent infinite dependency traversals */
+                       if (!sensor->attached || !sensor->pending ||
+                           sensor->hitem.report_ID != repid)
+                               continue;
+
+                       if (len > 0)
+                               sensor->entry->update_sensor(sc,
+                                   sensor, data, len);
+                       else
+                               upd_mark_sensor_invalid(sc, sensor, 1);
+                       sensor->pending = 0;
+                       updated = 1;
+               }
+       } while (updated);
+
+       report->pending = 0;
+}
+
+void
+upd_mark_sensor_invalid(struct upd_softc *sc, struct upd_sensor *sensor,
+    int deps)
+{
+       sensor->ksensor.status = SENSOR_S_UNKNOWN;
+       sensor->ksensor.flags |= SENSOR_FINVALID;
+       if (deps)
+               sensor->entry->update_sensor(sc, sensor, NULL, -1);
+}
+
 struct upd_usage_entry *
 upd_lookup_usage_entry(const struct hid_item *hitem)
 {
@@ -318,35 +426,61 @@ upd_lookup_sensor(struct upd_softc *sc, 
        return (NULL);
 }
 
-void
-upd_update_sensors(struct upd_softc *sc, uint8_t *buf, unsigned int len,
-    int repid)
+int
+upd_battery_present(struct upd_softc *sc)
 {
        struct upd_sensor       *sensor;
-       ulong                   hdata, batpres;
-       ulong                   adjust;
-       int                     i;
+       int                     batpres = 0;
 
        sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT);
-       batpres = sensor ? sensor->ksensor.value : -1;
+       if (sensor && !(sensor->ksensor.flags & SENSOR_FINVALID))
+               batpres = sensor->ksensor.value;
+       return (batpres);
+}
 
-       for (i = 0; i < sc->sc_num_sensors; i++) {
-               sensor = &sc->sc_sensors[i];
-               if (!(sensor->hitem.report_ID == repid && sensor->attached))
-                       continue;
+void
+upd_update_batpres_sensor(struct upd_softc *sc, struct upd_sensor *sensor,
+    uint8_t *buf, int len)
+{
+       int batpres;
 
-               /* invalidate battery dependent sensors */
-               if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
-                   batpres <= 0) {
-                       /* exception to the battery sensor itself */
-                       if (HID_GET_USAGE(sensor->hitem.usage) !=
-                           HUB_BATTERY_PRESENT) {
-                               sensor->ksensor.status = SENSOR_S_UNKNOWN;
-                               sensor->ksensor.flags |= SENSOR_FINVALID;
-                               continue;
-                       }
-               }
+       upd_update_sensor_value(sc, sensor, buf, len);
+       batpres = upd_battery_present(sc);
+
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_REL_STATEOF_CHARGE, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_ABS_STATEOF_CHARGE, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_REM_CAPACITY, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_FULLCHARGE_CAPACITY, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_CHARGING, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_DISCHARGING, batpres);
+       upd_request_sensor_refresh(sc, HUP_BATTERY,
+           HUB_ATRATE_TIMETOFULL, batpres);
+}
+
+void
+upd_update_batdep_sensor(struct upd_softc *sc, struct upd_sensor *sensor,
+    uint8_t *buf, int len)
+{
+       if (upd_battery_present(sc))
+               upd_update_sensor_value(sc, sensor, buf, len);
+       else
+               upd_mark_sensor_invalid(sc, sensor, 0);
+}
+
+void
+upd_update_sensor_value(struct upd_softc *sc, struct upd_sensor *sensor,
+    uint8_t *buf, int len)
+{
+       int64_t hdata, adjust;
 
+       if (len > 0) {
+               /* XXX ignores units, CapacityMode, and AtRate */
                switch (HID_GET_USAGE(sensor->hitem.usage)) {
                case HUB_REL_STATEOF_CHARGE:
                case HUB_ABS_STATEOF_CHARGE:
@@ -360,15 +494,13 @@ upd_update_sensors(struct upd_softc *sc,
                }
 
                hdata = hid_get_data(buf, len, &sensor->hitem.loc);
-
                sensor->ksensor.value = hdata * adjust;
                sensor->ksensor.status = SENSOR_S_OK;
                sensor->ksensor.flags &= ~SENSOR_FINVALID;
-               DPRINTF(("%s: hidget data: %lu\n",
-                   sc->sc_sensordev.xname, hdata));
+               DPRINTF(("%s: %s hidget data: %lld\n",
+                   sc->sc_sensordev.xname, sensor->ksensor.desc, hdata));
        }
 }
-
 
 void
 upd_intr(struct uhidev *uh, void *p, uint len)


Reply via email to