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