jcs's openbsd hax
openbsd
at jcs 468 lines 13 kB view raw
1/* $OpenBSD: upd.c,v 1.34 2025/03/02 08:18:12 landry Exp $ */ 2 3/* 4 * Copyright (c) 2015 David Higgs <higgsd@gmail.com> 5 * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* 21 * Driver for USB Power Devices sensors 22 * https://usb.org/sites/default/files/pdcv10.pdf 23 */ 24 25#include <sys/param.h> 26#include <sys/systm.h> 27#include <sys/malloc.h> 28#include <sys/device.h> 29#include <sys/queue.h> 30#include <sys/sensors.h> 31 32#include <dev/usb/usb.h> 33#include <dev/usb/usbdi.h> 34#include <dev/usb/usbhid.h> 35#include <dev/usb/uhidev.h> 36 37#ifdef UPD_DEBUG 38#define DPRINTF(x) do { printf x; } while (0) 39#else 40#define DPRINTF(x) 41#endif 42 43#define DEVNAME(sc) ((sc)->sc_hdev.sc_dev.dv_xname) 44 45struct upd_usage_entry { 46 uint8_t usage_pg; 47 uint8_t usage_id; 48 enum sensor_type senstype; 49 char *usage_name; /* sensor string */ 50 int nchildren; 51 struct upd_usage_entry *children; 52}; 53 54static struct upd_usage_entry upd_usage_batdep[] = { 55 { HUP_BATTERY, HUB_REL_STATEOF_CHARGE, 56 SENSOR_PERCENT, "RelativeStateOfCharge" }, 57 { HUP_BATTERY, HUB_ABS_STATEOF_CHARGE, 58 SENSOR_PERCENT, "AbsoluteStateOfCharge" }, 59 { HUP_BATTERY, HUB_REM_CAPACITY, 60 SENSOR_PERCENT, "RemainingCapacity" }, 61 { HUP_BATTERY, HUB_FULLCHARGE_CAPACITY, 62 SENSOR_PERCENT, "FullChargeCapacity" }, 63 { HUP_POWER, HUP_PERCENT_LOAD, 64 SENSOR_PERCENT, "PercentLoad" }, 65 { HUP_BATTERY, HUB_CHARGING, 66 SENSOR_INDICATOR, "Charging" }, 67 { HUP_BATTERY, HUB_DISCHARGING, 68 SENSOR_INDICATOR, "Discharging" }, 69 { HUP_BATTERY, HUB_ATRATE_TIMETOFULL, 70 SENSOR_TIMEDELTA, "AtRateTimeToFull" }, 71 { HUP_BATTERY, HUB_ATRATE_TIMETOEMPTY, 72 SENSOR_TIMEDELTA, "AtRateTimeToEmpty" }, 73 { HUP_BATTERY, HUB_RUNTIMETO_EMPTY, 74 SENSOR_TIMEDELTA, "RunTimeToEmpty" }, 75 { HUP_BATTERY, HUB_NEED_REPLACEMENT, 76 SENSOR_INDICATOR, "NeedReplacement" }, 77}; 78static struct upd_usage_entry upd_usage_roots[] = { 79 { HUP_BATTERY, HUB_BATTERY_PRESENT, 80 SENSOR_INDICATOR, "BatteryPresent", 81 nitems(upd_usage_batdep), upd_usage_batdep }, 82 { HUP_POWER, HUP_SHUTDOWN_IMMINENT, 83 SENSOR_INDICATOR, "ShutdownImminent" }, 84 { HUP_BATTERY, HUB_AC_PRESENT, 85 SENSOR_INDICATOR, "ACPresent" }, 86 { HUP_POWER, HUP_OVERLOAD, 87 SENSOR_INDICATOR, "Overload" }, 88}; 89#define UPD_MAX_SENSORS (nitems(upd_usage_batdep) + nitems(upd_usage_roots)) 90 91SLIST_HEAD(upd_sensor_head, upd_sensor); 92 93struct upd_report { 94 size_t size; /* Size of the report */ 95 struct upd_sensor_head sensors; /* List in dependency order */ 96 int pending; /* Waiting for an answer */ 97}; 98 99struct upd_sensor { 100 struct ksensor ksensor; 101 struct hid_item hitem; 102 int attached; /* Is there a matching report */ 103 struct upd_sensor_head children; /* list of children sensors */ 104 SLIST_ENTRY(upd_sensor) dep_next; /* next in the child list */ 105 SLIST_ENTRY(upd_sensor) rep_next; /* next in the report list */ 106}; 107 108struct upd_softc { 109 struct uhidev sc_hdev; 110 int sc_num_sensors; 111 u_int sc_max_repid; 112 char sc_buf[256]; 113 114 /* sensor framework */ 115 struct ksensordev sc_sensordev; 116 struct sensor_task *sc_sensortask; 117 struct upd_report *sc_reports; 118 struct upd_sensor *sc_sensors; 119 struct upd_sensor_head sc_root_sensors; 120}; 121 122int upd_match(struct device *, void *, void *); 123void upd_attach(struct device *, struct device *, void *); 124void upd_attach_sensor_tree(struct upd_softc *, void *, int, int, 125 struct upd_usage_entry *, struct upd_sensor_head *); 126int upd_detach(struct device *, int); 127 128void upd_intr(struct uhidev *, void *, uint); 129void upd_refresh(void *); 130void upd_request_children(struct upd_softc *, struct upd_sensor_head *); 131void upd_update_report_cb(void *, int, void *, int); 132 133void upd_sensor_invalidate(struct upd_softc *, struct upd_sensor *); 134void upd_sensor_update(struct upd_softc *, struct upd_sensor *, uint8_t *, int); 135int upd_lookup_usage_entry(void *, int, struct upd_usage_entry *, 136 struct hid_item *); 137struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int); 138 139struct cfdriver upd_cd = { 140 NULL, "upd", DV_DULL 141}; 142 143const struct cfattach upd_ca = { 144 sizeof(struct upd_softc), upd_match, upd_attach, upd_detach 145}; 146 147int 148upd_match(struct device *parent, void *match, void *aux) 149{ 150 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 151 int size; 152 void *desc; 153 struct hid_item item; 154 int ret = UMATCH_NONE; 155 int i; 156 157 if (!UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 158 return (ret); 159 160 DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor, 161 uha->uaa->product)); 162 163 /* need at least one sensor from root of tree */ 164 uhidev_get_report_desc(uha->parent, &desc, &size); 165 for (i = 0; i < nitems(upd_usage_roots); i++) 166 if (upd_lookup_usage_entry(desc, size, 167 upd_usage_roots + i, &item)) { 168 ret = UMATCH_VENDOR_PRODUCT; 169 uha->claimed[item.report_ID] = 1; 170 } 171 172 return (ret); 173} 174 175void 176upd_attach(struct device *parent, struct device *self, void *aux) 177{ 178 struct upd_softc *sc = (struct upd_softc *)self; 179 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 180 int size; 181 int i; 182 void *desc; 183 184 sc->sc_hdev.sc_intr = upd_intr; 185 sc->sc_hdev.sc_parent = uha->parent; 186 SLIST_INIT(&sc->sc_root_sensors); 187 188 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 189 sizeof(sc->sc_sensordev.xname)); 190 191 sc->sc_max_repid = uha->parent->sc_nrepid; 192 DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n", 193 DEVNAME(sc), sc->sc_max_repid)); 194 195 sc->sc_reports = mallocarray(sc->sc_max_repid, 196 sizeof(struct upd_report), M_USBDEV, M_WAITOK | M_ZERO); 197 for (i = 0; i < sc->sc_max_repid; i++) 198 SLIST_INIT(&sc->sc_reports[i].sensors); 199 sc->sc_sensors = mallocarray(UPD_MAX_SENSORS, 200 sizeof(struct upd_sensor), M_USBDEV, M_WAITOK | M_ZERO); 201 for (i = 0; i < UPD_MAX_SENSORS; i++) 202 SLIST_INIT(&sc->sc_sensors[i].children); 203 204 sc->sc_num_sensors = 0; 205 uhidev_get_report_desc(uha->parent, &desc, &size); 206 upd_attach_sensor_tree(sc, desc, size, nitems(upd_usage_roots), 207 upd_usage_roots, &sc->sc_root_sensors); 208 DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors)); 209 210 sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6); 211 if (sc->sc_sensortask == NULL) { 212 printf(", unable to register update task\n"); 213 return; 214 } 215 sensordev_install(&sc->sc_sensordev); 216 217 printf("\n"); 218 219 DPRINTF(("upd_attach: complete\n")); 220} 221 222void 223upd_attach_sensor_tree(struct upd_softc *sc, void *desc, int size, 224 int nentries, struct upd_usage_entry *entries, 225 struct upd_sensor_head *queue) 226{ 227 struct hid_item item; 228 struct upd_usage_entry *entry; 229 struct upd_sensor *sensor; 230 struct upd_report *report; 231 int i; 232 233 for (i = 0; i < nentries; i++) { 234 entry = entries + i; 235 if (!upd_lookup_usage_entry(desc, size, entry, &item)) { 236 /* dependency missing, add children to parent */ 237 upd_attach_sensor_tree(sc, desc, size, 238 entry->nchildren, entry->children, queue); 239 continue; 240 } 241 242 DPRINTF(("%s: found %s on repid=%d\n", DEVNAME(sc), 243 entry->usage_name, item.report_ID)); 244 if (item.report_ID < 0 || 245 item.report_ID >= sc->sc_max_repid) 246 continue; 247 248 sensor = &sc->sc_sensors[sc->sc_num_sensors]; 249 memcpy(&sensor->hitem, &item, sizeof(struct hid_item)); 250 strlcpy(sensor->ksensor.desc, entry->usage_name, 251 sizeof(sensor->ksensor.desc)); 252 sensor->ksensor.type = entry->senstype; 253 sensor->ksensor.flags |= SENSOR_FINVALID; 254 sensor->ksensor.status = SENSOR_S_UNKNOWN; 255 sensor->ksensor.value = 0; 256 sensor_attach(&sc->sc_sensordev, &sensor->ksensor); 257 sensor->attached = 1; 258 SLIST_INSERT_HEAD(queue, sensor, dep_next); 259 sc->sc_num_sensors++; 260 261 upd_attach_sensor_tree(sc, desc, size, entry->nchildren, 262 entry->children, &sensor->children); 263 264 report = &sc->sc_reports[item.report_ID]; 265 if (SLIST_EMPTY(&report->sensors)) 266 report->size = hid_report_size(desc, 267 size, item.kind, item.report_ID); 268 SLIST_INSERT_HEAD(&report->sensors, sensor, rep_next); 269 } 270} 271 272int 273upd_detach(struct device *self, int flags) 274{ 275 struct upd_softc *sc = (struct upd_softc *)self; 276 struct upd_sensor *sensor; 277 int i; 278 279 if (sc->sc_sensortask != NULL) 280 sensor_task_unregister(sc->sc_sensortask); 281 282 sensordev_deinstall(&sc->sc_sensordev); 283 284 for (i = 0; i < sc->sc_num_sensors; i++) { 285 sensor = &sc->sc_sensors[i]; 286 if (sensor->attached) 287 sensor_detach(&sc->sc_sensordev, &sensor->ksensor); 288 } 289 290 free(sc->sc_reports, M_USBDEV, sc->sc_max_repid * sizeof(struct upd_report)); 291 free(sc->sc_sensors, M_USBDEV, UPD_MAX_SENSORS * sizeof(struct upd_sensor)); 292 return (0); 293} 294 295void 296upd_refresh(void *arg) 297{ 298 struct upd_softc *sc = arg; 299 int s; 300 301 /* request root sensors, do not let async handlers fire yet */ 302 s = splusb(); 303 upd_request_children(sc, &sc->sc_root_sensors); 304 splx(s); 305} 306 307void 308upd_request_children(struct upd_softc *sc, struct upd_sensor_head *queue) 309{ 310 struct upd_sensor *sensor; 311 struct upd_report *report; 312 int len, repid; 313 314 SLIST_FOREACH(sensor, queue, dep_next) { 315 repid = sensor->hitem.report_ID; 316 report = &sc->sc_reports[repid]; 317 318 /* already requested */ 319 if (report->pending) 320 continue; 321 report->pending = 1; 322 323 len = uhidev_get_report_async(sc->sc_hdev.sc_parent, 324 UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size, sc, 325 upd_update_report_cb); 326 327 /* request failed, force-invalidate all sensors in report */ 328 if (len < 0) { 329 upd_update_report_cb(sc, repid, NULL, -1); 330 report->pending = 0; 331 } 332 } 333} 334 335int 336upd_lookup_usage_entry(void *desc, int size, struct upd_usage_entry *entry, 337 struct hid_item *item) 338{ 339 struct hid_data *hdata; 340 int ret = 0; 341 342 for (hdata = hid_start_parse(desc, size, hid_feature); 343 hid_get_item(hdata, item); ) { 344 if (item->kind == hid_feature && 345 entry->usage_pg == HID_GET_USAGE_PAGE(item->usage) && 346 entry->usage_id == HID_GET_USAGE(item->usage)) { 347 ret = 1; 348 break; 349 } 350 } 351 hid_end_parse(hdata); 352 353 return (ret); 354} 355 356struct upd_sensor * 357upd_lookup_sensor(struct upd_softc *sc, int page, int usage) 358{ 359 struct upd_sensor *sensor = NULL; 360 int i; 361 362 for (i = 0; i < sc->sc_num_sensors; i++) { 363 sensor = &sc->sc_sensors[i]; 364 if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) && 365 usage == HID_GET_USAGE(sensor->hitem.usage)) 366 return (sensor); 367 } 368 return (NULL); 369} 370 371void 372upd_update_report_cb(void *priv, int repid, void *data, int len) 373{ 374 struct upd_softc *sc = priv; 375 struct upd_report *report = &sc->sc_reports[repid]; 376 struct upd_sensor *sensor; 377 378 /* handle buggy firmware */ 379 if (len > 0 && report->size != len) 380 report->size = len; 381 382 if (data == NULL || len <= 0) { 383 SLIST_FOREACH(sensor, &report->sensors, rep_next) 384 upd_sensor_invalidate(sc, sensor); 385 } else { 386 SLIST_FOREACH(sensor, &report->sensors, rep_next) 387 upd_sensor_update(sc, sensor, data, len); 388 } 389 report->pending = 0; 390} 391 392void 393upd_sensor_invalidate(struct upd_softc *sc, struct upd_sensor *sensor) 394{ 395 struct upd_sensor *child; 396 397 sensor->ksensor.status = SENSOR_S_UNKNOWN; 398 sensor->ksensor.flags |= SENSOR_FINVALID; 399 400 SLIST_FOREACH(child, &sensor->children, dep_next) 401 upd_sensor_invalidate(sc, child); 402} 403 404void 405upd_sensor_update(struct upd_softc *sc, struct upd_sensor *sensor, 406 uint8_t *buf, int len) 407{ 408 struct upd_sensor *child; 409 int64_t hdata, adjust; 410 411 switch (HID_GET_USAGE(sensor->hitem.usage)) { 412 case HUB_REL_STATEOF_CHARGE: 413 case HUB_ABS_STATEOF_CHARGE: 414 case HUB_REM_CAPACITY: 415 case HUB_FULLCHARGE_CAPACITY: 416 case HUP_PERCENT_LOAD: 417 adjust = 1000; /* scale adjust */ 418 break; 419 case HUB_ATRATE_TIMETOFULL: 420 case HUB_ATRATE_TIMETOEMPTY: 421 case HUB_RUNTIMETO_EMPTY: 422 /* spec says minutes, not seconds */ 423 adjust = 1000000000LL; 424 break; 425 default: 426 adjust = 1; /* no scale adjust */ 427 break; 428 } 429 430 hdata = hid_get_data(buf, len, &sensor->hitem.loc); 431 switch (HID_GET_USAGE(sensor->hitem.usage)) { 432 case HUB_RUNTIMETO_EMPTY: 433 /* 434 * If the value is reported as a 4-byte item, 435 * assume the lowest 8 bits of the value are 436 * extra, unnecessary, precision, and discard 437 * them. 438 * This happens to match what sysutils/nut 439 * reports. 440 */ 441 if (len == 4) 442 hdata = hdata >> 8; 443 break; 444 } 445 if (sensor->ksensor.type == SENSOR_INDICATOR) 446 sensor->ksensor.value = hdata ? 1 : 0; 447 else 448 sensor->ksensor.value = hdata * adjust; 449 sensor->ksensor.status = SENSOR_S_OK; 450 sensor->ksensor.flags &= ~SENSOR_FINVALID; 451 452 /* if battery not present, invalidate children */ 453 if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY && 454 HID_GET_USAGE(sensor->hitem.usage) == HUB_BATTERY_PRESENT && 455 sensor->ksensor.value == 0) { 456 SLIST_FOREACH(child, &sensor->children, dep_next) 457 upd_sensor_invalidate(sc, child); 458 return; 459 } 460 461 upd_request_children(sc, &sensor->children); 462} 463 464void 465upd_intr(struct uhidev *uh, void *p, uint len) 466{ 467 /* noop */ 468}