1 /* $OpenBSD: ugold.c,v 1.29 2024/07/27 17:31:49 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Takayoshi SASANO <uaa@openbsd.org> 5 * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org> 6 * Copyright (c) 2015 Joerg Jung <jung@openbsd.org> 7 * Copyright (c) 2023 Miodrag Vallat. 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * Driver for Microdia's HID based TEMPer and TEMPerHUM temperature and 24 * humidity sensors 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/device.h> 30 #include <sys/sensors.h> 31 32 #include <dev/usb/usb.h> 33 #include <dev/usb/usbhid.h> 34 35 #include <dev/usb/usbdi.h> 36 #include <dev/usb/usbdevs.h> 37 #include <dev/usb/uhidev.h> 38 39 #define UGOLD_INNER 0 40 #define UGOLD_OUTER 1 41 #define UGOLD_HUM 1 42 #define UGOLD_MAX_SENSORS 2 43 44 #define UGOLD_CMD_DATA 0x80 45 #define UGOLD_CMD_INIT 0x82 46 47 #define UGOLD_TYPE_INVALID -1 48 #define UGOLD_TYPE_SI7005 1 49 #define UGOLD_TYPE_SI7006 2 50 #define UGOLD_TYPE_SHT1X 3 51 #define UGOLD_TYPE_GOLD 4 52 #define UGOLD_TYPE_TEMPERX 5 53 #define UGOLD_TYPE_DS75 6 54 55 /* 56 * This driver uses three known commands for the TEMPer and TEMPerHUM 57 * devices. 58 * 59 * The first byte of the answer corresponds to the command and the 60 * second one seems to be the size (in bytes) of the answer. 61 * 62 * The device always sends 8 bytes and if the length of the answer 63 * is less than that, it just leaves the last bytes untouched. That 64 * is why most of the time the last n bytes of the answers are the 65 * same. 66 * 67 * The type command below seems to generate two answers with a 68 * string corresponding to the device, for example: 69 * 'TEMPer1F' and '1.1Per1F' (here Per1F is repeated). 70 */ 71 static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 }; 72 static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 }; 73 static uint8_t cmd_type[8] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 }; 74 /* 75 * The following command is also recognized and reports some kind of status 76 * byte (i.e. 87 xx 00 00 00 00 00 00). 77 { 0x01, 0x87, 0xee, 0x01, 0x00, 0x00, 0x00, 0x00 }; 78 */ 79 80 struct ugold_softc; 81 82 struct ugold_softc { 83 struct uhidev sc_hdev; 84 struct usbd_device *sc_udev; 85 86 int sc_num_sensors; 87 int sc_type; 88 89 char sc_model[16 + 1]; 90 unsigned int sc_model_len; 91 92 struct ksensor sc_sensor[UGOLD_MAX_SENSORS]; 93 struct ksensordev sc_sensordev; 94 struct sensor_task *sc_sensortask; 95 96 void (*sc_intr)(struct ugold_softc *, uint8_t *, u_int); 97 }; 98 99 const struct usb_devno ugold_devs[] = { 100 { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER }, 101 { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPERHUM }, 102 { USB_VENDOR_PCSENSORS, USB_PRODUCT_PCSENSORS_TEMPER }, 103 { USB_VENDOR_RDING, USB_PRODUCT_RDING_TEMPER }, 104 { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_TEMPER }, 105 }; 106 107 int ugold_match(struct device *, void *, void *); 108 void ugold_attach(struct device *, struct device *, void *); 109 int ugold_detach(struct device *, int); 110 111 void ugold_setup_sensors(struct ugold_softc *); 112 void ugold_intr(struct uhidev *, void *, u_int); 113 void ugold_ds75_intr(struct ugold_softc *, uint8_t *, u_int); 114 void ugold_si700x_intr(struct ugold_softc *, uint8_t *, u_int); 115 void ugold_refresh(void *); 116 117 int ugold_issue_cmd(struct ugold_softc *, uint8_t *, int); 118 119 struct cfdriver ugold_cd = { 120 NULL, "ugold", DV_DULL 121 }; 122 123 const struct cfattach ugold_ca = { 124 sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach, 125 }; 126 127 int 128 ugold_match(struct device *parent, void *match, void *aux) 129 { 130 struct uhidev_attach_arg *uha = aux; 131 int size; 132 void *desc; 133 134 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 135 return (UMATCH_NONE); 136 137 if (usb_lookup(ugold_devs, uha->uaa->vendor, uha->uaa->product) == NULL) 138 return (UMATCH_NONE); 139 140 /* 141 * XXX Only match the sensor interface. 142 * 143 * Does it make sense to attach various uhidev(4) to these 144 * non-standard HID devices? 145 */ 146 uhidev_get_report_desc(uha->parent, &desc, &size); 147 if (hid_is_collection(desc, size, uha->reportid, 148 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 149 return (UMATCH_NONE); 150 151 return (UMATCH_VENDOR_PRODUCT); 152 153 } 154 155 void 156 ugold_attach(struct device *parent, struct device *self, void *aux) 157 { 158 struct ugold_softc *sc = (struct ugold_softc *)self; 159 struct uhidev_attach_arg *uha = aux; 160 int size, repid; 161 void *desc; 162 163 sc->sc_udev = uha->parent->sc_udev; 164 sc->sc_hdev.sc_parent = uha->parent; 165 sc->sc_hdev.sc_report_id = uha->reportid; 166 sc->sc_hdev.sc_intr = ugold_intr; 167 switch (uha->uaa->product) { 168 case USB_PRODUCT_MICRODIA_TEMPER: 169 sc->sc_intr = ugold_ds75_intr; 170 break; 171 case USB_PRODUCT_MICRODIA_TEMPERHUM: 172 case USB_PRODUCT_PCSENSORS_TEMPER: 173 case USB_PRODUCT_RDING_TEMPER: 174 case USB_PRODUCT_WCH2_TEMPER: 175 sc->sc_intr = ugold_si700x_intr; 176 break; 177 default: 178 printf(", unknown product\n"); 179 return; 180 } 181 182 uhidev_get_report_desc(uha->parent, &desc, &size); 183 repid = uha->reportid; 184 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 185 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 186 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 187 188 if (uhidev_open(&sc->sc_hdev)) { 189 printf(", unable to open interrupt pipe\n"); 190 return; 191 } 192 193 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 194 sizeof(sc->sc_sensordev.xname)); 195 196 /* 0.166Hz */ 197 sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6); 198 if (sc->sc_sensortask == NULL) { 199 printf(", unable to register update task\n"); 200 return; 201 } 202 printf("\n"); 203 204 /* speed up sensor identification */ 205 ugold_refresh(sc); 206 207 sensordev_install(&sc->sc_sensordev); 208 } 209 210 int 211 ugold_detach(struct device *self, int flags) 212 { 213 struct ugold_softc *sc = (struct ugold_softc *)self; 214 int i; 215 216 if (sc->sc_sensortask != NULL) { 217 sensor_task_unregister(sc->sc_sensortask); 218 sensordev_deinstall(&sc->sc_sensordev); 219 } 220 221 if (sc->sc_type != UGOLD_TYPE_INVALID) { 222 for (i = 0; i < sc->sc_num_sensors; i++) 223 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]); 224 } 225 226 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 227 uhidev_close(&sc->sc_hdev); 228 229 return (0); 230 } 231 232 void 233 ugold_setup_sensors(struct ugold_softc *sc) 234 { 235 int i; 236 237 switch (sc->sc_type) { 238 default: 239 return; 240 case UGOLD_TYPE_SI7005: 241 case UGOLD_TYPE_SI7006: 242 case UGOLD_TYPE_SHT1X: 243 case UGOLD_TYPE_TEMPERX: 244 /* 1 temperature and 1 humidity sensor */ 245 sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP; 246 strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner", 247 sizeof(sc->sc_sensor[UGOLD_INNER].desc)); 248 sc->sc_sensor[UGOLD_HUM].type = SENSOR_HUMIDITY; 249 strlcpy(sc->sc_sensor[UGOLD_HUM].desc, "RH", 250 sizeof(sc->sc_sensor[UGOLD_HUM].desc)); 251 break; 252 case UGOLD_TYPE_GOLD: 253 case UGOLD_TYPE_DS75: 254 /* up to 2 temperature sensors */ 255 sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP; 256 strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner", 257 sizeof(sc->sc_sensor[UGOLD_INNER].desc)); 258 sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP; 259 strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer", 260 sizeof(sc->sc_sensor[UGOLD_OUTER].desc)); 261 break; 262 } 263 for (i = 0; i < sc->sc_num_sensors; i++) { 264 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 265 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 266 } 267 } 268 269 static void 270 strnvis(char *dst, const char *src, size_t siz) 271 { 272 char *start, *end; 273 int c; 274 275 for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { 276 if (c >= 0x20 && c <= 0x7f) { 277 if (c == '\\') { 278 /* need space for the extra '\\' */ 279 if (dst + 2 > end) 280 break; 281 *dst++ = '\\'; 282 } 283 *dst++ = c; 284 } else { 285 if (dst + 4 > end) 286 break; 287 *dst++ = '\\'; 288 *dst++ = ((u_char)c >> 6 & 07) + '0'; 289 *dst++ = ((u_char)c >> 3 & 07) + '0'; 290 *dst++ = ((u_char)c & 07) + '0'; 291 } 292 src++; 293 } 294 if (siz > 0) 295 *dst = '\0'; 296 } 297 298 static int 299 ugold_ds75_temp(uint8_t msb, uint8_t lsb) 300 { 301 /* DS75 12bit precision mode: 0.0625 degrees Celsius ticks */ 302 return (((msb * 100) + ((lsb >> 4) * 25 / 4)) * 10000) + 273150000; 303 } 304 305 static void 306 ugold_ds75_type(struct ugold_softc *sc) 307 { 308 char model[4 * sizeof(sc->sc_model) + 1]; 309 310 strnvis(model, sc->sc_model, sizeof model); 311 312 if (memcmp(sc->sc_model, "TEMPer1F", 8) == 0 || 313 memcmp(sc->sc_model, "TEMPer2F", 8) == 0 || 314 memcmp(sc->sc_model, "TEMPerF1", 8) == 0) { 315 sc->sc_type = UGOLD_TYPE_DS75; 316 ugold_setup_sensors(sc); 317 printf("%s: \"%s\", %d sensor%s" 318 " type ds75/12bit (temperature)\n", 319 sc->sc_hdev.sc_dev.dv_xname, model, sc->sc_num_sensors, 320 (sc->sc_num_sensors == 1) ? "" : "s"); 321 ugold_refresh(sc); 322 return; 323 } 324 325 printf("%s: unknown model \"%s\"\n", 326 sc->sc_hdev.sc_dev.dv_xname, model); 327 sc->sc_num_sensors = 0; 328 sc->sc_type = UGOLD_TYPE_INVALID; 329 } 330 331 void 332 ugold_ds75_intr(struct ugold_softc *sc, uint8_t *buf, u_int len) 333 { 334 int temp; 335 336 switch (buf[0]) { 337 case UGOLD_CMD_INIT: 338 if (sc->sc_num_sensors != 0) 339 break; 340 sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS) /* XXX */; 341 ugold_refresh(sc); 342 break; 343 case UGOLD_CMD_DATA: 344 switch (buf[1]) { 345 case 4: 346 temp = ugold_ds75_temp(buf[4], buf[5]); 347 sc->sc_sensor[UGOLD_OUTER].value = temp; 348 sc->sc_sensor[UGOLD_OUTER].flags &= ~SENSOR_FINVALID; 349 /* FALLTHROUGH */ 350 case 2: 351 temp = ugold_ds75_temp(buf[2], buf[3]); 352 sc->sc_sensor[UGOLD_INNER].value = temp; 353 sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID; 354 break; 355 default: 356 #ifdef UGOLD_DEBUG 357 printf("%s: invalid data length (%d bytes)\n", 358 sc->sc_hdev.sc_dev.dv_xname, buf[1]); 359 #endif 360 break; 361 } 362 break; 363 default: 364 ugold_ds75_type(sc); 365 break; 366 } 367 } 368 369 static int 370 ugold_si700x_temp(int type, uint8_t msb, uint8_t lsb) 371 { 372 int temp = msb * 256 + lsb; 373 374 switch (type) { /* convert to mdegC */ 375 case UGOLD_TYPE_SI7005: /* 14bit 32 codes per degC 0x0000 = -50 degC */ 376 temp = (((temp & 0x3fff) * 1000) / 32) - 50000; 377 break; 378 case UGOLD_TYPE_SI7006: /* 14bit and status bit */ 379 temp = (((temp & ~3) * 21965) / 8192) - 46850; 380 break; 381 case UGOLD_TYPE_SHT1X: 382 temp = (temp * 1000) / 256; 383 break; 384 case UGOLD_TYPE_GOLD: 385 case UGOLD_TYPE_TEMPERX: 386 /* temp = temp / 100 to get degC, then * 1000 to get mdegC */ 387 temp = temp * 10; 388 break; 389 default: 390 temp = 0; 391 } 392 393 return temp; 394 } 395 396 static int 397 ugold_si700x_rhum(int type, uint8_t msb, uint8_t lsb, int temp) 398 { 399 int rhum = msb * 256 + lsb; 400 401 switch (type) { /* convert to m%RH */ 402 case UGOLD_TYPE_SI7005: /* 12bit 16 codes per %RH 0x0000 = -24 %RH */ 403 rhum = (((rhum & 0x0fff) * 1000) / 16) - 24000; 404 #if 0 /* todo: linearization and temperature compensation */ 405 rhum -= -0.00393 * rhum * rhum + 0.4008 * rhum - 4.7844; 406 rhum += (temp - 30) * (0.00237 * rhum + 0.1973); 407 #endif 408 break; 409 case UGOLD_TYPE_SI7006: /* 14bit and status bit */ 410 rhum = (((rhum & ~3) * 15625) / 8192) - 6000; 411 break; 412 case UGOLD_TYPE_SHT1X: /* 16 bit */ 413 rhum = rhum * 32; 414 break; 415 case UGOLD_TYPE_TEMPERX: 416 rhum = rhum * 10; 417 break; 418 default: 419 rhum = 0; 420 } 421 422 /* limit the humidity to valid values */ 423 if (rhum < 0) 424 rhum = 0; 425 else if (rhum > 100000) 426 rhum = 100000; 427 return rhum; 428 } 429 430 static void 431 ugold_si700x_type(struct ugold_softc *sc) 432 { 433 char model[4 * sizeof(sc->sc_model) + 1]; 434 const char *descr; 435 int nsensors = 0; 436 437 strnvis(model, sc->sc_model, sizeof model); 438 439 /* TEMPerHUM prefix */ 440 if (sc->sc_model_len >= 9 && 441 memcmp(sc->sc_model, "TEMPerHum", 9) == 0) { 442 if (memcmp(sc->sc_model + 9, "M12V1.0", 16 - 9) == 0) { 443 sc->sc_type = UGOLD_TYPE_SI7005; 444 descr = "si7005 (temperature and humidity)"; 445 goto identified; 446 } 447 if (memcmp(sc->sc_model + 9, "M12V1.2", 16 - 9) == 0) { 448 sc->sc_type = UGOLD_TYPE_SI7006; 449 descr = "si7006 (temperature and humidity)"; 450 goto identified; 451 } 452 } 453 if (sc->sc_model_len >= 9 && 454 memcmp(sc->sc_model, "TEMPerHUM", 9) == 0) { 455 if (memcmp(sc->sc_model + 9, "_V3.9 ", 16 - 9) == 0 || 456 memcmp(sc->sc_model + 9, "_V4.0 ", 16 - 9) == 0 || 457 memcmp(sc->sc_model + 9, "_V4.1\0\0", 16 - 9) == 0) { 458 sc->sc_type = UGOLD_TYPE_TEMPERX; 459 descr = "temperx (temperature and humidity)"; 460 goto identified; 461 } 462 } 463 464 /* TEMPerX prefix */ 465 if (sc->sc_model_len >= 8 && 466 memcmp(sc->sc_model, "TEMPerX_", 8) == 0) { 467 if (memcmp(sc->sc_model + 8, "V3.1 ", 16 - 8) == 0 || 468 memcmp(sc->sc_model + 8, "V3.3 ", 16 - 8) == 0) { 469 sc->sc_type = UGOLD_TYPE_TEMPERX; 470 descr = "temperx (temperature and humidity)"; 471 goto identified; 472 } 473 } 474 475 /* TEMPer1F or TEMPer2_ prefixes */ 476 if (sc->sc_model_len >= 16 && 477 memcmp(sc->sc_model, "TEMPer1F_H1V1.5F", 16) == 0) { 478 sc->sc_type = UGOLD_TYPE_SHT1X; 479 descr = "sht1x (temperature and humidity)"; 480 goto identified; 481 } 482 if (sc->sc_model_len >= 16 && 483 (memcmp(sc->sc_model, "TEMPer1F_V4.1\0\0\0", 16) == 0 || 484 memcmp(sc->sc_model, "TEMPer2_V4.1\0\0\0\0", 16) == 0)) { 485 sc->sc_type = UGOLD_TYPE_GOLD; 486 /* 487 * TEMPer1F devices lack the internal sensor, but will never 488 * report data for it, so it will never get marked as valid. 489 * We thus keep the value of sc_num_sensors unchanged at 2, 490 * and make sure we will only report one single sensor below. 491 */ 492 if (sc->sc_model[6] == '1') 493 nsensors = 1; 494 descr = "gold (temperature only)"; 495 goto identified; 496 } 497 498 /* TEMPerGold prefix */ 499 if (sc->sc_model_len >= 11 && 500 memcmp(sc->sc_model, "TEMPerGold_", 11) == 0) { 501 /* 502 * All V3.something models ought to work, but better be 503 * safe than sorry, and TEMPerHum models have been known 504 * to use slightly different sensors between models. 505 */ 506 if (memcmp(sc->sc_model + 11, "V3.1 ", 16 - 11) == 0 || 507 memcmp(sc->sc_model + 11, "V3.4 ", 16 - 11) == 0 || 508 memcmp(sc->sc_model + 11, "V3.5 ", 16 - 11) == 0) { 509 sc->sc_type = UGOLD_TYPE_GOLD; 510 sc->sc_num_sensors = 1; 511 descr = "gold (temperature only)"; 512 goto identified; 513 } 514 } 515 516 printf("%s: unknown model \"%s\"\n", 517 sc->sc_hdev.sc_dev.dv_xname, model); 518 sc->sc_num_sensors = 0; 519 sc->sc_type = UGOLD_TYPE_INVALID; 520 return; 521 522 identified: 523 ugold_setup_sensors(sc); 524 if (nsensors == 0) 525 nsensors = sc->sc_num_sensors; 526 printf("%s: \"%s\", %d sensor%s type %s\n", sc->sc_hdev.sc_dev.dv_xname, 527 model, nsensors, (nsensors == 1) ? "" : "s", descr); 528 ugold_refresh(sc); 529 } 530 531 void 532 ugold_si700x_intr(struct ugold_softc *sc, uint8_t *buf, u_int len) 533 { 534 int temp, sensor, rhum; 535 536 switch (buf[0]) { 537 case UGOLD_CMD_INIT: 538 if (sc->sc_num_sensors != 0) 539 break; 540 /* XXX some devices report 0x04 here */ 541 sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS); 542 ugold_refresh(sc); 543 break; 544 case UGOLD_CMD_DATA: 545 if (sc->sc_type == UGOLD_TYPE_GOLD) { 546 if (buf[1] == 0x80) 547 sensor = UGOLD_INNER; 548 else if (buf[1] == 0x01) 549 sensor = UGOLD_OUTER; 550 else 551 sensor = -1; 552 } else { 553 if (buf[1] == 0x04 || buf[1] == 0x20 || 554 buf[1] == 0x40 || buf[1] == 0x80) 555 sensor = UGOLD_INNER; 556 else 557 sensor = -1; 558 } 559 if (sensor < 0) { 560 /* unexpected data, ignore */ 561 #ifdef UGOLD_DEBUG 562 printf("%s: unexpected sensor id %02x\n", 563 sc->sc_hdev.sc_dev.dv_xname, buf[1]); 564 #endif 565 break; 566 } 567 568 temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]); 569 sc->sc_sensor[sensor].value = (temp * 1000) + 273150000; 570 /* 571 * TEMPer1F and TEMPer2 report 200C when the sensor probe is 572 * missing or not plugged correctly. 573 */ 574 if (sc->sc_type == UGOLD_TYPE_GOLD && temp == 200000) 575 sc->sc_sensor[sensor].flags |= SENSOR_FINVALID; 576 else 577 sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID; 578 579 if (sc->sc_type != UGOLD_TYPE_GOLD) { 580 rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp); 581 sc->sc_sensor[UGOLD_HUM].value = rhum; 582 sc->sc_sensor[UGOLD_HUM].flags &= ~SENSOR_FINVALID; 583 } 584 break; 585 default: 586 ugold_si700x_type(sc); 587 break; 588 } 589 } 590 591 void 592 ugold_intr(struct uhidev *addr, void *ibuf, u_int len) 593 { 594 struct ugold_softc *sc = (struct ugold_softc *)addr; 595 uint8_t *buf = ibuf; 596 unsigned long chunk; 597 598 #ifdef UGOLD_DEBUG 599 { 600 printf("%s: %u bytes\n", sc->sc_hdev.sc_dev.dv_xname, len); 601 u_int i; 602 for (i = 0; i < len; i++) { 603 if (i != 0 && (i % 8) == 0) 604 printf("\n"); 605 printf("%02x ", buf[i]); 606 } 607 printf("\n"); 608 } 609 #endif 610 611 switch (buf[0]) { 612 case UGOLD_CMD_INIT: 613 case UGOLD_CMD_DATA: 614 (*sc->sc_intr)(sc, buf, len); 615 break; 616 default: 617 if (!sc->sc_type) { 618 /* 619 * During initialization, some devices need a bit 620 * more time to submit their identification string. 621 */ 622 if (len == sc->sc_model_len && 623 !memcmp(sc->sc_model, buf, len)) { 624 #ifdef UGOLD_DEBUG 625 printf("%s: duplicate string component\n", 626 sc->sc_hdev.sc_dev.dv_xname); 627 #endif 628 break; 629 } 630 /* 631 * Exact sensor type is not known yet, type command 632 * returns arbitrary string. 633 */ 634 chunk = ulmin(len, 635 sizeof(sc->sc_model) - 1 - sc->sc_model_len); 636 if (chunk != 0) { 637 memcpy(sc->sc_model + sc->sc_model_len, buf, 638 chunk); 639 sc->sc_model_len += chunk; 640 } 641 if (sc->sc_model_len > 8) { 642 /* should have enough data now */ 643 (*sc->sc_intr)(sc, buf, len); 644 } 645 break; 646 } 647 printf("%s: unknown command 0x%02x\n", 648 sc->sc_hdev.sc_dev.dv_xname, buf[0]); 649 break; 650 } 651 } 652 653 void 654 ugold_refresh(void *arg) 655 { 656 struct ugold_softc *sc = arg; 657 int i; 658 659 /* 660 * Don't waste time talking to the device if we don't understand 661 * its language. 662 */ 663 if (sc->sc_type == UGOLD_TYPE_INVALID) 664 return; 665 666 if (!sc->sc_num_sensors) { 667 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init)); 668 return; 669 } 670 if (!sc->sc_type) { 671 ugold_issue_cmd(sc, cmd_type, sizeof(cmd_type)); 672 return; 673 } 674 675 if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) { 676 for (i = 0; i < sc->sc_num_sensors; i++) 677 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 678 } 679 } 680 681 int 682 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len) 683 { 684 int actlen; 685 686 actlen = uhidev_set_report_async(sc->sc_hdev.sc_parent, 687 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmd, len); 688 return (actlen != len); 689 } 690