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