1 /* $OpenBSD: upd.c,v 1.32 2024/05/23 03:21:09 jsg 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
45 struct 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
54 static 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_BATTERY, HUB_CHARGING,
64 SENSOR_INDICATOR, "Charging" },
65 { HUP_BATTERY, HUB_DISCHARGING,
66 SENSOR_INDICATOR, "Discharging" },
67 { HUP_BATTERY, HUB_ATRATE_TIMETOFULL,
68 SENSOR_TIMEDELTA, "AtRateTimeToFull" },
69 { HUP_BATTERY, HUB_ATRATE_TIMETOEMPTY,
70 SENSOR_TIMEDELTA, "AtRateTimeToEmpty" },
71 { HUP_BATTERY, HUB_RUNTIMETO_EMPTY,
72 SENSOR_TIMEDELTA, "RunTimeToEmpty" },
73 { HUP_BATTERY, HUB_NEED_REPLACEMENT,
74 SENSOR_INDICATOR, "NeedReplacement" },
75 };
76 static struct upd_usage_entry upd_usage_roots[] = {
77 { HUP_BATTERY, HUB_BATTERY_PRESENT,
78 SENSOR_INDICATOR, "BatteryPresent",
79 nitems(upd_usage_batdep), upd_usage_batdep },
80 { HUP_POWER, HUP_SHUTDOWN_IMMINENT,
81 SENSOR_INDICATOR, "ShutdownImminent" },
82 { HUP_BATTERY, HUB_AC_PRESENT,
83 SENSOR_INDICATOR, "ACPresent" },
84 { HUP_POWER, HUP_OVERLOAD,
85 SENSOR_INDICATOR, "Overload" },
86 };
87 #define UPD_MAX_SENSORS (nitems(upd_usage_batdep) + nitems(upd_usage_roots))
88
89 SLIST_HEAD(upd_sensor_head, upd_sensor);
90
91 struct upd_report {
92 size_t size; /* Size of the report */
93 struct upd_sensor_head sensors; /* List in dependency order */
94 int pending; /* Waiting for an answer */
95 };
96
97 struct upd_sensor {
98 struct ksensor ksensor;
99 struct hid_item hitem;
100 int attached; /* Is there a matching report */
101 struct upd_sensor_head children; /* list of children sensors */
102 SLIST_ENTRY(upd_sensor) dep_next; /* next in the child list */
103 SLIST_ENTRY(upd_sensor) rep_next; /* next in the report list */
104 };
105
106 struct upd_softc {
107 struct uhidev sc_hdev;
108 int sc_num_sensors;
109 u_int sc_max_repid;
110 char sc_buf[256];
111
112 /* sensor framework */
113 struct ksensordev sc_sensordev;
114 struct sensor_task *sc_sensortask;
115 struct upd_report *sc_reports;
116 struct upd_sensor *sc_sensors;
117 struct upd_sensor_head sc_root_sensors;
118 };
119
120 int upd_match(struct device *, void *, void *);
121 void upd_attach(struct device *, struct device *, void *);
122 void upd_attach_sensor_tree(struct upd_softc *, void *, int, int,
123 struct upd_usage_entry *, struct upd_sensor_head *);
124 int upd_detach(struct device *, int);
125
126 void upd_intr(struct uhidev *, void *, uint);
127 void upd_refresh(void *);
128 void upd_request_children(struct upd_softc *, struct upd_sensor_head *);
129 void upd_update_report_cb(void *, int, void *, int);
130
131 void upd_sensor_invalidate(struct upd_softc *, struct upd_sensor *);
132 void upd_sensor_update(struct upd_softc *, struct upd_sensor *, uint8_t *, int);
133 int upd_lookup_usage_entry(void *, int, struct upd_usage_entry *,
134 struct hid_item *);
135 struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int);
136
137 struct cfdriver upd_cd = {
138 NULL, "upd", DV_DULL
139 };
140
141 const struct cfattach upd_ca = {
142 sizeof(struct upd_softc), upd_match, upd_attach, upd_detach
143 };
144
145 int
upd_match(struct device * parent,void * match,void * aux)146 upd_match(struct device *parent, void *match, void *aux)
147 {
148 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
149 int size;
150 void *desc;
151 struct hid_item item;
152 int ret = UMATCH_NONE;
153 int i;
154
155 if (!UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
156 return (ret);
157
158 DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor,
159 uha->uaa->product));
160
161 /* need at least one sensor from root of tree */
162 uhidev_get_report_desc(uha->parent, &desc, &size);
163 for (i = 0; i < nitems(upd_usage_roots); i++)
164 if (upd_lookup_usage_entry(desc, size,
165 upd_usage_roots + i, &item)) {
166 ret = UMATCH_VENDOR_PRODUCT;
167 uha->claimed[item.report_ID] = 1;
168 }
169
170 return (ret);
171 }
172
173 void
upd_attach(struct device * parent,struct device * self,void * aux)174 upd_attach(struct device *parent, struct device *self, void *aux)
175 {
176 struct upd_softc *sc = (struct upd_softc *)self;
177 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
178 int size;
179 int i;
180 void *desc;
181
182 sc->sc_hdev.sc_intr = upd_intr;
183 sc->sc_hdev.sc_parent = uha->parent;
184 SLIST_INIT(&sc->sc_root_sensors);
185
186 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
187 sizeof(sc->sc_sensordev.xname));
188
189 sc->sc_max_repid = uha->parent->sc_nrepid;
190 DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n",
191 DEVNAME(sc), sc->sc_max_repid));
192
193 sc->sc_reports = mallocarray(sc->sc_max_repid,
194 sizeof(struct upd_report), M_USBDEV, M_WAITOK | M_ZERO);
195 for (i = 0; i < sc->sc_max_repid; i++)
196 SLIST_INIT(&sc->sc_reports[i].sensors);
197 sc->sc_sensors = mallocarray(UPD_MAX_SENSORS,
198 sizeof(struct upd_sensor), M_USBDEV, M_WAITOK | M_ZERO);
199 for (i = 0; i < UPD_MAX_SENSORS; i++)
200 SLIST_INIT(&sc->sc_sensors[i].children);
201
202 sc->sc_num_sensors = 0;
203 uhidev_get_report_desc(uha->parent, &desc, &size);
204 upd_attach_sensor_tree(sc, desc, size, nitems(upd_usage_roots),
205 upd_usage_roots, &sc->sc_root_sensors);
206 DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
207
208 sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
209 if (sc->sc_sensortask == NULL) {
210 printf(", unable to register update task\n");
211 return;
212 }
213 sensordev_install(&sc->sc_sensordev);
214
215 printf("\n");
216
217 DPRINTF(("upd_attach: complete\n"));
218 }
219
220 void
upd_attach_sensor_tree(struct upd_softc * sc,void * desc,int size,int nentries,struct upd_usage_entry * entries,struct upd_sensor_head * queue)221 upd_attach_sensor_tree(struct upd_softc *sc, void *desc, int size,
222 int nentries, struct upd_usage_entry *entries,
223 struct upd_sensor_head *queue)
224 {
225 struct hid_item item;
226 struct upd_usage_entry *entry;
227 struct upd_sensor *sensor;
228 struct upd_report *report;
229 int i;
230
231 for (i = 0; i < nentries; i++) {
232 entry = entries + i;
233 if (!upd_lookup_usage_entry(desc, size, entry, &item)) {
234 /* dependency missing, add children to parent */
235 upd_attach_sensor_tree(sc, desc, size,
236 entry->nchildren, entry->children, queue);
237 continue;
238 }
239
240 DPRINTF(("%s: found %s on repid=%d\n", DEVNAME(sc),
241 entry->usage_name, item.report_ID));
242 if (item.report_ID < 0 ||
243 item.report_ID >= sc->sc_max_repid)
244 continue;
245
246 sensor = &sc->sc_sensors[sc->sc_num_sensors];
247 memcpy(&sensor->hitem, &item, sizeof(struct hid_item));
248 strlcpy(sensor->ksensor.desc, entry->usage_name,
249 sizeof(sensor->ksensor.desc));
250 sensor->ksensor.type = entry->senstype;
251 sensor->ksensor.flags |= SENSOR_FINVALID;
252 sensor->ksensor.status = SENSOR_S_UNKNOWN;
253 sensor->ksensor.value = 0;
254 sensor_attach(&sc->sc_sensordev, &sensor->ksensor);
255 sensor->attached = 1;
256 SLIST_INSERT_HEAD(queue, sensor, dep_next);
257 sc->sc_num_sensors++;
258
259 upd_attach_sensor_tree(sc, desc, size, entry->nchildren,
260 entry->children, &sensor->children);
261
262 report = &sc->sc_reports[item.report_ID];
263 if (SLIST_EMPTY(&report->sensors))
264 report->size = hid_report_size(desc,
265 size, item.kind, item.report_ID);
266 SLIST_INSERT_HEAD(&report->sensors, sensor, rep_next);
267 }
268 }
269
270 int
upd_detach(struct device * self,int flags)271 upd_detach(struct device *self, int flags)
272 {
273 struct upd_softc *sc = (struct upd_softc *)self;
274 struct upd_sensor *sensor;
275 int i;
276
277 if (sc->sc_sensortask != NULL)
278 sensor_task_unregister(sc->sc_sensortask);
279
280 sensordev_deinstall(&sc->sc_sensordev);
281
282 for (i = 0; i < sc->sc_num_sensors; i++) {
283 sensor = &sc->sc_sensors[i];
284 if (sensor->attached)
285 sensor_detach(&sc->sc_sensordev, &sensor->ksensor);
286 }
287
288 free(sc->sc_reports, M_USBDEV, sc->sc_max_repid * sizeof(struct upd_report));
289 free(sc->sc_sensors, M_USBDEV, UPD_MAX_SENSORS * sizeof(struct upd_sensor));
290 return (0);
291 }
292
293 void
upd_refresh(void * arg)294 upd_refresh(void *arg)
295 {
296 struct upd_softc *sc = arg;
297 int s;
298
299 /* request root sensors, do not let async handlers fire yet */
300 s = splusb();
301 upd_request_children(sc, &sc->sc_root_sensors);
302 splx(s);
303 }
304
305 void
upd_request_children(struct upd_softc * sc,struct upd_sensor_head * queue)306 upd_request_children(struct upd_softc *sc, struct upd_sensor_head *queue)
307 {
308 struct upd_sensor *sensor;
309 struct upd_report *report;
310 int len, repid;
311
312 SLIST_FOREACH(sensor, queue, dep_next) {
313 repid = sensor->hitem.report_ID;
314 report = &sc->sc_reports[repid];
315
316 /* already requested */
317 if (report->pending)
318 continue;
319 report->pending = 1;
320
321 len = uhidev_get_report_async(sc->sc_hdev.sc_parent,
322 UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size, sc,
323 upd_update_report_cb);
324
325 /* request failed, force-invalidate all sensors in report */
326 if (len < 0) {
327 upd_update_report_cb(sc, repid, NULL, -1);
328 report->pending = 0;
329 }
330 }
331 }
332
333 int
upd_lookup_usage_entry(void * desc,int size,struct upd_usage_entry * entry,struct hid_item * item)334 upd_lookup_usage_entry(void *desc, int size, struct upd_usage_entry *entry,
335 struct hid_item *item)
336 {
337 struct hid_data *hdata;
338 int ret = 0;
339
340 for (hdata = hid_start_parse(desc, size, hid_feature);
341 hid_get_item(hdata, item); ) {
342 if (item->kind == hid_feature &&
343 entry->usage_pg == HID_GET_USAGE_PAGE(item->usage) &&
344 entry->usage_id == HID_GET_USAGE(item->usage)) {
345 ret = 1;
346 break;
347 }
348 }
349 hid_end_parse(hdata);
350
351 return (ret);
352 }
353
354 struct upd_sensor *
upd_lookup_sensor(struct upd_softc * sc,int page,int usage)355 upd_lookup_sensor(struct upd_softc *sc, int page, int usage)
356 {
357 struct upd_sensor *sensor = NULL;
358 int i;
359
360 for (i = 0; i < sc->sc_num_sensors; i++) {
361 sensor = &sc->sc_sensors[i];
362 if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) &&
363 usage == HID_GET_USAGE(sensor->hitem.usage))
364 return (sensor);
365 }
366 return (NULL);
367 }
368
369 void
upd_update_report_cb(void * priv,int repid,void * data,int len)370 upd_update_report_cb(void *priv, int repid, void *data, int len)
371 {
372 struct upd_softc *sc = priv;
373 struct upd_report *report = &sc->sc_reports[repid];
374 struct upd_sensor *sensor;
375
376 /* handle buggy firmware */
377 if (len > 0 && report->size != len)
378 report->size = len;
379
380 if (data == NULL || len <= 0) {
381 SLIST_FOREACH(sensor, &report->sensors, rep_next)
382 upd_sensor_invalidate(sc, sensor);
383 } else {
384 SLIST_FOREACH(sensor, &report->sensors, rep_next)
385 upd_sensor_update(sc, sensor, data, len);
386 }
387 report->pending = 0;
388 }
389
390 void
upd_sensor_invalidate(struct upd_softc * sc,struct upd_sensor * sensor)391 upd_sensor_invalidate(struct upd_softc *sc, struct upd_sensor *sensor)
392 {
393 struct upd_sensor *child;
394
395 sensor->ksensor.status = SENSOR_S_UNKNOWN;
396 sensor->ksensor.flags |= SENSOR_FINVALID;
397
398 SLIST_FOREACH(child, &sensor->children, dep_next)
399 upd_sensor_invalidate(sc, child);
400 }
401
402 void
upd_sensor_update(struct upd_softc * sc,struct upd_sensor * sensor,uint8_t * buf,int len)403 upd_sensor_update(struct upd_softc *sc, struct upd_sensor *sensor,
404 uint8_t *buf, int len)
405 {
406 struct upd_sensor *child;
407 int64_t hdata, adjust;
408
409 switch (HID_GET_USAGE(sensor->hitem.usage)) {
410 case HUB_REL_STATEOF_CHARGE:
411 case HUB_ABS_STATEOF_CHARGE:
412 case HUB_REM_CAPACITY:
413 case HUB_FULLCHARGE_CAPACITY:
414 adjust = 1000; /* scale adjust */
415 break;
416 case HUB_ATRATE_TIMETOFULL:
417 case HUB_ATRATE_TIMETOEMPTY:
418 case HUB_RUNTIMETO_EMPTY:
419 /* spec says minutes, not seconds */
420 adjust = 1000000000LL;
421 break;
422 default:
423 adjust = 1; /* no scale adjust */
424 break;
425 }
426
427 hdata = hid_get_data(buf, len, &sensor->hitem.loc);
428 if (sensor->ksensor.type == SENSOR_INDICATOR)
429 sensor->ksensor.value = hdata ? 1 : 0;
430 else
431 sensor->ksensor.value = hdata * adjust;
432 sensor->ksensor.status = SENSOR_S_OK;
433 sensor->ksensor.flags &= ~SENSOR_FINVALID;
434
435 /* if battery not present, invalidate children */
436 if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
437 HID_GET_USAGE(sensor->hitem.usage) == HUB_BATTERY_PRESENT &&
438 sensor->ksensor.value == 0) {
439 SLIST_FOREACH(child, &sensor->children, dep_next)
440 upd_sensor_invalidate(sc, child);
441 return;
442 }
443
444 upd_request_children(sc, &sensor->children);
445 }
446
447 void
upd_intr(struct uhidev * uh,void * p,uint len)448 upd_intr(struct uhidev *uh, void *p, uint len)
449 {
450 /* noop */
451 }
452