On Mar 3, 2015, at 8:44 AM, David Higgs <[email protected]> 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)