1 /* $OpenBSD: uthum.c,v 1.31 2016/03/19 11:34:22 mpi 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/malloc.h> 25 #include <sys/device.h> 26 #include <sys/conf.h> 27 #include <sys/sensors.h> 28 29 #include <dev/usb/usb.h> 30 #include <dev/usb/usbhid.h> 31 #include <dev/usb/usbdi.h> 32 #include <dev/usb/usbdi_util.h> 33 #include <dev/usb/usbdevs.h> 34 #include <dev/usb/uhidev.h> 35 36 #ifdef UTHUM_DEBUG 37 #define DPRINTF(x) do { printf x; } while (0) 38 #else 39 #define DPRINTF(x) 40 #endif 41 42 /* Device types */ 43 #define UTHUM_TYPE_TEMPERHUM 0x535a 44 #define UTHUM_TYPE_TEMPERHUM_2 0x575a /* alternative TEMPerHUM */ 45 #define UTHUM_TYPE_TEMPER1 0x5758 /* TEMPer1 and HID TEMPer */ 46 #define UTHUM_TYPE_TEMPER2 0x5759 47 #define UTHUM_TYPE_TEMPERNTC 0x575b 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 (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 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; 289 290 bzero(cmdbuf, sizeof(cmdbuf)); 291 memcpy(cmdbuf, cmd_issue, sizeof(cmd_issue)); 292 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 293 sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen); 294 if (actlen != sc->sc_olen) 295 return EIO; 296 297 bzero(cmdbuf, sizeof(cmdbuf)); 298 cmdbuf[0] = target_cmd; 299 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 300 sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen); 301 if (actlen != sc->sc_olen) 302 return EIO; 303 304 bzero(cmdbuf, sizeof(cmdbuf)); 305 for (i = 0; i < 7; i++) { 306 actlen = uhidev_set_report(sc->sc_hdev.sc_parent, 307 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmdbuf, 308 sc->sc_olen); 309 if (actlen != sc->sc_olen) 310 return EIO; 311 } 312 313 /* wait if required */ 314 if (delay > 0) 315 tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1); 316 317 return 0; 318 } 319 320 int 321 uthum_read_data(struct uthum_softc *sc, uint8_t target_cmd, uint8_t *buf, 322 size_t len, int delay) 323 { 324 uint8_t cmdbuf[32], report[256]; 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 bzero(cmdbuf, sizeof(cmdbuf)); 334 memcpy(cmdbuf, cmd_query, sizeof(cmd_query)); 335 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 336 sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen) != sc->sc_olen) 337 return EIO; 338 339 /* wait if required */ 340 if (delay > 0) 341 tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1); 342 343 /* get answer */ 344 if (uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT, 345 sc->sc_hdev.sc_report_id, report, sc->sc_flen) != sc->sc_flen) 346 return EIO; 347 memcpy(buf, report, len); 348 return 0; 349 } 350 351 int 352 uthum_check_device_info(struct uthum_softc *sc) 353 { 354 struct uthum_dev_info { 355 uint16_t dev_type; 356 uint8_t cal[2][2]; /* calibration offsets */ 357 uint8_t footer; 358 uint8_t padding[25]; 359 } dinfo; 360 int val, dev_type; 361 int retry = 3; 362 363 /* issue query to device */ 364 while (retry) { 365 if (uthum_read_data(sc, CMD_DEVTYPE, (void *)&dinfo, 366 sizeof(struct uthum_dev_info), 0) != 0) { 367 DPRINTF(("uthum: device information query fail.\n")); 368 retry--; 369 continue; 370 } 371 if (dinfo.footer != DEVTYPE_EOF) { 372 /* it will be a bogus entry, retry. */ 373 retry--; 374 } else 375 break; 376 } 377 378 if (retry <= 0) 379 return EIO; 380 381 dev_type = betoh16(dinfo.dev_type); 382 /* TEMPerHUM has 2 different device identifiers, unify them */ 383 if (dev_type == UTHUM_TYPE_TEMPERHUM_2) 384 dev_type = UTHUM_TYPE_TEMPERHUM; 385 386 /* check device type and calibration offset*/ 387 switch (dev_type) { 388 case UTHUM_TYPE_TEMPER2: 389 case UTHUM_TYPE_TEMPERHUM: 390 case UTHUM_TYPE_TEMPERNTC: 391 val = (dinfo.cal[1][0] - UTHUM_CAL_OFFSET) * 100; 392 val += dinfo.cal[1][1] * 10; 393 sc->sc_sensor[1].cal_offset = val; 394 /* fall down, don't break */ 395 case UTHUM_TYPE_TEMPER1: 396 val = (dinfo.cal[0][0] - UTHUM_CAL_OFFSET) * 100; 397 val += dinfo.cal[0][1] * 10; 398 sc->sc_sensor[0].cal_offset = val; 399 sc->sc_device_type = dev_type; 400 break; 401 default: 402 sc->sc_device_type = UTHUM_TYPE_UNKNOWN; 403 printf("uthum: unknown device (devtype = 0x%.2x)\n", 404 dev_type); 405 return EIO; 406 } 407 408 /* device specific init process */ 409 switch (dev_type) { 410 case UTHUM_TYPE_TEMPERHUM: 411 sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = 0; 412 break; 413 }; 414 415 uthum_reset_device(sc); 416 417 return 0; 418 }; 419 420 void 421 uthum_reset_device(struct uthum_softc *sc) 422 { 423 switch (sc->sc_device_type) { 424 case UTHUM_TYPE_TEMPER1: 425 case UTHUM_TYPE_TEMPERNTC: 426 uthum_issue_cmd(sc, CMD_RESET0, 200); 427 break; 428 case UTHUM_TYPE_TEMPER2: 429 uthum_issue_cmd(sc, CMD_RESET0, 200); 430 uthum_issue_cmd(sc, CMD_RESET1, 200); 431 break; 432 } 433 } 434 435 void 436 uthum_setup_sensors(struct uthum_softc *sc) 437 { 438 int i; 439 440 for (i = 0; i < UTHUM_MAX_SENSORS; i++) 441 sc->sc_sensor[i].dev_type = UTHUM_SENSOR_UNKNOWN; 442 443 switch (sc->sc_device_type) { 444 case UTHUM_TYPE_TEMPER2: /* 2 temperature sensors */ 445 sc->sc_sensor[UTHUM_TEMPER_OUTER].dev_type = 446 UTHUM_SENSOR_DS75; 447 sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.type = 448 SENSOR_TEMP; 449 strlcpy(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc, 450 "outer", 451 sizeof(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc)); 452 /* fall down */ 453 case UTHUM_TYPE_TEMPER1: /* 1 temperature sensor */ 454 sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type = 455 UTHUM_SENSOR_DS75; 456 sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.type = 457 SENSOR_TEMP; 458 strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc, 459 "inner", 460 sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc)); 461 break; 462 case UTHUM_TYPE_TEMPERHUM: 463 /* 1 temperature sensor and 1 humidity sensor */ 464 for (i = 0; i < 2; i++) 465 sc->sc_sensor[i].dev_type = UTHUM_SENSOR_SHT1X; 466 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.type = SENSOR_TEMP; 467 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.type = 468 SENSOR_HUMIDITY; 469 strlcpy(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc, 470 "RH", 471 sizeof(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc)); 472 break; 473 case UTHUM_TYPE_TEMPERNTC: 474 /* 2 temperature sensors */ 475 for (i = 0; i < 2; i++) 476 sc->sc_sensor[i].sensor.type = SENSOR_TEMP; 477 sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type = 478 UTHUM_SENSOR_DS75; 479 sc->sc_sensor[UTHUM_TEMPER_NTC].dev_type = 480 UTHUM_SENSOR_NTC; 481 strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc, 482 "inner", 483 sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc)); 484 strlcpy(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc, 485 "outer/ntc", 486 sizeof(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc)); 487 488 /* sensor state tuning */ 489 for (i = 0; i < 4; i++) 490 uthum_issue_cmd(sc, TEMPERNTC_MODE_BASE, 50); 491 sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = TEMPERNTC_MODE_BASE; 492 if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, NULL)) 493 DPRINTF(("uthum: NTC sensor tuning failed\n")); 494 uthum_issue_cmd(sc, CMD_TEMPERNTC_MODE_DONE, 100); 495 break; 496 default: 497 /* do nothing */ 498 break; 499 } 500 } 501 502 int 503 uthum_ntc_getdata(struct uthum_softc *sc, int *val) 504 { 505 uint8_t buf[8]; 506 507 if (val == NULL) 508 return EIO; 509 510 /* get sensor value */ 511 if (uthum_read_data(sc, CMD_GETDATA_NTC, buf, sizeof(buf), 10) != 0) { 512 DPRINTF(("uthum: data read fail\n")); 513 return EIO; 514 } 515 516 /* check data integrity */ 517 if (buf[2] != CMD_GETDATA_EOF2) { 518 DPRINTF(("uthum: broken ntc data 0x%.2x 0x%.2x 0x%.2x\n", 519 buf[0], buf[1], buf[2])); 520 return EIO; 521 } 522 523 *val = (buf[0] << 8) + buf[1]; 524 return 0; 525 } 526 527 int 528 uthum_ntc_tuning(struct uthum_softc *sc, int sensor, int *val) 529 { 530 struct uthum_sensor *s; 531 int done, state, ostate, curval; 532 int retry = 3; 533 534 s = &sc->sc_sensor[sensor]; 535 state = s->cur_state; 536 537 /* get current sensor value */ 538 if (val == NULL) { 539 while (retry) { 540 if (uthum_ntc_getdata(sc, &curval)) { 541 retry--; 542 continue; 543 } else 544 break; 545 } 546 if (retry <= 0) 547 return EIO; 548 } else { 549 curval = *val; 550 } 551 552 /* no state change is required */ 553 if ((curval >= UTHUM_NTC_MIN_THRESHOLD) && 554 (curval <= UTHUM_NTC_MAX_THRESHOLD)) { 555 return 0; 556 } 557 558 if (((curval < UTHUM_NTC_MIN_THRESHOLD) && 559 (state == TEMPERNTC_MODE_MAX)) || 560 ((curval > UTHUM_NTC_MAX_THRESHOLD) && 561 (state == TEMPERNTC_MODE_BASE))) 562 return 0; 563 564 DPRINTF(("uthum: ntc tuning start. cur state = 0x%.2x, val = 0x%.4x\n", 565 state, curval)); 566 567 /* tuning loop */ 568 ostate = state; 569 done = 0; 570 while (!done) { 571 if (curval < UTHUM_NTC_MIN_THRESHOLD) { 572 if (state == TEMPERNTC_MODE_MAX) 573 done++; 574 else 575 state++; 576 } else if (curval > UTHUM_NTC_MAX_THRESHOLD) { 577 if (state == TEMPERNTC_MODE_BASE) 578 done++; 579 else 580 state--; 581 } else { 582 uthum_ntc_getdata(sc, &curval); 583 if ((curval >= UTHUM_NTC_MIN_THRESHOLD) && 584 (curval <= UTHUM_NTC_MAX_THRESHOLD)) 585 done++; 586 } 587 588 /* update state */ 589 if (state != ostate) { 590 uthum_issue_cmd(sc, state, 50); 591 uthum_issue_cmd(sc, state, 50); 592 uthum_ntc_getdata(sc, &curval); 593 } 594 ostate = state; 595 } 596 597 DPRINTF(("uthum: ntc tuning done. state change: 0x%.2x->0x%.2x\n", 598 s->cur_state, state)); 599 s->cur_state = state; 600 if (val != NULL) 601 *val = curval; 602 603 return 0; 604 } 605 606 void 607 uthum_refresh(void *arg) 608 { 609 struct uthum_softc *sc = arg; 610 int i; 611 612 switch (sc->sc_device_type) { 613 case UTHUM_TYPE_TEMPER1: 614 case UTHUM_TYPE_TEMPER2: 615 case UTHUM_TYPE_TEMPERNTC: 616 for (i = 0; i < sc->sc_num_sensors; i++) { 617 if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_DS75) 618 uthum_refresh_temper(sc, i); 619 else if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_NTC) 620 uthum_refresh_temperntc(sc, i); 621 } 622 break; 623 case UTHUM_TYPE_TEMPERHUM: 624 uthum_refresh_temperhum(sc); 625 break; 626 default: 627 break; 628 /* never reach */ 629 } 630 } 631 632 void 633 uthum_refresh_temperhum(struct uthum_softc *sc) 634 { 635 uint8_t buf[8]; 636 int temp, rh; 637 638 if (uthum_read_data(sc, CMD_GETDATA, buf, sizeof(buf), 1000) != 0) { 639 DPRINTF(("uthum: data read fail\n")); 640 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags 641 |= SENSOR_FINVALID; 642 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags 643 |= SENSOR_FINVALID; 644 return; 645 } 646 647 temp = uthum_sht1x_temp(buf[0], buf[1]); 648 rh = uthum_sht1x_rh(buf[2], buf[3], temp); 649 650 /* apply calibration offsets */ 651 temp += sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].cal_offset; 652 rh += sc->sc_sensor[UTHUM_TEMPERHUM_HUM].cal_offset; 653 654 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.value = 655 (temp * 10000) + 273150000; 656 sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags &= ~SENSOR_FINVALID; 657 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.value = rh; 658 sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags &= ~SENSOR_FINVALID; 659 } 660 661 void 662 uthum_refresh_temper(struct uthum_softc *sc, int sensor) 663 { 664 uint8_t buf[8]; 665 uint8_t cmd; 666 int temp; 667 668 if (sensor == UTHUM_TEMPER_INNER) 669 cmd = CMD_GETDATA_INNER; 670 else if (sensor == UTHUM_TEMPER_OUTER) 671 cmd = CMD_GETDATA_OUTER; 672 else 673 return; 674 675 /* get sensor value */ 676 if (uthum_read_data(sc, cmd, buf, sizeof(buf), 1000) != 0) { 677 DPRINTF(("uthum: data read fail\n")); 678 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 679 return; 680 } 681 682 /* check integrity */ 683 if (buf[2] != CMD_GETDATA_EOF) { 684 DPRINTF(("uthum: broken ds75 data: 0x%.2x 0x%.2x 0x%.2x\n", 685 buf[0], buf[1], buf[2])); 686 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 687 return; 688 } 689 temp = uthum_ds75_temp(buf[0], buf[1]); 690 691 /* apply calibration offset */ 692 temp += sc->sc_sensor[sensor].cal_offset; 693 694 sc->sc_sensor[sensor].sensor.value = (temp * 10000) + 273150000; 695 sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID; 696 } 697 698 void 699 uthum_refresh_temperntc(struct uthum_softc *sc, int sensor) 700 { 701 int val; 702 int64_t temp; 703 704 /* get sensor data */ 705 if (uthum_ntc_getdata(sc, &val)) { 706 DPRINTF(("uthum: ntc data read fail\n")); 707 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 708 return; 709 } 710 711 /* adjust sensor state */ 712 if ((val < UTHUM_NTC_MIN_THRESHOLD) || 713 (val > UTHUM_NTC_MAX_THRESHOLD)) { 714 if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, &val)) { 715 DPRINTF(("uthum: NTC sensor tuning failed\n")); 716 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 717 return; 718 } 719 } 720 721 temp = uthum_ntc_temp(val, sc->sc_sensor[sensor].cur_state); 722 if (temp == 0) { 723 /* XXX: work around. */ 724 sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID; 725 } else { 726 /* apply calibration offset */ 727 temp += sc->sc_sensor[sensor].cal_offset * 10000; 728 sc->sc_sensor[sensor].sensor.value = temp; 729 sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID; 730 } 731 } 732 733 /* return C-degree * 100 value */ 734 int 735 uthum_ds75_temp(uint8_t msb, uint8_t lsb) 736 { 737 /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */ 738 return (msb * 100) + ((lsb >> 4) * 25 / 4); 739 } 740 741 /* return C-degree * 100 value */ 742 int 743 uthum_sht1x_temp(uint8_t msb, uint8_t lsb) 744 { 745 int nticks; 746 747 /* sensor device VDD-bias value table 748 * ---------------------------------------------- 749 * VDD 2.5V 3.0V 3.5V 4.0V 5.0V 750 * bias -3940 -3960 -3970 -3980 -4010 751 * ---------------------------------------------- 752 * 753 * as the VDD of the SHT10 on my TEMPerHUM is 3.43V +/- 0.05V, 754 * bias -3970 will be best for that device. 755 */ 756 757 nticks = (msb * 256 + lsb) & 0x3fff; 758 return (nticks - 3970); 759 } 760 761 /* return %RH * 1000 */ 762 int 763 uthum_sht1x_rh(uint8_t msb, uint8_t lsb, int temp) 764 { 765 int nticks, rh_l; 766 767 nticks = (msb * 256 + lsb) & 0x0fff; 768 rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250); 769 770 return ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10; 771 } 772 773 /* return muK */ 774 int64_t 775 uthum_ntc_temp(int64_t val, int state) 776 { 777 int64_t temp = 0; 778 779 switch (state) { 780 case TEMPERNTC_MODE_BASE: /* 0x61 */ 781 case TEMPERNTC_MODE_BASE+1: /* 0x62 */ 782 case TEMPERNTC_MODE_BASE+2: /* 0x63 */ 783 case TEMPERNTC_MODE_BASE+3: /* 0x64 */ 784 /* XXX, no data */ 785 temp = -273150000; 786 break; 787 case TEMPERNTC_MODE_BASE+4: /* 0x65 */ 788 temp = ((val * val * 2977) / 100000) - (val * 4300) + 152450000; 789 break; 790 case TEMPERNTC_MODE_BASE+5: /* 0x66 */ 791 temp = ((val * val * 3887) / 100000) - (val * 5300) + 197590000; 792 break; 793 case TEMPERNTC_MODE_BASE+6: /* 0x67 */ 794 temp = ((val * val * 3495) / 100000) - (val * 5000) + 210590000; 795 break; 796 case TEMPERNTC_MODE_BASE+7: /* 0x68 */ 797 if (val < UTHUM_NTC_MIN_THRESHOLD) 798 temp = (val * -1700) + 149630000; 799 else 800 temp = ((val * val * 3257) / 100000) - (val * 4900) + 801 230470000; 802 break; 803 default: 804 DPRINTF(("NTC state error, unknown state 0x%.2x\n", state)); 805 break; 806 } 807 808 /* convert muC->muK value */ 809 return temp + 273150000; 810 } 811 812 void 813 uthum_print_sensorinfo(struct uthum_softc *sc, int num) 814 { 815 struct uthum_sensor *s; 816 s = &sc->sc_sensor[num]; 817 818 printf("%s: ", sc->sc_hdev.sc_dev.dv_xname); 819 switch (s->sensor.type) { 820 case SENSOR_TEMP: 821 printf("type %s (temperature)", 822 uthum_sensor_type_s[s->dev_type]); 823 if (s->cal_offset) 824 printf(", calibration offset %c%d.%d degC", 825 (s->cal_offset < 0) ? '-' : '+', 826 abs(s->cal_offset / 100), 827 abs(s->cal_offset % 100)); 828 break; 829 case SENSOR_HUMIDITY: 830 printf("type %s (humidity)", 831 uthum_sensor_type_s[s->dev_type]); 832 if (s->cal_offset) 833 printf("calibration offset %c%d.%d %%RH", 834 (s->cal_offset < 0) ? '-' : '+', 835 abs(s->cal_offset / 100), 836 abs(s->cal_offset % 100)); 837 break; 838 default: 839 printf("unknown"); 840 } 841 printf("\n"); 842 } 843