xref: /openbsd-src/sys/dev/usb/upd.c (revision 8e650cf52b0571c05072b61eb11ed30d92720dda)
1*8e650cf5Slandry /*	$OpenBSD: upd.c,v 1.33 2024/12/01 09:05:05 landry Exp $ */
259b9e08dSandre 
359b9e08dSandre /*
4a2254ca0Smpi  * Copyright (c) 2015 David Higgs <higgsd@gmail.com>
559b9e08dSandre  * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org>
659b9e08dSandre  *
759b9e08dSandre  * Permission to use, copy, modify, and distribute this software for any
859b9e08dSandre  * purpose with or without fee is hereby granted, provided that the above
959b9e08dSandre  * copyright notice and this permission notice appear in all copies.
1059b9e08dSandre  *
1159b9e08dSandre  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
1259b9e08dSandre  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1359b9e08dSandre  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1459b9e08dSandre  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1559b9e08dSandre  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1659b9e08dSandre  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1759b9e08dSandre  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1859b9e08dSandre  */
1959b9e08dSandre 
20a007768dSsthen /*
21a007768dSsthen  * Driver for USB Power Devices sensors
22a007768dSsthen  * https://usb.org/sites/default/files/pdcv10.pdf
23a007768dSsthen  */
2459b9e08dSandre 
2559b9e08dSandre #include <sys/param.h>
2659b9e08dSandre #include <sys/systm.h>
2759b9e08dSandre #include <sys/malloc.h>
2859b9e08dSandre #include <sys/device.h>
293d645ebbSmpi #include <sys/queue.h>
3059b9e08dSandre #include <sys/sensors.h>
3159b9e08dSandre 
3259b9e08dSandre #include <dev/usb/usb.h>
3359b9e08dSandre #include <dev/usb/usbdi.h>
3459b9e08dSandre #include <dev/usb/usbhid.h>
3559b9e08dSandre #include <dev/usb/uhidev.h>
3659b9e08dSandre 
3759b9e08dSandre #ifdef UPD_DEBUG
3859b9e08dSandre #define DPRINTF(x)	do { printf x; } while (0)
3959b9e08dSandre #else
4059b9e08dSandre #define DPRINTF(x)
4159b9e08dSandre #endif
4259b9e08dSandre 
436acf6676Smpi #define DEVNAME(sc)	((sc)->sc_hdev.sc_dev.dv_xname)
446acf6676Smpi 
4559b9e08dSandre struct upd_usage_entry {
4659b9e08dSandre 	uint8_t			usage_pg;
4759b9e08dSandre 	uint8_t			usage_id;
4859b9e08dSandre 	enum sensor_type	senstype;
4959b9e08dSandre 	char			*usage_name; /* sensor string */
503d645ebbSmpi 	int			nchildren;
513d645ebbSmpi 	struct upd_usage_entry	*children;
5259b9e08dSandre };
5359b9e08dSandre 
543d645ebbSmpi static struct upd_usage_entry upd_usage_batdep[] = {
550415792bSandre 	{ HUP_BATTERY,	HUB_REL_STATEOF_CHARGE,
5659b9e08dSandre 	    SENSOR_PERCENT,	 "RelativeStateOfCharge" },
570415792bSandre 	{ HUP_BATTERY,	HUB_ABS_STATEOF_CHARGE,
5859b9e08dSandre 	    SENSOR_PERCENT,	 "AbsoluteStateOfCharge" },
590415792bSandre 	{ HUP_BATTERY,	HUB_REM_CAPACITY,
6059b9e08dSandre 	    SENSOR_PERCENT,	 "RemainingCapacity" },
610415792bSandre 	{ HUP_BATTERY,	HUB_FULLCHARGE_CAPACITY,
6259b9e08dSandre 	    SENSOR_PERCENT,	 "FullChargeCapacity" },
63*8e650cf5Slandry 	{ HUP_POWER,	HUP_PERCENT_LOAD,
64*8e650cf5Slandry 	    SENSOR_PERCENT,	 "PercentLoad" },
650415792bSandre 	{ HUP_BATTERY,	HUB_CHARGING,
6659b9e08dSandre 	    SENSOR_INDICATOR,	 "Charging" },
670415792bSandre 	{ HUP_BATTERY,	HUB_DISCHARGING,
6859b9e08dSandre 	    SENSOR_INDICATOR,	 "Discharging" },
690415792bSandre 	{ HUP_BATTERY,	HUB_ATRATE_TIMETOFULL,
70d55e24b2Smpi 	    SENSOR_TIMEDELTA,	 "AtRateTimeToFull" },
71d55e24b2Smpi 	{ HUP_BATTERY,	HUB_ATRATE_TIMETOEMPTY,
72d55e24b2Smpi 	    SENSOR_TIMEDELTA,	 "AtRateTimeToEmpty" },
73d55e24b2Smpi 	{ HUP_BATTERY,	HUB_RUNTIMETO_EMPTY,
74d55e24b2Smpi 	    SENSOR_TIMEDELTA,	 "RunTimeToEmpty" },
75d55e24b2Smpi 	{ HUP_BATTERY,	HUB_NEED_REPLACEMENT,
76d55e24b2Smpi 	    SENSOR_INDICATOR,	 "NeedReplacement" },
7759b9e08dSandre };
783d645ebbSmpi static struct upd_usage_entry upd_usage_roots[] = {
793d645ebbSmpi 	{ HUP_BATTERY,	HUB_BATTERY_PRESENT,
803d645ebbSmpi 	    SENSOR_INDICATOR,	 "BatteryPresent",
813d645ebbSmpi 	    nitems(upd_usage_batdep),	upd_usage_batdep },
823d645ebbSmpi 	{ HUP_POWER,	HUP_SHUTDOWN_IMMINENT,
833d645ebbSmpi 	    SENSOR_INDICATOR,	 "ShutdownImminent" },
843d645ebbSmpi 	{ HUP_BATTERY,	HUB_AC_PRESENT,
85d55e24b2Smpi 	    SENSOR_INDICATOR,	 "ACPresent" },
86d55e24b2Smpi 	{ HUP_POWER,	HUP_OVERLOAD,
87d55e24b2Smpi 	    SENSOR_INDICATOR,	 "Overload" },
883d645ebbSmpi };
893d645ebbSmpi #define UPD_MAX_SENSORS	(nitems(upd_usage_batdep) + nitems(upd_usage_roots))
9059b9e08dSandre 
91a2254ca0Smpi SLIST_HEAD(upd_sensor_head, upd_sensor);
92a2254ca0Smpi 
930415792bSandre struct upd_report {
94a2254ca0Smpi 	size_t			size;		/* Size of the report */
95a2254ca0Smpi 	struct upd_sensor_head	sensors;	/* List in dependency order */
96a2254ca0Smpi 	int			pending;	/* Waiting for an answer */
970415792bSandre };
980415792bSandre 
9959b9e08dSandre struct upd_sensor {
1000415792bSandre 	struct ksensor		ksensor;
1010415792bSandre 	struct hid_item		hitem;
102a2254ca0Smpi 	int			attached;	/* Is there a matching report */
103a2254ca0Smpi 	struct upd_sensor_head	children;	/* list of children sensors */
104a2254ca0Smpi 	SLIST_ENTRY(upd_sensor)	dep_next;	/* next in the child list */
105a2254ca0Smpi 	SLIST_ENTRY(upd_sensor)	rep_next;	/* next in the report list */
10659b9e08dSandre };
10759b9e08dSandre 
10859b9e08dSandre struct upd_softc {
10959b9e08dSandre 	struct uhidev		 sc_hdev;
11059b9e08dSandre 	int			 sc_num_sensors;
1110415792bSandre 	u_int			 sc_max_repid;
112a2254ca0Smpi 	char			 sc_buf[256];
11359b9e08dSandre 
11459b9e08dSandre 	/* sensor framework */
11559b9e08dSandre 	struct ksensordev	 sc_sensordev;
11659b9e08dSandre 	struct sensor_task	*sc_sensortask;
1170415792bSandre 	struct upd_report	*sc_reports;
1180415792bSandre 	struct upd_sensor	*sc_sensors;
1193d645ebbSmpi 	struct upd_sensor_head	 sc_root_sensors;
12059b9e08dSandre };
12159b9e08dSandre 
12259b9e08dSandre int  upd_match(struct device *, void *, void *);
12359b9e08dSandre void upd_attach(struct device *, struct device *, void *);
1243d645ebbSmpi void upd_attach_sensor_tree(struct upd_softc *, void *, int, int,
1253d645ebbSmpi     struct upd_usage_entry *, struct upd_sensor_head *);
12659b9e08dSandre int  upd_detach(struct device *, int);
12759b9e08dSandre 
12859b9e08dSandre void upd_intr(struct uhidev *, void *, uint);
129a2254ca0Smpi void upd_refresh(void *);
130a2254ca0Smpi void upd_request_children(struct upd_softc *, struct upd_sensor_head *);
131a2254ca0Smpi void upd_update_report_cb(void *, int, void *, int);
132a2254ca0Smpi 
133a2254ca0Smpi void upd_sensor_invalidate(struct upd_softc *, struct upd_sensor *);
134a2254ca0Smpi void upd_sensor_update(struct upd_softc *, struct upd_sensor *, uint8_t *, int);
135dd459b5aSmpi int upd_lookup_usage_entry(void *, int, struct upd_usage_entry *,
136dd459b5aSmpi     struct hid_item *);
1370415792bSandre struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int);
13859b9e08dSandre 
13959b9e08dSandre struct cfdriver upd_cd = {
14059b9e08dSandre 	NULL, "upd", DV_DULL
14159b9e08dSandre };
14259b9e08dSandre 
14359b9e08dSandre const struct cfattach upd_ca = {
144a2254ca0Smpi 	sizeof(struct upd_softc), upd_match, upd_attach, upd_detach
14559b9e08dSandre };
14659b9e08dSandre 
14759b9e08dSandre int
14859b9e08dSandre upd_match(struct device *parent, void *match, void *aux)
14959b9e08dSandre {
15059b9e08dSandre 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
1510415792bSandre 	int			  size;
1520415792bSandre 	void			 *desc;
1530415792bSandre 	struct hid_item		  item;
1540415792bSandre 	int			  ret = UMATCH_NONE;
155dd459b5aSmpi 	int			  i;
15659b9e08dSandre 
157faac88c0Santon 	if (!UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
1580415792bSandre 		return (ret);
15959b9e08dSandre 
1600415792bSandre 	DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor,
16159b9e08dSandre 	    uha->uaa->product));
16259b9e08dSandre 
1633d645ebbSmpi 	/* need at least one sensor from root of tree */
1640415792bSandre 	uhidev_get_report_desc(uha->parent, &desc, &size);
1653d645ebbSmpi 	for (i = 0; i < nitems(upd_usage_roots); i++)
166dd459b5aSmpi 		if (upd_lookup_usage_entry(desc, size,
1673d645ebbSmpi 		    upd_usage_roots + i, &item)) {
1680415792bSandre 			ret = UMATCH_VENDOR_PRODUCT;
169fac838e3Sabieber 			uha->claimed[item.report_ID] = 1;
1700415792bSandre 		}
1710415792bSandre 
1720415792bSandre 	return (ret);
17359b9e08dSandre }
17459b9e08dSandre 
17559b9e08dSandre void
17659b9e08dSandre upd_attach(struct device *parent, struct device *self, void *aux)
17759b9e08dSandre {
17859b9e08dSandre 	struct upd_softc	 *sc = (struct upd_softc *)self;
17959b9e08dSandre 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
18059b9e08dSandre 	int			  size;
181dd459b5aSmpi 	int			  i;
18259b9e08dSandre 	void			 *desc;
18359b9e08dSandre 
18459b9e08dSandre 	sc->sc_hdev.sc_intr = upd_intr;
18559b9e08dSandre 	sc->sc_hdev.sc_parent = uha->parent;
1863d645ebbSmpi 	SLIST_INIT(&sc->sc_root_sensors);
18759b9e08dSandre 
1886acf6676Smpi 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
18959b9e08dSandre 	    sizeof(sc->sc_sensordev.xname));
19059b9e08dSandre 
1910415792bSandre 	sc->sc_max_repid = uha->parent->sc_nrepid;
1920415792bSandre 	DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n",
1936acf6676Smpi 	    DEVNAME(sc), sc->sc_max_repid));
19459b9e08dSandre 
195b7958b67Sderaadt 	sc->sc_reports = mallocarray(sc->sc_max_repid,
196b7958b67Sderaadt 	    sizeof(struct upd_report), M_USBDEV, M_WAITOK | M_ZERO);
197c7bdea97Smpi 	for (i = 0; i < sc->sc_max_repid; i++)
198c7bdea97Smpi 		SLIST_INIT(&sc->sc_reports[i].sensors);
1993d645ebbSmpi 	sc->sc_sensors = mallocarray(UPD_MAX_SENSORS,
200b7958b67Sderaadt 	    sizeof(struct upd_sensor), M_USBDEV, M_WAITOK | M_ZERO);
2013d645ebbSmpi 	for (i = 0; i < UPD_MAX_SENSORS; i++)
2023d645ebbSmpi 		SLIST_INIT(&sc->sc_sensors[i].children);
203dd459b5aSmpi 
2043d645ebbSmpi 	sc->sc_num_sensors = 0;
20559b9e08dSandre 	uhidev_get_report_desc(uha->parent, &desc, &size);
2063d645ebbSmpi 	upd_attach_sensor_tree(sc, desc, size, nitems(upd_usage_roots),
2073d645ebbSmpi 	    upd_usage_roots, &sc->sc_root_sensors);
2083d645ebbSmpi 	DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
2093d645ebbSmpi 
2103d645ebbSmpi 	sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
2113d645ebbSmpi 	if (sc->sc_sensortask == NULL) {
2123d645ebbSmpi 		printf(", unable to register update task\n");
2133d645ebbSmpi 		return;
2143d645ebbSmpi 	}
2153d645ebbSmpi 	sensordev_install(&sc->sc_sensordev);
2163d645ebbSmpi 
2173d645ebbSmpi 	printf("\n");
2183d645ebbSmpi 
2193d645ebbSmpi 	DPRINTF(("upd_attach: complete\n"));
2203d645ebbSmpi }
2213d645ebbSmpi 
2223d645ebbSmpi void
2233d645ebbSmpi upd_attach_sensor_tree(struct upd_softc *sc, void *desc, int size,
2243d645ebbSmpi     int nentries, struct upd_usage_entry *entries,
2253d645ebbSmpi     struct upd_sensor_head *queue)
2263d645ebbSmpi {
2273d645ebbSmpi 	struct hid_item		  item;
2283d645ebbSmpi 	struct upd_usage_entry	 *entry;
2293d645ebbSmpi 	struct upd_sensor	 *sensor;
230c7bdea97Smpi 	struct upd_report	 *report;
2313d645ebbSmpi 	int			  i;
2323d645ebbSmpi 
2333d645ebbSmpi 	for (i = 0; i < nentries; i++) {
2343d645ebbSmpi 		entry = entries + i;
235d1175cffSmpi 		if (!upd_lookup_usage_entry(desc, size, entry, &item)) {
236d1175cffSmpi 			/* dependency missing, add children to parent */
237d1175cffSmpi 			upd_attach_sensor_tree(sc, desc, size,
238d1175cffSmpi 			    entry->nchildren, entry->children, queue);
239dd459b5aSmpi 			continue;
240d1175cffSmpi 		}
241dd459b5aSmpi 
242dd459b5aSmpi 		DPRINTF(("%s: found %s on repid=%d\n", DEVNAME(sc),
243dd459b5aSmpi 		    entry->usage_name, item.report_ID));
244dd459b5aSmpi 		if (item.report_ID < 0 ||
2455526e273Smpi 		    item.report_ID >= sc->sc_max_repid)
2460415792bSandre 			continue;
24759b9e08dSandre 
2480415792bSandre 		sensor = &sc->sc_sensors[sc->sc_num_sensors];
2490415792bSandre 		memcpy(&sensor->hitem, &item, sizeof(struct hid_item));
2500415792bSandre 		strlcpy(sensor->ksensor.desc, entry->usage_name,
2510415792bSandre 		    sizeof(sensor->ksensor.desc));
2520415792bSandre 		sensor->ksensor.type = entry->senstype;
2530415792bSandre 		sensor->ksensor.flags |= SENSOR_FINVALID;
2540415792bSandre 		sensor->ksensor.status = SENSOR_S_UNKNOWN;
2550415792bSandre 		sensor->ksensor.value = 0;
2560415792bSandre 		sensor_attach(&sc->sc_sensordev, &sensor->ksensor);
2570415792bSandre 		sensor->attached = 1;
2583d645ebbSmpi 		SLIST_INSERT_HEAD(queue, sensor, dep_next);
2590415792bSandre 		sc->sc_num_sensors++;
2600415792bSandre 
2613d645ebbSmpi 		upd_attach_sensor_tree(sc, desc, size, entry->nchildren,
2623d645ebbSmpi 		    entry->children, &sensor->children);
2633d645ebbSmpi 
264c7bdea97Smpi 		report = &sc->sc_reports[item.report_ID];
265c7bdea97Smpi 		if (SLIST_EMPTY(&report->sensors))
266c7bdea97Smpi 			report->size = hid_report_size(desc,
2670415792bSandre 			    size, item.kind, item.report_ID);
268c7bdea97Smpi 		SLIST_INSERT_HEAD(&report->sensors, sensor, rep_next);
2690415792bSandre 	}
27059b9e08dSandre }
27159b9e08dSandre 
27259b9e08dSandre int
27359b9e08dSandre upd_detach(struct device *self, int flags)
27459b9e08dSandre {
27559b9e08dSandre 	struct upd_softc	*sc = (struct upd_softc *)self;
27659b9e08dSandre 	struct upd_sensor	*sensor;
27759b9e08dSandre 	int			 i;
27859b9e08dSandre 
279f0f44f97Smpi 	if (sc->sc_sensortask != NULL)
28059b9e08dSandre 		sensor_task_unregister(sc->sc_sensortask);
28159b9e08dSandre 
28259b9e08dSandre 	sensordev_deinstall(&sc->sc_sensordev);
28359b9e08dSandre 
2840415792bSandre 	for (i = 0; i < sc->sc_num_sensors; i++) {
28559b9e08dSandre 		sensor = &sc->sc_sensors[i];
2860415792bSandre 		if (sensor->attached)
2870415792bSandre 			sensor_detach(&sc->sc_sensordev, &sensor->ksensor);
28859b9e08dSandre 	}
28959b9e08dSandre 
290234dfda1Sderaadt 	free(sc->sc_reports, M_USBDEV, sc->sc_max_repid * sizeof(struct upd_report));
291234dfda1Sderaadt 	free(sc->sc_sensors, M_USBDEV, UPD_MAX_SENSORS * sizeof(struct upd_sensor));
29259b9e08dSandre 	return (0);
29359b9e08dSandre }
29459b9e08dSandre 
29559b9e08dSandre void
29659b9e08dSandre upd_refresh(void *arg)
29759b9e08dSandre {
298a2254ca0Smpi 	struct upd_softc	*sc = arg;
299a2254ca0Smpi 	int			 s;
30059b9e08dSandre 
301a2254ca0Smpi 	/* request root sensors, do not let async handlers fire yet */
302a2254ca0Smpi 	s = splusb();
303a2254ca0Smpi 	upd_request_children(sc, &sc->sc_root_sensors);
304a2254ca0Smpi 	splx(s);
3056ffdf332Sandre }
3066ffdf332Sandre 
307a2254ca0Smpi void
308a2254ca0Smpi upd_request_children(struct upd_softc *sc, struct upd_sensor_head *queue)
309a2254ca0Smpi {
310a2254ca0Smpi 	struct upd_sensor	*sensor;
311a2254ca0Smpi 	struct upd_report	*report;
312a2254ca0Smpi 	int			 len, repid;
3132b2858ecSmpi 
314a2254ca0Smpi 	SLIST_FOREACH(sensor, queue, dep_next) {
315a2254ca0Smpi 		repid = sensor->hitem.report_ID;
316a2254ca0Smpi 		report = &sc->sc_reports[repid];
317a2254ca0Smpi 
318a2254ca0Smpi 		/* already requested */
319a2254ca0Smpi 		if (report->pending)
320a2254ca0Smpi 			continue;
321a2254ca0Smpi 		report->pending = 1;
322a2254ca0Smpi 
323a2254ca0Smpi 		len = uhidev_get_report_async(sc->sc_hdev.sc_parent,
324a2254ca0Smpi 		    UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size, sc,
325a2254ca0Smpi 		    upd_update_report_cb);
326a2254ca0Smpi 
327a2254ca0Smpi 		/* request failed, force-invalidate all sensors in report */
328a2254ca0Smpi 		if (len < 0) {
329a2254ca0Smpi 			upd_update_report_cb(sc, repid, NULL, -1);
330a2254ca0Smpi 			report->pending = 0;
331a2254ca0Smpi 		}
33259b9e08dSandre 	}
33359b9e08dSandre }
33459b9e08dSandre 
335dd459b5aSmpi int
336dd459b5aSmpi upd_lookup_usage_entry(void *desc, int size, struct upd_usage_entry *entry,
337dd459b5aSmpi     struct hid_item *item)
33859b9e08dSandre {
339dd459b5aSmpi 	struct hid_data	*hdata;
340dd459b5aSmpi 	int 		 ret = 0;
3410415792bSandre 
342dd459b5aSmpi 	for (hdata = hid_start_parse(desc, size, hid_feature);
343dd459b5aSmpi 	     hid_get_item(hdata, item); ) {
344dd459b5aSmpi 		if (item->kind == hid_feature &&
345dd459b5aSmpi 		    entry->usage_pg == HID_GET_USAGE_PAGE(item->usage) &&
346dd459b5aSmpi 		    entry->usage_id == HID_GET_USAGE(item->usage)) {
347dd459b5aSmpi 			ret = 1;
348dd459b5aSmpi 			break;
3490415792bSandre 		}
350dd459b5aSmpi 	}
351dd459b5aSmpi 	hid_end_parse(hdata);
352dd459b5aSmpi 
353dd459b5aSmpi 	return (ret);
3540415792bSandre }
3550415792bSandre 
3560415792bSandre struct upd_sensor *
3570415792bSandre upd_lookup_sensor(struct upd_softc *sc, int page, int usage)
3580415792bSandre {
35959b9e08dSandre 	struct upd_sensor	*sensor = NULL;
36059b9e08dSandre 	int			 i;
36159b9e08dSandre 
3620415792bSandre 	for (i = 0; i < sc->sc_num_sensors; i++) {
36359b9e08dSandre 		sensor = &sc->sc_sensors[i];
3640415792bSandre 		if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) &&
3650415792bSandre 		    usage == HID_GET_USAGE(sensor->hitem.usage))
3660415792bSandre 			return (sensor);
3670415792bSandre 	}
3680415792bSandre 	return (NULL);
3690415792bSandre }
3700415792bSandre 
3710415792bSandre void
372a2254ca0Smpi upd_update_report_cb(void *priv, int repid, void *data, int len)
3730415792bSandre {
374a2254ca0Smpi 	struct upd_softc	*sc = priv;
375a2254ca0Smpi 	struct upd_report	*report = &sc->sc_reports[repid];
3760415792bSandre 	struct upd_sensor	*sensor;
3770415792bSandre 
378a2254ca0Smpi 	/* handle buggy firmware */
379a2254ca0Smpi 	if (len > 0 && report->size != len)
380a2254ca0Smpi 		report->size = len;
3810415792bSandre 
382a2254ca0Smpi 	if (data == NULL || len <= 0) {
383a2254ca0Smpi 		SLIST_FOREACH(sensor, &report->sensors, rep_next)
384a2254ca0Smpi 			upd_sensor_invalidate(sc, sensor);
385a2254ca0Smpi 	} else {
386a2254ca0Smpi 		SLIST_FOREACH(sensor, &report->sensors, rep_next)
387a2254ca0Smpi 			upd_sensor_update(sc, sensor, data, len);
38859b9e08dSandre 	}
389a2254ca0Smpi 	report->pending = 0;
390136270beSmpi }
391136270beSmpi 
392136270beSmpi void
393a2254ca0Smpi upd_sensor_invalidate(struct upd_softc *sc, struct upd_sensor *sensor)
394a2254ca0Smpi {
395a2254ca0Smpi 	struct upd_sensor	*child;
396a2254ca0Smpi 
397a2254ca0Smpi 	sensor->ksensor.status = SENSOR_S_UNKNOWN;
398a2254ca0Smpi 	sensor->ksensor.flags |= SENSOR_FINVALID;
399a2254ca0Smpi 
400a2254ca0Smpi 	SLIST_FOREACH(child, &sensor->children, dep_next)
401a2254ca0Smpi 		upd_sensor_invalidate(sc, child);
402a2254ca0Smpi }
403a2254ca0Smpi 
404a2254ca0Smpi void
405a2254ca0Smpi upd_sensor_update(struct upd_softc *sc, struct upd_sensor *sensor,
406136270beSmpi     uint8_t *buf, int len)
407136270beSmpi {
408a2254ca0Smpi 	struct upd_sensor	*child;
409136270beSmpi 	int64_t			 hdata, adjust;
410136270beSmpi 
4110415792bSandre 	switch (HID_GET_USAGE(sensor->hitem.usage)) {
4120415792bSandre 	case HUB_REL_STATEOF_CHARGE:
4130415792bSandre 	case HUB_ABS_STATEOF_CHARGE:
4140415792bSandre 	case HUB_REM_CAPACITY:
4150415792bSandre 	case HUB_FULLCHARGE_CAPACITY:
416*8e650cf5Slandry 	case HUP_PERCENT_LOAD:
4170415792bSandre 		adjust = 1000; /* scale adjust */
4180415792bSandre 		break;
419d55e24b2Smpi 	case HUB_ATRATE_TIMETOFULL:
420d55e24b2Smpi 	case HUB_ATRATE_TIMETOEMPTY:
421d55e24b2Smpi 	case HUB_RUNTIMETO_EMPTY:
422d55e24b2Smpi 		/* spec says minutes, not seconds */
423d55e24b2Smpi 		adjust = 1000000000LL;
424d55e24b2Smpi 		break;
4250415792bSandre 	default:
4260415792bSandre 		adjust = 1; /* no scale adjust */
4270415792bSandre 		break;
4280415792bSandre 	}
4290415792bSandre 
430e6a02383Smpi 	hdata = hid_get_data(buf, len, &sensor->hitem.loc);
431b0a4640cSmpi 	if (sensor->ksensor.type == SENSOR_INDICATOR)
432b0a4640cSmpi 		sensor->ksensor.value = hdata ? 1 : 0;
433b0a4640cSmpi 	else
4340415792bSandre 		sensor->ksensor.value = hdata * adjust;
4350415792bSandre 	sensor->ksensor.status = SENSOR_S_OK;
4360415792bSandre 	sensor->ksensor.flags &= ~SENSOR_FINVALID;
437a2254ca0Smpi 
438a2254ca0Smpi 	/* if battery not present, invalidate children */
439a2254ca0Smpi 	if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
440a2254ca0Smpi 	    HID_GET_USAGE(sensor->hitem.usage) == HUB_BATTERY_PRESENT &&
441a2254ca0Smpi 	    sensor->ksensor.value == 0) {
442a2254ca0Smpi 		SLIST_FOREACH(child, &sensor->children, dep_next)
443a2254ca0Smpi 			upd_sensor_invalidate(sc, child);
444a2254ca0Smpi 		return;
445a2254ca0Smpi 	}
446a2254ca0Smpi 
447a2254ca0Smpi 	upd_request_children(sc, &sensor->children);
4480415792bSandre }
4490415792bSandre 
45059b9e08dSandre void
45159b9e08dSandre upd_intr(struct uhidev *uh, void *p, uint len)
45259b9e08dSandre {
45359b9e08dSandre 	/* noop */
45459b9e08dSandre }
455