Line data Source code
1 : /* $OpenBSD: uoaklux.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: lux sensor 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 UOAKLUX_DEBUG
39 : int uoakluxdebug = 0;
40 : #define DPRINTFN(n, x) do { if (uoakluxdebug > (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 UOAKLUX_SAMPLE_RATE 200 /* ms */
48 : #define UOAKLUX_REFRESH_PERIOD 5 /* 5 sec : 0.2Hz */
49 :
50 : struct uoaklux_sensor {
51 : struct uoak_sensor lux;
52 : /* lux sensor setting */
53 : uint8_t gain;
54 : int inttime;
55 :
56 : };
57 :
58 : struct uoaklux_softc {
59 : struct uhidev sc_hdev;
60 :
61 : /* uoak common */
62 : struct uoak_softc sc_uoak_softc;
63 :
64 : /* sensor framework */
65 : struct uoaklux_sensor sc_sensor;
66 : struct ksensordev sc_sensordev;
67 : struct sensor_task *sc_sensortask;
68 : };
69 :
70 : const struct usb_devno uoaklux_devs[] = {
71 : { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_LUX},
72 : };
73 : #define uoaklux_lookup(v, p) usb_lookup(uoaklux_devs, v, p)
74 :
75 : int uoaklux_match(struct device *, void *, void *);
76 : void uoaklux_attach(struct device *, struct device *, void *);
77 : int uoaklux_detach(struct device *, int);
78 :
79 : void uoaklux_intr(struct uhidev *, void *, u_int);
80 : void uoaklux_refresh(void *);
81 :
82 : int uoaklux_get_sensor_setting(struct uoaklux_softc *, enum uoak_target);
83 :
84 : void uoaklux_dev_setting(void *, enum uoak_target);
85 : void uoaklux_dev_print(void *, enum uoak_target);
86 :
87 :
88 : struct cfdriver uoaklux_cd = {
89 : NULL, "uoaklux", DV_DULL
90 : };
91 :
92 : const struct cfattach uoaklux_ca = {
93 : sizeof(struct uoaklux_softc),
94 : uoaklux_match,
95 : uoaklux_attach,
96 : uoaklux_detach,
97 : };
98 :
99 : struct uoak_methods uoaklux_methods = {
100 : uoaklux_dev_print,
101 : uoaklux_dev_setting
102 : };
103 :
104 :
105 : int
106 0 : uoaklux_match(struct device *parent, void *match, void *aux)
107 : {
108 0 : struct uhidev_attach_arg *uha = aux;
109 :
110 0 : if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
111 0 : return (UMATCH_NONE);
112 :
113 0 : if (uoaklux_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
114 0 : return UMATCH_NONE;
115 :
116 0 : return (UMATCH_VENDOR_PRODUCT);
117 0 : }
118 :
119 : void
120 0 : uoaklux_attach(struct device *parent, struct device *self, void *aux)
121 : {
122 0 : struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
123 0 : struct usb_attach_arg *uaa = aux;
124 0 : struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
125 0 : struct usbd_device *dev = uha->parent->sc_udev;
126 :
127 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
128 0 : int err, size, repid;
129 0 : void *desc;
130 :
131 0 : sc->sc_hdev.sc_intr = uoaklux_intr;
132 0 : sc->sc_hdev.sc_parent = uha->parent;
133 0 : sc->sc_hdev.sc_report_id = uha->reportid;
134 :
135 0 : scc->sc_parent = sc;
136 0 : scc->sc_udev = dev;
137 0 : scc->sc_hdev = &sc->sc_hdev;
138 0 : scc->sc_methods = &uoaklux_methods;
139 0 : scc->sc_sensordev = &sc->sc_sensordev;
140 :
141 0 : uhidev_get_report_desc(uha->parent, &desc, &size);
142 0 : repid = uha->reportid;
143 0 : scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
144 0 : scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
145 0 : scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
146 :
147 : /*device initialize */
148 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
149 0 : err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKLUX_SAMPLE_RATE);
150 0 : if (err) {
151 0 : printf("%s: could not set sampling rate. exit\n",
152 0 : sc->sc_hdev.sc_dev.dv_xname);
153 0 : return;
154 : }
155 :
156 : /* query and print device setting */
157 0 : uoak_get_devinfo(scc);
158 0 : uoak_print_devinfo(scc);
159 :
160 : DPRINTF((" config in RAM\n"));
161 0 : uoak_get_setting(scc, OAK_TARGET_RAM);
162 0 : uoak_print_setting(scc, OAK_TARGET_RAM);
163 : #ifdef UOAKLUX_DEBUG
164 : DPRINTF((" config in FLASh\n"));
165 : uoak_get_setting(scc, OAK_TARGET_FLASH);
166 : uoak_print_setting(scc, OAK_TARGET_FLASH);
167 : #endif
168 :
169 : /* attach sensor */
170 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
171 : sizeof(sc->sc_sensordev.xname));
172 0 : uoak_sensor_attach(scc, &sc->sc_sensor.lux, SENSOR_LUX);
173 :
174 : /* start sensor */
175 0 : sc->sc_sensortask = sensor_task_register(sc, uoaklux_refresh,
176 : UOAKLUX_REFRESH_PERIOD);
177 0 : if (sc->sc_sensortask == NULL) {
178 0 : printf(", unable to register update task\n");
179 0 : return;
180 : }
181 0 : sensordev_install(&sc->sc_sensordev);
182 :
183 0 : err = uhidev_open(&sc->sc_hdev);
184 0 : if (err) {
185 0 : printf("%s: could not open interrupt pipe, quit\n",
186 : sc->sc_hdev.sc_dev.dv_xname);
187 0 : return;
188 : }
189 0 : scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
190 :
191 : DPRINTF(("uoaklux_attach: complete\n"));
192 0 : }
193 :
194 :
195 : int
196 0 : uoaklux_detach(struct device *self, int flags)
197 : {
198 0 : struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
199 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
200 : int rv = 0;
201 :
202 0 : wakeup(&sc->sc_sensortask);
203 0 : sensordev_deinstall(&sc->sc_sensordev);
204 :
205 0 : uoak_sensor_detach(scc, &sc->sc_sensor.lux);
206 :
207 0 : if (sc->sc_sensortask != NULL)
208 0 : sensor_task_unregister(sc->sc_sensortask);
209 :
210 0 : if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
211 0 : uhidev_close(&sc->sc_hdev);
212 :
213 0 : if (scc->sc_ibuf != NULL) {
214 0 : free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
215 0 : scc->sc_ibuf = NULL;
216 0 : }
217 :
218 0 : return (rv);
219 : }
220 :
221 : void
222 0 : uoaklux_intr(struct uhidev *addr, void *ibuf, u_int len)
223 : {
224 0 : struct uoaklux_softc *sc = (struct uoaklux_softc *)addr;
225 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
226 : int frame, val;
227 :
228 0 : if (scc->sc_ibuf == NULL)
229 0 : return;
230 :
231 0 : memcpy(scc->sc_ibuf, ibuf, len);
232 0 : frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
233 0 : val = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
234 0 : uoak_sensor_update(&sc->sc_sensor.lux, val);
235 0 : }
236 :
237 : void
238 0 : uoaklux_refresh(void *arg)
239 : {
240 0 : struct uoaklux_softc *sc = arg;
241 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
242 0 : uint8_t led;
243 :
244 : /* blink LED for each cycle */
245 0 : if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
246 : DPRINTF(("status query error\n"));
247 0 : if (led == OAK_LED_OFF)
248 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
249 : else
250 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
251 :
252 0 : uoak_sensor_refresh(&sc->sc_sensor.lux, 1000000, 0);
253 0 : }
254 :
255 : int
256 0 : uoaklux_get_sensor_setting(struct uoaklux_softc *sc, enum uoak_target target)
257 : {
258 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
259 : uint8_t result;
260 :
261 0 : memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
262 0 : scc->sc_rcmd.target = target;
263 0 : scc->sc_rcmd.datasize = 0x1;
264 0 : USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
265 :
266 0 : if (uoak_get_cmd(scc) < 0)
267 0 : return EIO;
268 :
269 0 : result = scc->sc_buf[1];
270 :
271 0 : sc->sc_sensor.gain = ((result & OAK_LUX_SENSOR_GAIN_MASK) >> 3);
272 0 : sc->sc_sensor.inttime = (result & OAK_LUX_SENSOR_INTTIME_MASK);
273 :
274 0 : return 0;
275 0 : }
276 :
277 : /* device specific functions */
278 : void
279 0 : uoaklux_dev_setting(void *parent, enum uoak_target target)
280 : {
281 0 : struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
282 :
283 : /* get device specific configuration */
284 0 : (void)uoaklux_get_sensor_setting(sc, target);
285 0 : }
286 :
287 : void
288 0 : uoaklux_dev_print(void *parent, enum uoak_target target)
289 : {
290 0 : struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
291 :
292 0 : printf(", %s gain", (sc->sc_sensor.gain ? "HIGH" : "LOW"));
293 0 : printf(", speed ");
294 0 : switch(sc->sc_sensor.inttime) {
295 : case OAK_LUX_SENSOR_INTTIME_13_7ms:
296 0 : printf("13.7ms");
297 0 : break;
298 : case OAK_LUX_SENSOR_INTTIME_101ms:
299 0 : printf("101ms");
300 0 : break;
301 : case OAK_LUX_SENSOR_INTTIME_402ms:
302 0 : printf("402ms");
303 0 : break;
304 : default:
305 0 : printf("unknown");
306 0 : break;
307 : }
308 0 : }
|