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