1 /* $OpenBSD: uoakv.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: 8channel +/-10V ADC 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 UOAKV_DEBUG 40 int uoakvdebug = 0; 41 #define DPRINTFN(n, x) do { if (uoakvdebug > (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 UOAKV_SAMPLE_RATE 100 /* ms */ 49 #define UOAKV_REFRESH_PERIOD 1 /* 1 sec : 1Hz */ 50 51 struct uoakv_sensor { 52 struct uoak_sensor v; 53 /* ADC setting */ 54 unsigned int offset[OAK_V_TARGET_MAX]; /* absolute offset (mV) */ 55 }; 56 57 struct uoakv_softc { 58 struct uhidev sc_hdev; 59 60 /* uoak common */ 61 struct uoak_softc sc_uoak_softc; 62 63 /* sensor framework */ 64 struct uoakv_sensor sc_sensor[OAK_V_MAXSENSORS]; 65 struct ksensordev sc_sensordev; 66 struct sensor_task *sc_sensortask; 67 68 /* sensor setting */ 69 int sc_inputmode[OAK_V_TARGET_MAX]; 70 71 }; 72 73 const struct usb_devno uoakv_devs[] = { 74 { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_10V}, 75 }; 76 #define uoakv_lookup(v, p) usb_lookup(uoakv_devs, v, p) 77 78 int uoakv_match(struct device *, void *, void *); 79 void uoakv_attach(struct device *, struct device *, void *); 80 int uoakv_detach(struct device *, int); 81 82 void uoakv_intr(struct uhidev *, void *, u_int); 83 void uoakv_refresh(void *); 84 85 int uoakv_get_channel_setting(struct uoakv_softc *, enum uoak_target, int); 86 int uoakv_get_sensor_setting(struct uoakv_softc *, enum uoak_target); 87 88 void uoakv_dev_setting(void *, enum uoak_target); 89 void uoakv_dev_print(void *, enum uoak_target); 90 91 92 struct cfdriver uoakv_cd = { 93 NULL, "uoakv", DV_DULL 94 }; 95 96 const struct cfattach uoakv_ca = { 97 sizeof(struct uoakv_softc), 98 uoakv_match, 99 uoakv_attach, 100 uoakv_detach, 101 102 }; 103 104 struct uoak_methods uoakv_methods = { 105 uoakv_dev_print, 106 uoakv_dev_setting 107 }; 108 109 int 110 uoakv_match(struct device *parent, void *match, void *aux) 111 { 112 struct uhidev_attach_arg *uha = aux; 113 114 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 115 return (UMATCH_NONE); 116 117 if (uoakv_lookup(uha->uaa->vendor, uha->uaa->product) == NULL) 118 return UMATCH_NONE; 119 120 return (UMATCH_VENDOR_PRODUCT); 121 } 122 123 void 124 uoakv_attach(struct device *parent, struct device *self, void *aux) 125 { 126 struct uoakv_softc *sc = (struct uoakv_softc *)self; 127 struct usb_attach_arg *uaa = aux; 128 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 129 struct usbd_device *dev = uha->parent->sc_udev; 130 131 struct uoak_softc *scc = &sc->sc_uoak_softc; 132 int i, err, size, repid; 133 void *desc; 134 135 sc->sc_hdev.sc_intr = uoakv_intr; 136 sc->sc_hdev.sc_parent = uha->parent; 137 sc->sc_hdev.sc_report_id = uha->reportid; 138 139 scc->sc_parent = sc; 140 scc->sc_udev = dev; 141 scc->sc_hdev = &sc->sc_hdev; 142 scc->sc_methods = &uoakv_methods; 143 scc->sc_sensordev = &sc->sc_sensordev; 144 145 uhidev_get_report_desc(uha->parent, &desc, &size); 146 repid = uha->reportid; 147 scc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 148 scc->sc_olen = hid_report_size(desc, size, hid_output, repid); 149 scc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 150 151 /* device initialize */ 152 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON); 153 err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKV_SAMPLE_RATE); 154 if (err) { 155 printf("%s: could not set sampling rate. exit\n", 156 sc->sc_hdev.sc_dev.dv_xname); 157 return; 158 } 159 160 /* query and print device setting */ 161 uoak_get_devinfo(scc); 162 uoak_print_devinfo(scc); 163 164 DPRINTF((" config in RAM\n")); 165 uoak_get_setting(scc, OAK_TARGET_RAM); 166 uoak_print_setting(scc, OAK_TARGET_RAM); 167 #ifdef UOAKV_DEBUG 168 DPRINTF((" config in FRASH\n")); 169 uoak_get_setting(scc, OAK_TARGET_FLASH); 170 uoak_print_setting(scc, OAK_TARGET_FLASH); 171 #endif 172 173 /* attach sensor */ 174 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 175 sizeof(sc->sc_sensordev.xname)); 176 for (i = 0; i < OAK_V_MAXSENSORS; i++) 177 uoak_sensor_attach(scc, &sc->sc_sensor[i].v, SENSOR_VOLTS_DC); 178 179 /* start sensor */ 180 sc->sc_sensortask = sensor_task_register(sc, uoakv_refresh, 181 UOAKV_REFRESH_PERIOD); 182 if (sc->sc_sensortask == NULL) { 183 printf(", unable to register update task\n"); 184 return; 185 } 186 sensordev_install(&sc->sc_sensordev); 187 188 err = uhidev_open(&sc->sc_hdev); 189 if (err) { 190 printf("%s: could not open interrupt pipe, quit\n", 191 sc->sc_hdev.sc_dev.dv_xname); 192 return; 193 } 194 scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK); 195 196 DPRINTF(("uoakv_attach: complete\n")); 197 } 198 199 int 200 uoakv_detach(struct device *self, int flags) 201 { 202 struct uoakv_softc *sc = (struct uoakv_softc *)self; 203 struct uoak_softc *scc = &sc->sc_uoak_softc; 204 int i, rv = 0; 205 206 wakeup(&sc->sc_sensortask); 207 sensordev_deinstall(&sc->sc_sensordev); 208 209 for (i = 0; i < OAK_V_MAXSENSORS; i++) 210 uoak_sensor_detach(scc, &sc->sc_sensor[i].v); 211 212 if (sc->sc_sensortask != NULL) 213 sensor_task_unregister(sc->sc_sensortask); 214 215 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 216 uhidev_close(&sc->sc_hdev); 217 218 if (scc->sc_ibuf != NULL) { 219 free(scc->sc_ibuf, M_USBDEV, 0); 220 scc->sc_ibuf = NULL; 221 } 222 223 return (rv); 224 } 225 226 void 227 uoakv_intr(struct uhidev *addr, void *ibuf, u_int len) 228 { 229 struct uoakv_softc *sc = (struct uoakv_softc *)addr; 230 struct uoak_softc *scc = &sc->sc_uoak_softc; 231 int i, idx, frame; 232 int16_t val; 233 234 if (scc->sc_ibuf == NULL) 235 return; 236 237 memcpy(scc->sc_ibuf, ibuf, len); 238 frame = (scc->sc_ibuf[1] << 8) + scc->sc_ibuf[0]; 239 240 for (i = 0; i < OAK_V_MAXSENSORS; i++) { 241 idx = (i + 1) * 2; 242 val = (int16_t)((scc->sc_ibuf[idx+1] << 8) | scc->sc_ibuf[idx]); 243 uoak_sensor_update(&sc->sc_sensor[i].v, val); 244 } 245 } 246 247 void 248 uoakv_refresh(void *arg) 249 { 250 struct uoakv_softc *sc = arg; 251 struct uoak_softc *scc = &sc->sc_uoak_softc; 252 uint8_t led; 253 int i; 254 255 /* blink LED for each cycle */ 256 if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0) 257 DPRINTF(("status query error\n")); 258 if (led == OAK_LED_OFF) 259 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON); 260 else 261 (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF); 262 263 for (i = 0; i < OAK_V_MAXSENSORS; i++) 264 uoak_sensor_refresh(&sc->sc_sensor[i].v, 1000, 0); 265 } 266 267 int 268 uoakv_get_channel_setting(struct uoakv_softc *sc, enum uoak_target target, 269 int ch) 270 { 271 struct uoak_softc *scc = &sc->sc_uoak_softc; 272 uint16_t cmd, result; 273 274 memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 275 scc->sc_rcmd.target = target; 276 scc->sc_rcmd.datasize = 0x2; 277 278 #define OAK_V_CHANNEL_IDX_OFFSET 3 279 cmd = (ch + OAK_V_CHANNEL_IDX_OFFSET); 280 USETW(&scc->sc_rcmd.cmd, cmd); 281 282 if (uoak_get_cmd(scc) < 0) 283 return EIO; 284 285 result = (scc->sc_buf[2] << 8) + scc->sc_buf[1]; 286 sc->sc_sensor[ch].offset[target] = result; 287 288 return 0; 289 } 290 291 int 292 uoakv_get_sensor_setting(struct uoakv_softc *sc, enum uoak_target target) 293 { 294 struct uoak_softc *scc = &sc->sc_uoak_softc; 295 uint8_t result; 296 297 memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 298 scc->sc_rcmd.target = target; 299 scc->sc_rcmd.datasize = 0x1; 300 USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING); 301 302 if (uoak_get_cmd(scc) < 0) 303 return EIO; 304 305 result = scc->sc_buf[1]; 306 sc->sc_inputmode[target] = (result & OAK_V_SENSOR_INPUTMODEMASK); 307 308 return 0; 309 } 310 311 /* device specific functions */ 312 void 313 uoakv_dev_setting(void *parent, enum uoak_target target) 314 { 315 struct uoakv_softc *sc = (struct uoakv_softc *)parent; 316 int i; 317 318 /* get device specific configuration */ 319 (void)uoakv_get_sensor_setting(sc, target); 320 for (i = 0; i < OAK_V_MAXSENSORS; i++) 321 (void)uoakv_get_channel_setting(sc, target, i); 322 } 323 324 void 325 uoakv_dev_print(void *parent, enum uoak_target target) 326 { 327 struct uoakv_softc *sc = (struct uoakv_softc *)parent; 328 int i; 329 330 printf(", %s", 331 (sc->sc_inputmode[target] ? "Psuedo-Diffential" : "Single-Ended")); 332 333 printf(", ADC channel offsets:\n"); 334 printf("%s: ", sc->sc_hdev.sc_dev.dv_xname); 335 for (i = 0; i < OAK_V_MAXSENSORS; i++) 336 printf("ch%02d %2d.%02d, ", i, 337 sc->sc_sensor[i].offset[target] / 100, 338 sc->sc_sensor[i].offset[target] % 100); 339 } 340