1 /* $OpenBSD: uthum.c,v 1.17 2011/07/03 15:47:17 matthew 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/proc.h> 23 #include <sys/systm.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/device.h> 27 #include <sys/conf.h> 28 #include <sys/sensors.h> 29 30 #include <dev/usb/usb.h> 31 #include <dev/usb/usbhid.h> 32 #include <dev/usb/usbdi.h> 33 #include <dev/usb/usbdi_util.h> 34 #include <dev/usb/usbdevs.h> 35 #include <dev/usb/uhidev.h> 36 #include <dev/usb/hid.h> 37 38 #ifdef USB_DEBUG 39 #define UTHUM_DEBUG 40 #endif 41 42 #ifdef UTHUM_DEBUG 43 int uthumdebug = 0; 44 #define DPRINTFN(n, x) do { if (uthumdebug > (n)) printf x; } while (0) 45 #else 46 #define DPRINTFN(n, x) 47 #endif 48 49 #define DPRINTF(x) DPRINTFN(0, x) 50 51 /* Device types */ 52 #define UTHUM_TYPE_TEMPERHUM 0x535a 53 #define UTHUM_TYPE_TEMPERHUM_2 0x575a /* alternative TEMPerHUM */ 54 #define UTHUM_TYPE_TEMPER1 0x5758 /* TEMPer1 and HID TEMPer */ 55 #define UTHUM_TYPE_TEMPER2 0x5759 56 #define UTHUM_TYPE_TEMPERNTC 0x575b 57 #define UTHUM_TYPE_UNKNOWN 0xffff 58 59 /* Common */ 60 #define UTHUM_CAL_OFFSET 0x14 61 #define UTHUM_MAX_SENSORS 2 62 #define CMD_DEVTYPE 0x52 63 #define DEVTYPE_EOF 0x53 64 65 /* query commands */ 66 #define CMD_GETDATA_NTC 0x41 /* TEMPerNTC NTC part */ 67 #define CMD_RESET0 0x43 /* TEMPer, TEMPer[12], TEMPerNTC */ 68 #define CMD_RESET1 0x44 /* TEMPer, TEMPer[12] */ 69 #define CMD_GETDATA 0x48 /* TEMPerHUM */ 70 #define CMD_GETDATA_OUTER 0x53 /* TEMPer, TEMPer[12], TEMPerNTC */ 71 #define CMD_GETDATA_INNER 0x54 /* TEMPer, TEMPer[12], TEMPerNTC */ 72 #define CMD_GETDATA_EOF 0x31 73 #define CMD_GETDATA_EOF2 0xaa 74 75 /* temperntc mode */ 76 #define TEMPERNTC_MODE_BASE 0x61 /* 0x61 - 0x68 */ 77 #define TEMPERNTC_MODE_MAX 0x68 78 #define CMD_TEMPERNTC_MODE_DONE 0x69 79 #define UTHUM_NTC_MIN_THRESHOLD 0xb300 80 #define UTHUM_NTC_MAX_THRESHOLD 0xf200 81 82 /* sensor name */ 83 #define UTHUM_TEMPER_INNER 0 84 #define UTHUM_TEMPER_OUTER 1 85 #define UTHUM_TEMPER_NTC 1 86 #define UTHUM_TEMPERHUM_TEMP 0 87 #define UTHUM_TEMPERHUM_HUM 1 88 89 enum uthum_sensor_type { 90 UTHUM_SENSOR_UNKNOWN, 91 UTHUM_SENSOR_SHT1X, 92 UTHUM_SENSOR_DS75, 93 UTHUM_SENSOR_NTC, 94 UTHUM_SENSOR_MAXTYPES, 95 }; 96 97 static const char * const uthum_sensor_type_s[UTHUM_SENSOR_MAXTYPES] = { 98 "unknown", 99 "sht1x", 100 "ds75/12bit", 101 "NTC" 102 }; 103 104 static uint8_t cmd_issue[8] = 105 { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x02, 0x00 }; 106 static uint8_t cmd_query[8] = 107 { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x01, 0x00 }; 108 109 struct uthum_sensor { 110 struct ksensor sensor; 111 int cal_offset; /* mC or m%RH */ 112 int attached; 113 enum uthum_sensor_type dev_type; 114 int cur_state; /* for TEMPerNTC */ 115 }; 116 117 struct uthum_softc { 118 struct uhidev sc_hdev; 119 usbd_device_handle sc_udev; 120 u_char sc_dying; 121 uint16_t sc_flag; 122 int sc_device_type; 123 int sc_num_sensors; 124 125 /* uhidev parameters */ 126 size_t sc_flen; /* feature report length */ 127 size_t sc_ilen; /* input report length */ 128 size_t sc_olen; /* output report length */ 129 130 /* sensor framework */ 131 struct uthum_sensor sc_sensor[UTHUM_MAX_SENSORS]; 132 struct ksensordev sc_sensordev; 133 struct sensor_task *sc_sensortask; 134 }; 135 136 const struct usb_devno uthum_devs[] = { 137 /* XXX: various TEMPer variants are using same VID/PID */ 138 { USB_VENDOR_TENX, USB_PRODUCT_TENX_TEMPER}, 139 }; 140 #define uthum_lookup(v, p) usb_lookup(uthum_devs, v, p) 141 142 int uthum_match(struct device *, void *, void *); 143 void uthum_attach(struct device *, struct device *, void *); 144 int uthum_detach(struct device *, int); 145 int uthum_activate(struct device *, int); 146 147 int uthum_issue_cmd(struct uthum_softc *, uint8_t, int); 148 int uthum_read_data(struct uthum_softc *, uint8_t, uint8_t *, size_t, int); 149 int uthum_check_device_info(struct uthum_softc *); 150 void uthum_setup_sensors(struct uthum_softc *); 151 152 void uthum_intr(struct uhidev *, void *, u_int); 153 void uthum_refresh(void *); 154 void uthum_refresh_temper(struct uthum_softc *, int); 155 void uthum_refresh_temperhum(struct uthum_softc *); 156 void uthum_refresh_temperntc(struct uthum_softc *, int); 157 158 int uthum_ntc_getdata(struct uthum_softc *, int *); 159 int uthum_ntc_tuning(struct uthum_softc *, int, int *); 160 int64_t uthum_ntc_temp(int64_t, int); 161 int uthum_sht1x_temp(uint8_t, uint8_t); 162 int uthum_sht1x_rh(uint8_t, uint8_t, int); 163 int uthum_ds75_temp(uint8_t, uint8_t); 164 void uthum_print_sensorinfo(struct uthum_softc *, int); 165 166 struct cfdriver uthum_cd = { 167 NULL, "uthum", DV_DULL 168 }; 169 170 const struct cfattach uthum_ca = { 171 sizeof(struct uthum_softc), 172 uthum_match, 173 uthum_attach, 174 uthum_detach, 175 uthum_activate, 176 }; 177 178 int 179 uthum_match(struct device *parent, void *match, void *aux) 180 { 181 struct usb_attach_arg *uaa = aux; 182 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 183 184 if (uthum_lookup(uha->uaa->vendor, uha->uaa->product) == NULL) 185 return UMATCH_NONE; 186 187 #if 0 /* attach only sensor part of HID as uthum* */ 188 #define HUG_UNKNOWN_3 0x0003 189 void *desc; 190 int size; 191 uhidev_get_report_desc(uha->parent, &desc, &size); 192 if (!hid_is_collection(desc, size, uha->reportid, 193 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_UNKNOWN_3))) 194 return (UMATCH_NONE); 195 #undef HUG_UNKNOWN_3 196 #endif 197 198 return (UMATCH_VENDOR_PRODUCT); 199 } 200 201 void 202 uthum_attach(struct device *parent, struct device *self, void *aux) 203 { 204 struct uthum_softc *sc = (struct uthum_softc *)self; 205 struct usb_attach_arg *uaa = aux; 206 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 207 usbd_device_handle dev = uha->parent->sc_udev; 208 int i, size, repid; 209 void *desc; 210 211 sc->sc_udev = dev; 212 sc->sc_hdev.sc_intr = uthum_intr; 213 sc->sc_hdev.sc_parent = uha->parent; 214 sc->sc_hdev.sc_report_id = uha->reportid; 215 sc->sc_num_sensors = 0; 216 217 uhidev_get_report_desc(uha->parent, &desc, &size); 218 repid = uha->reportid; 219 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 220 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 221 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 222 223 printf("\n"); 224 225 if (sc->sc_flen < 32) { 226 /* not sensor interface, just attach */ 227 return; 228 } 229 230 /* maybe unsupported device */ 231 if (uthum_check_device_info(sc) < 0) { 232 DPRINTF(("uthum: unknown device\n")); 233 return; 234 }; 235 236 /* attach sensor */ 237 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 238 sizeof(sc->sc_sensordev.xname)); 239 uthum_setup_sensors(sc); 240 241 /* attach sensors */ 242 for (i = 0; i < UTHUM_MAX_SENSORS; i++) { 243 if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_UNKNOWN) 244 continue; 245 uthum_print_sensorinfo(sc, i); 246 sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID; 247 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor); 248 sc->sc_sensor[i].attached = 1; 249 sc->sc_num_sensors++; 250 } 251 252 if (sc->sc_num_sensors > 0) { 253 /* 0.1Hz */ 254 sc->sc_sensortask = sensor_task_register(sc, uthum_refresh, 6); 255 if (sc->sc_sensortask == NULL) { 256 printf(", unable to register update task\n"); 257 return; 258 } 259 sensordev_install(&sc->sc_sensordev); 260 } 261 262 DPRINTF(("uthum_attach: complete\n")); 263 } 264 265 int 266 uthum_detach(struct device *self, int flags) 267 { 268 struct uthum_softc *sc = (struct uthum_softc *)self; 269 int i, rv = 0; 270 271 if (sc->sc_num_sensors > 0) { 272 wakeup(&sc->sc_sensortask); 273 sensordev_deinstall(&sc->sc_sensordev); 274 for (i = 0; i < UTHUM_MAX_SENSORS; i++) { 275 if (sc->sc_sensor[i].attached) 276 sensor_detach(&sc->sc_sensordev, 277 &sc->sc_sensor[i].sensor); 278 } 279 if (sc->sc_sensortask != NULL) 280 sensor_task_unregister(sc->sc_sensortask); 281 } 282 283 return (rv); 284 } 285 286 int 287 uthum_activate(struct device *self, int act) 288 { 289 struct uthum_softc *sc = (struct uthum_softc *)self; 290 291 switch (act) { 292 case DVACT_DEACTIVATE: 293 sc->sc_dying = 1; 294 break; 295 } 296 return (0); 297 } 298 299 void 300 uthum_intr(struct uhidev *addr, void *ibuf, u_int len) 301 { 302 /* do nothing */ 303 } 304 305 int 306 uthum_issue_cmd(struct uthum_softc *sc, uint8_t target_cmd, int delay) 307 { 308 int i; 309 uint8_t cmdbuf[32]; 310 311 bzero(cmdbuf, sizeof(cmdbuf)); 312 memcpy(cmdbuf, cmd_issue, sizeof(cmd_issue)); 313 if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, 314 cmdbuf, sc->sc_olen)) 315 return EIO; 316 317 bzero(cmdbuf, sizeof(cmdbuf)); 318 cmdbuf[0] = target_cmd; 319 if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, 320 cmdbuf, sc->sc_olen)) 321 return EIO; 322 323 bzero(cmdbuf, sizeof(cmdbuf)); 324 for (i = 0; i < 7; i++) { 325 if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, 326 cmdbuf, sc->sc_olen)) 327 return EIO; 328 } 329 330 /* wait if required */ 331 if (delay > 0) 332 tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1); 333 334 return 0; 335 } 336 337 int 338 uthum_read_data(struct uthum_softc *sc, uint8_t target_cmd, uint8_t *buf, 339 size_t len, int delay) 340 { 341 uint8_t cmdbuf[32], report[256]; 342 343 /* if return buffer is null, do nothing */ 344 if ((buf == NULL) || len == 0) 345 return 0; 346 347 if (uthum_issue_cmd(sc, target_cmd, 50)) 348 return 0; 349 350 bzero(cmdbuf, sizeof(cmdbuf)); 351 memcpy(cmdbuf, cmd_query, sizeof(cmd_query)); 352 if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, 353 cmdbuf, sc->sc_olen)) 354 return EIO; 355 356 /* wait if required */ 357 if (delay > 0) 358 tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1); 359 360 /* get answer */ 361 if (uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 362 report, sc->sc_flen)) 363 return EIO; 364 memcpy(buf, report, len); 365 return 0; 366 } 367 368 int 369 uthum_check_device_info(struct uthum_softc *sc) 370 { 371 struct uthum_dev_info { 372 uint16_t dev_type; 373 uint8_t cal[2][2]; /* calibration offsets */ 374 uint8_t footer; 375 uint8_t padding[25]; 376 } dinfo; 377 int val, dev_type; 378 int retry = 3; 379 380 /* issue query to device */ 381 while (retry) { 382 if (uthum_read_data(sc, CMD_DEVTYPE, (void *)&dinfo, 383 sizeof(struct uthum_dev_info), 0) != 0) { 384 DPRINTF(("uthum: device information query fail.\n")); 385 retry--; 386 continue; 387 } 388 if (dinfo.footer != DEVTYPE_EOF) { 389 /* it will be a bogus entry, retry. */ 390 retry--; 391 } else 392 break; 393 } 394 395 if (retry <= 0) 396 return EIO; 397 398 dev_type = betoh16(dinfo.dev_type); 399 /* TEMPerHUM has 2 different device identifiers, unify them */ 400 if (dev_type == UTHUM_TYPE_TEMPERHUM_2) 401 dev_type = UTHUM_TYPE_TEMPERHUM; 402 403 /* check device type and calibration offset*/ 404 switch (dev_type) { 405 case UTHUM_TYPE_TEMPER2: 406 case UTHUM_TYPE_TEMPERHUM: 407 case UTHUM_TYPE_TEMPERNTC: 408 val = (dinfo.cal[1][0] - UTHUM_CAL_OFFSET) * 100; 409 val += dinfo.cal[1][1] * 10; 410 sc->sc_sensor[1].cal_offset = val; 411 /* fall down, don't break */ 412 case UTHUM_TYPE_TEMPER1: 413 val = (dinfo.cal[0][0] - UTHUM_CAL_OFFSET) * 100; 414 val += dinfo.cal[0][1] * 10; 415 sc->sc_sensor[0].cal_offset = val; 416 sc->sc_device_type = dev_type; 417 break; 418 default: 419 sc->sc_device_type = UTHUM_TYPE_UNKNOWN; 420 printf("uthum: unknown device (devtype = 0x%.2x)\n", 421 dev_type); 422 return EIO; 423 } 424 425 /* device specific init process */ 426 switch (dev_type) { 427 case UTHUM_TYPE_TEMPER1: 428 case UTHUM_TYPE_TEMPERNTC: 429 uthum_issue_cmd(sc, CMD_RESET0, 200); 430 break; 431 case UTHUM_TYPE_TEMPER2: 432 uthum_issue_cmd(sc, CMD_RESET0, 200); 433 uthum_issue_cmd(sc, CMD_RESET1, 200); 434 break; 435 case UTHUM_TYPE_TEMPERHUM: 436 sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = 0; 437 break; 438 }; 439 440 return 0; 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 ticks; 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 ticks = (msb * 256 + lsb) & 0x3fff; 766 return (ticks - 3970); 767 } 768 769 /* return %RH * 1000 */ 770 int 771 uthum_sht1x_rh(uint8_t msb, uint8_t lsb, int temp) 772 { 773 int ticks, rh_l; 774 775 ticks = (msb * 256 + lsb) & 0x0fff; 776 rh_l = (-40000 + 405 * ticks) - ((7 * ticks * ticks) / 250); 777 778 return ((temp - 2500) * (1 + (ticks >> 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 %d.%d degC", 833 s->cal_offset / 100, 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 %d.%d %%RH", 840 s->cal_offset / 100, abs(s->cal_offset % 100)); 841 break; 842 default: 843 printf("unknown"); 844 } 845 printf("\n"); 846 } 847