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