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