xref: /openbsd/sys/dev/usb/uoak_subr.c (revision 81508fe3)
1 /*	$OpenBSD: uoak_subr.c,v 1.11 2024/05/23 03:21:09 jsg Exp $   */
2 
3 /*
4  * Copyright (c) 2012 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 /* TORADEX OAK series sensors: common functions */
20 /* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/device.h>
25 #include <sys/sensors.h>
26 
27 #include <dev/usb/usb.h>
28 #include <dev/usb/usbhid.h>
29 #include <dev/usb/usbdi.h>
30 #include <dev/usb/usbdi_util.h>
31 #include <dev/usb/uhidev.h>
32 #include "uoak.h"
33 
34 #define UOAK_RETRY_DELAY	 100 /* 100ms, XXX too long? */
35 #define UOAK_RESPONSE_DELAY	 10  /* 10ms,  XXX too short? */
36 /*
37  *  basic procedure to issue command to the OAK device.
38  *  1) check the device is ready to accept command.
39  *     if a report of a FEATURE_REPORT request is not start 0xff,
40  *     wait for a while, and retry till the response start with 0xff.
41  *  2) issue command.  (set or get)
42  *  3) if the command will response, wait for a while, and issue
43  *     FEATURE_REPORT. leading 0xff indicate the response is valid.
44  *     if the first byte is not 0xff, retry.
45  */
46 int
uoak_check_device_ready(struct uoak_softc * sc)47 uoak_check_device_ready(struct uoak_softc *sc)
48 {
49 	int actlen;
50 
51 	actlen = uhidev_get_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
52 	    sc->sc_hdev->sc_report_id, &sc->sc_buf, sc->sc_flen);
53 	if (actlen != sc->sc_flen)
54 		return EIO;
55 
56 	if (sc->sc_buf[0] != 0xff)
57 		return -1;
58 
59 	return 0;
60 }
61 
62 int
uoak_set_cmd(struct uoak_softc * sc)63 uoak_set_cmd(struct uoak_softc *sc)
64 {
65 	int actlen;
66 	sc->sc_rcmd.dir = OAK_SET;
67 
68 	while (uoak_check_device_ready(sc) < 0)
69 		usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
70 
71 	actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
72 	    sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
73 	if (actlen != sc->sc_flen)
74 		return EIO;
75 
76 	return 0;
77 }
78 
79 int
uoak_get_cmd(struct uoak_softc * sc)80 uoak_get_cmd(struct uoak_softc *sc)
81 {
82 	int actlen;
83 	sc->sc_rcmd.dir = OAK_GET;
84 
85 	/* check the device is ready to request */
86 	while (uoak_check_device_ready(sc) < 0)
87 		usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
88 
89 	/* issue request */
90 	actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
91 	    sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
92 	if (actlen != sc->sc_flen)
93 		return EIO;
94 
95 	/* wait till the device ready to return the request */
96 	while (uoak_check_device_ready(sc) < 0)
97 		usbd_delay_ms(sc->sc_udev, UOAK_RESPONSE_DELAY);
98 
99 	return 0;
100 }
101 
102 /*
103  * Functions to access device configurations.
104  * OAK sensor have some storages to store its configuration.
105  * (RAM, FLASH and others)
106  */
107 int
uoak_get_device_name(struct uoak_softc * sc,enum uoak_target target)108 uoak_get_device_name(struct uoak_softc *sc, enum uoak_target target)
109 {
110 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
111 	sc->sc_rcmd.target = target;
112 	sc->sc_rcmd.datasize = 0x15;
113 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_DEVNAME);
114 
115 	if (uoak_get_cmd(sc) < 0)
116 		return EIO;
117 
118 	strlcpy(sc->sc_config[target].devname, sc->sc_buf+1,
119 	    sizeof(sc->sc_config[target].devname));
120 	return 0;
121 }
122 
123 int
uoak_get_report_mode(struct uoak_softc * sc,enum uoak_target target)124 uoak_get_report_mode(struct uoak_softc *sc, enum uoak_target target)
125 {
126 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
127 	sc->sc_rcmd.target = target;
128 	sc->sc_rcmd.datasize = 0x1;
129 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTMODE);
130 
131 	if (uoak_get_cmd(sc) < 0)
132 		return EIO;
133 
134 	sc->sc_config[target].report_mode = sc->sc_buf[1];
135 	return 0;
136 }
137 
138 int
uoak_get_report_rate(struct uoak_softc * sc,enum uoak_target target)139 uoak_get_report_rate(struct uoak_softc *sc, enum uoak_target target)
140 {
141 	uint16_t result;
142 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
143 	sc->sc_rcmd.target = target;
144 	sc->sc_rcmd.datasize = 0x2;
145 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTRATE);
146 
147 	if (uoak_get_cmd(sc) < 0)
148 		return EIO;
149 
150 	result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
151 	sc->sc_config[target].report_rate = result;
152 
153 	return 0;
154 }
155 
156 int
uoak_get_sample_rate(struct uoak_softc * sc,enum uoak_target target)157 uoak_get_sample_rate(struct uoak_softc *sc, enum uoak_target target)
158 {
159 	uint16_t result;
160 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
161 	sc->sc_rcmd.target = target;
162 	sc->sc_rcmd.datasize = 0x2;
163 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
164 
165 	if (uoak_get_cmd(sc) < 0)
166 		return EIO;
167 
168 	result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
169 	sc->sc_config[target].sample_rate = result;
170 
171 	return 0;
172 }
173 
174 int
uoak_set_sample_rate(struct uoak_softc * sc,enum uoak_target target,int rate)175 uoak_set_sample_rate(struct uoak_softc *sc, enum uoak_target target, int rate)
176 {
177 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
178 	sc->sc_rcmd.target = target;
179 	sc->sc_rcmd.datasize = 0x2;
180 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
181 
182 #if 0
183 	sc->sc_rcmd.val[0] = (uint8_t)(rate & 0xff);
184 	sc->sc_rcmd.val[1] = (uint8_t)((rate >> 8) & 0xff)
185 #else
186 	USETW(sc->sc_rcmd.val, rate);
187 #endif
188 
189 	if (uoak_set_cmd(sc) < 0)
190 		return EIO;
191 
192 	return 0;
193 }
194 
195 /*
196  * LED I/O
197  */
198 int
uoak_led_status(struct uoak_softc * sc,enum uoak_target target,uint8_t * mode)199 uoak_led_status(struct uoak_softc *sc, enum uoak_target target, uint8_t *mode)
200 {
201 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
202 	sc->sc_rcmd.target = target;
203 	sc->sc_rcmd.datasize = 0x1;
204 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
205 
206 	if (uoak_get_cmd(sc) < 0)
207 		return EIO;
208 
209 	*mode =  sc->sc_buf[1];
210 	return 0;
211 }
212 
213 int
uoak_led_ctrl(struct uoak_softc * sc,enum uoak_target target,uint8_t mode)214 uoak_led_ctrl(struct uoak_softc *sc, enum uoak_target target, uint8_t mode)
215 {
216 	memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
217 
218 	sc->sc_rcmd.target = target;
219 	sc->sc_rcmd.datasize = 0x1;
220 	USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
221 	sc->sc_rcmd.val[0] = mode;
222 
223 	return uoak_set_cmd(sc);
224 }
225 
226 /* device setting: query and pretty print */
227 void
uoak_get_devinfo(struct uoak_softc * sc)228 uoak_get_devinfo(struct uoak_softc *sc)
229 {
230 	/* get device serial# */
231 	usbd_fill_deviceinfo(sc->sc_udev, &sc->sc_udi);
232 }
233 
234 void
uoak_get_setting(struct uoak_softc * sc,enum uoak_target target)235 uoak_get_setting(struct uoak_softc *sc, enum uoak_target target)
236 {
237 	/* get device level */
238 	(void)uoak_get_device_name(sc, target);
239 
240 	/* get global sensor configuration */
241 	(void)uoak_get_report_mode(sc, target);
242 	(void)uoak_get_sample_rate(sc, target);
243 	(void)uoak_get_report_rate(sc, target);
244 
245 	/* get device specific information */
246 	if (sc->sc_methods->dev_setting != NULL)
247 		sc->sc_methods->dev_setting(sc->sc_parent, target);
248 }
249 
250 void
uoak_print_devinfo(struct uoak_softc * sc)251 uoak_print_devinfo(struct uoak_softc *sc)
252 {
253 	printf(": serial %s", sc->sc_udi.udi_serial);
254 }
255 
256 void
uoak_print_setting(struct uoak_softc * sc,enum uoak_target target)257 uoak_print_setting(struct uoak_softc *sc, enum uoak_target target)
258 {
259 	switch (sc->sc_config[target].report_mode) {
260 	case OAK_REPORTMODE_AFTERSAMPLING:
261 		printf(" sampling %dms",
262 		    sc->sc_config[target].sample_rate);
263 		break;
264 	case OAK_REPORTMODE_AFTERCHANGE:
265 		printf(" reports changes");
266 		break;
267 	case OAK_REPORTMODE_FIXEDRATE:
268 		printf(" rate %dms",
269 		    sc->sc_config[target].report_rate);
270 		break;
271 	default:
272 		printf(" unknown sampling");
273 		break;
274 	}
275 
276 	/* print device specific information */
277 	if (sc->sc_methods->dev_print != NULL)
278 		sc->sc_methods->dev_print(sc->sc_parent, target);
279 	printf("\n");
280 }
281 
282 void
uoak_sensor_attach(struct uoak_softc * sc,struct uoak_sensor * s,enum sensor_type type)283 uoak_sensor_attach(struct uoak_softc *sc, struct uoak_sensor *s,
284   enum sensor_type type)
285 {
286 	if (s == NULL)
287 		return;
288 
289 	s->avg.type = type;
290 	s->max.type = type;
291 	s->min.type = type;
292 	s->avg.flags |= SENSOR_FINVALID;
293 	s->max.flags |= SENSOR_FINVALID;
294 	s->min.flags |= SENSOR_FINVALID;
295 
296 	(void)snprintf(s->avg.desc, sizeof(s->avg.desc),
297 	    "avg(#%s)", sc->sc_udi.udi_serial);
298 	(void)snprintf(s->max.desc, sizeof(s->max.desc),
299 	    "max(#%s)", sc->sc_udi.udi_serial);
300 	(void)snprintf(s->min.desc, sizeof(s->min.desc),
301 	    "min(#%s)", sc->sc_udi.udi_serial);
302 
303 	sensor_attach(sc->sc_sensordev, &s->avg);
304 	sensor_attach(sc->sc_sensordev, &s->max);
305 	sensor_attach(sc->sc_sensordev, &s->min);
306 }
307 
308 void
uoak_sensor_detach(struct uoak_softc * sc,struct uoak_sensor * s)309 uoak_sensor_detach(struct uoak_softc *sc, struct uoak_sensor *s)
310 {
311 	if (s == NULL)
312 		return;
313 
314 	sensor_attach(sc->sc_sensordev, &s->avg);
315 	sensor_attach(sc->sc_sensordev, &s->max);
316 	sensor_attach(sc->sc_sensordev, &s->min);
317 }
318 
319 void
uoak_sensor_update(struct uoak_sensor * s,int val)320 uoak_sensor_update(struct uoak_sensor *s, int val)
321 {
322 	if (s == NULL)
323 		return;
324 
325 	/* reset */
326 	if (s->count == 0) {
327 		s->vmax = s->vmin = s->vavg = val;
328 		s->count++;
329 		return;
330 	}
331 
332 	/* update min/max */
333 	if (val > s->vmax)
334 		s->vmax = val;
335 	else if (val < s->vmin)
336 		s->vmin = val;
337 
338 	/* calc average */
339 	s->vavg = (s->vavg * s->count + val) / (s->count + 1);
340 
341 	s->count++;
342 }
343 
344 void
uoak_sensor_refresh(struct uoak_sensor * s,int mag,int offset)345 uoak_sensor_refresh(struct uoak_sensor *s, int mag, int offset)
346 {
347 	if (s == NULL)
348 		return;
349 	/* update value */
350 	s->avg.value = s->vavg * mag + offset;
351 	s->max.value = s->vmax * mag + offset;
352 	s->min.value = s->vmin * mag + offset;
353 
354 	/* update flag */
355 	s->avg.flags &= ~SENSOR_FINVALID;
356 	s->max.flags &= ~SENSOR_FINVALID;
357 	s->min.flags &= ~SENSOR_FINVALID;
358 	s->count = 0;
359 }
360 
361