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