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