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