xref: /openbsd-src/sys/dev/usb/uoakrh.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: uoakrh.c,v 1.14 2016/03/11 18:41:33 mmcc 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: Temperature/Humidity 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/usbdevs.h>
34 #include <dev/usb/uhidev.h>
35 
36 #include "uoak.h"
37 
38 #ifdef OARKRH_DEBUG
39 int	uoakrhdebug = 0;
40 #define DPRINTFN(n, x)	do { if (uoakrhdebug > (n)) printf x; } while (0)
41 #else
42 #define DPRINTFN(n, x)
43 #endif
44 
45 #define DPRINTF(x) DPRINTFN(0, x)
46 
47 #define UOAKRH_SAMPLE_RATE	200	/* ms */
48 #define UOAKRH_REFRESH_PERIOD	10	/* 10 sec : 0.1Hz */
49 
50 struct uoakrh_sensor {
51 	struct ksensor	 temp;
52 	struct ksensor	 humi;
53 	int count;
54 	int tempval, humival;
55 	int resolution;
56 };
57 
58 struct uoakrh_softc {
59 	struct uhidev		 sc_hdev;
60 
61 	/* uoak common */
62 	struct uoak_softc	 sc_uoak_softc;
63 
64 	/* sensor framework */
65 	struct uoakrh_sensor	 sc_sensor;
66 	struct ksensordev	 sc_sensordev;
67 	struct sensor_task	*sc_sensortask;
68 
69 	/* sensor setting */
70 	int			 sc_rh_heater;
71 };
72 
73 const struct usb_devno uoakrh_devs[] = {
74 	{ USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_RH},
75 };
76 #define uoakrh_lookup(v, p) usb_lookup(uoakrh_devs, v, p)
77 
78 int  uoakrh_match(struct device *, void *, void *);
79 void uoakrh_attach(struct device *, struct device *, void *);
80 int  uoakrh_detach(struct device *, int);
81 
82 void uoakrh_intr(struct uhidev *, void *, u_int);
83 void uoakrh_refresh(void *);
84 
85 int uoakrh_get_sensor_setting(struct uoakrh_softc *, enum uoak_target);
86 
87 void uoakrh_dev_setting(void *, enum uoak_target);
88 void uoakrh_dev_print(void *, enum uoak_target);
89 
90 
91 struct cfdriver uoakrh_cd = {
92 	NULL, "uoakrh", DV_DULL
93 };
94 
95 const struct cfattach uoakrh_ca = {
96 	sizeof(struct uoakrh_softc),
97 	uoakrh_match,
98 	uoakrh_attach,
99 	uoakrh_detach,
100 };
101 
102 struct uoak_methods uoakrh_methods = {
103 	uoakrh_dev_print,
104 	uoakrh_dev_setting
105 };
106 
107 
108 int
109 uoakrh_match(struct device *parent, void *match, void *aux)
110 {
111 	struct uhidev_attach_arg *uha = aux;
112 
113 	if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
114 		return (UMATCH_NONE);
115 
116 	if (uoakrh_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
117 		return UMATCH_NONE;
118 
119 	return (UMATCH_VENDOR_PRODUCT);
120 }
121 
122 void
123 uoakrh_attach(struct device *parent, struct device *self, void *aux)
124 {
125 	struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
126 	struct usb_attach_arg *uaa = aux;
127 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
128 	struct usbd_device *dev = uha->parent->sc_udev;
129 
130 	struct uoak_softc *scc = &sc->sc_uoak_softc;
131 	int err, size, repid;
132 	void *desc;
133 
134 	sc->sc_hdev.sc_intr = uoakrh_intr;
135 	sc->sc_hdev.sc_parent = uha->parent;
136 	sc->sc_hdev.sc_report_id = uha->reportid;
137 
138 	scc->sc_parent = sc;
139 	scc->sc_udev = dev;
140 	scc->sc_hdev = &sc->sc_hdev;
141 	scc->sc_methods = &uoakrh_methods;
142 	scc->sc_sensordev = &sc->sc_sensordev;
143 
144 	uhidev_get_report_desc(uha->parent, &desc, &size);
145 	repid = uha->reportid;
146 	scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
147 	scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
148 	scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
149 
150 	/* device initialize */
151 	(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
152 	err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKRH_SAMPLE_RATE);
153 	if (err) {
154 		printf("%s: could not set sampling rate. exit\n",
155 		    sc->sc_hdev.sc_dev.dv_xname);
156 		return;
157 	}
158 
159 	/* query and print device setting */
160 	uoak_get_devinfo(scc);
161 	uoak_print_devinfo(scc);
162 
163 	DPRINTF((" config in RAM\n"));
164 	uoak_get_setting(scc, OAK_TARGET_RAM);
165 	uoak_print_setting(scc, OAK_TARGET_RAM);
166 #ifdef UOAKV_DEBUG
167 	DPRINTF((" config in FLASH\n"));
168 	uoak_get_setting(scc, OAK_TARGET_FLASH);
169 	uoak_print_setting(scc, OAK_TARGET_FLASH);
170 #endif
171 
172 	/* attach sensor */
173 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
174 	    sizeof(sc->sc_sensordev.xname));
175 	sc->sc_sensor.temp.type = SENSOR_TEMP;
176 	sc->sc_sensor.humi.type = SENSOR_HUMIDITY;
177 	sc->sc_sensor.temp.flags |= SENSOR_FINVALID;
178 	sc->sc_sensor.humi.flags |= SENSOR_FINVALID;
179 
180 	/* add label with sensor serial# */
181 	(void)snprintf(sc->sc_sensor.temp.desc, sizeof(sc->sc_sensor.temp.desc),
182 	    "Temp.(#%s)", scc->sc_udi.udi_serial);
183 	(void)snprintf(sc->sc_sensor.humi.desc, sizeof(sc->sc_sensor.humi.desc),
184 	    "%%RH(#%s)", scc->sc_udi.udi_serial);
185 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.temp);
186 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.humi);
187 
188 	/* start sensor */
189 	sc->sc_sensortask = sensor_task_register(sc, uoakrh_refresh,
190 	    UOAKRH_REFRESH_PERIOD);
191 	if (sc->sc_sensortask == NULL) {
192 		printf(", unable to register update task\n");
193 		return;
194 	}
195 	sensordev_install(&sc->sc_sensordev);
196 
197 	err = uhidev_open(&sc->sc_hdev);
198 	if (err) {
199 		printf("%s: could not open interrupt pipe, quit\n",
200 		    sc->sc_hdev.sc_dev.dv_xname);
201 		return;
202 	}
203 	scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
204 
205 	DPRINTF(("uoakrh_attach: complete\n"));
206 }
207 
208 int
209 uoakrh_detach(struct device *self, int flags)
210 {
211 	struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
212 	struct uoak_softc *scc = &sc->sc_uoak_softc;
213 	int rv = 0;
214 
215 	wakeup(&sc->sc_sensortask);
216 	sensordev_deinstall(&sc->sc_sensordev);
217 
218 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.temp);
219 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.humi);
220 
221 	if (sc->sc_sensortask != NULL)
222 		sensor_task_unregister(sc->sc_sensortask);
223 
224 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
225 		uhidev_close(&sc->sc_hdev);
226 
227 	if (scc->sc_ibuf != NULL) {
228 		free(scc->sc_ibuf, M_USBDEV, 0);
229 		scc->sc_ibuf = NULL;
230 	}
231 
232 	return (rv);
233 }
234 
235 void
236 uoakrh_intr(struct uhidev *addr, void *ibuf, u_int len)
237 {
238 	struct uoakrh_softc *sc = (struct uoakrh_softc *)addr;
239 	struct uoakrh_sensor *s = &sc->sc_sensor;
240 	struct uoak_softc *scc = &sc->sc_uoak_softc;
241 	int frame, temp, humi;
242 
243 	if (scc->sc_ibuf == NULL)
244 		return;
245 
246 	memcpy(scc->sc_ibuf, ibuf, len);
247 	frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
248 	humi  = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
249 	temp  = (scc->sc_ibuf[5] << 8) + (scc->sc_ibuf[4]);
250 
251 	if (s->count == 0) {
252 		s->tempval = temp;
253 		s->humival = humi;
254 	}
255 
256 	/* calculate average value */
257 	s->tempval = (s->tempval * s->count + temp) / (s->count + 1);
258 	s->humival = (s->humival * s->count + humi) / (s->count + 1);
259 
260 	s->count++;
261 }
262 
263 void
264 uoakrh_refresh(void *arg)
265 {
266 	struct uoakrh_softc *sc = arg;
267 	struct uoakrh_sensor *s = &sc->sc_sensor;
268 	struct uoak_softc *scc = &sc->sc_uoak_softc;
269 	uint8_t led;
270 
271 	/* blink LED for each cycle */
272 	if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
273 		DPRINTF(("status query error\n"));
274 	if (led == OAK_LED_OFF)
275 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
276 	else
277 		(void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
278 
279 	/* update sensor value */
280 	s->temp.value = (uint64_t)(s->tempval) * 10000;
281 	s->humi.value = (uint64_t)(s->humival) * 10;
282 	s->temp.flags &= ~SENSOR_FINVALID;
283 	s->humi.flags &= ~SENSOR_FINVALID;
284 	s->count = 0;
285 }
286 
287 
288 int
289 uoakrh_get_sensor_setting(struct uoakrh_softc *sc, enum uoak_target target)
290 {
291 	uint8_t result;
292 	struct uoak_softc *scc = &sc->sc_uoak_softc;
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_sensor.resolution = (result & OAK_RH_SENSOR_RES_MASK);
304 	sc->sc_rh_heater = (result & OAK_RH_SENSOR_HEATER_MASK) >> 2;
305 
306 	return 0;
307 }
308 
309 /* device specific functions */
310 void
311 uoakrh_dev_setting(void *parent, enum uoak_target target)
312 {
313 	struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
314 
315 	/* get device specific configuration */
316 	(void)uoakrh_get_sensor_setting(sc, target);
317 }
318 
319 void
320 uoakrh_dev_print(void *parent, enum uoak_target target)
321 {
322 	struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
323 
324 	printf(", %s",
325 	    (sc->sc_sensor.resolution ? "8bit RH/12 bit" : "12bit RH/14bit"));
326 	printf(", heater %s", (sc->sc_rh_heater ? "ON" : "OFF"));
327 }
328