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