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