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