This is the big change that puts all the previous work together. When a sensor update is needed, mark its report as pending; do this in dependency order. When a report fails to query/reply, mark it and its children as invalid. When the BatteryPresent says there is no battery, mark its children as invalid too.
If BatteryPresent is on a repid that comes numerically after a child, the do/while loop will try again until there are no more pending reports. If BatteryPresent is on the same repid as a child, the report->sensors list will have already put it in the correct order so the parent will update before the child. If BatteryPresent=false invalidates children that belong to an already-pending report, they will not be updated, because sensor->pending will no longer be set. --david --- a/upd.c +++ b/upd.c @@ -81,6 +81,7 @@ static struct upd_usage_entry upd_usage_ struct upd_report { size_t size; SLIST_HEAD(, upd_sensor) sensors; + int pending; }; SLIST_HEAD(upd_sensor_head, upd_sensor); @@ -91,6 +92,7 @@ struct upd_sensor { struct upd_sensor_head children; SLIST_ENTRY(upd_sensor) dep_next; SLIST_ENTRY(upd_sensor) rep_next; + int pending; }; struct upd_softc { @@ -113,7 +115,9 @@ void upd_attach_sensor_tree(struct upd_s int upd_detach(struct device *, int); void upd_refresh(void *); -void upd_update_sensors(struct upd_softc *, uint8_t *, unsigned int, int); +void upd_request_children(struct upd_softc *, struct upd_sensor_head *, int); +void upd_update_report_cb(void *, int, void *, int); +void upd_update_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); @@ -285,30 +289,59 @@ upd_detach(struct device *self, int flag void upd_refresh(void *arg) { - struct upd_softc *sc = (struct upd_softc *)arg; + struct upd_softc *sc = arg; struct upd_report *report; uint8_t buf[256]; - int repid, actlen; + int repid, actlen, done; - for (repid = 0; repid < sc->sc_max_repid; repid++) { - report = &sc->sc_reports[repid]; - if (SLIST_EMPTY(&report->sensors)) - continue; + /* request root sensors */ + upd_request_children(sc, &sc->sc_root_sensors, 1); - memset(buf, 0x0, sizeof(buf)); - actlen = uhidev_get_report(sc->sc_hdev.sc_parent, - UHID_FEATURE_REPORT, repid, buf, report->size); - - if (actlen == -1) { - DPRINTF(("upd: failed to get report id=%02x\n", repid)); - continue; + /* repeat until all reports queried */ + do { + done = 1; + for (repid = 0; repid < sc->sc_max_repid; repid++) { + report = &sc->sc_reports[repid]; + if (!report->pending) + continue; + memset(buf, 0x0, sizeof(buf)); + actlen = uhidev_get_report(sc->sc_hdev.sc_parent, + UHID_FEATURE_REPORT, repid, buf, report->size); + upd_update_report_cb(sc, repid, buf, actlen); + done = 0; } + } while (!done); +} + +void +upd_request_children(struct upd_softc *sc, struct upd_sensor_head *queue, + int valid) +{ + struct upd_sensor *sensor; + struct upd_report *report; + int repid; - /* Deal with buggy firmwares. */ - if (actlen < report->size) - report->size = actlen; + SLIST_FOREACH(sensor, queue, dep_next) { + repid = sensor->hitem.report_ID; + report = &sc->sc_reports[repid]; - upd_update_sensors(sc, buf, report->size, repid); + if (sensor->pending) + DPRINTF(("%s: %s still pending (repid=%d)\n", + DEVNAME(sc), sensor->ksensor.desc, repid)); + else if (!valid) { + DPRINTF(("%s: marking %s invalid\n", + DEVNAME(sc), sensor->ksensor.desc)); + sensor->pending = 1; + upd_update_sensor(sc, sensor, NULL, -1); + } else if (report->pending) + /* already requested */ + sensor->pending = 1; + else { + DPRINTF(("%s: %s requests repid %d\n", + DEVNAME(sc), sensor->ksensor.desc, repid)); + sensor->pending = 1; + report->pending = 1; + } } } @@ -349,34 +382,52 @@ 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_report_cb(void *priv, int repid, void *data, int len) { + struct upd_softc *sc = priv; + struct upd_report *report; struct upd_sensor *sensor; - ulong batpres; - int i; - sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT); - batpres = sensor ? sensor->ksensor.value : -1; + /* handle buggy firmware */ + report = &sc->sc_reports[repid]; + if (len > 0 && report->size != len) { + DPRINTF(("%s: adjusting repid %d size (%zd -> %d)\n", + DEVNAME(sc), repid, report->size, len)); + report->size = len; + } - for (i = 0; i < sc->sc_num_sensors; i++) { - sensor = &sc->sc_sensors[i]; - if (!(sensor->hitem.report_ID == repid && sensor->attached)) - continue; + /* update all sensors in this report */ + SLIST_FOREACH(sensor, &report->sensors, rep_next) + upd_update_sensor(sc, sensor, data, len); + report->pending = 0; +} - /* 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; - } +void +upd_update_sensor(struct upd_softc *sc, struct upd_sensor *sensor, + uint8_t *buf, int len) +{ + int valid; + + if (sensor->pending) { + valid = (buf != NULL && len > 0); + if (valid) { + upd_update_sensor_value(sc, sensor, buf, len); + + /* if battery not present, invalidate children */ + if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == + HUP_BATTERY && + HID_GET_USAGE(sensor->hitem.usage) == + HUB_BATTERY_PRESENT && + sensor->ksensor.value == 0) + valid = 0; + } else { + sensor->ksensor.status = SENSOR_S_UNKNOWN; + sensor->ksensor.flags |= SENSOR_FINVALID; } - upd_update_sensor_value(sc, sensor, buf, len); + /* refresh children */ + upd_request_children(sc, &sensor->children, valid); + sensor->pending = 0; } }