1 /* $OpenBSD: uoaklux.c,v 1.9 2014/07/12 18:48:52 tedu 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: lux 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/usbdi_util.h> 34 #include <dev/usb/usbdevs.h> 35 #include <dev/usb/uhidev.h> 36 #include <dev/usb/hid.h> 37 #include "uoak.h" 38 39 #ifdef UOAKLUX_DEBUG 40 int uoakluxdebug = 0; 41 #define DPRINTFN(n, x) do { if (uoakluxdebug > (n)) printf x; } while (0) 42 #else 43 #define DPRINTFN(n, x) 44 #endif 45 46 #define DPRINTF(x) DPRINTFN(0, x) 47 48 #define UOAKLUX_SAMPLE_RATE 200 /* ms */ 49 #define UOAKLUX_REFRESH_PERIOD 5 /* 5 sec : 0.2Hz */ 50 51 struct uoaklux_sensor { 52 struct uoak_sensor lux; 53 /* lux sensor setting */ 54 uint8_t gain; 55 int inttime; 56 57 }; 58 59 struct uoaklux_softc { 60 struct uhidev sc_hdev; 61 62 /* uoak common */ 63 struct uoak_softc sc_uoak_softc; 64 65 /* sensor framework */ 66 struct uoaklux_sensor sc_sensor; 67 struct ksensordev sc_sensordev; 68 struct sensor_task *sc_sensortask; 69 }; 70 71 const struct usb_devno uoaklux_devs[] = { 72 { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_LUX}, 73 }; 74 #define uoaklux_lookup(v, p) usb_lookup(uoaklux_devs, v, p) 75 76 int uoaklux_match(struct device *, void *, void *); 77 void uoaklux_attach(struct device *, struct device *, void *); 78 int uoaklux_detach(struct device *, int); 79 80 void uoaklux_intr(struct uhidev *, void *, u_int); 81 void uoaklux_refresh(void *); 82 83 int uoaklux_get_sensor_setting(struct uoaklux_softc *, enum uoak_target); 84 85 void uoaklux_dev_setting(void *, enum uoak_target); 86 void uoaklux_dev_print(void *, enum uoak_target); 87 88 89 struct cfdriver uoaklux_cd = { 90 NULL, "uoaklux", DV_DULL 91 }; 92 93 const struct cfattach uoaklux_ca = { 94 sizeof(struct uoaklux_softc), 95 uoaklux_match, 96 uoaklux_attach, 97 uoaklux_detach, 98 }; 99 100 struct uoak_methods uoaklux_methods = { 101 uoaklux_dev_print, 102 uoaklux_dev_setting 103 }; 104 105 106 int 107 uoaklux_match(struct device *parent, void *match, void *aux) 108 { 109 struct uhidev_attach_arg *uha = aux; 110 111 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 112 return (UMATCH_NONE); 113 114 if (uoaklux_lookup(uha->uaa->vendor, uha->uaa->product) == NULL) 115 return UMATCH_NONE; 116 117 return (UMATCH_VENDOR_PRODUCT); 118 } 119 120 void 121 uoaklux_attach(struct device *parent, struct device *self, void *aux) 122 { 123 struct uoaklux_softc *sc = (struct uoaklux_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 = uoaklux_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 = &uoaklux_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, UOAKLUX_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 UOAKLUX_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 uoak_sensor_attach(scc, &sc->sc_sensor.lux, SENSOR_LUX); 174 175 /* start sensor */ 176 sc->sc_sensortask = sensor_task_register(sc, uoaklux_refresh, 177 UOAKLUX_REFRESH_PERIOD); 178 if (sc->sc_sensortask == NULL) { 179 printf(", unable to register update task\n"); 180 return; 181 } 182 sensordev_install(&sc->sc_sensordev); 183 184 err = uhidev_open(&sc->sc_hdev); 185 if (err) { 186 printf("%s: could not open interrupt pipe, quit\n", 187 sc->sc_hdev.sc_dev.dv_xname); 188 return; 189 } 190 scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK); 191 192 DPRINTF(("uoaklux_attach: complete\n")); 193 } 194 195 196 int 197 uoaklux_detach(struct device *self, int flags) 198 { 199 struct uoaklux_softc *sc = (struct uoaklux_softc *)self; 200 struct uoak_softc *scc = &sc->sc_uoak_softc; 201 int rv = 0; 202 203 wakeup(&sc->sc_sensortask); 204 sensordev_deinstall(&sc->sc_sensordev); 205 206 uoak_sensor_detach(scc, &sc->sc_sensor.lux); 207 208 if (sc->sc_sensortask != NULL) 209 sensor_task_unregister(sc->sc_sensortask); 210 211 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 212 uhidev_close(&sc->sc_hdev); 213 214 if (scc->sc_ibuf != NULL) { 215 free(scc->sc_ibuf, M_USBDEV, 0); 216 scc->sc_ibuf = NULL; 217 } 218 219 return (rv); 220 } 221 222 void 223 uoaklux_intr(struct uhidev *addr, void *ibuf, u_int len) 224 { 225 struct uoaklux_softc *sc = (struct uoaklux_softc *)addr; 226 struct uoak_softc *scc = &sc->sc_uoak_softc; 227 int frame, val; 228 229 if (scc->sc_ibuf == NULL) 230 return; 231 232 memcpy(scc->sc_ibuf, ibuf, len); 233 frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]); 234 val = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]); 235 uoak_sensor_update(&sc->sc_sensor.lux, val); 236 } 237 238 void 239 uoaklux_refresh(void *arg) 240 { 241 struct uoaklux_softc *sc = arg; 242 struct uoak_softc *scc = &sc->sc_uoak_softc; 243 uint8_t led; 244 245 /* blink LED for each cycle */ 246 if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0) 247 DPRINTF(("status query error\n")); 248 if (led == OAK_LED_OFF) 249 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON); 250 else 251 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF); 252 253 uoak_sensor_refresh(&sc->sc_sensor.lux, 1000000, 0); 254 } 255 256 int 257 uoaklux_get_sensor_setting(struct uoaklux_softc *sc, enum uoak_target target) 258 { 259 struct uoak_softc *scc = &sc->sc_uoak_softc; 260 uint8_t result; 261 262 memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 263 scc->sc_rcmd.target = target; 264 scc->sc_rcmd.datasize = 0x1; 265 USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING); 266 267 if (uoak_get_cmd(scc) < 0) 268 return EIO; 269 270 result = scc->sc_buf[1]; 271 272 sc->sc_sensor.gain = ((result & OAK_LUX_SENSOR_GAIN_MASK) >> 3); 273 sc->sc_sensor.inttime = (result & OAK_LUX_SENSOR_INTTIME_MASK); 274 275 return 0; 276 } 277 278 /* device specific functions */ 279 void 280 uoaklux_dev_setting(void *parent, enum uoak_target target) 281 { 282 struct uoaklux_softc *sc = (struct uoaklux_softc *)parent; 283 284 /* get device specific configuration */ 285 (void)uoaklux_get_sensor_setting(sc, target); 286 } 287 288 void 289 uoaklux_dev_print(void *parent, enum uoak_target target) 290 { 291 struct uoaklux_softc *sc = (struct uoaklux_softc *)parent; 292 293 printf(", %s gain", (sc->sc_sensor.gain ? "HIGH" : "LOW")); 294 printf(", speed "); 295 switch(sc->sc_sensor.inttime) { 296 case OAK_LUX_SENSOR_INTTIME_13_7ms: 297 printf("13.7ms"); 298 break; 299 case OAK_LUX_SENSOR_INTTIME_101ms: 300 printf("101ms"); 301 break; 302 case OAK_LUX_SENSOR_INTTIME_402ms: 303 printf("402ms"); 304 break; 305 default: 306 printf("unknown"); 307 break; 308 } 309 } 310