jcs's openbsd hax
openbsd
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}