xref: /openbsd/sys/dev/usb/uoaklux.c (revision 9e6efb0a)
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
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
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
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
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
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
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
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
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