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