xref: /openbsd/sys/dev/usb/uoaklux.c (revision 09467b48)
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 uoaklux_match(struct device *parent, void *match, void *aux)
107 {
108 	struct uhidev_attach_arg *uha = aux;
109 
110 	if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
111 		return (UMATCH_NONE);
112 
113 	if (uoaklux_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
114 		return UMATCH_NONE;
115 
116 	return (UMATCH_VENDOR_PRODUCT);
117 }
118 
119 void
120 uoaklux_attach(struct device *parent, struct device *self, void *aux)
121 {
122 	struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
123 	struct usb_attach_arg *uaa = aux;
124 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
125 	struct usbd_device *dev = uha->parent->sc_udev;
126 
127 	struct uoak_softc *scc = &sc->sc_uoak_softc;
128 	int err, size, repid;
129 	void *desc;
130 
131 	sc->sc_hdev.sc_intr = uoaklux_intr;
132 	sc->sc_hdev.sc_parent = uha->parent;
133 	sc->sc_hdev.sc_report_id = uha->reportid;
134 
135 	scc->sc_parent = sc;
136 	scc->sc_udev = dev;
137 	scc->sc_hdev = &sc->sc_hdev;
138 	scc->sc_methods = &uoaklux_methods;
139 	scc->sc_sensordev = &sc->sc_sensordev;
140 
141 	uhidev_get_report_desc(uha->parent, &desc, &size);
142 	repid = uha->reportid;
143 	scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
144 	scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
145 	scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
146 
147 	/*device initialize */
148 	(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
149 	err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKLUX_SAMPLE_RATE);
150 	if (err) {
151 		printf("%s: could not set sampling rate. exit\n",
152 		    sc->sc_hdev.sc_dev.dv_xname);
153 		return;
154 	}
155 
156 	/* query and print device setting */
157 	uoak_get_devinfo(scc);
158 	uoak_print_devinfo(scc);
159 
160 	DPRINTF((" config in RAM\n"));
161 	uoak_get_setting(scc, OAK_TARGET_RAM);
162 	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 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
171 	    sizeof(sc->sc_sensordev.xname));
172 	uoak_sensor_attach(scc, &sc->sc_sensor.lux, SENSOR_LUX);
173 
174 	/* start sensor */
175 	sc->sc_sensortask = sensor_task_register(sc, uoaklux_refresh,
176 	    UOAKLUX_REFRESH_PERIOD);
177 	if (sc->sc_sensortask == NULL) {
178 		printf(", unable to register update task\n");
179 		return;
180 	}
181 	sensordev_install(&sc->sc_sensordev);
182 
183 	err = uhidev_open(&sc->sc_hdev);
184 	if (err) {
185 		printf("%s: could not open interrupt pipe, quit\n",
186 		    sc->sc_hdev.sc_dev.dv_xname);
187 		return;
188 	}
189 	scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
190 
191 	DPRINTF(("uoaklux_attach: complete\n"));
192 }
193 
194 
195 int
196 uoaklux_detach(struct device *self, int flags)
197 {
198 	struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
199 	struct uoak_softc *scc = &sc->sc_uoak_softc;
200 	int rv = 0;
201 
202 	wakeup(&sc->sc_sensortask);
203 	sensordev_deinstall(&sc->sc_sensordev);
204 
205 	uoak_sensor_detach(scc, &sc->sc_sensor.lux);
206 
207 	if (sc->sc_sensortask != NULL)
208 		sensor_task_unregister(sc->sc_sensortask);
209 
210 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
211 		uhidev_close(&sc->sc_hdev);
212 
213 	if (scc->sc_ibuf != NULL) {
214 		free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
215 		scc->sc_ibuf = NULL;
216 	}
217 
218 	return (rv);
219 }
220 
221 void
222 uoaklux_intr(struct uhidev *addr, void *ibuf, u_int len)
223 {
224 	struct uoaklux_softc *sc = (struct uoaklux_softc *)addr;
225 	struct uoak_softc *scc = &sc->sc_uoak_softc;
226 	int frame, val;
227 
228 	if (scc->sc_ibuf == NULL)
229 		return;
230 
231 	memcpy(scc->sc_ibuf, ibuf, len);
232 	frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
233 	val = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
234 	uoak_sensor_update(&sc->sc_sensor.lux, val);
235 }
236 
237 void
238 uoaklux_refresh(void *arg)
239 {
240 	struct uoaklux_softc *sc = arg;
241 	struct uoak_softc *scc = &sc->sc_uoak_softc;
242 	uint8_t led;
243 
244 	/* blink LED for each cycle */
245 	if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
246 		DPRINTF(("status query error\n"));
247 	if (led == OAK_LED_OFF)
248 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
249 	else
250 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
251 
252 	uoak_sensor_refresh(&sc->sc_sensor.lux, 1000000, 0);
253 }
254 
255 int
256 uoaklux_get_sensor_setting(struct uoaklux_softc *sc, enum uoak_target target)
257 {
258 	struct uoak_softc *scc = &sc->sc_uoak_softc;
259 	uint8_t result;
260 
261 	memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
262 	scc->sc_rcmd.target = target;
263 	scc->sc_rcmd.datasize = 0x1;
264 	USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
265 
266 	if (uoak_get_cmd(scc) < 0)
267 		return EIO;
268 
269 	result =  scc->sc_buf[1];
270 
271 	sc->sc_sensor.gain = ((result & OAK_LUX_SENSOR_GAIN_MASK) >> 3);
272 	sc->sc_sensor.inttime = (result & OAK_LUX_SENSOR_INTTIME_MASK);
273 
274 	return 0;
275 }
276 
277 /* device specific functions */
278 void
279 uoaklux_dev_setting(void *parent, enum uoak_target target)
280 {
281 	struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
282 
283 	/* get device specific configuration */
284 	(void)uoaklux_get_sensor_setting(sc, target);
285 }
286 
287 void
288 uoaklux_dev_print(void *parent, enum uoak_target target)
289 {
290 	struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
291 
292 	printf(", %s gain", (sc->sc_sensor.gain ? "HIGH" : "LOW"));
293 	printf(", speed ");
294 	switch(sc->sc_sensor.inttime) {
295 	case OAK_LUX_SENSOR_INTTIME_13_7ms:
296 		printf("13.7ms");
297 		break;
298 	case OAK_LUX_SENSOR_INTTIME_101ms:
299 		printf("101ms");
300 		break;
301 	case OAK_LUX_SENSOR_INTTIME_402ms:
302 		printf("402ms");
303 		break;
304 	default:
305 		printf("unknown");
306 		break;
307 	}
308 }
309