1 /* $OpenBSD: uoakrh.c,v 1.20 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: 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/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 OARKRH_DEBUG
37 int uoakrhdebug = 0;
38 #define DPRINTFN(n, x) do { if (uoakrhdebug > (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 UOAKRH_SAMPLE_RATE 200 /* ms */
46 #define UOAKRH_REFRESH_PERIOD 10 /* 10 sec : 0.1Hz */
47
48 struct uoakrh_sensor {
49 struct ksensor temp;
50 struct ksensor humi;
51 int count;
52 int tempval, humival;
53 int resolution;
54 };
55
56 struct uoakrh_softc {
57 struct uhidev sc_hdev;
58
59 /* uoak common */
60 struct uoak_softc sc_uoak_softc;
61
62 /* sensor framework */
63 struct uoakrh_sensor sc_sensor;
64 struct ksensordev sc_sensordev;
65 struct sensor_task *sc_sensortask;
66
67 /* sensor setting */
68 int sc_rh_heater;
69 };
70
71 const struct usb_devno uoakrh_devs[] = {
72 { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_RH},
73 };
74 #define uoakrh_lookup(v, p) usb_lookup(uoakrh_devs, v, p)
75
76 int uoakrh_match(struct device *, void *, void *);
77 void uoakrh_attach(struct device *, struct device *, void *);
78 int uoakrh_detach(struct device *, int);
79
80 void uoakrh_intr(struct uhidev *, void *, u_int);
81 void uoakrh_refresh(void *);
82
83 int uoakrh_get_sensor_setting(struct uoakrh_softc *, enum uoak_target);
84
85 void uoakrh_dev_setting(void *, enum uoak_target);
86 void uoakrh_dev_print(void *, enum uoak_target);
87
88
89 struct cfdriver uoakrh_cd = {
90 NULL, "uoakrh", DV_DULL
91 };
92
93 const struct cfattach uoakrh_ca = {
94 sizeof(struct uoakrh_softc),
95 uoakrh_match,
96 uoakrh_attach,
97 uoakrh_detach,
98 };
99
100 const struct uoak_methods uoakrh_methods = {
101 uoakrh_dev_print,
102 uoakrh_dev_setting
103 };
104
105
106 int
uoakrh_match(struct device * parent,void * match,void * aux)107 uoakrh_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 (uoakrh_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
115 return UMATCH_NONE;
116
117 return (UMATCH_VENDOR_PRODUCT);
118 }
119
120 void
uoakrh_attach(struct device * parent,struct device * self,void * aux)121 uoakrh_attach(struct device *parent, struct device *self, void *aux)
122 {
123 struct uoakrh_softc *sc = (struct uoakrh_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 err, size, repid;
130 void *desc;
131
132 sc->sc_hdev.sc_intr = uoakrh_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 = &uoakrh_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, UOAKRH_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 FLASH\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 sc->sc_sensor.temp.type = SENSOR_TEMP;
174 sc->sc_sensor.humi.type = SENSOR_HUMIDITY;
175 sc->sc_sensor.temp.flags |= SENSOR_FINVALID;
176 sc->sc_sensor.humi.flags |= SENSOR_FINVALID;
177
178 /* add label with sensor serial# */
179 (void)snprintf(sc->sc_sensor.temp.desc, sizeof(sc->sc_sensor.temp.desc),
180 "Temp.(#%s)", scc->sc_udi.udi_serial);
181 (void)snprintf(sc->sc_sensor.humi.desc, sizeof(sc->sc_sensor.humi.desc),
182 "%%RH(#%s)", scc->sc_udi.udi_serial);
183 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.temp);
184 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.humi);
185
186 /* start sensor */
187 sc->sc_sensortask = sensor_task_register(sc, uoakrh_refresh,
188 UOAKRH_REFRESH_PERIOD);
189 if (sc->sc_sensortask == NULL) {
190 printf(", unable to register update task\n");
191 return;
192 }
193 sensordev_install(&sc->sc_sensordev);
194
195 err = uhidev_open(&sc->sc_hdev);
196 if (err) {
197 printf("%s: could not open interrupt pipe, quit\n",
198 sc->sc_hdev.sc_dev.dv_xname);
199 return;
200 }
201 scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
202
203 DPRINTF(("uoakrh_attach: complete\n"));
204 }
205
206 int
uoakrh_detach(struct device * self,int flags)207 uoakrh_detach(struct device *self, int flags)
208 {
209 struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
210 struct uoak_softc *scc = &sc->sc_uoak_softc;
211 int rv = 0;
212
213 wakeup(&sc->sc_sensortask);
214 sensordev_deinstall(&sc->sc_sensordev);
215
216 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.temp);
217 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.humi);
218
219 if (sc->sc_sensortask != NULL)
220 sensor_task_unregister(sc->sc_sensortask);
221
222 if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
223 uhidev_close(&sc->sc_hdev);
224
225 if (scc->sc_ibuf != NULL) {
226 free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen);
227 scc->sc_ibuf = NULL;
228 }
229
230 return (rv);
231 }
232
233 void
uoakrh_intr(struct uhidev * addr,void * ibuf,u_int len)234 uoakrh_intr(struct uhidev *addr, void *ibuf, u_int len)
235 {
236 struct uoakrh_softc *sc = (struct uoakrh_softc *)addr;
237 struct uoakrh_sensor *s = &sc->sc_sensor;
238 struct uoak_softc *scc = &sc->sc_uoak_softc;
239 int frame, temp, humi;
240
241 if (scc->sc_ibuf == NULL)
242 return;
243
244 memcpy(scc->sc_ibuf, ibuf, len);
245 frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
246 humi = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
247 temp = (scc->sc_ibuf[5] << 8) + (scc->sc_ibuf[4]);
248
249 if (s->count == 0) {
250 s->tempval = temp;
251 s->humival = humi;
252 }
253
254 /* calculate average value */
255 s->tempval = (s->tempval * s->count + temp) / (s->count + 1);
256 s->humival = (s->humival * s->count + humi) / (s->count + 1);
257
258 s->count++;
259 }
260
261 void
uoakrh_refresh(void * arg)262 uoakrh_refresh(void *arg)
263 {
264 struct uoakrh_softc *sc = arg;
265 struct uoakrh_sensor *s = &sc->sc_sensor;
266 struct uoak_softc *scc = &sc->sc_uoak_softc;
267 uint8_t led;
268
269 /* blink LED for each cycle */
270 if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
271 DPRINTF(("status query error\n"));
272 if (led == OAK_LED_OFF)
273 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
274 else
275 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
276
277 /* update sensor value */
278 s->temp.value = (uint64_t)(s->tempval) * 10000;
279 s->humi.value = (uint64_t)(s->humival) * 10;
280 s->temp.flags &= ~SENSOR_FINVALID;
281 s->humi.flags &= ~SENSOR_FINVALID;
282 s->count = 0;
283 }
284
285
286 int
uoakrh_get_sensor_setting(struct uoakrh_softc * sc,enum uoak_target target)287 uoakrh_get_sensor_setting(struct uoakrh_softc *sc, enum uoak_target target)
288 {
289 uint8_t result;
290 struct uoak_softc *scc = &sc->sc_uoak_softc;
291
292 memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
293 scc->sc_rcmd.target = target;
294 scc->sc_rcmd.datasize = 0x1;
295 USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
296
297 if (uoak_get_cmd(scc) < 0)
298 return EIO;
299
300 result = scc->sc_buf[1];
301 sc->sc_sensor.resolution = (result & OAK_RH_SENSOR_RES_MASK);
302 sc->sc_rh_heater = (result & OAK_RH_SENSOR_HEATER_MASK) >> 2;
303
304 return 0;
305 }
306
307 /* device specific functions */
308 void
uoakrh_dev_setting(void * parent,enum uoak_target target)309 uoakrh_dev_setting(void *parent, enum uoak_target target)
310 {
311 struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
312
313 /* get device specific configuration */
314 (void)uoakrh_get_sensor_setting(sc, target);
315 }
316
317 void
uoakrh_dev_print(void * parent,enum uoak_target target)318 uoakrh_dev_print(void *parent, enum uoak_target target)
319 {
320 struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
321
322 printf(", %s",
323 (sc->sc_sensor.resolution ? "8bit RH/12 bit" : "12bit RH/14bit"));
324 printf(", heater %s", (sc->sc_rh_heater ? "ON" : "OFF"));
325 }
326