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;
}
}