Line data Source code
1 : /* $OpenBSD: uoakv.c,v 1.13 2017/04/08 02:57:25 deraadt 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: 8channel +/-10V ADC driver */
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/usbdevs.h>
34 : #include <dev/usb/uhidev.h>
35 :
36 : #include "uoak.h"
37 :
38 : #ifdef UOAKV_DEBUG
39 : int uoakvdebug = 0;
40 : #define DPRINTFN(n, x) do { if (uoakvdebug > (n)) printf x; } while (0)
41 : #else
42 : #define DPRINTFN(n, x)
43 : #endif
44 :
45 : #define DPRINTF(x) DPRINTFN(0, x)
46 :
47 : #define UOAKV_SAMPLE_RATE 100 /* ms */
48 : #define UOAKV_REFRESH_PERIOD 1 /* 1 sec : 1Hz */
49 :
50 : struct uoakv_sensor {
51 : struct uoak_sensor v;
52 : /* ADC setting */
53 : unsigned int offset[OAK_V_TARGET_MAX]; /* absolute offset (mV) */
54 : };
55 :
56 : struct uoakv_softc {
57 : struct uhidev sc_hdev;
58 :
59 : /* uoak common */
60 : struct uoak_softc sc_uoak_softc;
61 :
62 : /* sensor framework */
63 : struct uoakv_sensor sc_sensor[OAK_V_MAXSENSORS];
64 : struct ksensordev sc_sensordev;
65 : struct sensor_task *sc_sensortask;
66 :
67 : /* sensor setting */
68 : int sc_inputmode[OAK_V_TARGET_MAX];
69 :
70 : };
71 :
72 : const struct usb_devno uoakv_devs[] = {
73 : { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_10V},
74 : };
75 : #define uoakv_lookup(v, p) usb_lookup(uoakv_devs, v, p)
76 :
77 : int uoakv_match(struct device *, void *, void *);
78 : void uoakv_attach(struct device *, struct device *, void *);
79 : int uoakv_detach(struct device *, int);
80 :
81 : void uoakv_intr(struct uhidev *, void *, u_int);
82 : void uoakv_refresh(void *);
83 :
84 : int uoakv_get_channel_setting(struct uoakv_softc *, enum uoak_target, int);
85 : int uoakv_get_sensor_setting(struct uoakv_softc *, enum uoak_target);
86 :
87 : void uoakv_dev_setting(void *, enum uoak_target);
88 : void uoakv_dev_print(void *, enum uoak_target);
89 :
90 :
91 : struct cfdriver uoakv_cd = {
92 : NULL, "uoakv", DV_DULL
93 : };
94 :
95 : const struct cfattach uoakv_ca = {
96 : sizeof(struct uoakv_softc),
97 : uoakv_match,
98 : uoakv_attach,
99 : uoakv_detach,
100 :
101 : };
102 :
103 : struct uoak_methods uoakv_methods = {
104 : uoakv_dev_print,
105 : uoakv_dev_setting
106 : };
107 :
108 : int
109 0 : uoakv_match(struct device *parent, void *match, void *aux)
110 : {
111 0 : struct uhidev_attach_arg *uha = aux;
112 :
113 0 : if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
114 0 : return (UMATCH_NONE);
115 :
116 0 : if (uoakv_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
117 0 : return UMATCH_NONE;
118 :
119 0 : return (UMATCH_VENDOR_PRODUCT);
120 0 : }
121 :
122 : void
123 0 : uoakv_attach(struct device *parent, struct device *self, void *aux)
124 : {
125 0 : struct uoakv_softc *sc = (struct uoakv_softc *)self;
126 0 : struct usb_attach_arg *uaa = aux;
127 0 : struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
128 0 : struct usbd_device *dev = uha->parent->sc_udev;
129 :
130 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
131 0 : int i, err, size, repid;
132 0 : void *desc;
133 :
134 0 : sc->sc_hdev.sc_intr = uoakv_intr;
135 0 : sc->sc_hdev.sc_parent = uha->parent;
136 0 : sc->sc_hdev.sc_report_id = uha->reportid;
137 :
138 0 : scc->sc_parent = sc;
139 0 : scc->sc_udev = dev;
140 0 : scc->sc_hdev = &sc->sc_hdev;
141 0 : scc->sc_methods = &uoakv_methods;
142 0 : scc->sc_sensordev = &sc->sc_sensordev;
143 :
144 0 : uhidev_get_report_desc(uha->parent, &desc, &size);
145 0 : repid = uha->reportid;
146 0 : scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
147 0 : scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
148 0 : scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
149 :
150 : /* device initialize */
151 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
152 0 : err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKV_SAMPLE_RATE);
153 0 : if (err) {
154 0 : printf("%s: could not set sampling rate. exit\n",
155 0 : sc->sc_hdev.sc_dev.dv_xname);
156 0 : return;
157 : }
158 :
159 : /* query and print device setting */
160 0 : uoak_get_devinfo(scc);
161 0 : uoak_print_devinfo(scc);
162 :
163 : DPRINTF((" config in RAM\n"));
164 0 : uoak_get_setting(scc, OAK_TARGET_RAM);
165 0 : uoak_print_setting(scc, OAK_TARGET_RAM);
166 : #ifdef UOAKV_DEBUG
167 : DPRINTF((" config in FRASH\n"));
168 : uoak_get_setting(scc, OAK_TARGET_FLASH);
169 : uoak_print_setting(scc, OAK_TARGET_FLASH);
170 : #endif
171 :
172 : /* attach sensor */
173 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
174 : sizeof(sc->sc_sensordev.xname));
175 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++)
176 0 : uoak_sensor_attach(scc, &sc->sc_sensor[i].v, SENSOR_VOLTS_DC);
177 :
178 : /* start sensor */
179 0 : sc->sc_sensortask = sensor_task_register(sc, uoakv_refresh,
180 : UOAKV_REFRESH_PERIOD);
181 0 : if (sc->sc_sensortask == NULL) {
182 0 : printf(", unable to register update task\n");
183 0 : return;
184 : }
185 0 : sensordev_install(&sc->sc_sensordev);
186 :
187 0 : err = uhidev_open(&sc->sc_hdev);
188 0 : if (err) {
189 0 : printf("%s: could not open interrupt pipe, quit\n",
190 : sc->sc_hdev.sc_dev.dv_xname);
191 0 : return;
192 : }
193 0 : scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
194 :
195 : DPRINTF(("uoakv_attach: complete\n"));
196 0 : }
197 :
198 : int
199 0 : uoakv_detach(struct device *self, int flags)
200 : {
201 0 : struct uoakv_softc *sc = (struct uoakv_softc *)self;
202 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
203 : int i, rv = 0;
204 :
205 0 : wakeup(&sc->sc_sensortask);
206 0 : sensordev_deinstall(&sc->sc_sensordev);
207 :
208 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++)
209 0 : uoak_sensor_detach(scc, &sc->sc_sensor[i].v);
210 :
211 0 : if (sc->sc_sensortask != NULL)
212 0 : sensor_task_unregister(sc->sc_sensortask);
213 :
214 0 : if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
215 0 : uhidev_close(&sc->sc_hdev);
216 :
217 0 : if (scc->sc_ibuf != NULL) {
218 0 : free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
219 0 : scc->sc_ibuf = NULL;
220 0 : }
221 :
222 0 : return (rv);
223 : }
224 :
225 : void
226 0 : uoakv_intr(struct uhidev *addr, void *ibuf, u_int len)
227 : {
228 0 : struct uoakv_softc *sc = (struct uoakv_softc *)addr;
229 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
230 : int i, idx, frame;
231 : int16_t val;
232 :
233 0 : if (scc->sc_ibuf == NULL)
234 0 : return;
235 :
236 0 : memcpy(scc->sc_ibuf, ibuf, len);
237 0 : frame = (scc->sc_ibuf[1] << 8) + scc->sc_ibuf[0];
238 :
239 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++) {
240 0 : idx = (i + 1) * 2;
241 0 : val = (int16_t)((scc->sc_ibuf[idx+1] << 8) | scc->sc_ibuf[idx]);
242 0 : uoak_sensor_update(&sc->sc_sensor[i].v, val);
243 : }
244 0 : }
245 :
246 : void
247 0 : uoakv_refresh(void *arg)
248 : {
249 0 : struct uoakv_softc *sc = arg;
250 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
251 0 : uint8_t led;
252 : int i;
253 :
254 : /* blink LED for each cycle */
255 0 : if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
256 : DPRINTF(("status query error\n"));
257 0 : if (led == OAK_LED_OFF)
258 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
259 : else
260 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
261 :
262 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++)
263 0 : uoak_sensor_refresh(&sc->sc_sensor[i].v, 1000, 0);
264 0 : }
265 :
266 : int
267 0 : uoakv_get_channel_setting(struct uoakv_softc *sc, enum uoak_target target,
268 : int ch)
269 : {
270 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
271 : uint16_t cmd, result;
272 :
273 0 : memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
274 0 : scc->sc_rcmd.target = target;
275 0 : scc->sc_rcmd.datasize = 0x2;
276 :
277 : #define OAK_V_CHANNEL_IDX_OFFSET 3
278 0 : cmd = (ch + OAK_V_CHANNEL_IDX_OFFSET);
279 0 : USETW(&scc->sc_rcmd.cmd, cmd);
280 :
281 0 : if (uoak_get_cmd(scc) < 0)
282 0 : return EIO;
283 :
284 0 : result = (scc->sc_buf[2] << 8) + scc->sc_buf[1];
285 0 : sc->sc_sensor[ch].offset[target] = result;
286 :
287 0 : return 0;
288 0 : }
289 :
290 : int
291 0 : uoakv_get_sensor_setting(struct uoakv_softc *sc, enum uoak_target target)
292 : {
293 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
294 : uint8_t result;
295 :
296 0 : memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
297 0 : scc->sc_rcmd.target = target;
298 0 : scc->sc_rcmd.datasize = 0x1;
299 0 : USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
300 :
301 0 : if (uoak_get_cmd(scc) < 0)
302 0 : return EIO;
303 :
304 0 : result = scc->sc_buf[1];
305 0 : sc->sc_inputmode[target] = (result & OAK_V_SENSOR_INPUTMODEMASK);
306 :
307 0 : return 0;
308 0 : }
309 :
310 : /* device specific functions */
311 : void
312 0 : uoakv_dev_setting(void *parent, enum uoak_target target)
313 : {
314 0 : struct uoakv_softc *sc = (struct uoakv_softc *)parent;
315 : int i;
316 :
317 : /* get device specific configuration */
318 0 : (void)uoakv_get_sensor_setting(sc, target);
319 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++)
320 0 : (void)uoakv_get_channel_setting(sc, target, i);
321 0 : }
322 :
323 : void
324 0 : uoakv_dev_print(void *parent, enum uoak_target target)
325 : {
326 0 : struct uoakv_softc *sc = (struct uoakv_softc *)parent;
327 : int i;
328 :
329 0 : printf(", %s",
330 0 : (sc->sc_inputmode[target] ? "Psuedo-Diffential" : "Single-Ended"));
331 :
332 0 : printf(", ADC channel offsets:\n");
333 0 : printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
334 0 : for (i = 0; i < OAK_V_MAXSENSORS; i++)
335 0 : printf("ch%02d %2d.%02d, ", i,
336 0 : sc->sc_sensor[i].offset[target] / 100,
337 0 : sc->sc_sensor[i].offset[target] % 100);
338 0 : }
|