xref: /openbsd-src/sys/dev/usb/uthum.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
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