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