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