Line data Source code
1 : /* $OpenBSD: uoakrh.c,v 1.15 2017/04/08 02:57:25 deraadt 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 0 : uoakrh_match(struct device *parent, void *match, void *aux)
110 : {
111 0 : struct uhidev_attach_arg *uha = aux;
112 :
113 0 : if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
114 0 : return (UMATCH_NONE);
115 :
116 0 : if (uoakrh_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
117 0 : return UMATCH_NONE;
118 :
119 0 : return (UMATCH_VENDOR_PRODUCT);
120 0 : }
121 :
122 : void
123 0 : uoakrh_attach(struct device *parent, struct device *self, void *aux)
124 : {
125 0 : struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
126 0 : struct usb_attach_arg *uaa = aux;
127 0 : struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
128 0 : struct usbd_device *dev = uha->parent->sc_udev;
129 :
130 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
131 0 : int err, size, repid;
132 0 : void *desc;
133 :
134 0 : sc->sc_hdev.sc_intr = uoakrh_intr;
135 0 : sc->sc_hdev.sc_parent = uha->parent;
136 0 : sc->sc_hdev.sc_report_id = uha->reportid;
137 :
138 0 : scc->sc_parent = sc;
139 0 : scc->sc_udev = dev;
140 0 : scc->sc_hdev = &sc->sc_hdev;
141 0 : scc->sc_methods = &uoakrh_methods;
142 0 : scc->sc_sensordev = &sc->sc_sensordev;
143 :
144 0 : uhidev_get_report_desc(uha->parent, &desc, &size);
145 0 : repid = uha->reportid;
146 0 : scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
147 0 : scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
148 0 : scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
149 :
150 : /* device initialize */
151 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
152 0 : err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKRH_SAMPLE_RATE);
153 0 : if (err) {
154 0 : printf("%s: could not set sampling rate. exit\n",
155 0 : sc->sc_hdev.sc_dev.dv_xname);
156 0 : return;
157 : }
158 :
159 : /* query and print device setting */
160 0 : uoak_get_devinfo(scc);
161 0 : uoak_print_devinfo(scc);
162 :
163 : DPRINTF((" config in RAM\n"));
164 0 : uoak_get_setting(scc, OAK_TARGET_RAM);
165 0 : 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 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
174 : sizeof(sc->sc_sensordev.xname));
175 0 : sc->sc_sensor.temp.type = SENSOR_TEMP;
176 0 : sc->sc_sensor.humi.type = SENSOR_HUMIDITY;
177 0 : sc->sc_sensor.temp.flags |= SENSOR_FINVALID;
178 0 : sc->sc_sensor.humi.flags |= SENSOR_FINVALID;
179 :
180 : /* add label with sensor serial# */
181 0 : (void)snprintf(sc->sc_sensor.temp.desc, sizeof(sc->sc_sensor.temp.desc),
182 0 : "Temp.(#%s)", scc->sc_udi.udi_serial);
183 0 : (void)snprintf(sc->sc_sensor.humi.desc, sizeof(sc->sc_sensor.humi.desc),
184 : "%%RH(#%s)", scc->sc_udi.udi_serial);
185 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.temp);
186 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.humi);
187 :
188 : /* start sensor */
189 0 : sc->sc_sensortask = sensor_task_register(sc, uoakrh_refresh,
190 : UOAKRH_REFRESH_PERIOD);
191 0 : if (sc->sc_sensortask == NULL) {
192 0 : printf(", unable to register update task\n");
193 0 : return;
194 : }
195 0 : sensordev_install(&sc->sc_sensordev);
196 :
197 0 : err = uhidev_open(&sc->sc_hdev);
198 0 : if (err) {
199 0 : printf("%s: could not open interrupt pipe, quit\n",
200 : sc->sc_hdev.sc_dev.dv_xname);
201 0 : return;
202 : }
203 0 : scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
204 :
205 : DPRINTF(("uoakrh_attach: complete\n"));
206 0 : }
207 :
208 : int
209 0 : uoakrh_detach(struct device *self, int flags)
210 : {
211 0 : struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
212 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
213 : int rv = 0;
214 :
215 0 : wakeup(&sc->sc_sensortask);
216 0 : sensordev_deinstall(&sc->sc_sensordev);
217 :
218 0 : sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.temp);
219 0 : sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.humi);
220 :
221 0 : if (sc->sc_sensortask != NULL)
222 0 : sensor_task_unregister(sc->sc_sensortask);
223 :
224 0 : if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
225 0 : uhidev_close(&sc->sc_hdev);
226 :
227 0 : if (scc->sc_ibuf != NULL) {
228 0 : free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
229 0 : scc->sc_ibuf = NULL;
230 0 : }
231 :
232 0 : return (rv);
233 : }
234 :
235 : void
236 0 : uoakrh_intr(struct uhidev *addr, void *ibuf, u_int len)
237 : {
238 0 : struct uoakrh_softc *sc = (struct uoakrh_softc *)addr;
239 0 : struct uoakrh_sensor *s = &sc->sc_sensor;
240 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
241 : int frame, temp, humi;
242 :
243 0 : if (scc->sc_ibuf == NULL)
244 0 : return;
245 :
246 0 : memcpy(scc->sc_ibuf, ibuf, len);
247 0 : frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
248 0 : humi = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
249 0 : temp = (scc->sc_ibuf[5] << 8) + (scc->sc_ibuf[4]);
250 :
251 0 : if (s->count == 0) {
252 0 : s->tempval = temp;
253 0 : s->humival = humi;
254 0 : }
255 :
256 : /* calculate average value */
257 0 : s->tempval = (s->tempval * s->count + temp) / (s->count + 1);
258 0 : s->humival = (s->humival * s->count + humi) / (s->count + 1);
259 :
260 0 : s->count++;
261 0 : }
262 :
263 : void
264 0 : uoakrh_refresh(void *arg)
265 : {
266 0 : struct uoakrh_softc *sc = arg;
267 0 : struct uoakrh_sensor *s = &sc->sc_sensor;
268 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
269 0 : uint8_t led;
270 :
271 : /* blink LED for each cycle */
272 0 : if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
273 : DPRINTF(("status query error\n"));
274 0 : if (led == OAK_LED_OFF)
275 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
276 : else
277 0 : (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
278 :
279 : /* update sensor value */
280 0 : s->temp.value = (uint64_t)(s->tempval) * 10000;
281 0 : s->humi.value = (uint64_t)(s->humival) * 10;
282 0 : s->temp.flags &= ~SENSOR_FINVALID;
283 0 : s->humi.flags &= ~SENSOR_FINVALID;
284 0 : s->count = 0;
285 0 : }
286 :
287 :
288 : int
289 0 : uoakrh_get_sensor_setting(struct uoakrh_softc *sc, enum uoak_target target)
290 : {
291 : uint8_t result;
292 0 : struct uoak_softc *scc = &sc->sc_uoak_softc;
293 :
294 0 : memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
295 0 : scc->sc_rcmd.target = target;
296 0 : scc->sc_rcmd.datasize = 0x1;
297 0 : USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
298 :
299 0 : if (uoak_get_cmd(scc) < 0)
300 0 : return EIO;
301 :
302 0 : result = scc->sc_buf[1];
303 0 : sc->sc_sensor.resolution = (result & OAK_RH_SENSOR_RES_MASK);
304 0 : sc->sc_rh_heater = (result & OAK_RH_SENSOR_HEATER_MASK) >> 2;
305 :
306 0 : return 0;
307 0 : }
308 :
309 : /* device specific functions */
310 : void
311 0 : uoakrh_dev_setting(void *parent, enum uoak_target target)
312 : {
313 0 : struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
314 :
315 : /* get device specific configuration */
316 0 : (void)uoakrh_get_sensor_setting(sc, target);
317 0 : }
318 :
319 : void
320 0 : uoakrh_dev_print(void *parent, enum uoak_target target)
321 : {
322 0 : struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
323 :
324 0 : printf(", %s",
325 0 : (sc->sc_sensor.resolution ? "8bit RH/12 bit" : "12bit RH/14bit"));
326 0 : printf(", heater %s", (sc->sc_rh_heater ? "ON" : "OFF"));
327 0 : }
|