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