1 /* $OpenBSD: uthum.c,v 1.40 2024/05/23 03:21:09 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2010 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 /* Driver for HID based TEMPer series Temperature(/Humidity) sensors */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/device.h> 24 #include <sys/sensors.h> 25 26 #include <dev/usb/usb.h> 27 #include <dev/usb/usbhid.h> 28 #include <dev/usb/usbdi.h> 29 #include <dev/usb/usbdevs.h> 30 #include <dev/usb/uhidev.h> 31 32 #ifdef UTHUM_DEBUG 33 #define DPRINTF(x) do { printf x; } while (0) 34 #else 35 #define DPRINTF(x) 36 #endif 37 38 /* Device types */ 39 #define UTHUM_TYPE_TEMPERHUM 0x535a 40 #define UTHUM_TYPE_TEMPERHUM_2 0x575a /* alternative TEMPerHUM */ 41 #define UTHUM_TYPE_TEMPER1 0x5758 /* TEMPer1 and HID TEMPer */ 42 #define UTHUM_TYPE_TEMPER2 0x5759 43 #define UTHUM_TYPE_TEMPERNTC 0x575b 44 #define UTHUM_TYPE_TEMPERHUM_3 0x5f5a 45 #define UTHUM_TYPE_UNKNOWN 0xffff 46 47 /* Common */ 48 #define UTHUM_CAL_OFFSET 0x14 49 #define UTHUM_MAX_SENSORS 2 50 #define CMD_DEVTYPE 0x52 51 #define DEVTYPE_EOF 0x53 52 53 /* query commands */ 54 #define CMD_GETDATA_NTC 0x41 /* TEMPerNTC NTC part */ 55 #define CMD_RESET0 0x43 /* TEMPer, TEMPer[12], TEMPerNTC */ 56 #define CMD_RESET1 0x44 /* TEMPer, TEMPer[12] */ 57 #define CMD_GETDATA 0x48 /* TEMPerHUM */ 58 #define CMD_GETDATA_OUTER 0x53 /* TEMPer, TEMPer[12], TEMPerNTC */ 59 #define CMD_GETDATA_INNER 0x54 /* TEMPer, TEMPer[12], TEMPerNTC */ 60 #define CMD_GETDATA_EOF 0x31 61 #define CMD_GETDATA_EOF2 0xaa 62 63 /* temperntc mode */ 64 #define TEMPERNTC_MODE_BASE 0x61 /* 0x61 - 0x68 */ 65 #define TEMPERNTC_MODE_MAX 0x68 66 #define CMD_TEMPERNTC_MODE_DONE 0x69 67 #define UTHUM_NTC_MIN_THRESHOLD 0xb300 68 #define UTHUM_NTC_MAX_THRESHOLD 0xf200 69 70 /* sensor name */ 71 #define UTHUM_TEMPER_INNER 0 72 #define UTHUM_TEMPER_OUTER 1 73 #define UTHUM_TEMPER_NTC 1 74 #define UTHUM_TEMPERHUM_TEMP 0 75 #define UTHUM_TEMPERHUM_HUM 1 76 77 enum uthum_sensor_type { 78 UTHUM_SENSOR_UNKNOWN, 79 UTHUM_SENSOR_SHT1X, 80 UTHUM_SENSOR_DS75, 81 UTHUM_SENSOR_NTC, 82 UTHUM_SENSOR_MAXTYPES, 83 }; 84 85 static const char * const uthum_sensor_type_s[UTHUM_SENSOR_MAXTYPES] = { 86 "unknown", 87 "sht1x", 88 "ds75/12bit", 89 "NTC" 90 }; 91 92 static uint8_t cmd_issue[8] = 93 { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x02, 0x00 }; 94 static uint8_t cmd_query[8] = 95 { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x01, 0x00 }; 96 97 struct uthum_sensor { 98 struct ksensor sensor; 99 int cal_offset; /* mC or m%RH */ 100 int attached; 101 enum uthum_sensor_type dev_type; 102 int cur_state; /* for TEMPerNTC */ 103 }; 104 105 struct uthum_softc { 106 struct uhidev sc_hdev; 107 struct usbd_device *sc_udev; 108 int sc_device_type; 109 int sc_num_sensors; 110 111 /* uhidev parameters */ 112 size_t sc_flen; /* feature report length */ 113 size_t sc_ilen; /* input report length */ 114 size_t sc_olen; /* output report length */ 115 116 /* sensor framework */ 117 struct uthum_sensor sc_sensor[UTHUM_MAX_SENSORS]; 118 struct ksensordev sc_sensordev; 119 struct sensor_task *sc_sensortask; 120 }; 121 122 const struct usb_devno uthum_devs[] = { 123 /* XXX: various TEMPer variants are using same VID/PID */ 124 { USB_VENDOR_TENX, USB_PRODUCT_TENX_TEMPER}, 125 }; 126 #define uthum_lookup(v, p) usb_lookup(uthum_devs, v, p) 127 128 int uthum_match(struct device *, void *, void *); 129 void uthum_attach(struct device *, struct device *, void *); 130 int uthum_detach(struct device *, int); 131 132 int uthum_issue_cmd(struct uthum_softc *, uint8_t, int); 133 int uthum_read_data(struct uthum_softc *, uint8_t, uint8_t *, size_t, int); 134 int uthum_check_device_info(struct uthum_softc *); 135 void uthum_reset_device(struct uthum_softc *); 136 void uthum_setup_sensors(struct uthum_softc *); 137 138 void uthum_intr(struct uhidev *, void *, u_int); 139 void uthum_refresh(void *); 140 void uthum_refresh_temper(struct uthum_softc *, int); 141 void uthum_refresh_temperhum(struct uthum_softc *); 142 void uthum_refresh_temperntc(struct uthum_softc *, int); 143 144 int uthum_ntc_getdata(struct uthum_softc *, int *); 145 int uthum_ntc_tuning(struct uthum_softc *, int, int *); 146 int64_t uthum_ntc_temp(int64_t, int); 147 int uthum_sht1x_temp(uint8_t, uint8_t); 148 int uthum_sht1x_rh(uint8_t, uint8_t, int); 149 int uthum_ds75_temp(uint8_t, uint8_t); 150 void uthum_print_sensorinfo(struct uthum_softc *, int); 151 152 struct cfdriver uthum_cd = { 153 NULL, "uthum", DV_DULL 154 }; 155 156 const struct cfattach uthum_ca = { 157 sizeof(struct uthum_softc), 158 uthum_match, 159 uthum_attach, 160 uthum_detach 161 }; 162 163 int 164 uthum_match(struct device *parent, void *match, void *aux) 165 { 166 struct uhidev_attach_arg *uha = aux; 167 168 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 169 return (UMATCH_NONE); 170 171 if (uthum_lookup(uha->uaa->vendor, uha->uaa->product) == NULL) 172 return UMATCH_NONE; 173 174 #if 0 /* attach only sensor part of HID as uthum* */ 175 #define HUG_UNKNOWN_3 0x0003 176 void *desc; 177 int size; 178 uhidev_get_report_desc(uha->parent, &desc, &size); 179 if (!hid_is_collection(desc, size, uha->reportid, 180 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_UNKNOWN_3))) 181 return (UMATCH_NONE); 182 #undef HUG_UNKNOWN_3 183 #endif 184 185 return (UMATCH_VENDOR_PRODUCT); 186 } 187 188 void 189 uthum_attach(struct device *parent, struct device *self, void *aux) 190 { 191 struct uthum_softc *sc = (struct uthum_softc *)self; 192 struct usb_attach_arg *uaa = aux; 193 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 194 struct usbd_device *dev = uha->parent->sc_udev; 195 int i, size, repid; 196 void *desc; 197 198 sc->sc_udev = dev; 199 sc->sc_hdev.sc_intr = uthum_intr; 200 sc->sc_hdev.sc_parent = uha->parent; 201 sc->sc_hdev.sc_report_id = uha->reportid; 202 sc->sc_num_sensors = 0; 203 204 uhidev_get_report_desc(uha->parent, &desc, &size); 205 repid = uha->reportid; 206 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 207 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 208 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 209 210 printf("\n"); 211 212 if (sc->sc_flen < 32) { 213 /* not sensor interface, just attach */ 214 return; 215 } 216 217 /* maybe unsupported device */ 218 if (uthum_check_device_info(sc) < 0) { 219 DPRINTF(("uthum: unknown device\n")); 220 return; 221 }; 222 223 /* attach sensor */ 224 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 225 sizeof(sc->sc_sensordev.xname)); 226 uthum_setup_sensors(sc); 227 228 /* attach sensors */ 229 for (i = 0; i < UTHUM_MAX_SENSORS; i++) { 230 if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_UNKNOWN) 231 continue; 232 uthum_print_sensorinfo(sc, i); 233 sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID; 234 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor); 235 sc->sc_sensor[i].attached = 1; 236 sc->sc_num_sensors++; 237 } 238 239 if (sc->sc_num_sensors > 0) { 240 /* 0.1Hz */ 241 sc->sc_sensortask = sensor_task_register(sc, uthum_refresh, 6); 242 if (sc->sc_sensortask == NULL) { 243 printf(", unable to register update task\n"); 244 return; 245 } 246 sensordev_install(&sc->sc_sensordev); 247 } 248 249 DPRINTF(("uthum_attach: complete\n")); 250 } 251 252 int 253 uthum_detach(struct device *self, int flags) 254 { 255 struct uthum_softc *sc = (struct uthum_softc *)self; 256 int i, rv = 0; 257 258 if (sc->sc_num_sensors > 0) { 259 wakeup(&sc->sc_sensortask); 260 sensordev_deinstall(&sc->sc_sensordev); 261 for (i = 0; i < UTHUM_MAX_SENSORS; i++) { 262 if (sc->sc_sensor[i].attached) 263 sensor_detach(&sc->sc_sensordev, 264 &sc->sc_sensor[i].sensor); 265 } 266 if (sc->sc_sensortask != NULL) 267 sensor_task_unregister(sc->sc_sensortask); 268 } 269 270 uthum_reset_device(sc); 271 272 return (rv); 273 } 274 275 void 276 uthum_intr(struct uhidev *addr, void *ibuf, u_int len) 277 { 278 /* do nothing */ 279 } 280 281 int 282 uthum_issue_cmd(struct uthum_softc *sc, uint8_t target_cmd, int delay) 283 { 284 uint8_t cmdbuf[32]; 285 int i, actlen, olen; 286 287 olen = MIN(sc->sc_olen, sizeof(cmdbuf)); 288 289 bzero(cmdbuf, sizeof(cmdbuf)); 290 memcpy(cmdbuf, cmd_issue, sizeof(cmd_issue)); 291 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 292 sc->sc_hdev.sc_report_id, cmdbuf, olen); 293 if (actlen != olen) 294 return EIO; 295 296 bzero(cmdbuf, sizeof(cmdbuf)); 297 cmdbuf[0] = target_cmd; 298 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 299 sc->sc_hdev.sc_report_id, cmdbuf, olen); 300 if (actlen != olen) 301 return EIO; 302 303 bzero(cmdbuf, sizeof(cmdbuf)); 304 for (i = 0; i < 7; i++) { 305 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, 306 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmdbuf, olen); 307 if (actlen != olen) 308 return EIO; 309 } 310 311 /* wait if required */ 312 if (delay > 0) 313 tsleep_nsec(&sc->sc_sensortask, 0, "uthum", 314 MSEC_TO_NSEC(delay)); 315 316 return 0; 317 } 318 319 int 320 uthum_read_data(struct uthum_softc *sc, uint8_t target_cmd, uint8_t *buf, 321 size_t len, int delay) 322 { 323 uint8_t cmdbuf[32], report[256]; 324 int olen, flen; 325 326 /* if return buffer is null, do nothing */ 327 if ((buf == NULL) || len == 0) 328 return 0; 329 330 if (uthum_issue_cmd(sc, target_cmd, 50)) 331 return 0; 332 333 olen = MIN(sc->sc_olen, sizeof(cmdbuf)); 334 335 bzero(cmdbuf, sizeof(cmdbuf)); 336 memcpy(cmdbuf, cmd_query, sizeof(cmd_query)); 337 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 338 sc->sc_hdev.sc_report_id, cmdbuf, olen) != olen) 339 return EIO; 340 341 /* wait if required */ 342 if (delay > 0) 343 tsleep_nsec(&sc->sc_sensortask, 0, "uthum", 344 MSEC_TO_NSEC(delay)); 345 346 /* get answer */ 347 flen = MIN(sc->sc_flen, sizeof(report)); 348 if (uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT, 349 sc->sc_hdev.sc_report_id, report, flen) != flen) 350 return EIO; 351 memcpy(buf, report, len); 352 return 0; 353 } 354 355 int 356 uthum_check_device_info(struct uthum_softc *sc) 357 { 358 struct uthum_dev_info { 359 uint16_t dev_type; 360 uint8_t cal[2][2]; /* calibration offsets */ 361 uint8_t footer; 362 uint8_t padding[25]; 363 } dinfo; 364 int val, dev_type; 365 int retry = 3; 366 367 /* issue query to device */ 368 while (retry) { 369 if (uthum_read_data(sc, CMD_DEVTYPE, (void *)&dinfo, 370 sizeof(struct uthum_dev_info), 0) != 0) { 371 DPRINTF(("uthum: device information query fail.\n")); 372 retry--; 373 continue; 374 } 375 if (dinfo.footer != DEVTYPE_EOF) { 376 /* it will be a bogus entry, retry. */ 377 retry--; 378 } else 379 break; 380 } 381 382 if (retry <= 0) 383 return EIO; 384 385 dev_type = betoh16(dinfo.dev_type); 386 /* TEMPerHUM has 3 different device identifiers, unify them */ 387 if (dev_type == UTHUM_TYPE_TEMPERHUM_2 || 388 dev_type == UTHUM_TYPE_TEMPERHUM_3) 389 dev_type = UTHUM_TYPE_TEMPERHUM; 390 391 /* check device type and calibration offset*/ 392 switch (dev_type) { 393 case UTHUM_TYPE_TEMPER2: 394 case UTHUM_TYPE_TEMPERHUM: 395 case UTHUM_TYPE_TEMPERNTC: 396 val = (dinfo.cal[1][0] - UTHUM_CAL_OFFSET) * 100; 397 val += dinfo.cal[1][1] * 10; 398 sc->sc_sensor[1].cal_offset = val; 399 /* fall down, don't break */ 400 case UTHUM_TYPE_TEMPER1: 401 val = (dinfo.cal[0][0] - UTHUM_CAL_OFFSET) * 100; 402 val += dinfo.cal[0][1] * 10; 403 sc->sc_sensor[0].cal_offset = val; 404 sc->sc_device_type = dev_type; 405 break; 406 default: 407 sc->sc_device_type = UTHUM_TYPE_UNKNOWN; 408 printf("uthum: unknown device (devtype = 0x%.2x)\n", 409 dev_type); 410 return EIO; 411 } 412 413 /* device specific init process */ 414 switch (dev_type) { 415 case UTHUM_TYPE_TEMPERHUM: 416 sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = 0; 417 break; 418 }; 419 420 uthum_reset_device(sc); 421 422 return 0; 423 }; 424 425 void 426 uthum_reset_device(struct uthum_softc *sc) 427 { 428 switch (sc->sc_device_type) { 429 case UTHUM_TYPE_TEMPER1: 430 case UTHUM_TYPE_TEMPERNTC: 431 uthum_issue_cmd(sc, CMD_RESET0, 200); 432 break; 433 case UTHUM_TYPE_TEMPER2: 434 uthum_issue_cmd(sc, CMD_RESET0, 200); 435 uthum_issue_cmd(sc, CMD_RESET1, 200); 436 break; 437 } 438 } 439 440 void 441 uthum_setup_sensors(struct uthum_softc *sc) 442 { 443 int i; 444 445 for (i = 0; i < UTHUM_MAX_SENSORS; i++) 446 sc->sc_sensor[i].dev_type = UTHUM_SENSOR_UNKNOWN; 447 448 switch (sc->sc_device_type) { 449 case UTHUM_TYPE_TEMPER2: /* 2 temperature sensors */ 450 sc->sc_sensor[UTHUM_TEMPER_OUTER].dev_type = 451 UTHUM_SENSOR_DS75; 452 sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.type = 453 SENSOR_TEMP; 454 strlcpy(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc, 455 "outer", 456 sizeof(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc)); 457 /* fall down */ 458 case UTHUM_TYPE_TEMPER1: /* 1 temperature sensor */ 459 sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type = 460 UTHUM_SENSOR_DS75; 461 sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.type = 462 SENSOR_TEMP; 463 strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc, 464 "inner", 465 sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc)); 466 break; 467 case UTHUM_TYPE_TEMPERHUM: 468 /* 1 temperature sensor and 1 humidity sensor */ 469 for (i = 0; i < 2; i++) 470 sc->sc_sensor[i].dev_type = UTHUM_SENSOR_SHT1X; 471 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.type = SENSOR_TEMP; 472 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.type = 473 SENSOR_HUMIDITY; 474 strlcpy(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc, 475 "RH", 476 sizeof(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc)); 477 break; 478 case UTHUM_TYPE_TEMPERNTC: 479 /* 2 temperature sensors */ 480 for (i = 0; i < 2; i++) 481 sc->sc_sensor[i].sensor.type = SENSOR_TEMP; 482 sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type = 483 UTHUM_SENSOR_DS75; 484 sc->sc_sensor[UTHUM_TEMPER_NTC].dev_type = 485 UTHUM_SENSOR_NTC; 486 strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc, 487 "inner", 488 sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc)); 489 strlcpy(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc, 490 "outer/ntc", 491 sizeof(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc)); 492 493 /* sensor state tuning */ 494 for (i = 0; i < 4; i++) 495 uthum_issue_cmd(sc, TEMPERNTC_MODE_BASE, 50); 496 sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = TEMPERNTC_MODE_BASE; 497 if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, NULL)) 498 DPRINTF(("uthum: NTC sensor tuning failed\n")); 499 uthum_issue_cmd(sc, CMD_TEMPERNTC_MODE_DONE, 100); 500 break; 501 default: 502 /* do nothing */ 503 break; 504 } 505 } 506 507 int 508 uthum_ntc_getdata(struct uthum_softc *sc, int *val) 509 { 510 uint8_t buf[8]; 511 512 if (val == NULL) 513 return EIO; 514 515 /* get sensor value */ 516 if (uthum_read_data(sc, CMD_GETDATA_NTC, buf, sizeof(buf), 10) != 0) { 517 DPRINTF(("uthum: data read fail\n")); 518 return EIO; 519 } 520 521 /* check data integrity */ 522 if (buf[2] != CMD_GETDATA_EOF2) { 523 DPRINTF(("uthum: broken ntc data 0x%.2x 0x%.2x 0x%.2x\n", 524 buf[0], buf[1], buf[2])); 525 return EIO; 526 } 527 528 *val = (buf[0] << 8) + buf[1]; 529 return 0; 530 } 531 532 int 533 uthum_ntc_tuning(struct uthum_softc *sc, int sensor, int *val) 534 { 535 struct uthum_sensor *s; 536 int done, state, ostate, curval; 537 int retry = 3; 538 539 s = &sc->sc_sensor[sensor]; 540 state = s->cur_state; 541 542 /* get current sensor value */ 543 if (val == NULL) { 544 while (retry) { 545 if (uthum_ntc_getdata(sc, &curval)) { 546 retry--; 547 continue; 548 } else 549 break; 550 } 551 if (retry <= 0) 552 return EIO; 553 } else { 554 curval = *val; 555 } 556 557 /* no state change is required */ 558 if ((curval >= UTHUM_NTC_MIN_THRESHOLD) && 559 (curval <= UTHUM_NTC_MAX_THRESHOLD)) { 560 return 0; 561 } 562 563 if (((curval < UTHUM_NTC_MIN_THRESHOLD) && 564 (state == TEMPERNTC_MODE_MAX)) || 565 ((curval > UTHUM_NTC_MAX_THRESHOLD) && 566 (state == TEMPERNTC_MODE_BASE))) 567 return 0; 568 569 DPRINTF(("uthum: ntc tuning start. cur state = 0x%.2x, val = 0x%.4x\n", 570 state, curval)); 571 572 /* tuning loop */ 573 ostate = state; 574 done = 0; 575 while (!done) { 576 if (curval < UTHUM_NTC_MIN_THRESHOLD) { 577 if (state == TEMPERNTC_MODE_MAX) 578 done++; 579 else 580 state++; 581 } else if (curval > UTHUM_NTC_MAX_THRESHOLD) { 582 if (state == TEMPERNTC_MODE_BASE) 583 done++; 584 else 585 state--; 586 } else { 587 uthum_ntc_getdata(sc, &curval); 588 if ((curval >= UTHUM_NTC_MIN_THRESHOLD) && 589 (curval <= UTHUM_NTC_MAX_THRESHOLD)) 590 done++; 591 } 592 593 /* update state */ 594 if (state != ostate) { 595 uthum_issue_cmd(sc, state, 50); 596 uthum_issue_cmd(sc, state, 50); 597 uthum_ntc_getdata(sc, &curval); 598 } 599 ostate = state; 600 } 601 602 DPRINTF(("uthum: ntc tuning done. state change: 0x%.2x->0x%.2x\n", 603 s->cur_state, state)); 604 s->cur_state = state; 605 if (val != NULL) 606 *val = curval; 607 608 return 0; 609 } 610 611 void 612 uthum_refresh(void *arg) 613 { 614 struct uthum_softc *sc = arg; 615 int i; 616 617 switch (sc->sc_device_type) { 618 case UTHUM_TYPE_TEMPER1: 619 case UTHUM_TYPE_TEMPER2: 620 case UTHUM_TYPE_TEMPERNTC: 621 for (i = 0; i < sc->sc_num_sensors; i++) { 622 if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_DS75) 623 uthum_refresh_temper(sc, i); 624 else if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_NTC) 625 uthum_refresh_temperntc(sc, i); 626 } 627 break; 628 case UTHUM_TYPE_TEMPERHUM: 629 uthum_refresh_temperhum(sc); 630 break; 631 default: 632 break; 633 /* never reach */ 634 } 635 } 636 637 void 638 uthum_refresh_temperhum(struct uthum_softc *sc) 639 { 640 uint8_t buf[8]; 641 int temp, rh; 642 643 if (uthum_read_data(sc, CMD_GETDATA, buf, sizeof(buf), 1000) != 0) { 644 DPRINTF(("uthum: data read fail\n")); 645 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags 646 |= SENSOR_FINVALID; 647 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags 648 |= SENSOR_FINVALID; 649 return; 650 } 651 652 temp = uthum_sht1x_temp(buf[0], buf[1]); 653 rh = uthum_sht1x_rh(buf[2], buf[3], temp); 654 655 /* apply calibration offsets */ 656 temp += sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].cal_offset; 657 rh += sc->sc_sensor[UTHUM_TEMPERHUM_HUM].cal_offset; 658 659 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.value = 660 (temp * 10000) + 273150000; 661 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags &= ~SENSOR_FINVALID; 662 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.value = rh; 663 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags &= ~SENSOR_FINVALID; 664 } 665 666 void 667 uthum_refresh_temper(struct uthum_softc *sc, int sensor) 668 { 669 uint8_t buf[8]; 670 uint8_t cmd; 671 int temp; 672 673 if (sensor == UTHUM_TEMPER_INNER) 674 cmd = CMD_GETDATA_INNER; 675 else if (sensor == UTHUM_TEMPER_OUTER) 676 cmd = CMD_GETDATA_OUTER; 677 else 678 return; 679 680 /* get sensor value */ 681 if (uthum_read_data(sc, cmd, buf, sizeof(buf), 1000) != 0) { 682 DPRINTF(("uthum: data read fail\n")); 683 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 684 return; 685 } 686 687 /* check integrity */ 688 if (buf[2] != CMD_GETDATA_EOF) { 689 DPRINTF(("uthum: broken ds75 data: 0x%.2x 0x%.2x 0x%.2x\n", 690 buf[0], buf[1], buf[2])); 691 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 692 return; 693 } 694 temp = uthum_ds75_temp(buf[0], buf[1]); 695 696 /* apply calibration offset */ 697 temp += sc->sc_sensor[sensor].cal_offset; 698 699 sc->sc_sensor[sensor].sensor.value = (temp * 10000) + 273150000; 700 sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID; 701 } 702 703 void 704 uthum_refresh_temperntc(struct uthum_softc *sc, int sensor) 705 { 706 int val; 707 int64_t temp; 708 709 /* get sensor data */ 710 if (uthum_ntc_getdata(sc, &val)) { 711 DPRINTF(("uthum: ntc data read fail\n")); 712 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 713 return; 714 } 715 716 /* adjust sensor state */ 717 if ((val < UTHUM_NTC_MIN_THRESHOLD) || 718 (val > UTHUM_NTC_MAX_THRESHOLD)) { 719 if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, &val)) { 720 DPRINTF(("uthum: NTC sensor tuning failed\n")); 721 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 722 return; 723 } 724 } 725 726 temp = uthum_ntc_temp(val, sc->sc_sensor[sensor].cur_state); 727 if (temp == 0) { 728 /* XXX: work around. */ 729 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 730 } else { 731 /* apply calibration offset */ 732 temp += sc->sc_sensor[sensor].cal_offset * 10000; 733 sc->sc_sensor[sensor].sensor.value = temp; 734 sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID; 735 } 736 } 737 738 /* return C-degree * 100 value */ 739 int 740 uthum_ds75_temp(uint8_t msb, uint8_t lsb) 741 { 742 int val; 743 744 /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */ 745 746 val = (msb << 8) | lsb; 747 if (val >= 32768) 748 val = val - 65536; 749 val = (val * 100) >> 8; 750 751 return val; 752 } 753 754 /* return C-degree * 100 value */ 755 int 756 uthum_sht1x_temp(uint8_t msb, uint8_t lsb) 757 { 758 int nticks; 759 760 /* sensor device VDD-bias value table 761 * ---------------------------------------------- 762 * VDD 2.5V 3.0V 3.5V 4.0V 5.0V 763 * bias -3940 -3960 -3970 -3980 -4010 764 * ---------------------------------------------- 765 * 766 * as the VDD of the SHT10 on my TEMPerHUM is 3.43V +/- 0.05V, 767 * bias -3970 will be best for that device. 768 */ 769 770 nticks = (msb * 256 + lsb) & 0x3fff; 771 return (nticks - 3970); 772 } 773 774 /* return %RH * 1000 */ 775 int 776 uthum_sht1x_rh(uint8_t msb, uint8_t lsb, int temp) 777 { 778 int nticks, rh_l; 779 780 nticks = (msb * 256 + lsb) & 0x0fff; 781 rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250); 782 783 return ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10; 784 } 785 786 /* return muK */ 787 int64_t 788 uthum_ntc_temp(int64_t val, int state) 789 { 790 int64_t temp = 0; 791 792 switch (state) { 793 case TEMPERNTC_MODE_BASE: /* 0x61 */ 794 case TEMPERNTC_MODE_BASE+1: /* 0x62 */ 795 case TEMPERNTC_MODE_BASE+2: /* 0x63 */ 796 case TEMPERNTC_MODE_BASE+3: /* 0x64 */ 797 /* XXX, no data */ 798 temp = -273150000; 799 break; 800 case TEMPERNTC_MODE_BASE+4: /* 0x65 */ 801 temp = ((val * val * 2977) / 100000) - (val * 4300) + 152450000; 802 break; 803 case TEMPERNTC_MODE_BASE+5: /* 0x66 */ 804 temp = ((val * val * 3887) / 100000) - (val * 5300) + 197590000; 805 break; 806 case TEMPERNTC_MODE_BASE+6: /* 0x67 */ 807 temp = ((val * val * 3495) / 100000) - (val * 5000) + 210590000; 808 break; 809 case TEMPERNTC_MODE_BASE+7: /* 0x68 */ 810 if (val < UTHUM_NTC_MIN_THRESHOLD) 811 temp = (val * -1700) + 149630000; 812 else 813 temp = ((val * val * 3257) / 100000) - (val * 4900) + 814 230470000; 815 break; 816 default: 817 DPRINTF(("NTC state error, unknown state 0x%.2x\n", state)); 818 break; 819 } 820 821 /* convert muC->muK value */ 822 return temp + 273150000; 823 } 824 825 void 826 uthum_print_sensorinfo(struct uthum_softc *sc, int num) 827 { 828 struct uthum_sensor *s; 829 s = &sc->sc_sensor[num]; 830 831 printf("%s: ", sc->sc_hdev.sc_dev.dv_xname); 832 switch (s->sensor.type) { 833 case SENSOR_TEMP: 834 printf("type %s (temperature)", 835 uthum_sensor_type_s[s->dev_type]); 836 if (s->cal_offset) 837 printf(", calibration offset %c%d.%d degC", 838 (s->cal_offset < 0) ? '-' : '+', 839 abs(s->cal_offset / 100), 840 abs(s->cal_offset % 100)); 841 break; 842 case SENSOR_HUMIDITY: 843 printf("type %s (humidity)", 844 uthum_sensor_type_s[s->dev_type]); 845 if (s->cal_offset) 846 printf("calibration offset %c%d.%d %%RH", 847 (s->cal_offset < 0) ? '-' : '+', 848 abs(s->cal_offset / 100), 849 abs(s->cal_offset % 100)); 850 break; 851 default: 852 printf("unknown"); 853 } 854 printf("\n"); 855 } 856