xref: /openbsd-src/sys/dev/usb/uoak_subr.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: uoak_subr.c,v 1.11 2024/05/23 03:21:09 jsg Exp $   */
2ae06e6b2Syuo 
3ae06e6b2Syuo /*
4ae06e6b2Syuo  * Copyright (c) 2012 Yojiro UO <yuo@nui.org>
5ae06e6b2Syuo  *
6ae06e6b2Syuo  * Permission to use, copy, modify, and distribute this software for any
7ae06e6b2Syuo  * purpose with or without fee is hereby granted, provided that the above
8ae06e6b2Syuo  * copyright notice and this permission notice appear in all copies.
9ae06e6b2Syuo  *
10ae06e6b2Syuo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11ae06e6b2Syuo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ae06e6b2Syuo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ae06e6b2Syuo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ae06e6b2Syuo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15ae06e6b2Syuo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16ae06e6b2Syuo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ae06e6b2Syuo  */
18ae06e6b2Syuo 
194b1a56afSjsg /* TORADEX OAK series sensors: common functions */
20ae06e6b2Syuo /* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
21ae06e6b2Syuo 
22ae06e6b2Syuo #include <sys/param.h>
23ae06e6b2Syuo #include <sys/systm.h>
24ae06e6b2Syuo #include <sys/device.h>
25ae06e6b2Syuo #include <sys/sensors.h>
26ae06e6b2Syuo 
27ae06e6b2Syuo #include <dev/usb/usb.h>
28ae06e6b2Syuo #include <dev/usb/usbhid.h>
29ae06e6b2Syuo #include <dev/usb/usbdi.h>
30ae06e6b2Syuo #include <dev/usb/usbdi_util.h>
31ae06e6b2Syuo #include <dev/usb/uhidev.h>
32ae06e6b2Syuo #include "uoak.h"
33ae06e6b2Syuo 
34ae06e6b2Syuo #define UOAK_RETRY_DELAY	 100 /* 100ms, XXX too long? */
35ae06e6b2Syuo #define UOAK_RESPONSE_DELAY	 10  /* 10ms,  XXX too short? */
36ae06e6b2Syuo /*
37ae06e6b2Syuo  *  basic procedure to issue command to the OAK device.
38ae06e6b2Syuo  *  1) check the device is ready to accept command.
39ae06e6b2Syuo  *     if a report of a FEATURE_REPORT request is not start 0xff,
404b1a56afSjsg  *     wait for a while, and retry till the response start with 0xff.
41ae06e6b2Syuo  *  2) issue command.  (set or get)
42ae06e6b2Syuo  *  3) if the command will response, wait for a while, and issue
43ae06e6b2Syuo  *     FEATURE_REPORT. leading 0xff indicate the response is valid.
44ae06e6b2Syuo  *     if the first byte is not 0xff, retry.
45ae06e6b2Syuo  */
46ae06e6b2Syuo int
uoak_check_device_ready(struct uoak_softc * sc)47ae06e6b2Syuo uoak_check_device_ready(struct uoak_softc *sc)
48ae06e6b2Syuo {
49e6a02383Smpi 	int actlen;
50e6a02383Smpi 
51e6a02383Smpi 	actlen = uhidev_get_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
52e6a02383Smpi 	    sc->sc_hdev->sc_report_id, &sc->sc_buf, sc->sc_flen);
53e6a02383Smpi 	if (actlen != sc->sc_flen)
54ae06e6b2Syuo 		return EIO;
55ae06e6b2Syuo 
56ae06e6b2Syuo 	if (sc->sc_buf[0] != 0xff)
57ae06e6b2Syuo 		return -1;
58ae06e6b2Syuo 
59ae06e6b2Syuo 	return 0;
60ae06e6b2Syuo }
61ae06e6b2Syuo 
62ae06e6b2Syuo int
uoak_set_cmd(struct uoak_softc * sc)63ae06e6b2Syuo uoak_set_cmd(struct uoak_softc *sc)
64ae06e6b2Syuo {
65e6a02383Smpi 	int actlen;
66ae06e6b2Syuo 	sc->sc_rcmd.dir = OAK_SET;
67ae06e6b2Syuo 
68ae06e6b2Syuo 	while (uoak_check_device_ready(sc) < 0)
69ae06e6b2Syuo 		usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
70ae06e6b2Syuo 
71e6a02383Smpi 	actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
72e6a02383Smpi 	    sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
73e6a02383Smpi 	if (actlen != sc->sc_flen)
74ae06e6b2Syuo 		return EIO;
75ae06e6b2Syuo 
76ae06e6b2Syuo 	return 0;
77ae06e6b2Syuo }
78ae06e6b2Syuo 
79ae06e6b2Syuo int
uoak_get_cmd(struct uoak_softc * sc)80ae06e6b2Syuo uoak_get_cmd(struct uoak_softc *sc)
81ae06e6b2Syuo {
82e6a02383Smpi 	int actlen;
83ae06e6b2Syuo 	sc->sc_rcmd.dir = OAK_GET;
84ae06e6b2Syuo 
85ae06e6b2Syuo 	/* check the device is ready to request */
86ae06e6b2Syuo 	while (uoak_check_device_ready(sc) < 0)
87ae06e6b2Syuo 		usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
88ae06e6b2Syuo 
89ae06e6b2Syuo 	/* issue request */
90e6a02383Smpi 	actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
91e6a02383Smpi 	    sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
92e6a02383Smpi 	if (actlen != sc->sc_flen)
93ae06e6b2Syuo 		return EIO;
94ae06e6b2Syuo 
95ae06e6b2Syuo 	/* wait till the device ready to return the request */
96ae06e6b2Syuo 	while (uoak_check_device_ready(sc) < 0)
97ae06e6b2Syuo 		usbd_delay_ms(sc->sc_udev, UOAK_RESPONSE_DELAY);
98ae06e6b2Syuo 
99ae06e6b2Syuo 	return 0;
100ae06e6b2Syuo }
101ae06e6b2Syuo 
102ae06e6b2Syuo /*
103ae06e6b2Syuo  * Functions to access device configurations.
104ae06e6b2Syuo  * OAK sensor have some storages to store its configuration.
105ae06e6b2Syuo  * (RAM, FLASH and others)
106ae06e6b2Syuo  */
107ae06e6b2Syuo int
uoak_get_device_name(struct uoak_softc * sc,enum uoak_target target)108ae06e6b2Syuo uoak_get_device_name(struct uoak_softc *sc, enum uoak_target target)
109ae06e6b2Syuo {
110ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
111ae06e6b2Syuo 	sc->sc_rcmd.target = target;
112ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x15;
113ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_DEVNAME);
114ae06e6b2Syuo 
115ae06e6b2Syuo 	if (uoak_get_cmd(sc) < 0)
116ae06e6b2Syuo 		return EIO;
117ae06e6b2Syuo 
118ae06e6b2Syuo 	strlcpy(sc->sc_config[target].devname, sc->sc_buf+1,
119ae06e6b2Syuo 	    sizeof(sc->sc_config[target].devname));
120ae06e6b2Syuo 	return 0;
121ae06e6b2Syuo }
122ae06e6b2Syuo 
123ae06e6b2Syuo int
uoak_get_report_mode(struct uoak_softc * sc,enum uoak_target target)124ae06e6b2Syuo uoak_get_report_mode(struct uoak_softc *sc, enum uoak_target target)
125ae06e6b2Syuo {
126ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
127ae06e6b2Syuo 	sc->sc_rcmd.target = target;
128ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x1;
129ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTMODE);
130ae06e6b2Syuo 
131ae06e6b2Syuo 	if (uoak_get_cmd(sc) < 0)
132ae06e6b2Syuo 		return EIO;
133ae06e6b2Syuo 
134ae06e6b2Syuo 	sc->sc_config[target].report_mode = sc->sc_buf[1];
135ae06e6b2Syuo 	return 0;
136ae06e6b2Syuo }
137ae06e6b2Syuo 
138ae06e6b2Syuo int
uoak_get_report_rate(struct uoak_softc * sc,enum uoak_target target)139ae06e6b2Syuo uoak_get_report_rate(struct uoak_softc *sc, enum uoak_target target)
140ae06e6b2Syuo {
141ae06e6b2Syuo 	uint16_t result;
142ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
143ae06e6b2Syuo 	sc->sc_rcmd.target = target;
144ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x2;
145ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTRATE);
146ae06e6b2Syuo 
147ae06e6b2Syuo 	if (uoak_get_cmd(sc) < 0)
148ae06e6b2Syuo 		return EIO;
149ae06e6b2Syuo 
150ae06e6b2Syuo 	result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
151ae06e6b2Syuo 	sc->sc_config[target].report_rate = result;
152ae06e6b2Syuo 
153ae06e6b2Syuo 	return 0;
154ae06e6b2Syuo }
155ae06e6b2Syuo 
156ae06e6b2Syuo int
uoak_get_sample_rate(struct uoak_softc * sc,enum uoak_target target)157ae06e6b2Syuo uoak_get_sample_rate(struct uoak_softc *sc, enum uoak_target target)
158ae06e6b2Syuo {
159ae06e6b2Syuo 	uint16_t result;
160ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
161ae06e6b2Syuo 	sc->sc_rcmd.target = target;
162ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x2;
163ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
164ae06e6b2Syuo 
165ae06e6b2Syuo 	if (uoak_get_cmd(sc) < 0)
166ae06e6b2Syuo 		return EIO;
167ae06e6b2Syuo 
168ae06e6b2Syuo 	result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
169ae06e6b2Syuo 	sc->sc_config[target].sample_rate = result;
170ae06e6b2Syuo 
171ae06e6b2Syuo 	return 0;
172ae06e6b2Syuo }
173ae06e6b2Syuo 
174ae06e6b2Syuo int
uoak_set_sample_rate(struct uoak_softc * sc,enum uoak_target target,int rate)175ae06e6b2Syuo uoak_set_sample_rate(struct uoak_softc *sc, enum uoak_target target, int rate)
176ae06e6b2Syuo {
177ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
178ae06e6b2Syuo 	sc->sc_rcmd.target = target;
179ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x2;
180ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
181ae06e6b2Syuo 
182ae06e6b2Syuo #if 0
183ae06e6b2Syuo 	sc->sc_rcmd.val[0] = (uint8_t)(rate & 0xff);
184ae06e6b2Syuo 	sc->sc_rcmd.val[1] = (uint8_t)((rate >> 8) & 0xff)
185ae06e6b2Syuo #else
186978c02abSjsg 	USETW(sc->sc_rcmd.val, rate);
187ae06e6b2Syuo #endif
188ae06e6b2Syuo 
189ae06e6b2Syuo 	if (uoak_set_cmd(sc) < 0)
190ae06e6b2Syuo 		return EIO;
191ae06e6b2Syuo 
192ae06e6b2Syuo 	return 0;
193ae06e6b2Syuo }
194ae06e6b2Syuo 
195ae06e6b2Syuo /*
196ae06e6b2Syuo  * LED I/O
197ae06e6b2Syuo  */
198ae06e6b2Syuo int
uoak_led_status(struct uoak_softc * sc,enum uoak_target target,uint8_t * mode)199ae06e6b2Syuo uoak_led_status(struct uoak_softc *sc, enum uoak_target target, uint8_t *mode)
200ae06e6b2Syuo {
201ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
202ae06e6b2Syuo 	sc->sc_rcmd.target = target;
203ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x1;
204ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
205ae06e6b2Syuo 
206ae06e6b2Syuo 	if (uoak_get_cmd(sc) < 0)
207ae06e6b2Syuo 		return EIO;
208ae06e6b2Syuo 
209ae06e6b2Syuo 	*mode =  sc->sc_buf[1];
210ae06e6b2Syuo 	return 0;
211ae06e6b2Syuo }
212ae06e6b2Syuo 
213ae06e6b2Syuo int
uoak_led_ctrl(struct uoak_softc * sc,enum uoak_target target,uint8_t mode)214ae06e6b2Syuo uoak_led_ctrl(struct uoak_softc *sc, enum uoak_target target, uint8_t mode)
215ae06e6b2Syuo {
216ae06e6b2Syuo 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
217ae06e6b2Syuo 
218ae06e6b2Syuo 	sc->sc_rcmd.target = target;
219ae06e6b2Syuo 	sc->sc_rcmd.datasize = 0x1;
220ae06e6b2Syuo 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
221ae06e6b2Syuo 	sc->sc_rcmd.val[0] = mode;
222ae06e6b2Syuo 
223ae06e6b2Syuo 	return uoak_set_cmd(sc);
224ae06e6b2Syuo }
225ae06e6b2Syuo 
226ae06e6b2Syuo /* device setting: query and pretty print */
227ae06e6b2Syuo void
uoak_get_devinfo(struct uoak_softc * sc)228ae06e6b2Syuo uoak_get_devinfo(struct uoak_softc *sc)
229ae06e6b2Syuo {
230ae06e6b2Syuo 	/* get device serial# */
23139369402Slandry 	usbd_fill_deviceinfo(sc->sc_udev, &sc->sc_udi);
232ae06e6b2Syuo }
233ae06e6b2Syuo 
234ae06e6b2Syuo void
uoak_get_setting(struct uoak_softc * sc,enum uoak_target target)235ae06e6b2Syuo uoak_get_setting(struct uoak_softc *sc, enum uoak_target target)
236ae06e6b2Syuo {
237448c0c4aSderaadt 	/* get device level */
238ae06e6b2Syuo 	(void)uoak_get_device_name(sc, target);
239ae06e6b2Syuo 
240ae06e6b2Syuo 	/* get global sensor configuration */
241ae06e6b2Syuo 	(void)uoak_get_report_mode(sc, target);
242ae06e6b2Syuo 	(void)uoak_get_sample_rate(sc, target);
243ae06e6b2Syuo 	(void)uoak_get_report_rate(sc, target);
244ae06e6b2Syuo 
2454b1a56afSjsg 	/* get device specific information */
246ae06e6b2Syuo 	if (sc->sc_methods->dev_setting != NULL)
247ae06e6b2Syuo 		sc->sc_methods->dev_setting(sc->sc_parent, target);
248ae06e6b2Syuo }
249ae06e6b2Syuo 
250ae06e6b2Syuo void
uoak_print_devinfo(struct uoak_softc * sc)251ae06e6b2Syuo uoak_print_devinfo(struct uoak_softc *sc)
252ae06e6b2Syuo {
253448c0c4aSderaadt 	printf(": serial %s", sc->sc_udi.udi_serial);
254ae06e6b2Syuo }
255ae06e6b2Syuo 
256ae06e6b2Syuo void
uoak_print_setting(struct uoak_softc * sc,enum uoak_target target)257ae06e6b2Syuo uoak_print_setting(struct uoak_softc *sc, enum uoak_target target)
258ae06e6b2Syuo {
259ae06e6b2Syuo 	switch (sc->sc_config[target].report_mode) {
260448c0c4aSderaadt 	case OAK_REPORTMODE_AFTERSAMPLING:
261448c0c4aSderaadt 		printf(" sampling %dms",
262ae06e6b2Syuo 		    sc->sc_config[target].sample_rate);
263ae06e6b2Syuo 		break;
264ae06e6b2Syuo 	case OAK_REPORTMODE_AFTERCHANGE:
265448c0c4aSderaadt 		printf(" reports changes");
266ae06e6b2Syuo 		break;
267ae06e6b2Syuo 	case OAK_REPORTMODE_FIXEDRATE:
268448c0c4aSderaadt 		printf(" rate %dms",
269ae06e6b2Syuo 		    sc->sc_config[target].report_rate);
270ae06e6b2Syuo 		break;
271ae06e6b2Syuo 	default:
272448c0c4aSderaadt 		printf(" unknown sampling");
273ae06e6b2Syuo 		break;
274ae06e6b2Syuo 	}
275ae06e6b2Syuo 
2764b1a56afSjsg 	/* print device specific information */
277ae06e6b2Syuo 	if (sc->sc_methods->dev_print != NULL)
278ae06e6b2Syuo 		sc->sc_methods->dev_print(sc->sc_parent, target);
279448c0c4aSderaadt 	printf("\n");
280ae06e6b2Syuo }
281ae06e6b2Syuo 
282ae06e6b2Syuo void
uoak_sensor_attach(struct uoak_softc * sc,struct uoak_sensor * s,enum sensor_type type)283ae06e6b2Syuo uoak_sensor_attach(struct uoak_softc *sc, struct uoak_sensor *s,
284ae06e6b2Syuo   enum sensor_type type)
285ae06e6b2Syuo {
286ae06e6b2Syuo 	if (s == NULL)
287ae06e6b2Syuo 		return;
288ae06e6b2Syuo 
289448c0c4aSderaadt 	s->avg.type = type;
290ae06e6b2Syuo 	s->max.type = type;
291ae06e6b2Syuo 	s->min.type = type;
292448c0c4aSderaadt 	s->avg.flags |= SENSOR_FINVALID;
293ae06e6b2Syuo 	s->max.flags |= SENSOR_FINVALID;
294ae06e6b2Syuo 	s->min.flags |= SENSOR_FINVALID;
295ae06e6b2Syuo 
296448c0c4aSderaadt 	(void)snprintf(s->avg.desc, sizeof(s->avg.desc),
297448c0c4aSderaadt 	    "avg(#%s)", sc->sc_udi.udi_serial);
298ae06e6b2Syuo 	(void)snprintf(s->max.desc, sizeof(s->max.desc),
299ae06e6b2Syuo 	    "max(#%s)", sc->sc_udi.udi_serial);
300ae06e6b2Syuo 	(void)snprintf(s->min.desc, sizeof(s->min.desc),
301ae06e6b2Syuo 	    "min(#%s)", sc->sc_udi.udi_serial);
302ae06e6b2Syuo 
303448c0c4aSderaadt 	sensor_attach(sc->sc_sensordev, &s->avg);
304ae06e6b2Syuo 	sensor_attach(sc->sc_sensordev, &s->max);
305ae06e6b2Syuo 	sensor_attach(sc->sc_sensordev, &s->min);
306ae06e6b2Syuo }
307ae06e6b2Syuo 
308ae06e6b2Syuo void
uoak_sensor_detach(struct uoak_softc * sc,struct uoak_sensor * s)309ae06e6b2Syuo uoak_sensor_detach(struct uoak_softc *sc, struct uoak_sensor *s)
310ae06e6b2Syuo {
311ae06e6b2Syuo 	if (s == NULL)
312ae06e6b2Syuo 		return;
313ae06e6b2Syuo 
314448c0c4aSderaadt 	sensor_attach(sc->sc_sensordev, &s->avg);
315ae06e6b2Syuo 	sensor_attach(sc->sc_sensordev, &s->max);
316ae06e6b2Syuo 	sensor_attach(sc->sc_sensordev, &s->min);
317ae06e6b2Syuo }
318ae06e6b2Syuo 
319ae06e6b2Syuo void
uoak_sensor_update(struct uoak_sensor * s,int val)320ae06e6b2Syuo uoak_sensor_update(struct uoak_sensor *s, int val)
321ae06e6b2Syuo {
322ae06e6b2Syuo 	if (s == NULL)
323ae06e6b2Syuo 		return;
324ae06e6b2Syuo 
325ae06e6b2Syuo 	/* reset */
326ae06e6b2Syuo 	if (s->count == 0) {
327448c0c4aSderaadt 		s->vmax = s->vmin = s->vavg = val;
328ae06e6b2Syuo 		s->count++;
329ae06e6b2Syuo 		return;
330ae06e6b2Syuo 	}
331ae06e6b2Syuo 
332ae06e6b2Syuo 	/* update min/max */
333ae06e6b2Syuo 	if (val > s->vmax)
334ae06e6b2Syuo 		s->vmax = val;
335ae06e6b2Syuo 	else if (val < s->vmin)
336ae06e6b2Syuo 		s->vmin = val;
337ae06e6b2Syuo 
338448c0c4aSderaadt 	/* calc average */
339448c0c4aSderaadt 	s->vavg = (s->vavg * s->count + val) / (s->count + 1);
340ae06e6b2Syuo 
341ae06e6b2Syuo 	s->count++;
342ae06e6b2Syuo }
343ae06e6b2Syuo 
344ae06e6b2Syuo void
uoak_sensor_refresh(struct uoak_sensor * s,int mag,int offset)345ae06e6b2Syuo uoak_sensor_refresh(struct uoak_sensor *s, int mag, int offset)
346ae06e6b2Syuo {
347ae06e6b2Syuo 	if (s == NULL)
348ae06e6b2Syuo 		return;
349ae06e6b2Syuo 	/* update value */
350448c0c4aSderaadt 	s->avg.value = s->vavg * mag + offset;
351ae06e6b2Syuo 	s->max.value = s->vmax * mag + offset;
352ae06e6b2Syuo 	s->min.value = s->vmin * mag + offset;
353ae06e6b2Syuo 
354ae06e6b2Syuo 	/* update flag */
355448c0c4aSderaadt 	s->avg.flags &= ~SENSOR_FINVALID;
356ae06e6b2Syuo 	s->max.flags &= ~SENSOR_FINVALID;
357ae06e6b2Syuo 	s->min.flags &= ~SENSOR_FINVALID;
358ae06e6b2Syuo 	s->count = 0;
359ae06e6b2Syuo }
360ae06e6b2Syuo 
361