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