xref: /openbsd-src/sys/dev/usb/usps.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: usps.c,v 1.7 2014/07/12 21:24:33 mpi Exp $   */
2 
3 /*
4  * Copyright (c) 2011 Yojiro UO <yuo@nui.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 smart power strip FX-5204PS */
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/conf.h>
27 #include <sys/sensors.h>
28 
29 #include <dev/usb/usb.h>
30 #include <dev/usb/usbdi.h>
31 #include <dev/usb/usbdi_util.h>
32 #include <dev/usb/usbdevs.h>
33 
34 #ifdef USPS_DEBUG
35 int	uspsdebug = 0;
36 #define DPRINTFN(n, x)	do { if (uspsdebug > (n)) printf x; } while (0)
37 #else
38 #define DPRINTFN(n, x)
39 #endif
40 
41 #define DPRINTF(x) DPRINTFN(0, x)
42 
43 #define USPS_UPDATE_TICK	1 /* sec */
44 #define USPS_TIMEOUT		1000 /* ms */
45 #define USPS_INTR_TICKS		50 /* ms */
46 
47 /* protocol */
48 #define USPS_CMD_START		0x01
49 #define USPS_CMD_VALUE		0x20
50 #define USPS_CMD_GET_FIRMWARE	0xc0
51 #define USPS_CMD_GET_SERIAL	0xc1
52 #define USPS_CMD_GET_VOLTAGE	0xb0
53 #define USPS_CMD_GET_TEMP	0xb4
54 #define USPS_CMD_GET_FREQ	0xa1
55 #define USPS_CMD_GET_UNK0	0xa2
56 
57 #define USPS_MODE_WATTAGE	0x10
58 #define USPS_MODE_CURRENT	0x30
59 
60 #define FX5204_NUM_PORTS	4
61 
62 struct usps_port_sensor {
63 	struct ksensor ave;
64 	struct ksensor min;
65 	struct ksensor max;
66 	int vave, vmin, vmax;
67 };
68 
69 struct usps_softc {
70 	struct device		 sc_dev;
71 	struct usbd_device	*sc_udev;
72 	struct usbd_interface	*sc_iface;
73 	struct usbd_pipe	*sc_ipipe;
74 	int			 sc_isize;
75 	struct usbd_xfer	*sc_xfer;
76 	uint8_t			 sc_buf[16];
77 	uint8_t			 *sc_intrbuf;
78 
79 	uint16_t		 sc_flag;
80 
81 	/* device info */
82 	uint8_t		 	 sc_firmware_version[2];
83 	uint32_t		 sc_device_serial;
84 
85 	/* sensor framework */
86 	struct usps_port_sensor	 sc_port_sensor[FX5204_NUM_PORTS];
87 	struct usps_port_sensor	 sc_total_sensor;
88 	struct ksensor 		 sc_voltage_sensor;
89 	struct ksensor		 sc_frequency_sensor;
90 	struct ksensor		 sc_temp_sensor;
91 	struct ksensor		 sc_serial_sensor;
92 	struct ksensordev	 sc_sensordev;
93 	struct sensor_task	*sc_sensortask;
94 
95 	int			 sc_count;
96 };
97 
98 struct usps_port_pkt {
99 	uint8_t		header; /* should be 0x80 */
100 	uint16_t	seq;
101 	uint8_t		padding[5];
102 	uint16_t	port[4];
103 } __packed; /* 16 byte length struct */
104 
105 static const struct usb_devno usps_devs[] = {
106 	{ USB_VENDOR_FUJITSUCOMP, USB_PRODUCT_FUJITSUCOMP_FX5204PS},
107 };
108 #define usps_lookup(v, p) usb_lookup(usps_devs, v, p)
109 
110 int  usps_match(struct device *, void *, void *);
111 void usps_attach(struct device *, struct device *, void *);
112 int  usps_detach(struct device *, int);
113 void usps_intr(struct usbd_xfer *, void *, usbd_status);
114 
115 usbd_status usps_cmd(struct usps_softc *, uint8_t, uint16_t, uint16_t);
116 usbd_status usps_set_measurement_mode(struct usps_softc *, int);
117 
118 void usps_get_device_info(struct usps_softc *);
119 void usps_refresh(void *);
120 void usps_refresh_temp(struct usps_softc *);
121 void usps_refresh_power(struct usps_softc *);
122 void usps_refresh_ports(struct usps_softc *);
123 
124 struct cfdriver usps_cd = {
125 	NULL, "usps", DV_DULL
126 };
127 
128 const struct cfattach usps_ca = {
129 	sizeof(struct usps_softc), usps_match, usps_attach, usps_detach
130 };
131 
132 int
133 usps_match(struct device *parent, void *match, void *aux)
134 {
135 	struct usb_attach_arg *uaa = aux;
136 
137 	if (uaa->iface != NULL)
138 		return UMATCH_NONE;
139 
140 	if (usps_lookup(uaa->vendor, uaa->product) == NULL)
141 		return UMATCH_NONE;
142 
143 	return (UMATCH_VENDOR_PRODUCT);
144 }
145 
146 void
147 usps_attach(struct device *parent, struct device *self, void *aux)
148 {
149 	struct usps_softc *sc = (struct usps_softc *)self;
150 	struct usb_attach_arg *uaa = aux;
151 	usb_interface_descriptor_t *id;
152 	usb_endpoint_descriptor_t *ed;
153 	int ep_ibulk, ep_obulk, ep_intr;
154 	usbd_status err;
155 	int i;
156 
157 	sc->sc_udev = uaa->device;
158 
159 #define USPS_USB_IFACE 0
160 #define USPS_USB_CONFIG 1
161 
162 	/* set configuration */
163 	if ((err = usbd_set_config_no(sc->sc_udev, USPS_USB_CONFIG, 0)) != 0){
164 		printf("%s: failed to set config %d: %s\n",
165 		    sc->sc_dev.dv_xname, USPS_USB_CONFIG, usbd_errstr(err));
166 		return;
167 	}
168 
169 	/* get interface handle */
170 	if ((err = usbd_device2interface_handle(sc->sc_udev, USPS_USB_IFACE,
171 		&sc->sc_iface)) != 0) {
172 		printf("%s: failed to get interface %d: %s\n",
173 		    sc->sc_dev.dv_xname, USPS_USB_IFACE, usbd_errstr(err));
174 		return;
175 	}
176 
177 	/* find endpoints */
178 	ep_ibulk = ep_obulk = ep_intr = -1;
179 	id = usbd_get_interface_descriptor(sc->sc_iface);
180 	for (i = 0; i < id->bNumEndpoints; i++) {
181 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
182 		if (ed == NULL) {
183 			printf("%s: failed to get endpoint %d descriptor\n",
184 			    sc->sc_dev.dv_xname, i);
185 			return;
186 		}
187 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
188 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
189 			ep_ibulk = ed->bEndpointAddress;
190 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
191 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
192 			ep_obulk = ed->bEndpointAddress;
193 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
194 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT){
195 			ep_intr = ed->bEndpointAddress;
196 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
197 		}
198 	}
199 
200 	if (ep_intr == -1) {
201 		printf("%s: no data endpoint found\n", sc->sc_dev.dv_xname);
202 		return;
203 	}
204 
205 	usps_get_device_info(sc);
206 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
207 	    sizeof(sc->sc_sensordev.xname));
208 
209 	/* attach sensor */
210 	sc->sc_voltage_sensor.type = SENSOR_VOLTS_AC;
211 	sc->sc_frequency_sensor.type = SENSOR_FREQ;
212 	sc->sc_temp_sensor.type = SENSOR_TEMP;
213 	sc->sc_serial_sensor.type = SENSOR_INTEGER;
214 	sensor_attach(&sc->sc_sensordev, &sc->sc_voltage_sensor);
215 	sensor_attach(&sc->sc_sensordev, &sc->sc_frequency_sensor);
216 	sensor_attach(&sc->sc_sensordev, &sc->sc_temp_sensor);
217 	sensor_attach(&sc->sc_sensordev, &sc->sc_serial_sensor);
218 
219 	sc->sc_serial_sensor.value = sc->sc_device_serial;
220 	strlcpy(sc->sc_serial_sensor.desc, "unit serial#",
221 	    sizeof(sc->sc_serial_sensor.desc));
222 
223 	/*
224 	 * XXX: the device has mode of par port sensor, Watt of Ampair.
225 	 * currently only watt mode is selected.
226 	 */
227 	usps_set_measurement_mode(sc, USPS_MODE_WATTAGE);
228 	for (i = 0; i < FX5204_NUM_PORTS; i++) {
229 		sc->sc_port_sensor[i].ave.type = SENSOR_WATTS;
230 		sc->sc_port_sensor[i].min.type = SENSOR_WATTS;
231 		sc->sc_port_sensor[i].max.type = SENSOR_WATTS;
232 		sensor_attach(&sc->sc_sensordev, &sc->sc_port_sensor[i].ave);
233 		sensor_attach(&sc->sc_sensordev, &sc->sc_port_sensor[i].min);
234 		sensor_attach(&sc->sc_sensordev, &sc->sc_port_sensor[i].max);
235 		(void)snprintf(sc->sc_port_sensor[i].ave.desc,
236 		    sizeof(sc->sc_port_sensor[i].ave.desc),
237 		    "port#%d (average)", i);
238 		(void)snprintf(sc->sc_port_sensor[i].min.desc,
239 		    sizeof(sc->sc_port_sensor[i].min.desc),
240 		    "port#%d (min)", i);
241 		(void)snprintf(sc->sc_port_sensor[i].max.desc,
242 		    sizeof(sc->sc_port_sensor[i].max.desc),
243 		    "port#%d (max)", i);
244 	}
245 
246 	sc->sc_total_sensor.ave.type = SENSOR_WATTS;
247 	sc->sc_total_sensor.min.type = SENSOR_WATTS;
248 	sc->sc_total_sensor.max.type = SENSOR_WATTS;
249 	sensor_attach(&sc->sc_sensordev, &sc->sc_total_sensor.ave);
250 	sensor_attach(&sc->sc_sensordev, &sc->sc_total_sensor.min);
251 	sensor_attach(&sc->sc_sensordev, &sc->sc_total_sensor.max);
252 	(void)snprintf(sc->sc_total_sensor.ave.desc,
253 	    sizeof(sc->sc_total_sensor.ave.desc), "total (average)", i);
254 	(void)snprintf(sc->sc_total_sensor.min.desc,
255 	    sizeof(sc->sc_total_sensor.ave.desc), "total (min)", i);
256 	(void)snprintf(sc->sc_total_sensor.max.desc,
257 	    sizeof(sc->sc_total_sensor.ave.desc), "total (max)", i);
258 
259 	sc->sc_sensortask = sensor_task_register(sc, usps_refresh,
260 	    USPS_UPDATE_TICK);
261 	if (sc->sc_sensortask == NULL) {
262 		printf(", unable to register update task\n");
263 		goto fail;
264 	}
265 
266 	printf("%s: device#=%d, firmware version=V%02dL%02d\n",
267 	    sc->sc_dev.dv_xname, sc->sc_device_serial,
268 	    sc->sc_firmware_version[0],
269 	    sc->sc_firmware_version[1]);
270 
271 	sensordev_install(&sc->sc_sensordev);
272 
273 	/* open interrupt endpoint */
274 	sc->sc_intrbuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
275 	if (sc->sc_intrbuf == NULL)
276 		goto fail;
277 	err = usbd_open_pipe_intr(sc->sc_iface, ep_intr,
278 	    USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_intrbuf,
279 	    sc->sc_isize, usps_intr, USPS_INTR_TICKS);
280 	if (err) {
281 		printf("%s: could not open intr pipe %s\n",
282 		    sc->sc_dev.dv_xname, usbd_errstr(err));
283 		goto fail;
284 	}
285 
286 	DPRINTF(("usps_attach: complete\n"));
287 	return;
288 
289 fail:
290 	if (sc->sc_ipipe != NULL)
291 		usbd_close_pipe(sc->sc_ipipe);
292 	if (sc->sc_xfer != NULL)
293 		usbd_free_xfer(sc->sc_xfer);
294 	if (sc->sc_intrbuf != NULL)
295 		free(sc->sc_intrbuf, M_USBDEV, 0);
296 }
297 
298 int
299 usps_detach(struct device *self, int flags)
300 {
301 	struct usps_softc *sc = (struct usps_softc *)self;
302 	int i, rv = 0, s;
303 
304 	usbd_deactivate(sc->sc_udev);
305 
306 	s = splusb();
307 	if (sc->sc_ipipe != NULL) {
308 		usbd_abort_pipe(sc->sc_ipipe);
309 		usbd_close_pipe(sc->sc_ipipe);
310 		if (sc->sc_intrbuf != NULL)
311 			free(sc->sc_intrbuf, M_USBDEV, 0);
312 		sc->sc_ipipe = NULL;
313 	}
314 	if (sc->sc_xfer != NULL)
315 		usbd_free_xfer(sc->sc_xfer);
316 	splx(s);
317 
318 	wakeup(&sc->sc_sensortask);
319 	sensordev_deinstall(&sc->sc_sensordev);
320 	sensor_detach(&sc->sc_sensordev, &sc->sc_voltage_sensor);
321 	sensor_detach(&sc->sc_sensordev, &sc->sc_frequency_sensor);
322 	sensor_detach(&sc->sc_sensordev, &sc->sc_temp_sensor);
323 	sensor_detach(&sc->sc_sensordev, &sc->sc_serial_sensor);
324 	for (i = 0; i < FX5204_NUM_PORTS; i++) {
325 		sensor_detach(&sc->sc_sensordev, &sc->sc_port_sensor[i].ave);
326 		sensor_detach(&sc->sc_sensordev, &sc->sc_port_sensor[i].min);
327 		sensor_detach(&sc->sc_sensordev, &sc->sc_port_sensor[i].max);
328 	}
329 	sensor_detach(&sc->sc_sensordev, &sc->sc_total_sensor.ave);
330 	sensor_detach(&sc->sc_sensordev, &sc->sc_total_sensor.min);
331 	sensor_detach(&sc->sc_sensordev, &sc->sc_total_sensor.max);
332 
333 	if (sc->sc_sensortask != NULL)
334 		sensor_task_unregister(sc->sc_sensortask);
335 
336 	return (rv);
337 }
338 
339 usbd_status
340 usps_cmd(struct usps_softc *sc, uint8_t cmd, uint16_t val, uint16_t len)
341 {
342 	usb_device_request_t req;
343 	usbd_status err;
344 
345 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
346 	req.bRequest = cmd;
347 	USETW(req.wValue, val);
348 	USETW(req.wIndex, 0);
349 	USETW(req.wLength, len);
350 
351 	err = usbd_do_request(sc->sc_udev, &req, &sc->sc_buf);
352 	if (err) {
353 		printf("%s: could not issue sensor cmd: %s\n",
354 		    sc->sc_dev.dv_xname, usbd_errstr(err));
355 		return (EIO);
356 	}
357 
358 	return (0);
359 }
360 
361 usbd_status
362 usps_set_measurement_mode(struct usps_softc *sc, int mode)
363 {
364 	usb_device_request_t req;
365 	usbd_status err;
366 
367 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
368 	req.bRequest = USPS_CMD_START;
369 	USETW(req.wValue, 0);
370 	USETW(req.wIndex, 0);
371 	USETW(req.wLength, 0);
372 
373 	err = usbd_do_request(sc->sc_udev, &req, &sc->sc_buf);
374 	if (err) {
375 		printf("%s: fail to set sensor mode: %s\n",
376 		    sc->sc_dev.dv_xname, usbd_errstr(err));
377 		return (EIO);
378 	}
379 
380 	req.bRequest = USPS_CMD_VALUE;
381 	USETW(req.wValue, mode);
382 
383 	err = usbd_do_request(sc->sc_udev, &req, &sc->sc_buf);
384 	if (err) {
385 		printf("%s: could not set sensor mode: %s\n",
386 		    sc->sc_dev.dv_xname, usbd_errstr(err));
387 		return (EIO);
388 	}
389 
390 	return (0);
391 }
392 
393 void
394 usps_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
395 {
396 	struct usps_softc *sc = priv;
397 	struct usps_port_pkt *pkt;
398 	struct usps_port_sensor *ps;
399 	int i, total;
400 
401 	if (usbd_is_dying(sc->sc_udev))
402 		return;
403 
404 	if (status != USBD_NORMAL_COMPLETION) {
405 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
406 			return;
407 		if (status == USBD_STALLED)
408 			usbd_clear_endpoint_stall_async(sc->sc_ipipe);
409 		return;
410 	}
411 
412 	/* process intr packet */
413 	if (sc->sc_intrbuf == NULL)
414 		return;
415 
416 	pkt = (struct usps_port_pkt *)sc->sc_intrbuf;
417 
418 	total = 0;
419 	for (i = 0; i < FX5204_NUM_PORTS; i++) {
420 		ps = &sc->sc_port_sensor[i];
421 		if (sc->sc_count == 0)
422 			ps->vmax = ps->vmin = pkt->port[i];
423 		if (pkt->port[i] > ps->vmax)
424 			ps->vmax = pkt->port[i];
425 		if (pkt->port[i] < ps->vmin)
426 			ps->vmin = pkt->port[i];
427 		ps->vave =
428 		    (ps->vave * sc->sc_count + pkt->port[i])/(sc->sc_count +1);
429 		total += pkt->port[i];
430 	}
431 
432 	/* calculate ports total */
433 	ps = &sc->sc_total_sensor;
434 	if (sc->sc_count == 0)
435 		ps->vmax = ps->vmin = total;
436 	if (total > ps->vmax)
437 		ps->vmax = total;
438 	if (total < ps->vmin)
439 		ps->vmin = total;
440 	ps->vave = (ps->vave * sc->sc_count + total)/(sc->sc_count +1);
441 
442 	sc->sc_count++;
443 }
444 
445 void
446 usps_get_device_info(struct usps_softc *sc)
447 {
448 	int serial;
449 
450 	/* get Firmware version */
451 	usps_cmd(sc, USPS_CMD_GET_FIRMWARE, 0, 2);
452 	sc->sc_firmware_version[0] =
453 	    (sc->sc_buf[0]>>4) * 10 + (sc->sc_buf[0] & 0xf);
454 	sc->sc_firmware_version[1] =
455 	    (sc->sc_buf[1]>>4) * 10 + (sc->sc_buf[1] & 0xf);
456 
457 	/* get device serial number */
458 	usps_cmd(sc, USPS_CMD_GET_SERIAL, 0, 3);
459 
460 	serial = 0;
461 	serial += ((sc->sc_buf[0]>>4) * 10 + (sc->sc_buf[0] & 0xf)) * 10000;
462 	serial += ((sc->sc_buf[1]>>4) * 10 + (sc->sc_buf[1] & 0xf)) * 100;
463 	serial += ((sc->sc_buf[2]>>4) * 10 + (sc->sc_buf[2] & 0xf));
464 	sc->sc_device_serial = serial;
465 }
466 
467 void
468 usps_refresh(void *arg)
469 {
470 	struct usps_softc *sc = arg;
471 
472 	usps_refresh_temp(sc);
473 	usps_refresh_power(sc);
474 	usps_refresh_ports(sc);
475 }
476 
477 void
478 usps_refresh_ports(struct usps_softc *sc)
479 {
480 	int i;
481 	struct usps_port_sensor *ps;
482 
483 	/* update port values */
484 	for (i = 0; i < FX5204_NUM_PORTS; i++) {
485 		ps = &sc->sc_port_sensor[i];
486 		ps->ave.value = ps->vave * 1000000;
487 		ps->min.value = ps->vmin * 1000000;
488 		ps->max.value = ps->vmax * 1000000;
489 	}
490 
491 	/* update total value */
492 	ps = &sc->sc_total_sensor;
493 	ps->ave.value = ps->vave * 1000000;
494 	ps->min.value = ps->vmin * 1000000;
495 	ps->max.value = ps->vmax * 1000000;
496 
497 	sc->sc_count = 0;
498 }
499 
500 void
501 usps_refresh_temp(struct usps_softc *sc)
502 {
503 	int temp;
504 
505 	if (usps_cmd(sc, USPS_CMD_GET_TEMP, 0, 2) != 0) {
506 		DPRINTF(("%s: temperature data read error\n",
507 		    sc->sc_dev.dv_xname));
508 		sc->sc_temp_sensor.flags |= SENSOR_FINVALID;
509 		return;
510 	}
511 	temp = (sc->sc_buf[1] << 8) + sc->sc_buf[0];
512 	sc->sc_temp_sensor.value = (temp * 10000) + 273150000;
513 	sc->sc_temp_sensor.flags &= ~SENSOR_FINVALID;
514 }
515 
516 void
517 usps_refresh_power(struct usps_softc *sc)
518 {
519 	int v;
520 	uint val;
521 	uint64_t f;
522 
523 	/* update source voltage */
524 	if (usps_cmd(sc, USPS_CMD_GET_VOLTAGE, 0, 1) != 0) {
525 		DPRINTF(("%s: voltage data read error\n", sc->sc_dev.dv_xname));
526 		sc->sc_voltage_sensor.flags |= SENSOR_FINVALID;
527 		return;
528 	}
529 
530 	v = sc->sc_buf[0] * 1000000;
531 	sc->sc_voltage_sensor.value = v;
532 	sc->sc_voltage_sensor.flags &= ~SENSOR_FINVALID;
533 
534 	/* update source frequency */
535 	if (usps_cmd(sc, USPS_CMD_GET_FREQ, 0, 8) != 0) {
536 		DPRINTF(("%s: frequency data read error\n",
537 		    sc->sc_dev.dv_xname));
538 		sc->sc_frequency_sensor.flags |= SENSOR_FINVALID;
539 		return;
540 	}
541 
542 	if (sc->sc_buf[7] == 0 && sc->sc_buf[6] == 0) {
543 		/* special case */
544 		f = 0;
545 	} else {
546 		val = (sc->sc_buf[1] << 8) + sc->sc_buf[0];
547 		if (val == 0) {
548 			/* guard against "division by zero" */
549 			sc->sc_frequency_sensor.flags |= SENSOR_FINVALID;
550 			return;
551 		}
552 		f = 2000000L;
553 		f *=  1000000L;
554 		f /= val;
555 	}
556 
557 	sc->sc_frequency_sensor.value = f;
558 	sc->sc_frequency_sensor.flags &= ~SENSOR_FINVALID;
559 }
560