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