xref: /openbsd-src/sys/dev/usb/uoakv.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: uoakv.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: 8channel +/-10V ADC 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 UOAKV_DEBUG
40 int	uoakvdebug = 0;
41 #define DPRINTFN(n, x)	do { if (uoakvdebug > (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 UOAKV_SAMPLE_RATE	100	/* ms */
49 #define UOAKV_REFRESH_PERIOD	1	/* 1 sec : 1Hz */
50 
51 struct uoakv_sensor {
52 	struct uoak_sensor v;
53 	/* ADC setting */
54 	unsigned int offset[OAK_V_TARGET_MAX];	/* absolute offset (mV) */
55 };
56 
57 struct uoakv_softc {
58 	struct uhidev		 sc_hdev;
59 
60 	/* uoak common */
61 	struct uoak_softc	 sc_uoak_softc;
62 
63 	/* sensor framework */
64 	struct uoakv_sensor	 sc_sensor[OAK_V_MAXSENSORS];
65 	struct ksensordev	 sc_sensordev;
66 	struct sensor_task	*sc_sensortask;
67 
68 	/* sensor setting */
69 	int			 sc_inputmode[OAK_V_TARGET_MAX];
70 
71 };
72 
73 const struct usb_devno uoakv_devs[] = {
74 	{ USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_10V},
75 };
76 #define uoakv_lookup(v, p) usb_lookup(uoakv_devs, v, p)
77 
78 int  uoakv_match(struct device *, void *, void *);
79 void uoakv_attach(struct device *, struct device *, void *);
80 int  uoakv_detach(struct device *, int);
81 
82 void uoakv_intr(struct uhidev *, void *, u_int);
83 void uoakv_refresh(void *);
84 
85 int uoakv_get_channel_setting(struct uoakv_softc *, enum uoak_target, int);
86 int uoakv_get_sensor_setting(struct uoakv_softc *, enum uoak_target);
87 
88 void uoakv_dev_setting(void *, enum uoak_target);
89 void uoakv_dev_print(void *, enum uoak_target);
90 
91 
92 struct cfdriver uoakv_cd = {
93 	NULL, "uoakv", DV_DULL
94 };
95 
96 const struct cfattach uoakv_ca = {
97 	sizeof(struct uoakv_softc),
98 	uoakv_match,
99 	uoakv_attach,
100 	uoakv_detach,
101 
102 };
103 
104 struct uoak_methods uoakv_methods = {
105 	uoakv_dev_print,
106 	uoakv_dev_setting
107 };
108 
109 int
110 uoakv_match(struct device *parent, void *match, void *aux)
111 {
112 	struct uhidev_attach_arg *uha = aux;
113 
114 	if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
115 		return (UMATCH_NONE);
116 
117 	if (uoakv_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
118 		return UMATCH_NONE;
119 
120 	return (UMATCH_VENDOR_PRODUCT);
121 }
122 
123 void
124 uoakv_attach(struct device *parent, struct device *self, void *aux)
125 {
126 	struct uoakv_softc *sc = (struct uoakv_softc *)self;
127 	struct usb_attach_arg *uaa = aux;
128 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
129 	struct usbd_device *dev = uha->parent->sc_udev;
130 
131 	struct uoak_softc *scc = &sc->sc_uoak_softc;
132 	int i, err, size, repid;
133 	void *desc;
134 
135 	sc->sc_hdev.sc_intr = uoakv_intr;
136 	sc->sc_hdev.sc_parent = uha->parent;
137 	sc->sc_hdev.sc_report_id = uha->reportid;
138 
139 	scc->sc_parent = sc;
140 	scc->sc_udev = dev;
141 	scc->sc_hdev = &sc->sc_hdev;
142 	scc->sc_methods = &uoakv_methods;
143 	scc->sc_sensordev = &sc->sc_sensordev;
144 
145 	uhidev_get_report_desc(uha->parent, &desc, &size);
146 	repid = uha->reportid;
147 	scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
148 	scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
149 	scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
150 
151 	/* device initialize */
152 	(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
153 	err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKV_SAMPLE_RATE);
154 	if (err) {
155 		printf("%s: could not set sampling rate. exit\n",
156 		    sc->sc_hdev.sc_dev.dv_xname);
157 		return;
158 	}
159 
160 	/* query and print device setting */
161 	uoak_get_devinfo(scc);
162 	uoak_print_devinfo(scc);
163 
164 	DPRINTF((" config in RAM\n"));
165 	uoak_get_setting(scc, OAK_TARGET_RAM);
166 	uoak_print_setting(scc, OAK_TARGET_RAM);
167 #ifdef UOAKV_DEBUG
168 	DPRINTF((" config in FRASH\n"));
169 	uoak_get_setting(scc, OAK_TARGET_FLASH);
170 	uoak_print_setting(scc, OAK_TARGET_FLASH);
171 #endif
172 
173 	/* attach sensor */
174 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
175 	    sizeof(sc->sc_sensordev.xname));
176 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
177 		uoak_sensor_attach(scc, &sc->sc_sensor[i].v, SENSOR_VOLTS_DC);
178 
179 	/* start sensor */
180 	sc->sc_sensortask = sensor_task_register(sc, uoakv_refresh,
181 	    UOAKV_REFRESH_PERIOD);
182 	if (sc->sc_sensortask == NULL) {
183 		printf(", unable to register update task\n");
184 		return;
185 	}
186 	sensordev_install(&sc->sc_sensordev);
187 
188 	err = uhidev_open(&sc->sc_hdev);
189 	if (err) {
190 		printf("%s: could not open interrupt pipe, quit\n",
191 		    sc->sc_hdev.sc_dev.dv_xname);
192 		return;
193 	}
194 	scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
195 
196 	DPRINTF(("uoakv_attach: complete\n"));
197 }
198 
199 int
200 uoakv_detach(struct device *self, int flags)
201 {
202 	struct uoakv_softc *sc = (struct uoakv_softc *)self;
203 	struct uoak_softc *scc = &sc->sc_uoak_softc;
204 	int i, rv = 0;
205 
206 	wakeup(&sc->sc_sensortask);
207 	sensordev_deinstall(&sc->sc_sensordev);
208 
209 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
210 		uoak_sensor_detach(scc, &sc->sc_sensor[i].v);
211 
212 	if (sc->sc_sensortask != NULL)
213 		sensor_task_unregister(sc->sc_sensortask);
214 
215 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
216 		uhidev_close(&sc->sc_hdev);
217 
218 	if (scc->sc_ibuf != NULL) {
219 		free(scc->sc_ibuf, M_USBDEV, 0);
220 		scc->sc_ibuf = NULL;
221 	}
222 
223 	return (rv);
224 }
225 
226 void
227 uoakv_intr(struct uhidev *addr, void *ibuf, u_int len)
228 {
229 	struct uoakv_softc *sc = (struct uoakv_softc *)addr;
230 	struct uoak_softc *scc = &sc->sc_uoak_softc;
231 	int i, idx, frame;
232 	int16_t val;
233 
234 	if (scc->sc_ibuf == NULL)
235 		return;
236 
237 	memcpy(scc->sc_ibuf, ibuf, len);
238 	frame = (scc->sc_ibuf[1] << 8) + scc->sc_ibuf[0];
239 
240 	for (i = 0; i < OAK_V_MAXSENSORS; i++) {
241 		idx = (i + 1) * 2;
242 		val = (int16_t)((scc->sc_ibuf[idx+1] << 8) | scc->sc_ibuf[idx]);
243 		uoak_sensor_update(&sc->sc_sensor[i].v, val);
244 	}
245 }
246 
247 void
248 uoakv_refresh(void *arg)
249 {
250 	struct uoakv_softc *sc = arg;
251 	struct uoak_softc *scc = &sc->sc_uoak_softc;
252 	uint8_t led;
253 	int i;
254 
255 	/* blink LED for each cycle */
256 	if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
257 		DPRINTF(("status query error\n"));
258 	if (led == OAK_LED_OFF)
259 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
260 	else
261 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
262 
263 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
264 		uoak_sensor_refresh(&sc->sc_sensor[i].v, 1000, 0);
265 }
266 
267 int
268 uoakv_get_channel_setting(struct uoakv_softc *sc, enum uoak_target target,
269   int ch)
270 {
271 	struct uoak_softc *scc = &sc->sc_uoak_softc;
272 	uint16_t cmd, result;
273 
274 	memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
275 	scc->sc_rcmd.target = target;
276 	scc->sc_rcmd.datasize = 0x2;
277 
278 #define OAK_V_CHANNEL_IDX_OFFSET 3
279 	cmd = (ch + OAK_V_CHANNEL_IDX_OFFSET);
280 	USETW(&scc->sc_rcmd.cmd, cmd);
281 
282 	if (uoak_get_cmd(scc) < 0)
283 		return EIO;
284 
285 	result = (scc->sc_buf[2] << 8) + scc->sc_buf[1];
286 	sc->sc_sensor[ch].offset[target] = result;
287 
288 	return 0;
289 }
290 
291 int
292 uoakv_get_sensor_setting(struct uoakv_softc *sc, enum uoak_target target)
293 {
294 	struct uoak_softc *scc = &sc->sc_uoak_softc;
295 	uint8_t result;
296 
297 	memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
298 	scc->sc_rcmd.target = target;
299 	scc->sc_rcmd.datasize = 0x1;
300 	USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
301 
302 	if (uoak_get_cmd(scc) < 0)
303 		return EIO;
304 
305 	result =  scc->sc_buf[1];
306 	sc->sc_inputmode[target] = (result & OAK_V_SENSOR_INPUTMODEMASK);
307 
308 	return 0;
309 }
310 
311 /* device specific functions */
312 void
313 uoakv_dev_setting(void *parent, enum uoak_target target)
314 {
315 	struct uoakv_softc *sc = (struct uoakv_softc *)parent;
316 	int i;
317 
318 	/* get device specific configuration */
319 	(void)uoakv_get_sensor_setting(sc, target);
320 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
321 		(void)uoakv_get_channel_setting(sc, target, i);
322 }
323 
324 void
325 uoakv_dev_print(void *parent, enum uoak_target target)
326 {
327 	struct uoakv_softc *sc = (struct uoakv_softc *)parent;
328 	int i;
329 
330 	printf(", %s",
331 	    (sc->sc_inputmode[target] ? "Psuedo-Diffential" : "Single-Ended"));
332 
333 	printf(", ADC channel offsets:\n");
334 	printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
335 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
336 		printf("ch%02d %2d.%02d, ", i,
337 		    sc->sc_sensor[i].offset[target] / 100,
338 		    sc->sc_sensor[i].offset[target] % 100);
339 }
340