Line data Source code
1 : /* $OpenBSD: uthum.c,v 1.32 2017/01/09 14:44:28 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 0 : uthum_match(struct device *parent, void *match, void *aux)
167 : {
168 0 : struct uhidev_attach_arg *uha = aux;
169 :
170 0 : if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
171 0 : return (UMATCH_NONE);
172 :
173 0 : if (uthum_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
174 0 : 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 0 : return (UMATCH_VENDOR_PRODUCT);
188 0 : }
189 :
190 : void
191 0 : uthum_attach(struct device *parent, struct device *self, void *aux)
192 : {
193 0 : struct uthum_softc *sc = (struct uthum_softc *)self;
194 0 : struct usb_attach_arg *uaa = aux;
195 0 : struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
196 0 : struct usbd_device *dev = uha->parent->sc_udev;
197 0 : int i, size, repid;
198 0 : void *desc;
199 :
200 0 : sc->sc_udev = dev;
201 0 : sc->sc_hdev.sc_intr = uthum_intr;
202 0 : sc->sc_hdev.sc_parent = uha->parent;
203 0 : sc->sc_hdev.sc_report_id = uha->reportid;
204 0 : sc->sc_num_sensors = 0;
205 :
206 0 : uhidev_get_report_desc(uha->parent, &desc, &size);
207 0 : repid = uha->reportid;
208 0 : sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
209 0 : sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
210 0 : sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
211 :
212 0 : printf("\n");
213 :
214 0 : if (sc->sc_flen < 32) {
215 : /* not sensor interface, just attach */
216 0 : return;
217 : }
218 :
219 : /* maybe unsupported device */
220 0 : if (uthum_check_device_info(sc) < 0) {
221 : DPRINTF(("uthum: unknown device\n"));
222 0 : return;
223 : };
224 :
225 : /* attach sensor */
226 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
227 : sizeof(sc->sc_sensordev.xname));
228 0 : uthum_setup_sensors(sc);
229 :
230 : /* attach sensors */
231 0 : for (i = 0; i < UTHUM_MAX_SENSORS; i++) {
232 0 : if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_UNKNOWN)
233 : continue;
234 0 : uthum_print_sensorinfo(sc, i);
235 0 : sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID;
236 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor);
237 0 : sc->sc_sensor[i].attached = 1;
238 0 : sc->sc_num_sensors++;
239 0 : }
240 :
241 0 : if (sc->sc_num_sensors > 0) {
242 : /* 0.1Hz */
243 0 : sc->sc_sensortask = sensor_task_register(sc, uthum_refresh, 6);
244 0 : if (sc->sc_sensortask == NULL) {
245 0 : printf(", unable to register update task\n");
246 0 : return;
247 : }
248 0 : sensordev_install(&sc->sc_sensordev);
249 0 : }
250 :
251 : DPRINTF(("uthum_attach: complete\n"));
252 0 : }
253 :
254 : int
255 0 : uthum_detach(struct device *self, int flags)
256 : {
257 0 : struct uthum_softc *sc = (struct uthum_softc *)self;
258 : int i, rv = 0;
259 :
260 0 : if (sc->sc_num_sensors > 0) {
261 0 : wakeup(&sc->sc_sensortask);
262 0 : sensordev_deinstall(&sc->sc_sensordev);
263 0 : for (i = 0; i < UTHUM_MAX_SENSORS; i++) {
264 0 : if (sc->sc_sensor[i].attached)
265 0 : sensor_detach(&sc->sc_sensordev,
266 0 : &sc->sc_sensor[i].sensor);
267 : }
268 0 : if (sc->sc_sensortask != NULL)
269 0 : sensor_task_unregister(sc->sc_sensortask);
270 : }
271 :
272 0 : uthum_reset_device(sc);
273 :
274 0 : return (rv);
275 : }
276 :
277 : void
278 0 : uthum_intr(struct uhidev *addr, void *ibuf, u_int len)
279 : {
280 : /* do nothing */
281 0 : }
282 :
283 : int
284 0 : uthum_issue_cmd(struct uthum_softc *sc, uint8_t target_cmd, int delay)
285 : {
286 0 : uint8_t cmdbuf[32];
287 : int i, actlen;
288 :
289 0 : bzero(cmdbuf, sizeof(cmdbuf));
290 0 : memcpy(cmdbuf, cmd_issue, sizeof(cmd_issue));
291 0 : actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
292 0 : sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen);
293 0 : if (actlen != sc->sc_olen)
294 0 : return EIO;
295 :
296 0 : bzero(cmdbuf, sizeof(cmdbuf));
297 0 : cmdbuf[0] = target_cmd;
298 0 : actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
299 0 : sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen);
300 0 : if (actlen != sc->sc_olen)
301 0 : return EIO;
302 :
303 0 : bzero(cmdbuf, sizeof(cmdbuf));
304 0 : for (i = 0; i < 7; i++) {
305 0 : actlen = uhidev_set_report(sc->sc_hdev.sc_parent,
306 0 : UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmdbuf,
307 0 : sc->sc_olen);
308 0 : if (actlen != sc->sc_olen)
309 0 : return EIO;
310 : }
311 :
312 : /* wait if required */
313 0 : if (delay > 0)
314 0 : tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1);
315 :
316 0 : return 0;
317 0 : }
318 :
319 : int
320 0 : uthum_read_data(struct uthum_softc *sc, uint8_t target_cmd, uint8_t *buf,
321 : size_t len, int delay)
322 : {
323 0 : uint8_t cmdbuf[32], report[256];
324 :
325 : /* if return buffer is null, do nothing */
326 0 : if ((buf == NULL) || len == 0)
327 0 : return 0;
328 :
329 0 : if (uthum_issue_cmd(sc, target_cmd, 50))
330 0 : return 0;
331 :
332 0 : bzero(cmdbuf, sizeof(cmdbuf));
333 0 : memcpy(cmdbuf, cmd_query, sizeof(cmd_query));
334 0 : if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
335 0 : sc->sc_hdev.sc_report_id, cmdbuf, sc->sc_olen) != sc->sc_olen)
336 0 : return EIO;
337 :
338 : /* wait if required */
339 0 : if (delay > 0)
340 0 : tsleep(&sc->sc_sensortask, 0, "uthum", (delay*hz+999)/1000 + 1);
341 :
342 : /* get answer */
343 0 : if (uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
344 0 : sc->sc_hdev.sc_report_id, report, sc->sc_flen) != sc->sc_flen)
345 0 : return EIO;
346 0 : memcpy(buf, report, len);
347 0 : return 0;
348 0 : }
349 :
350 : int
351 0 : uthum_check_device_info(struct uthum_softc *sc)
352 : {
353 0 : struct uthum_dev_info {
354 : uint16_t dev_type;
355 : uint8_t cal[2][2]; /* calibration offsets */
356 : uint8_t footer;
357 : uint8_t padding[25];
358 : } dinfo;
359 : int val, dev_type;
360 : int retry = 3;
361 :
362 : /* issue query to device */
363 0 : while (retry) {
364 0 : if (uthum_read_data(sc, CMD_DEVTYPE, (void *)&dinfo,
365 0 : sizeof(struct uthum_dev_info), 0) != 0) {
366 : DPRINTF(("uthum: device information query fail.\n"));
367 0 : retry--;
368 0 : continue;
369 : }
370 0 : if (dinfo.footer != DEVTYPE_EOF) {
371 : /* it will be a bogus entry, retry. */
372 0 : retry--;
373 : } else
374 : break;
375 : }
376 :
377 0 : if (retry <= 0)
378 0 : return EIO;
379 :
380 0 : dev_type = betoh16(dinfo.dev_type);
381 : /* TEMPerHUM has 2 different device identifiers, unify them */
382 0 : if (dev_type == UTHUM_TYPE_TEMPERHUM_2)
383 : dev_type = UTHUM_TYPE_TEMPERHUM;
384 :
385 : /* check device type and calibration offset*/
386 0 : switch (dev_type) {
387 : case UTHUM_TYPE_TEMPER2:
388 : case UTHUM_TYPE_TEMPERHUM:
389 : case UTHUM_TYPE_TEMPERNTC:
390 0 : val = (dinfo.cal[1][0] - UTHUM_CAL_OFFSET) * 100;
391 0 : val += dinfo.cal[1][1] * 10;
392 0 : sc->sc_sensor[1].cal_offset = val;
393 : /* fall down, don't break */
394 : case UTHUM_TYPE_TEMPER1:
395 0 : val = (dinfo.cal[0][0] - UTHUM_CAL_OFFSET) * 100;
396 0 : val += dinfo.cal[0][1] * 10;
397 0 : sc->sc_sensor[0].cal_offset = val;
398 0 : sc->sc_device_type = dev_type;
399 : break;
400 : default:
401 0 : sc->sc_device_type = UTHUM_TYPE_UNKNOWN;
402 0 : printf("uthum: unknown device (devtype = 0x%.2x)\n",
403 : dev_type);
404 0 : return EIO;
405 : }
406 :
407 : /* device specific init process */
408 0 : switch (dev_type) {
409 : case UTHUM_TYPE_TEMPERHUM:
410 0 : sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = 0;
411 0 : break;
412 : };
413 :
414 0 : uthum_reset_device(sc);
415 :
416 0 : return 0;
417 0 : };
418 :
419 : void
420 0 : uthum_reset_device(struct uthum_softc *sc)
421 : {
422 0 : switch (sc->sc_device_type) {
423 : case UTHUM_TYPE_TEMPER1:
424 : case UTHUM_TYPE_TEMPERNTC:
425 0 : uthum_issue_cmd(sc, CMD_RESET0, 200);
426 0 : break;
427 : case UTHUM_TYPE_TEMPER2:
428 0 : uthum_issue_cmd(sc, CMD_RESET0, 200);
429 0 : uthum_issue_cmd(sc, CMD_RESET1, 200);
430 0 : break;
431 : }
432 0 : }
433 :
434 : void
435 0 : uthum_setup_sensors(struct uthum_softc *sc)
436 : {
437 : int i;
438 :
439 0 : for (i = 0; i < UTHUM_MAX_SENSORS; i++)
440 0 : sc->sc_sensor[i].dev_type = UTHUM_SENSOR_UNKNOWN;
441 :
442 0 : switch (sc->sc_device_type) {
443 : case UTHUM_TYPE_TEMPER2: /* 2 temperature sensors */
444 0 : sc->sc_sensor[UTHUM_TEMPER_OUTER].dev_type =
445 : UTHUM_SENSOR_DS75;
446 0 : sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.type =
447 : SENSOR_TEMP;
448 0 : strlcpy(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc,
449 : "outer",
450 : sizeof(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc));
451 : /* fall down */
452 : case UTHUM_TYPE_TEMPER1: /* 1 temperature sensor */
453 0 : sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type =
454 : UTHUM_SENSOR_DS75;
455 0 : sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.type =
456 : SENSOR_TEMP;
457 0 : strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc,
458 : "inner",
459 : sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc));
460 0 : break;
461 : case UTHUM_TYPE_TEMPERHUM:
462 : /* 1 temperature sensor and 1 humidity sensor */
463 0 : for (i = 0; i < 2; i++)
464 0 : sc->sc_sensor[i].dev_type = UTHUM_SENSOR_SHT1X;
465 0 : sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.type = SENSOR_TEMP;
466 0 : sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.type =
467 : SENSOR_HUMIDITY;
468 0 : strlcpy(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc,
469 : "RH",
470 : sizeof(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc));
471 0 : break;
472 : case UTHUM_TYPE_TEMPERNTC:
473 : /* 2 temperature sensors */
474 0 : for (i = 0; i < 2; i++)
475 0 : sc->sc_sensor[i].sensor.type = SENSOR_TEMP;
476 0 : sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type =
477 : UTHUM_SENSOR_DS75;
478 0 : sc->sc_sensor[UTHUM_TEMPER_NTC].dev_type =
479 : UTHUM_SENSOR_NTC;
480 0 : strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc,
481 : "inner",
482 : sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc));
483 0 : strlcpy(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc,
484 : "outer/ntc",
485 : sizeof(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc));
486 :
487 : /* sensor state tuning */
488 0 : for (i = 0; i < 4; i++)
489 0 : uthum_issue_cmd(sc, TEMPERNTC_MODE_BASE, 50);
490 0 : sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = TEMPERNTC_MODE_BASE;
491 0 : if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, NULL))
492 : DPRINTF(("uthum: NTC sensor tuning failed\n"));
493 0 : uthum_issue_cmd(sc, CMD_TEMPERNTC_MODE_DONE, 100);
494 0 : break;
495 : default:
496 : /* do nothing */
497 : break;
498 : }
499 0 : }
500 :
501 : int
502 0 : uthum_ntc_getdata(struct uthum_softc *sc, int *val)
503 : {
504 0 : uint8_t buf[8];
505 :
506 0 : if (val == NULL)
507 0 : return EIO;
508 :
509 : /* get sensor value */
510 0 : if (uthum_read_data(sc, CMD_GETDATA_NTC, buf, sizeof(buf), 10) != 0) {
511 : DPRINTF(("uthum: data read fail\n"));
512 0 : return EIO;
513 : }
514 :
515 : /* check data integrity */
516 0 : if (buf[2] != CMD_GETDATA_EOF2) {
517 : DPRINTF(("uthum: broken ntc data 0x%.2x 0x%.2x 0x%.2x\n",
518 : buf[0], buf[1], buf[2]));
519 0 : return EIO;
520 : }
521 :
522 0 : *val = (buf[0] << 8) + buf[1];
523 0 : return 0;
524 0 : }
525 :
526 : int
527 0 : uthum_ntc_tuning(struct uthum_softc *sc, int sensor, int *val)
528 : {
529 : struct uthum_sensor *s;
530 0 : int done, state, ostate, curval;
531 : int retry = 3;
532 :
533 0 : s = &sc->sc_sensor[sensor];
534 0 : state = s->cur_state;
535 :
536 : /* get current sensor value */
537 0 : if (val == NULL) {
538 0 : while (retry) {
539 0 : if (uthum_ntc_getdata(sc, &curval)) {
540 0 : retry--;
541 0 : continue;
542 : } else
543 : break;
544 : }
545 0 : if (retry <= 0)
546 0 : return EIO;
547 : } else {
548 0 : curval = *val;
549 : }
550 :
551 : /* no state change is required */
552 0 : if ((curval >= UTHUM_NTC_MIN_THRESHOLD) &&
553 0 : (curval <= UTHUM_NTC_MAX_THRESHOLD)) {
554 0 : return 0;
555 : }
556 :
557 0 : if (((curval < UTHUM_NTC_MIN_THRESHOLD) &&
558 0 : (state == TEMPERNTC_MODE_MAX)) ||
559 0 : ((curval > UTHUM_NTC_MAX_THRESHOLD) &&
560 0 : (state == TEMPERNTC_MODE_BASE)))
561 0 : return 0;
562 :
563 : DPRINTF(("uthum: ntc tuning start. cur state = 0x%.2x, val = 0x%.4x\n",
564 : state, curval));
565 :
566 : /* tuning loop */
567 : ostate = state;
568 : done = 0;
569 0 : while (!done) {
570 0 : if (curval < UTHUM_NTC_MIN_THRESHOLD) {
571 0 : if (state == TEMPERNTC_MODE_MAX)
572 0 : done++;
573 : else
574 0 : state++;
575 0 : } else if (curval > UTHUM_NTC_MAX_THRESHOLD) {
576 0 : if (state == TEMPERNTC_MODE_BASE)
577 0 : done++;
578 : else
579 0 : state--;
580 : } else {
581 0 : uthum_ntc_getdata(sc, &curval);
582 0 : if ((curval >= UTHUM_NTC_MIN_THRESHOLD) &&
583 0 : (curval <= UTHUM_NTC_MAX_THRESHOLD))
584 0 : done++;
585 : }
586 :
587 : /* update state */
588 0 : if (state != ostate) {
589 0 : uthum_issue_cmd(sc, state, 50);
590 0 : uthum_issue_cmd(sc, state, 50);
591 0 : uthum_ntc_getdata(sc, &curval);
592 0 : }
593 : ostate = state;
594 : }
595 :
596 : DPRINTF(("uthum: ntc tuning done. state change: 0x%.2x->0x%.2x\n",
597 : s->cur_state, state));
598 0 : s->cur_state = state;
599 0 : if (val != NULL)
600 0 : *val = curval;
601 :
602 0 : return 0;
603 0 : }
604 :
605 : void
606 0 : uthum_refresh(void *arg)
607 : {
608 0 : struct uthum_softc *sc = arg;
609 : int i;
610 :
611 0 : switch (sc->sc_device_type) {
612 : case UTHUM_TYPE_TEMPER1:
613 : case UTHUM_TYPE_TEMPER2:
614 : case UTHUM_TYPE_TEMPERNTC:
615 0 : for (i = 0; i < sc->sc_num_sensors; i++) {
616 0 : if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_DS75)
617 0 : uthum_refresh_temper(sc, i);
618 0 : else if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_NTC)
619 0 : uthum_refresh_temperntc(sc, i);
620 : }
621 : break;
622 : case UTHUM_TYPE_TEMPERHUM:
623 0 : uthum_refresh_temperhum(sc);
624 0 : break;
625 : default:
626 : break;
627 : /* never reach */
628 : }
629 0 : }
630 :
631 : void
632 0 : uthum_refresh_temperhum(struct uthum_softc *sc)
633 : {
634 0 : uint8_t buf[8];
635 : int temp, rh;
636 :
637 0 : if (uthum_read_data(sc, CMD_GETDATA, buf, sizeof(buf), 1000) != 0) {
638 : DPRINTF(("uthum: data read fail\n"));
639 0 : sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags
640 0 : |= SENSOR_FINVALID;
641 0 : sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags
642 0 : |= SENSOR_FINVALID;
643 0 : return;
644 : }
645 :
646 0 : temp = uthum_sht1x_temp(buf[0], buf[1]);
647 0 : rh = uthum_sht1x_rh(buf[2], buf[3], temp);
648 :
649 : /* apply calibration offsets */
650 0 : temp += sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].cal_offset;
651 0 : rh += sc->sc_sensor[UTHUM_TEMPERHUM_HUM].cal_offset;
652 :
653 0 : sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.value =
654 0 : (temp * 10000) + 273150000;
655 0 : sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags &= ~SENSOR_FINVALID;
656 0 : sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.value = rh;
657 0 : sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags &= ~SENSOR_FINVALID;
658 0 : }
659 :
660 : void
661 0 : uthum_refresh_temper(struct uthum_softc *sc, int sensor)
662 : {
663 0 : uint8_t buf[8];
664 : uint8_t cmd;
665 : int temp;
666 :
667 0 : if (sensor == UTHUM_TEMPER_INNER)
668 0 : cmd = CMD_GETDATA_INNER;
669 0 : else if (sensor == UTHUM_TEMPER_OUTER)
670 : cmd = CMD_GETDATA_OUTER;
671 : else
672 0 : return;
673 :
674 : /* get sensor value */
675 0 : if (uthum_read_data(sc, cmd, buf, sizeof(buf), 1000) != 0) {
676 : DPRINTF(("uthum: data read fail\n"));
677 0 : sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
678 0 : return;
679 : }
680 :
681 : /* check integrity */
682 0 : if (buf[2] != CMD_GETDATA_EOF) {
683 : DPRINTF(("uthum: broken ds75 data: 0x%.2x 0x%.2x 0x%.2x\n",
684 : buf[0], buf[1], buf[2]));
685 0 : sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
686 0 : return;
687 : }
688 0 : temp = uthum_ds75_temp(buf[0], buf[1]);
689 :
690 : /* apply calibration offset */
691 0 : temp += sc->sc_sensor[sensor].cal_offset;
692 :
693 0 : sc->sc_sensor[sensor].sensor.value = (temp * 10000) + 273150000;
694 0 : sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID;
695 0 : }
696 :
697 : void
698 0 : uthum_refresh_temperntc(struct uthum_softc *sc, int sensor)
699 : {
700 0 : int val;
701 : int64_t temp;
702 :
703 : /* get sensor data */
704 0 : if (uthum_ntc_getdata(sc, &val)) {
705 : DPRINTF(("uthum: ntc data read fail\n"));
706 0 : sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
707 0 : return;
708 : }
709 :
710 : /* adjust sensor state */
711 0 : if ((val < UTHUM_NTC_MIN_THRESHOLD) ||
712 0 : (val > UTHUM_NTC_MAX_THRESHOLD)) {
713 0 : if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, &val)) {
714 : DPRINTF(("uthum: NTC sensor tuning failed\n"));
715 0 : sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
716 0 : return;
717 : }
718 : }
719 :
720 0 : temp = uthum_ntc_temp(val, sc->sc_sensor[sensor].cur_state);
721 0 : if (temp == 0) {
722 : /* XXX: work around. */
723 0 : sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
724 0 : } else {
725 : /* apply calibration offset */
726 0 : temp += sc->sc_sensor[sensor].cal_offset * 10000;
727 0 : sc->sc_sensor[sensor].sensor.value = temp;
728 0 : sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID;
729 : }
730 0 : }
731 :
732 : /* return C-degree * 100 value */
733 : int
734 0 : uthum_ds75_temp(uint8_t msb, uint8_t lsb)
735 : {
736 : /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */
737 0 : return (msb * 100) + ((lsb >> 4) * 25 / 4);
738 : }
739 :
740 : /* return C-degree * 100 value */
741 : int
742 0 : uthum_sht1x_temp(uint8_t msb, uint8_t lsb)
743 : {
744 : int nticks;
745 :
746 : /* sensor device VDD-bias value table
747 : * ----------------------------------------------
748 : * VDD 2.5V 3.0V 3.5V 4.0V 5.0V
749 : * bias -3940 -3960 -3970 -3980 -4010
750 : * ----------------------------------------------
751 : *
752 : * as the VDD of the SHT10 on my TEMPerHUM is 3.43V +/- 0.05V,
753 : * bias -3970 will be best for that device.
754 : */
755 :
756 0 : nticks = (msb * 256 + lsb) & 0x3fff;
757 0 : return (nticks - 3970);
758 : }
759 :
760 : /* return %RH * 1000 */
761 : int
762 0 : uthum_sht1x_rh(uint8_t msb, uint8_t lsb, int temp)
763 : {
764 : int nticks, rh_l;
765 :
766 0 : nticks = (msb * 256 + lsb) & 0x0fff;
767 0 : rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250);
768 :
769 0 : return ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10;
770 : }
771 :
772 : /* return muK */
773 : int64_t
774 0 : uthum_ntc_temp(int64_t val, int state)
775 : {
776 : int64_t temp = 0;
777 :
778 0 : switch (state) {
779 : case TEMPERNTC_MODE_BASE: /* 0x61 */
780 : case TEMPERNTC_MODE_BASE+1: /* 0x62 */
781 : case TEMPERNTC_MODE_BASE+2: /* 0x63 */
782 : case TEMPERNTC_MODE_BASE+3: /* 0x64 */
783 : /* XXX, no data */
784 : temp = -273150000;
785 0 : break;
786 : case TEMPERNTC_MODE_BASE+4: /* 0x65 */
787 0 : temp = ((val * val * 2977) / 100000) - (val * 4300) + 152450000;
788 0 : break;
789 : case TEMPERNTC_MODE_BASE+5: /* 0x66 */
790 0 : temp = ((val * val * 3887) / 100000) - (val * 5300) + 197590000;
791 0 : break;
792 : case TEMPERNTC_MODE_BASE+6: /* 0x67 */
793 0 : temp = ((val * val * 3495) / 100000) - (val * 5000) + 210590000;
794 0 : break;
795 : case TEMPERNTC_MODE_BASE+7: /* 0x68 */
796 0 : if (val < UTHUM_NTC_MIN_THRESHOLD)
797 0 : temp = (val * -1700) + 149630000;
798 : else
799 0 : temp = ((val * val * 3257) / 100000) - (val * 4900) +
800 : 230470000;
801 : break;
802 : default:
803 : DPRINTF(("NTC state error, unknown state 0x%.2x\n", state));
804 : break;
805 : }
806 :
807 : /* convert muC->muK value */
808 0 : return temp + 273150000;
809 : }
810 :
811 : void
812 0 : uthum_print_sensorinfo(struct uthum_softc *sc, int num)
813 : {
814 : struct uthum_sensor *s;
815 0 : s = &sc->sc_sensor[num];
816 :
817 0 : printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
818 0 : switch (s->sensor.type) {
819 : case SENSOR_TEMP:
820 0 : printf("type %s (temperature)",
821 0 : uthum_sensor_type_s[s->dev_type]);
822 0 : if (s->cal_offset)
823 0 : printf(", calibration offset %c%d.%d degC",
824 0 : (s->cal_offset < 0) ? '-' : '+',
825 0 : abs(s->cal_offset / 100),
826 0 : abs(s->cal_offset % 100));
827 : break;
828 : case SENSOR_HUMIDITY:
829 0 : printf("type %s (humidity)",
830 0 : uthum_sensor_type_s[s->dev_type]);
831 0 : if (s->cal_offset)
832 0 : printf("calibration offset %c%d.%d %%RH",
833 0 : (s->cal_offset < 0) ? '-' : '+',
834 0 : abs(s->cal_offset / 100),
835 0 : abs(s->cal_offset % 100));
836 : break;
837 : default:
838 0 : printf("unknown");
839 0 : }
840 0 : printf("\n");
841 0 : }
|