xref: /openbsd-src/sys/dev/usb/uoakv.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: uoakv.c,v 1.18 2024/05/23 03:21:09 jsg Exp $   */
2ae06e6b2Syuo 
3ae06e6b2Syuo /*
4ae06e6b2Syuo  * Copyright (c) 2012 Yojiro UO <yuo@nui.org>
5ae06e6b2Syuo  *
6ae06e6b2Syuo  * Permission to use, copy, modify, and distribute this software for any
7ae06e6b2Syuo  * purpose with or without fee is hereby granted, provided that the above
8ae06e6b2Syuo  * copyright notice and this permission notice appear in all copies.
9ae06e6b2Syuo  *
10ae06e6b2Syuo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11ae06e6b2Syuo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ae06e6b2Syuo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ae06e6b2Syuo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ae06e6b2Syuo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15ae06e6b2Syuo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16ae06e6b2Syuo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ae06e6b2Syuo  */
18ae06e6b2Syuo 
194b1a56afSjsg /* TORADEX OAK series sensors: 8channel +/-10V ADC driver */
20ae06e6b2Syuo /* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
21ae06e6b2Syuo 
22ae06e6b2Syuo #include <sys/param.h>
23ae06e6b2Syuo #include <sys/systm.h>
24ae06e6b2Syuo #include <sys/malloc.h>
25ae06e6b2Syuo #include <sys/device.h>
26ae06e6b2Syuo #include <sys/sensors.h>
27ae06e6b2Syuo 
28ae06e6b2Syuo #include <dev/usb/usb.h>
29ae06e6b2Syuo #include <dev/usb/usbhid.h>
30ae06e6b2Syuo #include <dev/usb/usbdi.h>
31ae06e6b2Syuo #include <dev/usb/usbdevs.h>
32ae06e6b2Syuo #include <dev/usb/uhidev.h>
338a83145eSjcs 
34ae06e6b2Syuo #include "uoak.h"
35ae06e6b2Syuo 
36ae06e6b2Syuo #ifdef UOAKV_DEBUG
37ae06e6b2Syuo int	uoakvdebug = 0;
38ae06e6b2Syuo #define DPRINTFN(n, x)	do { if (uoakvdebug > (n)) printf x; } while (0)
39ae06e6b2Syuo #else
40ae06e6b2Syuo #define DPRINTFN(n, x)
41ae06e6b2Syuo #endif
42ae06e6b2Syuo 
43ae06e6b2Syuo #define DPRINTF(x) DPRINTFN(0, x)
44ae06e6b2Syuo 
45ae06e6b2Syuo #define UOAKV_SAMPLE_RATE	100	/* ms */
46ae06e6b2Syuo #define UOAKV_REFRESH_PERIOD	1	/* 1 sec : 1Hz */
47ae06e6b2Syuo 
48ae06e6b2Syuo struct uoakv_sensor {
49ae06e6b2Syuo 	struct uoak_sensor v;
50ae06e6b2Syuo 	/* ADC setting */
51ae06e6b2Syuo 	unsigned int offset[OAK_V_TARGET_MAX];	/* absolute offset (mV) */
52ae06e6b2Syuo };
53ae06e6b2Syuo 
54ae06e6b2Syuo struct uoakv_softc {
55ae06e6b2Syuo 	struct uhidev		 sc_hdev;
56ae06e6b2Syuo 
57ae06e6b2Syuo 	/* uoak common */
58ae06e6b2Syuo 	struct uoak_softc	 sc_uoak_softc;
59ae06e6b2Syuo 
60ae06e6b2Syuo 	/* sensor framework */
61ae06e6b2Syuo 	struct uoakv_sensor	 sc_sensor[OAK_V_MAXSENSORS];
62ae06e6b2Syuo 	struct ksensordev	 sc_sensordev;
63ae06e6b2Syuo 	struct sensor_task	*sc_sensortask;
64ae06e6b2Syuo 
65ae06e6b2Syuo 	/* sensor setting */
66ae06e6b2Syuo 	int			 sc_inputmode[OAK_V_TARGET_MAX];
67ae06e6b2Syuo 
68ae06e6b2Syuo };
69ae06e6b2Syuo 
70ae06e6b2Syuo const struct usb_devno uoakv_devs[] = {
71ae06e6b2Syuo 	{ USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_10V},
72ae06e6b2Syuo };
73ae06e6b2Syuo #define uoakv_lookup(v, p) usb_lookup(uoakv_devs, v, p)
74ae06e6b2Syuo 
75ae06e6b2Syuo int  uoakv_match(struct device *, void *, void *);
76ae06e6b2Syuo void uoakv_attach(struct device *, struct device *, void *);
77ae06e6b2Syuo int  uoakv_detach(struct device *, int);
78ae06e6b2Syuo 
79ae06e6b2Syuo void uoakv_intr(struct uhidev *, void *, u_int);
80ae06e6b2Syuo void uoakv_refresh(void *);
81ae06e6b2Syuo 
82ae06e6b2Syuo int uoakv_get_channel_setting(struct uoakv_softc *, enum uoak_target, int);
83ae06e6b2Syuo int uoakv_get_sensor_setting(struct uoakv_softc *, enum uoak_target);
84ae06e6b2Syuo 
85ae06e6b2Syuo void uoakv_dev_setting(void *, enum uoak_target);
86ae06e6b2Syuo void uoakv_dev_print(void *, enum uoak_target);
87ae06e6b2Syuo 
88ae06e6b2Syuo 
89ae06e6b2Syuo struct cfdriver uoakv_cd = {
90ae06e6b2Syuo 	NULL, "uoakv", DV_DULL
91ae06e6b2Syuo };
92ae06e6b2Syuo 
93ae06e6b2Syuo const struct cfattach uoakv_ca = {
94ae06e6b2Syuo 	sizeof(struct uoakv_softc),
95ae06e6b2Syuo 	uoakv_match,
96ae06e6b2Syuo 	uoakv_attach,
97ae06e6b2Syuo 	uoakv_detach,
98ae06e6b2Syuo 
99ae06e6b2Syuo };
100ae06e6b2Syuo 
1018197d3ceSnaddy const struct uoak_methods uoakv_methods = {
102ae06e6b2Syuo 	uoakv_dev_print,
103ae06e6b2Syuo 	uoakv_dev_setting
104ae06e6b2Syuo };
105ae06e6b2Syuo 
106ae06e6b2Syuo int
uoakv_match(struct device * parent,void * match,void * aux)107ae06e6b2Syuo uoakv_match(struct device *parent, void *match, void *aux)
108ae06e6b2Syuo {
109f964a65cSmpi 	struct uhidev_attach_arg *uha = aux;
110f964a65cSmpi 
111faac88c0Santon 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
112f964a65cSmpi 		return (UMATCH_NONE);
113ae06e6b2Syuo 
114ae06e6b2Syuo 	if (uoakv_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
115ae06e6b2Syuo 		return UMATCH_NONE;
116ae06e6b2Syuo 
117ae06e6b2Syuo 	return (UMATCH_VENDOR_PRODUCT);
118ae06e6b2Syuo }
119ae06e6b2Syuo 
120ae06e6b2Syuo void
uoakv_attach(struct device * parent,struct device * self,void * aux)121ae06e6b2Syuo uoakv_attach(struct device *parent, struct device *self, void *aux)
122ae06e6b2Syuo {
123ae06e6b2Syuo 	struct uoakv_softc *sc = (struct uoakv_softc *)self;
124ae06e6b2Syuo 	struct usb_attach_arg *uaa = aux;
125ae06e6b2Syuo 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
126ab0b1be7Smglocker 	struct usbd_device *dev = uha->parent->sc_udev;
127ae06e6b2Syuo 
128ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
129ae06e6b2Syuo 	int i, err, size, repid;
130ae06e6b2Syuo 	void *desc;
131ae06e6b2Syuo 
132ae06e6b2Syuo 	sc->sc_hdev.sc_intr = uoakv_intr;
133ae06e6b2Syuo 	sc->sc_hdev.sc_parent = uha->parent;
134ae06e6b2Syuo 	sc->sc_hdev.sc_report_id = uha->reportid;
135ae06e6b2Syuo 
136ae06e6b2Syuo 	scc->sc_parent = sc;
137ae06e6b2Syuo 	scc->sc_udev = dev;
138ae06e6b2Syuo 	scc->sc_hdev = &sc->sc_hdev;
139ae06e6b2Syuo 	scc->sc_methods = &uoakv_methods;
140ae06e6b2Syuo 	scc->sc_sensordev = &sc->sc_sensordev;
141ae06e6b2Syuo 
142ae06e6b2Syuo 	uhidev_get_report_desc(uha->parent, &desc, &size);
143ae06e6b2Syuo 	repid = uha->reportid;
144ae06e6b2Syuo 	scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
145ae06e6b2Syuo 	scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
146ae06e6b2Syuo 	scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
147ae06e6b2Syuo 
148ae06e6b2Syuo 	/* device initialize */
149ae06e6b2Syuo 	(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
150ae06e6b2Syuo 	err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKV_SAMPLE_RATE);
151ae06e6b2Syuo 	if (err) {
152ae06e6b2Syuo 		printf("%s: could not set sampling rate. exit\n",
153ae06e6b2Syuo 		    sc->sc_hdev.sc_dev.dv_xname);
154ae06e6b2Syuo 		return;
155ae06e6b2Syuo 	}
156ae06e6b2Syuo 
157ae06e6b2Syuo 	/* query and print device setting */
158ae06e6b2Syuo 	uoak_get_devinfo(scc);
159ae06e6b2Syuo 	uoak_print_devinfo(scc);
160ae06e6b2Syuo 
161ae06e6b2Syuo 	DPRINTF((" config in RAM\n"));
162ae06e6b2Syuo 	uoak_get_setting(scc, OAK_TARGET_RAM);
163ae06e6b2Syuo 	uoak_print_setting(scc, OAK_TARGET_RAM);
164ae06e6b2Syuo #ifdef UOAKV_DEBUG
165ae06e6b2Syuo 	DPRINTF((" config in FRASH\n"));
166ae06e6b2Syuo 	uoak_get_setting(scc, OAK_TARGET_FLASH);
167ae06e6b2Syuo 	uoak_print_setting(scc, OAK_TARGET_FLASH);
168ae06e6b2Syuo #endif
169ae06e6b2Syuo 
170ae06e6b2Syuo 	/* attach sensor */
171ae06e6b2Syuo 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
172ae06e6b2Syuo 	    sizeof(sc->sc_sensordev.xname));
173ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
174ae06e6b2Syuo 		uoak_sensor_attach(scc, &sc->sc_sensor[i].v, SENSOR_VOLTS_DC);
175ae06e6b2Syuo 
176ae06e6b2Syuo 	/* start sensor */
177ae06e6b2Syuo 	sc->sc_sensortask = sensor_task_register(sc, uoakv_refresh,
178ae06e6b2Syuo 	    UOAKV_REFRESH_PERIOD);
179ae06e6b2Syuo 	if (sc->sc_sensortask == NULL) {
180ae06e6b2Syuo 		printf(", unable to register update task\n");
181ae06e6b2Syuo 		return;
182ae06e6b2Syuo 	}
183ae06e6b2Syuo 	sensordev_install(&sc->sc_sensordev);
184ae06e6b2Syuo 
185ae06e6b2Syuo 	err = uhidev_open(&sc->sc_hdev);
186ae06e6b2Syuo 	if (err) {
187ae06e6b2Syuo 		printf("%s: could not open interrupt pipe, quit\n",
188ae06e6b2Syuo 		    sc->sc_hdev.sc_dev.dv_xname);
189ae06e6b2Syuo 		return;
190ae06e6b2Syuo 	}
191ae06e6b2Syuo 	scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
192ae06e6b2Syuo 
193ae06e6b2Syuo 	DPRINTF(("uoakv_attach: complete\n"));
194ae06e6b2Syuo }
195ae06e6b2Syuo 
196ae06e6b2Syuo int
uoakv_detach(struct device * self,int flags)197ae06e6b2Syuo uoakv_detach(struct device *self, int flags)
198ae06e6b2Syuo {
199ae06e6b2Syuo 	struct uoakv_softc *sc = (struct uoakv_softc *)self;
200ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
201ae06e6b2Syuo 	int i, rv = 0;
202ae06e6b2Syuo 
203ae06e6b2Syuo 	wakeup(&sc->sc_sensortask);
204ae06e6b2Syuo 	sensordev_deinstall(&sc->sc_sensordev);
205ae06e6b2Syuo 
206ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
207ae06e6b2Syuo 		uoak_sensor_detach(scc, &sc->sc_sensor[i].v);
208ae06e6b2Syuo 
209ae06e6b2Syuo 	if (sc->sc_sensortask != NULL)
210ae06e6b2Syuo 		sensor_task_unregister(sc->sc_sensortask);
211ae06e6b2Syuo 
2129dcdfc61Smpi 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
2139dcdfc61Smpi 		uhidev_close(&sc->sc_hdev);
2149dcdfc61Smpi 
215ae06e6b2Syuo 	if (scc->sc_ibuf != NULL) {
216234dfda1Sderaadt 		free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
217ae06e6b2Syuo 		scc->sc_ibuf = NULL;
218ae06e6b2Syuo 	}
219ae06e6b2Syuo 
220ae06e6b2Syuo 	return (rv);
221ae06e6b2Syuo }
222ae06e6b2Syuo 
223ae06e6b2Syuo void
uoakv_intr(struct uhidev * addr,void * ibuf,u_int len)224ae06e6b2Syuo uoakv_intr(struct uhidev *addr, void *ibuf, u_int len)
225ae06e6b2Syuo {
226ae06e6b2Syuo 	struct uoakv_softc *sc = (struct uoakv_softc *)addr;
227ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
228ae06e6b2Syuo 	int i, idx, frame;
229ae06e6b2Syuo 	int16_t val;
230ae06e6b2Syuo 
231ae06e6b2Syuo 	if (scc->sc_ibuf == NULL)
232ae06e6b2Syuo 		return;
233ae06e6b2Syuo 
234ae06e6b2Syuo 	memcpy(scc->sc_ibuf, ibuf, len);
235ae06e6b2Syuo 	frame = (scc->sc_ibuf[1] << 8) + scc->sc_ibuf[0];
236ae06e6b2Syuo 
237ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++) {
238ae06e6b2Syuo 		idx = (i + 1) * 2;
239ae06e6b2Syuo 		val = (int16_t)((scc->sc_ibuf[idx+1] << 8) | scc->sc_ibuf[idx]);
240ae06e6b2Syuo 		uoak_sensor_update(&sc->sc_sensor[i].v, val);
241ae06e6b2Syuo 	}
242ae06e6b2Syuo }
243ae06e6b2Syuo 
244ae06e6b2Syuo void
uoakv_refresh(void * arg)245ae06e6b2Syuo uoakv_refresh(void *arg)
246ae06e6b2Syuo {
247ae06e6b2Syuo 	struct uoakv_softc *sc = arg;
248ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
249ae06e6b2Syuo 	uint8_t led;
250ae06e6b2Syuo 	int i;
251ae06e6b2Syuo 
252ae06e6b2Syuo 	/* blink LED for each cycle */
253ae06e6b2Syuo 	if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
254ae06e6b2Syuo 		DPRINTF(("status query error\n"));
255ae06e6b2Syuo 	if (led == OAK_LED_OFF)
256ae06e6b2Syuo 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
257ae06e6b2Syuo 	else
258ae06e6b2Syuo 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
259ae06e6b2Syuo 
260ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
261ae06e6b2Syuo 		uoak_sensor_refresh(&sc->sc_sensor[i].v, 1000, 0);
262ae06e6b2Syuo }
263ae06e6b2Syuo 
264ae06e6b2Syuo int
uoakv_get_channel_setting(struct uoakv_softc * sc,enum uoak_target target,int ch)265ae06e6b2Syuo uoakv_get_channel_setting(struct uoakv_softc *sc, enum uoak_target target,
266ae06e6b2Syuo   int ch)
267ae06e6b2Syuo {
268ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
269ae06e6b2Syuo 	uint16_t cmd, result;
270ae06e6b2Syuo 
271ae06e6b2Syuo 	memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
272ae06e6b2Syuo 	scc->sc_rcmd.target = target;
273ae06e6b2Syuo 	scc->sc_rcmd.datasize = 0x2;
274ae06e6b2Syuo 
275ae06e6b2Syuo #define OAK_V_CHANNEL_IDX_OFFSET 3
276ae06e6b2Syuo 	cmd = (ch + OAK_V_CHANNEL_IDX_OFFSET);
277ae06e6b2Syuo 	USETW(&scc->sc_rcmd.cmd, cmd);
278ae06e6b2Syuo 
279ae06e6b2Syuo 	if (uoak_get_cmd(scc) < 0)
280ae06e6b2Syuo 		return EIO;
281ae06e6b2Syuo 
282ae06e6b2Syuo 	result = (scc->sc_buf[2] << 8) + scc->sc_buf[1];
283ae06e6b2Syuo 	sc->sc_sensor[ch].offset[target] = result;
284ae06e6b2Syuo 
285ae06e6b2Syuo 	return 0;
286ae06e6b2Syuo }
287ae06e6b2Syuo 
288ae06e6b2Syuo int
uoakv_get_sensor_setting(struct uoakv_softc * sc,enum uoak_target target)289ae06e6b2Syuo uoakv_get_sensor_setting(struct uoakv_softc *sc, enum uoak_target target)
290ae06e6b2Syuo {
291ae06e6b2Syuo 	struct uoak_softc *scc = &sc->sc_uoak_softc;
292ae06e6b2Syuo 	uint8_t result;
293ae06e6b2Syuo 
294ae06e6b2Syuo 	memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
295ae06e6b2Syuo 	scc->sc_rcmd.target = target;
296ae06e6b2Syuo 	scc->sc_rcmd.datasize = 0x1;
297ae06e6b2Syuo 	USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
298ae06e6b2Syuo 
299ae06e6b2Syuo 	if (uoak_get_cmd(scc) < 0)
300ae06e6b2Syuo 		return EIO;
301ae06e6b2Syuo 
302ae06e6b2Syuo 	result =  scc->sc_buf[1];
303ae06e6b2Syuo 	sc->sc_inputmode[target] = (result & OAK_V_SENSOR_INPUTMODEMASK);
304ae06e6b2Syuo 
305ae06e6b2Syuo 	return 0;
306ae06e6b2Syuo }
307ae06e6b2Syuo 
308ae06e6b2Syuo /* device specific functions */
309ae06e6b2Syuo void
uoakv_dev_setting(void * parent,enum uoak_target target)310ae06e6b2Syuo uoakv_dev_setting(void *parent, enum uoak_target target)
311ae06e6b2Syuo {
312ae06e6b2Syuo 	struct uoakv_softc *sc = (struct uoakv_softc *)parent;
313ae06e6b2Syuo 	int i;
314ae06e6b2Syuo 
315ae06e6b2Syuo 	/* get device specific configuration */
316ae06e6b2Syuo 	(void)uoakv_get_sensor_setting(sc, target);
317ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
318ae06e6b2Syuo 		(void)uoakv_get_channel_setting(sc, target, i);
319ae06e6b2Syuo }
320ae06e6b2Syuo 
321ae06e6b2Syuo void
uoakv_dev_print(void * parent,enum uoak_target target)322ae06e6b2Syuo uoakv_dev_print(void *parent, enum uoak_target target)
323ae06e6b2Syuo {
324ae06e6b2Syuo 	struct uoakv_softc *sc = (struct uoakv_softc *)parent;
325ae06e6b2Syuo 	int i;
326ae06e6b2Syuo 
3274b1a56afSjsg 	printf(", %s", (sc->sc_inputmode[target] ?
3284b1a56afSjsg 	    "Pseudo-Differential" : "Single-Ended"));
329ae06e6b2Syuo 
330448c0c4aSderaadt 	printf(", ADC channel offsets:\n");
331448c0c4aSderaadt 	printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
332ae06e6b2Syuo 	for (i = 0; i < OAK_V_MAXSENSORS; i++)
333448c0c4aSderaadt 		printf("ch%02d %2d.%02d, ", i,
334ae06e6b2Syuo 		    sc->sc_sensor[i].offset[target] / 100,
335ae06e6b2Syuo 		    sc->sc_sensor[i].offset[target] % 100);
336ae06e6b2Syuo }
337