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