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 Suggestions, bug reports, and feedback 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 20 Feb 2015 02:28:04 -0000 @@ -39,44 +39,15 @@ #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; }; @@ -85,6 +56,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,6 +65,17 @@ struct upd_softc { struct upd_sensor *sc_sensors; }; +struct upd_usage_entry { + uint8_t usage_pg; + uint8_t usage_id; + uint8_t parent_pg; + uint8_t parent_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); @@ -100,8 +83,58 @@ 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_refresh_sensor_tree(struct upd_softc *, uint8_t, uint8_t, int); +void upd_get_report_async_cb(void *, int, void *, int); struct upd_usage_entry *upd_lookup_usage_entry(const struct hid_item *); struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, 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); + +/* dependency order only for developer sanity */ +static struct upd_usage_entry upd_usage_table[] = { + { HUP_POWER, HUP_SHUTDOWN_IMMINENT, + HUP_UNDEFINED, 0, + SENSOR_INDICATOR, "ShutdownImminent", + upd_update_sensor_value }, + { HUP_BATTERY, HUB_BATTERY_PRESENT, + HUP_UNDEFINED, 0, + SENSOR_INDICATOR, "BatteryPresent", + upd_update_sensor_value }, + { HUP_BATTERY, HUB_AC_PRESENT, + HUP_UNDEFINED, 0, + SENSOR_INDICATOR, "ACPresent", + upd_update_sensor_value }, + { HUP_BATTERY, HUB_REL_STATEOF_CHARGE, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_PERCENT, "RelativeStateOfCharge", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_ABS_STATEOF_CHARGE, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_PERCENT, "AbsoluteStateOfCharge", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_REM_CAPACITY, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_PERCENT, "RemainingCapacity", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_FULLCHARGE_CAPACITY, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_PERCENT, "FullChargeCapacity", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_CHARGING, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_INDICATOR, "Charging", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_DISCHARGING, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_INDICATOR, "Discharging", + upd_update_batdep_sensor }, + { HUP_BATTERY, HUB_ATRATE_TIMETOFULL, + HUP_BATTERY, HUB_BATTERY_PRESENT, + SENSOR_TIMEDELTA, "AtRateTimeToFull", + upd_update_batdep_sensor } +}; struct cfdriver upd_cd = { NULL, "upd", DV_DULL @@ -183,20 +216,15 @@ 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)); @@ -204,17 +232,14 @@ upd_attach(struct device *parent, struct sensor->ksensor.flags |= SENSOR_FINVALID; sensor->ksensor.status = SENSOR_S_UNKNOWN; sensor->ksensor.value = 0; + sensor->entry = entry; sensor_attach(&sc->sc_sensordev, &sensor->ksensor); sensor->attached = 1; 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,29 +287,98 @@ 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; - for (repid = 0; repid < sc->sc_max_repid; repid++) { - report = &sc->sc_reports[repid]; - if (!report->enabled) + pending = 0; + 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 (report->pending) { + pending = 1; + sensor->ksensor.status = SENSOR_S_UNKNOWN; + sensor->ksensor.flags |= SENSOR_FINVALID; + DPRINTF(("%s: repid=%d still pending\n", + sc->sc_sensordev.xname, 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) + printf("%s: async reports still pending\n", + sc->sc_sensordev.xname); + else + upd_refresh_sensor_tree(sc, HUP_UNDEFINED, 0, 1); +} - if (actlen == -1) { - DPRINTF(("upd: failed to get report id=%02x\n", repid)); +void +upd_refresh_sensor_tree(struct upd_softc *sc, uint8_t page, uint8_t usage, + int valid) +{ + struct upd_sensor *sensor; + struct upd_report *report; + int i, repid; + + for (i = 0; i < sc->sc_num_sensors; i++) { + sensor = &sc->sc_sensors[i]; + if (sensor->entry->parent_pg != page || + sensor->entry->parent_id != usage) continue; + repid = sensor->hitem.report_ID; + report = &sc->sc_reports[repid]; + if (report->pending) + continue; + + if (valid && sensor->attached && + uhidev_get_report_async(sc->sc_hdev.sc_parent, + UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size, + sc, upd_get_report_async_cb) == report->size) { + DPRINTF(("%s: repid=%d requested by %s\n", + sc->sc_sensordev.xname, repid, + sensor->ksensor.desc)); + report->pending = 1; + } else { + DPRINTF(("%s: repid=%d invalidated by %s\n", + sc->sc_sensordev.xname, repid, + sensor->ksensor.desc)); + upd_get_report_async_cb(sc, repid, NULL, -1); } + } +} - /* Deal with buggy firmwares. */ - if (actlen < report->size) - report->size = actlen; +void +upd_get_report_async_cb(void *priv, int repid, void *data, int len) +{ + struct upd_softc *sc = priv; + struct upd_report *report; + struct upd_sensor *sensor; + int i, valid; - upd_update_sensors(sc, buf, report->size, repid); + report = &sc->sc_reports[repid]; + report->pending = 0; + if (len > 0 && report->size != len) { + printf("%s: adjusting size of repid %d\n", + sc->sc_sensordev.xname, repid); + report->size = len; + } + + for (i = 0; i < sc->sc_num_sensors; i++) { + sensor = &sc->sc_sensors[i]; + if (sensor->hitem.report_ID == repid) { + valid = (len > 0 && sensor->attached); + if (valid) + sensor->entry->update_sensor(sc, + sensor, data, len); + else { + sensor->ksensor.status = SENSOR_S_UNKNOWN; + sensor->ksensor.flags |= SENSOR_FINVALID; + } + upd_refresh_sensor_tree(sc, + HID_GET_USAGE_PAGE(sensor->hitem.usage), + HID_GET_USAGE(sensor->hitem.usage), valid); + } } } @@ -319,56 +413,49 @@ upd_lookup_sensor(struct upd_softc *sc, } void -upd_update_sensors(struct upd_softc *sc, uint8_t *buf, unsigned int len, - int repid) +upd_update_batdep_sensor(struct upd_softc *sc, struct upd_sensor *sensor, + uint8_t *buf, int len) { - struct upd_sensor *sensor; - ulong hdata, batpres; - ulong adjust; - int i; - - sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT); - batpres = sensor ? sensor->ksensor.value : -1; - - for (i = 0; i < sc->sc_num_sensors; i++) { - sensor = &sc->sc_sensors[i]; - if (!(sensor->hitem.report_ID == repid && sensor->attached)) - continue; - - /* 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; - } - } + struct upd_sensor *bat; + int batpres = 0; - switch (HID_GET_USAGE(sensor->hitem.usage)) { - case HUB_REL_STATEOF_CHARGE: - case HUB_ABS_STATEOF_CHARGE: - case HUB_REM_CAPACITY: - case HUB_FULLCHARGE_CAPACITY: - adjust = 1000; /* scale adjust */ - break; - default: - adjust = 1; /* no scale adjust */ - break; - } + bat = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT); + if (bat && !(bat->ksensor.flags & SENSOR_FINVALID)) + batpres = bat->ksensor.value; + + if (batpres) + upd_update_sensor_value(sc, sensor, buf, len); + else { + sensor->ksensor.status = SENSOR_S_UNKNOWN; + sensor->ksensor.flags |= SENSOR_FINVALID; + } +} - hdata = hid_get_data(buf, len, &sensor->hitem.loc); +void +upd_update_sensor_value(struct upd_softc *sc, struct upd_sensor *sensor, + uint8_t *buf, int len) +{ + int64_t hdata, adjust; - 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)); + switch (HID_GET_USAGE(sensor->hitem.usage)) { + case HUB_REL_STATEOF_CHARGE: + case HUB_ABS_STATEOF_CHARGE: + case HUB_REM_CAPACITY: + case HUB_FULLCHARGE_CAPACITY: + adjust = 1000; /* scale adjust */ + break; + default: + adjust = 1; /* no scale adjust */ + break; } -} + 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: %s hidget data: %lld\n", + sc->sc_sensordev.xname, sensor->ksensor.desc, hdata)); +} void upd_intr(struct uhidev *uh, void *p, uint len)