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