1 /* $OpenBSD: uoak_subr.c,v 1.7 2015/05/25 12:53:12 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 seriese sensors: common functions */ 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 "uoak.h" 37 38 #define UOAK_RETRY_DELAY 100 /* 100ms, XXX too long? */ 39 #define UOAK_RESPONSE_DELAY 10 /* 10ms, XXX too short? */ 40 /* 41 * basic procedure to issue command to the OAK device. 42 * 1) check the device is ready to accept command. 43 * if a report of a FEATURE_REPORT request is not start 0xff, 44 * wait for a while, and retry till the reponse start with 0xff. 45 * 2) issue command. (set or get) 46 * 3) if the command will response, wait for a while, and issue 47 * FEATURE_REPORT. leading 0xff indicate the response is valid. 48 * if the first byte is not 0xff, retry. 49 */ 50 int 51 uoak_check_device_ready(struct uoak_softc *sc) 52 { 53 int actlen; 54 55 actlen = uhidev_get_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT, 56 sc->sc_hdev->sc_report_id, &sc->sc_buf, sc->sc_flen); 57 if (actlen != sc->sc_flen) 58 return EIO; 59 60 if (sc->sc_buf[0] != 0xff) 61 return -1; 62 63 return 0; 64 } 65 66 int 67 uoak_set_cmd(struct uoak_softc *sc) 68 { 69 int actlen; 70 sc->sc_rcmd.dir = OAK_SET; 71 72 while (uoak_check_device_ready(sc) < 0) 73 usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY); 74 75 actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT, 76 sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen); 77 if (actlen != sc->sc_flen) 78 return EIO; 79 80 return 0; 81 } 82 83 int 84 uoak_get_cmd(struct uoak_softc *sc) 85 { 86 int actlen; 87 sc->sc_rcmd.dir = OAK_GET; 88 89 /* check the device is ready to request */ 90 while (uoak_check_device_ready(sc) < 0) 91 usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY); 92 93 /* issue request */ 94 actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT, 95 sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen); 96 if (actlen != sc->sc_flen) 97 return EIO; 98 99 /* wait till the device ready to return the request */ 100 while (uoak_check_device_ready(sc) < 0) 101 usbd_delay_ms(sc->sc_udev, UOAK_RESPONSE_DELAY); 102 103 return 0; 104 } 105 106 /* 107 * Functions to access device configurations. 108 * OAK sensor have some storages to store its configuration. 109 * (RAM, FLASH and others) 110 */ 111 int 112 uoak_get_device_name(struct uoak_softc *sc, enum uoak_target target) 113 { 114 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 115 sc->sc_rcmd.target = target; 116 sc->sc_rcmd.datasize = 0x15; 117 USETW(&sc->sc_rcmd.cmd, OAK_CMD_DEVNAME); 118 119 if (uoak_get_cmd(sc) < 0) 120 return EIO; 121 122 strlcpy(sc->sc_config[target].devname, sc->sc_buf+1, 123 sizeof(sc->sc_config[target].devname)); 124 return 0; 125 } 126 127 int 128 uoak_get_report_mode(struct uoak_softc *sc, enum uoak_target target) 129 { 130 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 131 sc->sc_rcmd.target = target; 132 sc->sc_rcmd.datasize = 0x1; 133 USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTMODE); 134 135 if (uoak_get_cmd(sc) < 0) 136 return EIO; 137 138 sc->sc_config[target].report_mode = sc->sc_buf[1]; 139 return 0; 140 } 141 142 int 143 uoak_get_report_rate(struct uoak_softc *sc, enum uoak_target target) 144 { 145 uint16_t result; 146 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 147 sc->sc_rcmd.target = target; 148 sc->sc_rcmd.datasize = 0x2; 149 USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTRATE); 150 151 if (uoak_get_cmd(sc) < 0) 152 return EIO; 153 154 result = (sc->sc_buf[2] << 8) + sc->sc_buf[1]; 155 sc->sc_config[target].report_rate = result; 156 157 return 0; 158 } 159 160 int 161 uoak_get_sample_rate(struct uoak_softc *sc, enum uoak_target target) 162 { 163 uint16_t result; 164 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 165 sc->sc_rcmd.target = target; 166 sc->sc_rcmd.datasize = 0x2; 167 USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE); 168 169 if (uoak_get_cmd(sc) < 0) 170 return EIO; 171 172 result = (sc->sc_buf[2] << 8) + sc->sc_buf[1]; 173 sc->sc_config[target].sample_rate = result; 174 175 return 0; 176 } 177 178 int 179 uoak_set_sample_rate(struct uoak_softc *sc, enum uoak_target target, int rate) 180 { 181 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 182 sc->sc_rcmd.target = target; 183 sc->sc_rcmd.datasize = 0x2; 184 USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE); 185 186 #if 0 187 sc->sc_rcmd.val[0] = (uint8_t)(rate & 0xff); 188 sc->sc_rcmd.val[1] = (uint8_t)((rate >> 8) & 0xff) 189 #else 190 USETW(sc->sc_rcmd.val, rate); 191 #endif 192 193 if (uoak_set_cmd(sc) < 0) 194 return EIO; 195 196 return 0; 197 } 198 199 /* 200 * LED I/O 201 */ 202 int 203 uoak_led_status(struct uoak_softc *sc, enum uoak_target target, uint8_t *mode) 204 { 205 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 206 sc->sc_rcmd.target = target; 207 sc->sc_rcmd.datasize = 0x1; 208 USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE); 209 210 if (uoak_get_cmd(sc) < 0) 211 return EIO; 212 213 *mode = sc->sc_buf[1]; 214 return 0; 215 } 216 217 int 218 uoak_led_ctrl(struct uoak_softc *sc, enum uoak_target target, uint8_t mode) 219 { 220 memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd)); 221 222 sc->sc_rcmd.target = target; 223 sc->sc_rcmd.datasize = 0x1; 224 USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE); 225 sc->sc_rcmd.val[0] = mode; 226 227 return uoak_set_cmd(sc); 228 } 229 230 /* device setting: query and pretty print */ 231 void 232 uoak_get_devinfo(struct uoak_softc *sc) 233 { 234 /* get device serial# */ 235 usbd_fill_deviceinfo(sc->sc_udev, &sc->sc_udi, 1); 236 } 237 238 void 239 uoak_get_setting(struct uoak_softc *sc, enum uoak_target target) 240 { 241 /* get device level */ 242 (void)uoak_get_device_name(sc, target); 243 244 /* get global sensor configuration */ 245 (void)uoak_get_report_mode(sc, target); 246 (void)uoak_get_sample_rate(sc, target); 247 (void)uoak_get_report_rate(sc, target); 248 249 /* get device spcecific information */ 250 if (sc->sc_methods->dev_setting != NULL) 251 sc->sc_methods->dev_setting(sc->sc_parent, target); 252 } 253 254 void 255 uoak_print_devinfo(struct uoak_softc *sc) 256 { 257 printf(": serial %s", sc->sc_udi.udi_serial); 258 } 259 260 void 261 uoak_print_setting(struct uoak_softc *sc, enum uoak_target target) 262 { 263 switch (sc->sc_config[target].report_mode) { 264 case OAK_REPORTMODE_AFTERSAMPLING: 265 printf(" sampling %dms", 266 sc->sc_config[target].sample_rate); 267 break; 268 case OAK_REPORTMODE_AFTERCHANGE: 269 printf(" reports changes"); 270 break; 271 case OAK_REPORTMODE_FIXEDRATE: 272 printf(" rate %dms", 273 sc->sc_config[target].report_rate); 274 break; 275 default: 276 printf(" unknown sampling"); 277 break; 278 } 279 280 /* print device spcecific information */ 281 if (sc->sc_methods->dev_print != NULL) 282 sc->sc_methods->dev_print(sc->sc_parent, target); 283 printf("\n"); 284 } 285 286 void 287 uoak_sensor_attach(struct uoak_softc *sc, struct uoak_sensor *s, 288 enum sensor_type type) 289 { 290 if (s == NULL) 291 return; 292 293 s->avg.type = type; 294 s->max.type = type; 295 s->min.type = type; 296 s->avg.flags |= SENSOR_FINVALID; 297 s->max.flags |= SENSOR_FINVALID; 298 s->min.flags |= SENSOR_FINVALID; 299 300 (void)snprintf(s->avg.desc, sizeof(s->avg.desc), 301 "avg(#%s)", sc->sc_udi.udi_serial); 302 (void)snprintf(s->max.desc, sizeof(s->max.desc), 303 "max(#%s)", sc->sc_udi.udi_serial); 304 (void)snprintf(s->min.desc, sizeof(s->min.desc), 305 "min(#%s)", sc->sc_udi.udi_serial); 306 307 sensor_attach(sc->sc_sensordev, &s->avg); 308 sensor_attach(sc->sc_sensordev, &s->max); 309 sensor_attach(sc->sc_sensordev, &s->min); 310 } 311 312 void 313 uoak_sensor_detach(struct uoak_softc *sc, struct uoak_sensor *s) 314 { 315 if (s == NULL) 316 return; 317 318 sensor_attach(sc->sc_sensordev, &s->avg); 319 sensor_attach(sc->sc_sensordev, &s->max); 320 sensor_attach(sc->sc_sensordev, &s->min); 321 } 322 323 void 324 uoak_sensor_update(struct uoak_sensor *s, int val) 325 { 326 if (s == NULL) 327 return; 328 329 /* reset */ 330 if (s->count == 0) { 331 s->vmax = s->vmin = s->vavg = val; 332 s->count++; 333 return; 334 } 335 336 /* update min/max */ 337 if (val > s->vmax) 338 s->vmax = val; 339 else if (val < s->vmin) 340 s->vmin = val; 341 342 /* calc average */ 343 s->vavg = (s->vavg * s->count + val) / (s->count + 1); 344 345 s->count++; 346 } 347 348 void 349 uoak_sensor_refresh(struct uoak_sensor *s, int mag, int offset) 350 { 351 if (s == NULL) 352 return; 353 /* update value */ 354 s->avg.value = s->vavg * mag + offset; 355 s->max.value = s->vmax * mag + offset; 356 s->min.value = s->vmin * mag + offset; 357 358 /* update flag */ 359 s->avg.flags &= ~SENSOR_FINVALID; 360 s->max.flags &= ~SENSOR_FINVALID; 361 s->min.flags &= ~SENSOR_FINVALID; 362 s->count = 0; 363 } 364 365