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