xref: /openbsd-src/sys/dev/usb/upd.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: upd.c,v 1.5 2014/03/20 13:07:01 andre 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 enum upd_sensor_id {
43 	UPD_SENSOR_UNKNOWN,
44 	UPD_SENSOR_RELCHARGE,
45 	UPD_SENSOR_ABSCHARGE,
46 	UPD_SENSOR_REMCAPACI,
47 	UPD_SENSOR_FULLCHARG,
48 	UPD_SENSOR_CHARGING,
49 	UPD_SENSOR_DISCHARG,
50 	UPD_SENSOR_BATTPRESENT,
51 	UPD_SENSOR_SHUTIMMINENT,
52 	UPD_SENSOR_ACPRESENT,
53 	UPD_SENSOR_TIMETOFULL,
54 	UPD_SENSOR_NUM
55 	/*
56 	 * TODO
57 	 * - atratetimetofull
58 	 * - atratetimetoempty
59 	 * - cyclecount
60 	 */
61 };
62 
63 struct upd_usage_entry {
64 	enum upd_sensor_id	upd_sid;
65 	uint8_t			usage_pg;
66 	uint8_t			usage_id;
67 	enum sensor_type	senstype;
68 	char			*usage_name; /* sensor string */
69 };
70 
71 static struct upd_usage_entry upd_usage_table[UPD_SENSOR_NUM] = {
72 	{ UPD_SENSOR_UNKNOWN,	  HUP_UNDEFINED,HUP_UNDEFINED,
73 	    -1,			 "unknown" },
74 	{ UPD_SENSOR_RELCHARGE,	  HUP_BATTERY,	HUB_REL_STATEOF_CHARGE,
75 	    SENSOR_PERCENT,	 "RelativeStateOfCharge" },
76 	{ UPD_SENSOR_ABSCHARGE,	  HUP_BATTERY,	HUB_ABS_STATEOF_CHARGE,
77 	    SENSOR_PERCENT,	 "AbsoluteStateOfCharge" },
78 	{ UPD_SENSOR_REMCAPACI,	  HUP_BATTERY,	HUB_REM_CAPACITY,
79 	    SENSOR_PERCENT,	 "RemainingCapacity" },
80 	{ UPD_SENSOR_FULLCHARG,	  HUP_BATTERY,	HUB_FULLCHARGE_CAPACITY,
81 	    SENSOR_PERCENT,	 "FullChargeCapacity" },
82 	{ UPD_SENSOR_CHARGING,	  HUP_BATTERY,	HUB_CHARGING,
83 	    SENSOR_INDICATOR,	 "Charging" },
84 	{ UPD_SENSOR_DISCHARG,	  HUP_BATTERY,	HUB_DISCHARGING,
85 	    SENSOR_INDICATOR,	 "Discharging" },
86 	{ UPD_SENSOR_BATTPRESENT, HUP_BATTERY,	HUB_BATTERY_PRESENT,
87 	    SENSOR_INDICATOR,	 "BatteryPresent" },
88 	{ UPD_SENSOR_SHUTIMMINENT,HUP_POWER,	HUP_SHUTDOWN_IMMINENT,
89 	    SENSOR_INDICATOR,	 "ShutdownImminent" },
90 	{ UPD_SENSOR_ACPRESENT,	  HUP_BATTERY,	HUB_AC_PRESENT,
91 	    SENSOR_INDICATOR,	 "ACPresent" },
92 	{ UPD_SENSOR_TIMETOFULL,  HUP_BATTERY,	HUB_ATRATE_TIMETOFULL,
93 	    SENSOR_TIMEDELTA,	 "AtRateTimeToFull" }
94 };
95 
96 struct upd_sensor {
97 	int			attached;
98 	struct ksensor		sensor;
99 	struct hid_item		item;
100 	size_t			flen;
101 };
102 
103 struct upd_softc {
104 	struct uhidev		 sc_hdev;
105 	int			 sc_num_sensors;
106 
107 	/* sensor framework */
108 	struct upd_sensor	 sc_sensors[UPD_SENSOR_NUM];
109 	struct ksensordev	 sc_sensordev;
110 	struct sensor_task	*sc_sensortask;
111 };
112 
113 static const struct usb_devno upd_devs[] = {
114 	{ USB_VENDOR_APC, USB_PRODUCT_APC_UPS },
115 	{ USB_VENDOR_APC, USB_PRODUCT_APC_UPS5G },
116 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C100 },
117 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C1100 },
118 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C120 },
119 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C1250EITWRK },
120 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C1500EITWRK },
121 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR },
122 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C800 },
123 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C900 },
124 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6H375 },
125 	{ USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_1500 },
126 	{ USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_OR2200 },
127 	{ USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS },
128 	{ USB_VENDOR_DELL2, USB_PRODUCT_DELL2_UPS },
129 	{ USB_VENDOR_HP, USB_PRODUCT_HP_R1500G2 },
130 	{ USB_VENDOR_HP, USB_PRODUCT_HP_RT2200 },
131 	{ USB_VENDOR_HP, USB_PRODUCT_HP_T1000 },
132 	{ USB_VENDOR_HP, USB_PRODUCT_HP_T1500 },
133 	{ USB_VENDOR_HP, USB_PRODUCT_HP_T750 },
134 	{ USB_VENDOR_HP, USB_PRODUCT_HP_T750G2 },
135 	{ USB_VENDOR_IDOWELL, USB_PRODUCT_IDOWELL_IDOWELL },
136 	{ USB_VENDOR_LIEBERT, USB_PRODUCT_LIEBERT_UPS },
137 	{ USB_VENDOR_LIEBERT2, USB_PRODUCT_LIEBERT2_PSA },
138 	{ USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1 },
139 	{ USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2 },
140 	{ USB_VENDOR_OMRON, USB_PRODUCT_OMRON_BX35F },
141 	{ USB_VENDOR_OMRON, USB_PRODUCT_OMRON_BX50F },
142 	{ USB_VENDOR_OMRON, USB_PRODUCT_OMRON_BY35S }
143 };
144 #define upd_lookup(v, p) usb_lookup(upd_devs, v, p)
145 
146 int  upd_match(struct device *, void *, void *);
147 void upd_attach(struct device *, struct device *, void *);
148 int  upd_detach(struct device *, int);
149 
150 void upd_add_sensor(struct upd_softc *, const struct hid_item *, void *, int);
151 void upd_refresh(void *);
152 void upd_intr(struct uhidev *, void *, uint);
153 
154 struct cfdriver upd_cd = {
155 	NULL, "upd", DV_DULL
156 };
157 
158 const struct cfattach upd_ca = {
159 	sizeof(struct upd_softc),
160 	upd_match,
161 	upd_attach,
162 	upd_detach
163 };
164 
165 int
166 upd_match(struct device *parent, void *match, void *aux)
167 {
168 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
169 
170 	if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
171 		return (UMATCH_NONE);
172 
173 	if (upd_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
174 		return (UMATCH_NONE);
175 
176 	DPRINTF(("upd: vendor=0x%x, product=0x%x\n", uha->uaa->vendor,
177 	    uha->uaa->product));
178 
179 	return (UMATCH_VENDOR_PRODUCT);
180 }
181 
182 void
183 upd_attach(struct device *parent, struct device *self, void *aux)
184 {
185 	struct upd_softc	 *sc = (struct upd_softc *)self;
186 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
187 	struct hid_item		  item;
188 	struct hid_data		 *hdata;
189 	int			  size;
190 	void			 *desc;
191 
192 	sc->sc_hdev.sc_intr = upd_intr;
193 	sc->sc_hdev.sc_parent = uha->parent;
194 	sc->sc_num_sensors = 0;
195 
196 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
197 	    sizeof(sc->sc_sensordev.xname));
198 
199 	DPRINTF(("upd: devname=%s sc_nrepid=%d\n", sc->sc_hdev.sc_dev.dv_xname,
200 	    uha->parent->sc_nrepid));
201 
202 	uhidev_get_report_desc(uha->parent, &desc, &size);
203 	hdata = hid_start_parse(desc, size, hid_feature);
204 	/* lookup for item in our sensors list */
205 	while (hid_get_item(hdata, &item))
206 		upd_add_sensor(sc, &item, desc, size);
207 
208 	hid_end_parse(hdata);
209 	DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
210 
211 	if (sc->sc_num_sensors > 0) {
212 		sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
213 		if (sc->sc_sensortask == NULL) {
214 			printf(", unable to register update task\n");
215 			return;
216 		}
217 		sensordev_install(&sc->sc_sensordev);
218 	}
219 
220 	printf("\n");
221 
222 	DPRINTF(("upd_attach: complete\n"));
223 }
224 
225 int
226 upd_detach(struct device *self, int flags)
227 {
228 	struct upd_softc	*sc = (struct upd_softc *)self;
229 	struct upd_sensor	*sensor;
230 	int			 i;
231 
232 	if (sc->sc_num_sensors <= 0)
233 		goto finish;
234 
235 	if (sc->sc_sensortask != NULL) {
236 		wakeup(&sc->sc_sensortask);
237 		sensor_task_unregister(sc->sc_sensortask);
238 	}
239 
240 	sensordev_deinstall(&sc->sc_sensordev);
241 
242 	for (i = 0; i < UPD_SENSOR_NUM; i++) {
243 		sensor = &sc->sc_sensors[i];
244 		if (!sensor->attached)
245 			continue;
246 
247 		sensor_detach(&sc->sc_sensordev, &sensor->sensor);
248 		DPRINTF(("upd_detach: %s\n", sensor->sensor.desc));
249 	}
250 
251 finish:
252 	DPRINTF(("upd_detach: complete\n"));
253 	return (0);
254 }
255 
256 void
257 upd_refresh(void *arg)
258 {
259 	struct upd_softc	*sc = (struct upd_softc *)arg;
260 	struct hid_location	*loc;
261 	struct upd_sensor	*sensor;
262 	ulong			hdata;
263 	ulong 			adjust;
264 	uint8_t			buf[256];
265 	int			i, err;
266 
267 	for (i = 0; i < UPD_SENSOR_NUM; i++) {
268 		sensor = &sc->sc_sensors[i];
269 		if (sensor && !sensor->attached)
270 			continue;
271 
272 		switch (i) {
273 		case UPD_SENSOR_RELCHARGE:
274 		case UPD_SENSOR_ABSCHARGE:
275 		case UPD_SENSOR_REMCAPACI:
276 		case UPD_SENSOR_FULLCHARG:
277 			adjust = 1000; /* scale adjust */
278 			/* FALLTHROUGH */
279 		case UPD_SENSOR_CHARGING:
280 		case UPD_SENSOR_DISCHARG:
281 		case UPD_SENSOR_TIMETOFULL:
282 			/*
283 			 * don't query nor show battery dependent sensors if no
284 			 * battery
285 			 */
286 			if (sc->sc_sensors[UPD_SENSOR_BATTPRESENT].sensor.value
287 			    <= 0) {
288 				sensor->sensor.flags |= SENSOR_FINVALID;
289 				continue;
290 			}
291 			break;
292 		default:
293 			adjust = 1; /* no scale adjust */
294 		}
295 
296 		loc = &sensor->item.loc;
297 		sc->sc_hdev.sc_report_id = sensor->item.report_ID;
298 		memset(buf, 0x0, sizeof(buf));
299 		err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, buf,
300 		    sensor->flen);
301 		if (err) {
302 			DPRINTF(("read failure: sens=%02x reportid=%02x "
303 			    "err=%d\n", i, sc->sc_hdev.sc_report_id, err));
304 			continue;
305 		}
306 
307 		hdata = hid_get_data(buf + 1, loc);
308 
309 		sensor->sensor.flags &= ~SENSOR_FINVALID;
310 		sensor->sensor.value = hdata * adjust;
311 		DPRINTF(("%s: %s: hidget data: %d\n", sc->sc_sensordev.xname,
312 		    upd_usage_table[i].usage_name, hdata));
313 	}
314 }
315 
316 void
317 upd_add_sensor(struct upd_softc *sc, const struct hid_item *item, void *desc,
318     int dsiz)
319 {
320 	struct upd_usage_entry	*entry = NULL;
321 	struct upd_sensor	*sensor = NULL;
322 	int i;
323 
324 	for (i = 0; i < UPD_SENSOR_NUM; i++) {
325 		entry = &upd_usage_table[i];
326 		if (entry->upd_sid == UPD_SENSOR_UNKNOWN ||
327 		    entry->usage_pg != HID_GET_USAGE_PAGE(item->usage) ||
328 		    entry->usage_id != HID_GET_USAGE(item->usage))
329 			continue;
330 
331 		sensor = &sc->sc_sensors[i];
332 		if (sensor && sensor->attached)
333 			continue;
334 
335 		/* keep our copy of hid_item */
336 		memset(&sensor->item, 0x0, sizeof(struct hid_item));
337 		memcpy(&sensor->item, item, sizeof(struct hid_item));
338 		sensor->flen = hid_report_size(desc, dsiz, hid_feature,
339 		    item->report_ID) + 1;
340 		strlcpy(sensor->sensor.desc, entry->usage_name,
341 		    sizeof(sensor->sensor.desc));
342 		sensor->sensor.type = entry->senstype;
343 		sensor->sensor.flags |= SENSOR_FINVALID;
344 		sensor->sensor.value = 0;
345 		sensor_attach(&sc->sc_sensordev, &sensor->sensor);
346 		sensor->attached = 1;
347 		sc->sc_num_sensors++;
348 	}
349 }
350 
351 void
352 upd_intr(struct uhidev *uh, void *p, uint len)
353 {
354 	/* noop */
355 }
356