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