1 /* $OpenBSD: uoaklux.c,v 1.18 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: 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/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 UOAKLUX_DEBUG 37 int uoakluxdebug = 0; 38 #define DPRINTFN(n, x) do { if (uoakluxdebug > (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 UOAKLUX_SAMPLE_RATE 200 /* ms */ 46 #define UOAKLUX_REFRESH_PERIOD 5 /* 5 sec : 0.2Hz */ 47 48 struct uoaklux_sensor { 49 struct uoak_sensor lux; 50 /* lux sensor setting */ 51 uint8_t gain; 52 int inttime; 53 54 }; 55 56 struct uoaklux_softc { 57 struct uhidev sc_hdev; 58 59 /* uoak common */ 60 struct uoak_softc sc_uoak_softc; 61 62 /* sensor framework */ 63 struct uoaklux_sensor sc_sensor; 64 struct ksensordev sc_sensordev; 65 struct sensor_task *sc_sensortask; 66 }; 67 68 const struct usb_devno uoaklux_devs[] = { 69 { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_LUX}, 70 }; 71 #define uoaklux_lookup(v, p) usb_lookup(uoaklux_devs, v, p) 72 73 int uoaklux_match(struct device *, void *, void *); 74 void uoaklux_attach(struct device *, struct device *, void *); 75 int uoaklux_detach(struct device *, int); 76 77 void uoaklux_intr(struct uhidev *, void *, u_int); 78 void uoaklux_refresh(void *); 79 80 int uoaklux_get_sensor_setting(struct uoaklux_softc *, enum uoak_target); 81 82 void uoaklux_dev_setting(void *, enum uoak_target); 83 void uoaklux_dev_print(void *, enum uoak_target); 84 85 86 struct cfdriver uoaklux_cd = { 87 NULL, "uoaklux", DV_DULL 88 }; 89 90 const struct cfattach uoaklux_ca = { 91 sizeof(struct uoaklux_softc), 92 uoaklux_match, 93 uoaklux_attach, 94 uoaklux_detach, 95 }; 96 97 const struct uoak_methods uoaklux_methods = { 98 uoaklux_dev_print, 99 uoaklux_dev_setting 100 }; 101 102 103 int 104 uoaklux_match(struct device *parent, void *match, void *aux) 105 { 106 struct uhidev_attach_arg *uha = aux; 107 108 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 109 return (UMATCH_NONE); 110 111 if (uoaklux_lookup(uha->uaa->vendor, uha->uaa->product) == NULL) 112 return UMATCH_NONE; 113 114 return (UMATCH_VENDOR_PRODUCT); 115 } 116 117 void 118 uoaklux_attach(struct device *parent, struct device *self, void *aux) 119 { 120 struct uoaklux_softc *sc = (struct uoaklux_softc *)self; 121 struct usb_attach_arg *uaa = aux; 122 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 123 struct usbd_device *dev = uha->parent->sc_udev; 124 125 struct uoak_softc *scc = &sc->sc_uoak_softc; 126 int err, size, repid; 127 void *desc; 128 129 sc->sc_hdev.sc_intr = uoaklux_intr; 130 sc->sc_hdev.sc_parent = uha->parent; 131 sc->sc_hdev.sc_report_id = uha->reportid; 132 133 scc->sc_parent = sc; 134 scc->sc_udev = dev; 135 scc->sc_hdev = &sc->sc_hdev; 136 scc->sc_methods = &uoaklux_methods; 137 scc->sc_sensordev = &sc->sc_sensordev; 138 139 uhidev_get_report_desc(uha->parent, &desc, &size); 140 repid = uha->reportid; 141 scc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 142 scc->sc_olen = hid_report_size(desc, size, hid_output, repid); 143 scc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 144 145 /*device initialize */ 146 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON); 147 err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKLUX_SAMPLE_RATE); 148 if (err) { 149 printf("%s: could not set sampling rate. exit\n", 150 sc->sc_hdev.sc_dev.dv_xname); 151 return; 152 } 153 154 /* query and print device setting */ 155 uoak_get_devinfo(scc); 156 uoak_print_devinfo(scc); 157 158 DPRINTF((" config in RAM\n")); 159 uoak_get_setting(scc, OAK_TARGET_RAM); 160 uoak_print_setting(scc, OAK_TARGET_RAM); 161 #ifdef UOAKLUX_DEBUG 162 DPRINTF((" config in FLASh\n")); 163 uoak_get_setting(scc, OAK_TARGET_FLASH); 164 uoak_print_setting(scc, OAK_TARGET_FLASH); 165 #endif 166 167 /* attach sensor */ 168 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 169 sizeof(sc->sc_sensordev.xname)); 170 uoak_sensor_attach(scc, &sc->sc_sensor.lux, SENSOR_LUX); 171 172 /* start sensor */ 173 sc->sc_sensortask = sensor_task_register(sc, uoaklux_refresh, 174 UOAKLUX_REFRESH_PERIOD); 175 if (sc->sc_sensortask == NULL) { 176 printf(", unable to register update task\n"); 177 return; 178 } 179 sensordev_install(&sc->sc_sensordev); 180 181 err = uhidev_open(&sc->sc_hdev); 182 if (err) { 183 printf("%s: could not open interrupt pipe, quit\n", 184 sc->sc_hdev.sc_dev.dv_xname); 185 return; 186 } 187 scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK); 188 189 DPRINTF(("uoaklux_attach: complete\n")); 190 } 191 192 193 int 194 uoaklux_detach(struct device *self, int flags) 195 { 196 struct uoaklux_softc *sc = (struct uoaklux_softc *)self; 197 struct uoak_softc *scc = &sc->sc_uoak_softc; 198 int rv = 0; 199 200 wakeup(&sc->sc_sensortask); 201 sensordev_deinstall(&sc->sc_sensordev); 202 203 uoak_sensor_detach(scc, &sc->sc_sensor.lux); 204 205 if (sc->sc_sensortask != NULL) 206 sensor_task_unregister(sc->sc_sensortask); 207 208 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 209 uhidev_close(&sc->sc_hdev); 210 211 if (scc->sc_ibuf != NULL) { 212 free(scc->sc_ibuf, M_USBDEV, scc->sc_ilen); 213 scc->sc_ibuf = NULL; 214 } 215 216 return (rv); 217 } 218 219 void 220 uoaklux_intr(struct uhidev *addr, void *ibuf, u_int len) 221 { 222 struct uoaklux_softc *sc = (struct uoaklux_softc *)addr; 223 struct uoak_softc *scc = &sc->sc_uoak_softc; 224 int frame, val; 225 226 if (scc->sc_ibuf == NULL) 227 return; 228 229 memcpy(scc->sc_ibuf, ibuf, len); 230 frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]); 231 val = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]); 232 uoak_sensor_update(&sc->sc_sensor.lux, val); 233 } 234 235 void 236 uoaklux_refresh(void *arg) 237 { 238 struct uoaklux_softc *sc = arg; 239 struct uoak_softc *scc = &sc->sc_uoak_softc; 240 uint8_t led; 241 242 /* blink LED for each cycle */ 243 if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0) 244 DPRINTF(("status query error\n")); 245 if (led == OAK_LED_OFF) 246 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON); 247 else 248 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF); 249 250 uoak_sensor_refresh(&sc->sc_sensor.lux, 1000000, 0); 251 } 252 253 int 254 uoaklux_get_sensor_setting(struct uoaklux_softc *sc, enum uoak_target target) 255 { 256 struct uoak_softc *scc = &sc->sc_uoak_softc; 257 uint8_t result; 258 259 memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 260 scc->sc_rcmd.target = target; 261 scc->sc_rcmd.datasize = 0x1; 262 USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING); 263 264 if (uoak_get_cmd(scc) < 0) 265 return EIO; 266 267 result = scc->sc_buf[1]; 268 269 sc->sc_sensor.gain = ((result & OAK_LUX_SENSOR_GAIN_MASK) >> 3); 270 sc->sc_sensor.inttime = (result & OAK_LUX_SENSOR_INTTIME_MASK); 271 272 return 0; 273 } 274 275 /* device specific functions */ 276 void 277 uoaklux_dev_setting(void *parent, enum uoak_target target) 278 { 279 struct uoaklux_softc *sc = (struct uoaklux_softc *)parent; 280 281 /* get device specific configuration */ 282 (void)uoaklux_get_sensor_setting(sc, target); 283 } 284 285 void 286 uoaklux_dev_print(void *parent, enum uoak_target target) 287 { 288 struct uoaklux_softc *sc = (struct uoaklux_softc *)parent; 289 290 printf(", %s gain", (sc->sc_sensor.gain ? "HIGH" : "LOW")); 291 printf(", speed "); 292 switch(sc->sc_sensor.inttime) { 293 case OAK_LUX_SENSOR_INTTIME_13_7ms: 294 printf("13.7ms"); 295 break; 296 case OAK_LUX_SENSOR_INTTIME_101ms: 297 printf("101ms"); 298 break; 299 case OAK_LUX_SENSOR_INTTIME_402ms: 300 printf("402ms"); 301 break; 302 default: 303 printf("unknown"); 304 break; 305 } 306 } 307