jcs's openbsd hax
openbsd
at jcs 325 lines 8.8 kB view raw
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 37int 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 48struct uoakrh_sensor { 49 struct ksensor temp; 50 struct ksensor humi; 51 int count; 52 int tempval, humival; 53 int resolution; 54}; 55 56struct 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 71const 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 76int uoakrh_match(struct device *, void *, void *); 77void uoakrh_attach(struct device *, struct device *, void *); 78int uoakrh_detach(struct device *, int); 79 80void uoakrh_intr(struct uhidev *, void *, u_int); 81void uoakrh_refresh(void *); 82 83int uoakrh_get_sensor_setting(struct uoakrh_softc *, enum uoak_target); 84 85void uoakrh_dev_setting(void *, enum uoak_target); 86void uoakrh_dev_print(void *, enum uoak_target); 87 88 89struct cfdriver uoakrh_cd = { 90 NULL, "uoakrh", DV_DULL 91}; 92 93const struct cfattach uoakrh_ca = { 94 sizeof(struct uoakrh_softc), 95 uoakrh_match, 96 uoakrh_attach, 97 uoakrh_detach, 98}; 99 100const struct uoak_methods uoakrh_methods = { 101 uoakrh_dev_print, 102 uoakrh_dev_setting 103}; 104 105 106int 107uoakrh_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 120void 121uoakrh_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 206int 207uoakrh_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 233void 234uoakrh_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 261void 262uoakrh_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 286int 287uoakrh_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 */ 308void 309uoakrh_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 317void 318uoakrh_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}