xref: /openbsd-src/sys/dev/usb/upd.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: upd.c,v 1.10 2014/07/12 18:48:52 tedu Exp $ */
2 
3 /*
4  * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* Driver for USB Power Devices sensors */
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/kernel.h>
24 #include <sys/malloc.h>
25 #include <sys/device.h>
26 #include <sys/sensors.h>
27 
28 #include <dev/usb/hid.h>
29 #include <dev/usb/usb.h>
30 #include <dev/usb/usbdi.h>
31 #include <dev/usb/usbdevs.h>
32 #include <dev/usb/usbhid.h>
33 #include <dev/usb/uhidev.h>
34 #include <dev/usb/usbdi_util.h>
35 
36 #ifdef UPD_DEBUG
37 #define DPRINTF(x)	do { printf x; } while (0)
38 #else
39 #define DPRINTF(x)
40 #endif
41 
42 struct upd_usage_entry {
43 	uint8_t			usage_pg;
44 	uint8_t			usage_id;
45 	enum sensor_type	senstype;
46 	char			*usage_name; /* sensor string */
47 };
48 
49 static struct upd_usage_entry upd_usage_table[] = {
50 	{ HUP_BATTERY,	HUB_REL_STATEOF_CHARGE,
51 	    SENSOR_PERCENT,	 "RelativeStateOfCharge" },
52 	{ HUP_BATTERY,	HUB_ABS_STATEOF_CHARGE,
53 	    SENSOR_PERCENT,	 "AbsoluteStateOfCharge" },
54 	{ HUP_BATTERY,	HUB_REM_CAPACITY,
55 	    SENSOR_PERCENT,	 "RemainingCapacity" },
56 	{ HUP_BATTERY,	HUB_FULLCHARGE_CAPACITY,
57 	    SENSOR_PERCENT,	 "FullChargeCapacity" },
58 	{ HUP_BATTERY,	HUB_CHARGING,
59 	    SENSOR_INDICATOR,	 "Charging" },
60 	{ HUP_BATTERY,	HUB_DISCHARGING,
61 	    SENSOR_INDICATOR,	 "Discharging" },
62 	{ HUP_BATTERY,	HUB_BATTERY_PRESENT,
63 	    SENSOR_INDICATOR,	 "BatteryPresent" },
64 	{ HUP_POWER,	HUP_SHUTDOWN_IMMINENT,
65 	    SENSOR_INDICATOR,	 "ShutdownImminent" },
66 	{ HUP_BATTERY,	HUB_AC_PRESENT,
67 	    SENSOR_INDICATOR,	 "ACPresent" },
68 	{ HUP_BATTERY,	HUB_ATRATE_TIMETOFULL,
69 	    SENSOR_TIMEDELTA,	 "AtRateTimeToFull" }
70 };
71 
72 struct upd_report {
73 	size_t		size;
74 	int		enabled;
75 };
76 
77 struct upd_sensor {
78 	struct ksensor		ksensor;
79 	struct hid_item		hitem;
80 	int			attached;
81 };
82 
83 struct upd_softc {
84 	struct uhidev		 sc_hdev;
85 	int			 sc_num_sensors;
86 	u_int			 sc_max_repid;
87 	u_int			 sc_max_sensors;
88 
89 	/* sensor framework */
90 	struct ksensordev	 sc_sensordev;
91 	struct sensor_task	*sc_sensortask;
92 	struct upd_report	*sc_reports;
93 	struct upd_sensor	*sc_sensors;
94 };
95 
96 int  upd_match(struct device *, void *, void *);
97 void upd_attach(struct device *, struct device *, void *);
98 int  upd_detach(struct device *, int);
99 
100 void upd_refresh(void *);
101 void upd_update_sensors(struct upd_softc *, uint8_t *, unsigned int, int);
102 void upd_intr(struct uhidev *, void *, uint);
103 struct upd_usage_entry *upd_lookup_usage_entry(const struct hid_item *);
104 struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int);
105 
106 struct cfdriver upd_cd = {
107 	NULL, "upd", DV_DULL
108 };
109 
110 const struct cfattach upd_ca = {
111 	sizeof(struct upd_softc),
112 	upd_match,
113 	upd_attach,
114 	upd_detach
115 };
116 
117 int
118 upd_match(struct device *parent, void *match, void *aux)
119 {
120 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
121 	int			  size;
122 	void			 *desc;
123 	struct hid_data		 *hdata;
124 	struct hid_item		  item;
125 	int			  ret = UMATCH_NONE;
126 
127 	if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
128 		return (ret);
129 
130 	DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor,
131 	    uha->uaa->product));
132 
133 	/*
134 	 * look for at least one sensor of our table
135 	 */
136 	uhidev_get_report_desc(uha->parent, &desc, &size);
137 	for (hdata = hid_start_parse(desc, size, hid_feature);
138 	     hid_get_item(hdata, &item); ) {
139 		if (upd_lookup_usage_entry(&item) != NULL) {
140 			ret = UMATCH_VENDOR_PRODUCT;
141 			break;
142 		}
143 	}
144 	hid_end_parse(hdata);
145 
146 	return (ret);
147 }
148 
149 void
150 upd_attach(struct device *parent, struct device *self, void *aux)
151 {
152 	struct upd_softc	 *sc = (struct upd_softc *)self;
153 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
154 	struct hid_item		  item;
155 	struct hid_data		 *hdata;
156 	struct upd_usage_entry	 *entry;
157 	struct upd_sensor	 *sensor;
158 	int			  size;
159 	void			 *desc;
160 
161 	sc->sc_hdev.sc_intr = upd_intr;
162 	sc->sc_hdev.sc_parent = uha->parent;
163 	sc->sc_reports = NULL;
164 	sc->sc_sensors = NULL;
165 	sc->sc_max_sensors = nitems(upd_usage_table);
166 
167 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
168 	    sizeof(sc->sc_sensordev.xname));
169 
170 	sc->sc_max_repid = uha->parent->sc_nrepid;
171 	DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n",
172 	    sc->sc_hdev.sc_dev.dv_xname, sc->sc_max_repid));
173 
174 	sc->sc_reports = malloc(sc->sc_max_repid * sizeof(struct upd_report),
175 	    M_USBDEV, M_WAITOK | M_ZERO);
176 	size = sc->sc_max_sensors * sizeof(struct upd_sensor);
177 	sc->sc_sensors = malloc(size, M_USBDEV, M_WAITOK | M_ZERO);
178 	sc->sc_num_sensors = 0;
179 	uhidev_get_report_desc(uha->parent, &desc, &size);
180 	for (hdata = hid_start_parse(desc, size, hid_feature);
181 	     hid_get_item(hdata, &item) &&
182 	     sc->sc_num_sensors < sc->sc_max_sensors; ) {
183 		DPRINTF(("upd: repid=%d\n", item.report_ID));
184 		if (item.kind != hid_feature ||
185 		    item.report_ID < 0)
186 			continue;
187 
188 		if ((entry = upd_lookup_usage_entry(&item)) == NULL)
189 			continue;
190 
191 		/* filter repeated usages, avoid duplicated sensors */
192 		sensor = upd_lookup_sensor(sc, entry->usage_pg,
193 		    entry->usage_id);
194 		if (sensor && sensor->attached)
195 			continue;
196 
197 		sensor = &sc->sc_sensors[sc->sc_num_sensors];
198 		/* keep our copy of hid_item */
199 		memcpy(&sensor->hitem, &item, sizeof(struct hid_item));
200 		strlcpy(sensor->ksensor.desc, entry->usage_name,
201 		    sizeof(sensor->ksensor.desc));
202 		sensor->ksensor.type = entry->senstype;
203 		sensor->ksensor.flags |= SENSOR_FINVALID;
204 		sensor->ksensor.status = SENSOR_S_UNKNOWN;
205 		sensor->ksensor.value = 0;
206 		sensor_attach(&sc->sc_sensordev, &sensor->ksensor);
207 		sensor->attached = 1;
208 		sc->sc_num_sensors++;
209 
210 		if (item.report_ID >= sc->sc_max_repid ||
211 		    sc->sc_reports[item.report_ID].enabled)
212 			continue;
213 
214 		sc->sc_reports[item.report_ID].size = hid_report_size(desc,
215 		    size, item.kind, item.report_ID);
216 		sc->sc_reports[item.report_ID].enabled = 1;
217 	}
218 	hid_end_parse(hdata);
219 	DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
220 
221 	sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
222 	if (sc->sc_sensortask == NULL) {
223 		printf(", unable to register update task\n");
224 		return;
225 	}
226 	sensordev_install(&sc->sc_sensordev);
227 
228 	printf("\n");
229 
230 	DPRINTF(("upd_attach: complete\n"));
231 }
232 
233 int
234 upd_detach(struct device *self, int flags)
235 {
236 	struct upd_softc	*sc = (struct upd_softc *)self;
237 	struct upd_sensor	*sensor;
238 	int			 i;
239 
240 	if (sc->sc_sensortask != NULL) {
241 		wakeup(&sc->sc_sensortask);
242 		sensor_task_unregister(sc->sc_sensortask);
243 	}
244 
245 	sensordev_deinstall(&sc->sc_sensordev);
246 
247 	for (i = 0; i < sc->sc_num_sensors; i++) {
248 		sensor = &sc->sc_sensors[i];
249 		if (sensor->attached)
250 			sensor_detach(&sc->sc_sensordev, &sensor->ksensor);
251 		DPRINTF(("upd_detach: %s\n", sensor->ksensor.desc));
252 	}
253 
254 	free(sc->sc_reports, M_USBDEV, 0);
255 	free(sc->sc_sensors, M_USBDEV, 0);
256 	DPRINTF(("upd_detach: complete\n"));
257 	return (0);
258 }
259 
260 void
261 upd_refresh(void *arg)
262 {
263 	struct upd_softc	*sc = (struct upd_softc *)arg;
264 	struct upd_report	*report;
265 	uint8_t			buf[256];
266 	int			repid, err;
267 
268 	for (repid = 0; repid < sc->sc_max_repid; repid++) {
269 		report = &sc->sc_reports[repid];
270 		if (!report->enabled)
271 			continue;
272 
273 		memset(buf, 0x0, sizeof(buf));
274 		/*
275 		 * XXX uhidev_get_report() is not clever enough to handle
276 		 * non-NUl reportID, so add an extra byte for it.
277 		 */
278 		err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
279 		    repid, buf, report->size + 1);
280 		if (err) {
281 			DPRINTF(("read failure: reportid=%02x err=%d\n", repid,
282 			    err));
283 			continue;
284 		}
285 
286 		upd_update_sensors(sc, buf, report->size, repid);
287 	}
288 }
289 
290 struct upd_usage_entry *
291 upd_lookup_usage_entry(const struct hid_item *hitem)
292 {
293 	struct upd_usage_entry	*entry = NULL;
294 	int			 i;
295 
296 	for (i = 0; i < nitems(upd_usage_table); i++) {
297 		entry = &upd_usage_table[i];
298 		if (entry->usage_pg == HID_GET_USAGE_PAGE(hitem->usage) &&
299 		    entry->usage_id == HID_GET_USAGE(hitem->usage))
300 			return (entry);
301 	}
302 	return (NULL);
303 }
304 
305 struct upd_sensor *
306 upd_lookup_sensor(struct upd_softc *sc, int page, int usage)
307 {
308 	struct upd_sensor	*sensor = NULL;
309 	int			 i;
310 
311 	for (i = 0; i < sc->sc_num_sensors; i++) {
312 		sensor = &sc->sc_sensors[i];
313 		if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) &&
314 		    usage == HID_GET_USAGE(sensor->hitem.usage))
315 			return (sensor);
316 	}
317 	return (NULL);
318 }
319 
320 void
321 upd_update_sensors(struct upd_softc *sc, uint8_t *buf, unsigned int len,
322     int repid)
323 {
324 	struct upd_sensor	*sensor;
325 	ulong			hdata, batpres;
326 	ulong 			adjust;
327 	int			i;
328 
329 	sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT);
330 	batpres = sensor ? sensor->ksensor.value : -1;
331 
332 	for (i = 0; i < sc->sc_num_sensors; i++) {
333 		sensor = &sc->sc_sensors[i];
334 		if (!(sensor->hitem.report_ID == repid && sensor->attached))
335 			continue;
336 
337 		/* invalidate battery dependent sensors */
338 		if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
339 		    batpres <= 0) {
340 			/* exception to the battery sensor itself */
341 			if (HID_GET_USAGE(sensor->hitem.usage) !=
342 			    HUB_BATTERY_PRESENT) {
343 				sensor->ksensor.status = SENSOR_S_UNKNOWN;
344 				sensor->ksensor.flags |= SENSOR_FINVALID;
345 				continue;
346 			}
347 		}
348 
349 		switch (HID_GET_USAGE(sensor->hitem.usage)) {
350 		case HUB_REL_STATEOF_CHARGE:
351 		case HUB_ABS_STATEOF_CHARGE:
352 		case HUB_REM_CAPACITY:
353 		case HUB_FULLCHARGE_CAPACITY:
354 			adjust = 1000; /* scale adjust */
355 			break;
356 		default:
357 			adjust = 1; /* no scale adjust */
358 			break;
359 		}
360 
361 		/* XXX first byte which is the report id */
362 		hdata = hid_get_data(buf + 1, len, &sensor->hitem.loc);
363 
364 		sensor->ksensor.value = hdata * adjust;
365 		sensor->ksensor.status = SENSOR_S_OK;
366 		sensor->ksensor.flags &= ~SENSOR_FINVALID;
367 		DPRINTF(("%s: hidget data: %d\n",
368 		    sc->sc_sensordev.xname, hdata));
369 	}
370 }
371 
372 
373 void
374 upd_intr(struct uhidev *uh, void *p, uint len)
375 {
376 	/* noop */
377 }
378