xref: /openbsd-src/sys/dev/usb/upd.c (revision 6238f0914acb09a8c6b9c24af6ad642fe4473191)
1 /*	$OpenBSD: upd.c,v 1.25 2016/01/09 04:14:42 jcs 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 /* Driver for USB Power Devices sensors */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/device.h>
27 #include <sys/queue.h>
28 #include <sys/sensors.h>
29 
30 #include <dev/usb/usb.h>
31 #include <dev/usb/usbdi.h>
32 #include <dev/usb/usbdevs.h>
33 #include <dev/usb/usbhid.h>
34 #include <dev/usb/uhidev.h>
35 #include <dev/usb/usbdi_util.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
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 (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
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 			break;
168 		}
169 
170 	return (ret);
171 }
172 
173 void
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 	sc->sc_reports = NULL;
185 	sc->sc_sensors = NULL;
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 
222 void
223 upd_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 
272 int
273 upd_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, 0);
291 	free(sc->sc_sensors, M_USBDEV, 0);
292 	return (0);
293 }
294 
295 void
296 upd_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 
307 void
308 upd_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 
335 int
336 upd_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 
356 struct upd_sensor *
357 upd_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 
371 void
372 upd_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 
392 void
393 upd_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 
404 void
405 upd_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 		adjust = 1000; /* scale adjust */
417 		break;
418 	case HUB_ATRATE_TIMETOFULL:
419 	case HUB_ATRATE_TIMETOEMPTY:
420 	case HUB_RUNTIMETO_EMPTY:
421 		/* spec says minutes, not seconds */
422 		adjust = 1000000000LL;
423 		break;
424 	default:
425 		adjust = 1; /* no scale adjust */
426 		break;
427 	}
428 
429 	hdata = hid_get_data(buf, len, &sensor->hitem.loc);
430 	sensor->ksensor.value = hdata * adjust;
431 	sensor->ksensor.status = SENSOR_S_OK;
432 	sensor->ksensor.flags &= ~SENSOR_FINVALID;
433 
434 	/* if battery not present, invalidate children */
435 	if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
436 	    HID_GET_USAGE(sensor->hitem.usage) == HUB_BATTERY_PRESENT &&
437 	    sensor->ksensor.value == 0) {
438 		SLIST_FOREACH(child, &sensor->children, dep_next)
439 			upd_sensor_invalidate(sc, child);
440 		return;
441 	}
442 
443 	upd_request_children(sc, &sensor->children);
444 }
445 
446 void
447 upd_intr(struct uhidev *uh, void *p, uint len)
448 {
449 	/* noop */
450 }
451