1/* $OpenBSD: ugold.c,v 1.28 2024/05/23 03:21:09 jsg 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 */ 71static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 }; 72static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 }; 73static 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 80struct ugold_softc; 81 82struct 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 99const 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 107int ugold_match(struct device *, void *, void *); 108void ugold_attach(struct device *, struct device *, void *); 109int ugold_detach(struct device *, int); 110 111void ugold_setup_sensors(struct ugold_softc *); 112void ugold_intr(struct uhidev *, void *, u_int); 113void ugold_ds75_intr(struct ugold_softc *, uint8_t *, u_int); 114void ugold_si700x_intr(struct ugold_softc *, uint8_t *, u_int); 115void ugold_refresh(void *); 116 117int ugold_issue_cmd(struct ugold_softc *, uint8_t *, int); 118 119struct cfdriver ugold_cd = { 120 NULL, "ugold", DV_DULL 121}; 122 123const struct cfattach ugold_ca = { 124 sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach, 125}; 126 127int 128ugold_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 155void 156ugold_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 210int 211ugold_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 232void 233ugold_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 269static void 270strnvis(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 298static int 299ugold_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 305static void 306ugold_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 331void 332ugold_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 369static int 370ugold_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 396static int 397ugold_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 430static void 431ugold_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 sc->sc_type = UGOLD_TYPE_TEMPERX; 458 descr = "temperx (temperature and humidity)"; 459 goto identified; 460 } 461 } 462 463 /* TEMPerX prefix */ 464 if (sc->sc_model_len >= 8 && 465 memcmp(sc->sc_model, "TEMPerX_", 8) == 0) { 466 if (memcmp(sc->sc_model + 8, "V3.1 ", 16 - 8) == 0 || 467 memcmp(sc->sc_model + 8, "V3.3 ", 16 - 8) == 0) { 468 sc->sc_type = UGOLD_TYPE_TEMPERX; 469 descr = "temperx (temperature and humidity)"; 470 goto identified; 471 } 472 } 473 474 /* TEMPer1F or TEMPer2_ prefixes */ 475 if (sc->sc_model_len >= 16 && 476 memcmp(sc->sc_model, "TEMPer1F_H1V1.5F", 16) == 0) { 477 sc->sc_type = UGOLD_TYPE_SHT1X; 478 descr = "sht1x (temperature and humidity)"; 479 goto identified; 480 } 481 if (sc->sc_model_len >= 16 && 482 (memcmp(sc->sc_model, "TEMPer1F_V4.1\0\0\0", 16) == 0 || 483 memcmp(sc->sc_model, "TEMPer2_V4.1\0\0\0\0", 16) == 0)) { 484 sc->sc_type = UGOLD_TYPE_GOLD; 485 /* 486 * TEMPer1F devices lack the internal sensor, but will never 487 * report data for it, so it will never gets marked as valid. 488 * We thus keep the value of sc_num_sensors unchanged at 2, 489 * and make sure we will only report one single sensor below. 490 */ 491 if (sc->sc_model[6] == '1') 492 nsensors = 1; 493 descr = "gold (temperature only)"; 494 goto identified; 495 } 496 497 /* TEMPerGold prefix */ 498 if (sc->sc_model_len >= 11 && 499 memcmp(sc->sc_model, "TEMPerGold_", 11) == 0) { 500 /* 501 * All V3.something models ought to work, but better be 502 * safe than sorry, and TEMPerHum models have been known 503 * to use slightly different sensors between models. 504 */ 505 if (memcmp(sc->sc_model + 11, "V3.1 ", 16 - 11) == 0 || 506 memcmp(sc->sc_model + 11, "V3.4 ", 16 - 11) == 0 || 507 memcmp(sc->sc_model + 11, "V3.5 ", 16 - 11) == 0) { 508 sc->sc_type = UGOLD_TYPE_GOLD; 509 sc->sc_num_sensors = 1; 510 descr = "gold (temperature only)"; 511 goto identified; 512 } 513 } 514 515 printf("%s: unknown model \"%s\"\n", 516 sc->sc_hdev.sc_dev.dv_xname, model); 517 sc->sc_num_sensors = 0; 518 sc->sc_type = UGOLD_TYPE_INVALID; 519 return; 520 521 identified: 522 ugold_setup_sensors(sc); 523 if (nsensors == 0) 524 nsensors = sc->sc_num_sensors; 525 printf("%s: \"%s\", %d sensor%s type %s\n", sc->sc_hdev.sc_dev.dv_xname, 526 model, nsensors, (nsensors == 1) ? "" : "s", descr); 527 ugold_refresh(sc); 528} 529 530void 531ugold_si700x_intr(struct ugold_softc *sc, uint8_t *buf, u_int len) 532{ 533 int temp, sensor, rhum; 534 535 switch (buf[0]) { 536 case UGOLD_CMD_INIT: 537 if (sc->sc_num_sensors != 0) 538 break; 539 /* XXX some devices report 0x04 here */ 540 sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS); 541 ugold_refresh(sc); 542 break; 543 case UGOLD_CMD_DATA: 544 if (sc->sc_type == UGOLD_TYPE_GOLD) { 545 if (buf[1] == 0x80) 546 sensor = UGOLD_INNER; 547 else if (buf[1] == 0x01) 548 sensor = UGOLD_OUTER; 549 else 550 sensor = -1; 551 } else { 552 if (buf[1] == 0x04 || buf[1] == 0x20 || 553 buf[1] == 0x40 || buf[1] == 0x80) 554 sensor = UGOLD_INNER; 555 else 556 sensor = -1; 557 } 558 if (sensor < 0) { 559 /* unexpected data, ignore */ 560#ifdef UGOLD_DEBUG 561 printf("%s: unexpected sensor id %02x\n", 562 sc->sc_hdev.sc_dev.dv_xname, buf[1]); 563#endif 564 break; 565 } 566 567 temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]); 568 sc->sc_sensor[sensor].value = (temp * 1000) + 273150000; 569 /* 570 * TEMPer1F and TEMPer2 report 200C when the sensor probe is 571 * missing or not plugged correctly. 572 */ 573 if (sc->sc_type == UGOLD_TYPE_GOLD && temp == 200000) 574 sc->sc_sensor[sensor].flags |= SENSOR_FINVALID; 575 else 576 sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID; 577 578 if (sc->sc_type != UGOLD_TYPE_GOLD) { 579 rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp); 580 sc->sc_sensor[UGOLD_HUM].value = rhum; 581 sc->sc_sensor[UGOLD_HUM].flags &= ~SENSOR_FINVALID; 582 } 583 break; 584 default: 585 ugold_si700x_type(sc); 586 break; 587 } 588} 589 590void 591ugold_intr(struct uhidev *addr, void *ibuf, u_int len) 592{ 593 struct ugold_softc *sc = (struct ugold_softc *)addr; 594 uint8_t *buf = ibuf; 595 unsigned long chunk; 596 597#ifdef UGOLD_DEBUG 598 { 599 printf("%s: %u bytes\n", sc->sc_hdev.sc_dev.dv_xname, len); 600 u_int i; 601 for (i = 0; i < len; i++) { 602 if (i != 0 && (i % 8) == 0) 603 printf("\n"); 604 printf("%02x ", buf[i]); 605 } 606 printf("\n"); 607 } 608#endif 609 610 switch (buf[0]) { 611 case UGOLD_CMD_INIT: 612 case UGOLD_CMD_DATA: 613 (*sc->sc_intr)(sc, buf, len); 614 break; 615 default: 616 if (!sc->sc_type) { 617 /* 618 * During initialization, some devices need a bit 619 * more time to submit their identification string. 620 */ 621 if (len == sc->sc_model_len && 622 !memcmp(sc->sc_model, buf, len)) { 623#ifdef UGOLD_DEBUG 624 printf("%s: duplicate string component\n", 625 sc->sc_hdev.sc_dev.dv_xname); 626#endif 627 break; 628 } 629 /* 630 * Exact sensor type is not known yet, type command 631 * returns arbitrary string. 632 */ 633 chunk = ulmin(len, 634 sizeof(sc->sc_model) - 1 - sc->sc_model_len); 635 if (chunk != 0) { 636 memcpy(sc->sc_model + sc->sc_model_len, buf, 637 chunk); 638 sc->sc_model_len += chunk; 639 } 640 if (sc->sc_model_len > 8) { 641 /* should have enough data now */ 642 (*sc->sc_intr)(sc, buf, len); 643 } 644 break; 645 } 646 printf("%s: unknown command 0x%02x\n", 647 sc->sc_hdev.sc_dev.dv_xname, buf[0]); 648 break; 649 } 650} 651 652void 653ugold_refresh(void *arg) 654{ 655 struct ugold_softc *sc = arg; 656 int i; 657 658 /* 659 * Don't waste time talking to the device if we don't understand 660 * its language. 661 */ 662 if (sc->sc_type == UGOLD_TYPE_INVALID) 663 return; 664 665 if (!sc->sc_num_sensors) { 666 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init)); 667 return; 668 } 669 if (!sc->sc_type) { 670 ugold_issue_cmd(sc, cmd_type, sizeof(cmd_type)); 671 return; 672 } 673 674 if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) { 675 for (i = 0; i < sc->sc_num_sensors; i++) 676 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 677 } 678} 679 680int 681ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len) 682{ 683 int actlen; 684 685 actlen = uhidev_set_report_async(sc->sc_hdev.sc_parent, 686 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmd, len); 687 return (actlen != len); 688} 689