xref: /openbsd-src/sys/dev/usb/ugold.c (revision 513cf72f82c16dd38f89b0e7a0ec4a163d87f81f)
1 /*	$OpenBSD: ugold.c,v 1.28 2024/05/23 03:21:09 jsg Exp $   */
2 
3 /*
4  * Copyright (c) 2013 Takayoshi SASANO <uaa@openbsd.org>
5  * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
6  * Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
7  * Copyright (c) 2023 Miodrag Vallat.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 /*
23  * Driver for Microdia's HID based TEMPer and TEMPerHUM temperature and
24  * humidity sensors
25  */
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/device.h>
30 #include <sys/sensors.h>
31 
32 #include <dev/usb/usb.h>
33 #include <dev/usb/usbhid.h>
34 
35 #include <dev/usb/usbdi.h>
36 #include <dev/usb/usbdevs.h>
37 #include <dev/usb/uhidev.h>
38 
39 #define UGOLD_INNER		0
40 #define UGOLD_OUTER		1
41 #define UGOLD_HUM		1
42 #define UGOLD_MAX_SENSORS	2
43 
44 #define UGOLD_CMD_DATA		0x80
45 #define UGOLD_CMD_INIT		0x82
46 
47 #define UGOLD_TYPE_INVALID	-1
48 #define UGOLD_TYPE_SI7005	1
49 #define UGOLD_TYPE_SI7006	2
50 #define UGOLD_TYPE_SHT1X	3
51 #define UGOLD_TYPE_GOLD		4
52 #define UGOLD_TYPE_TEMPERX	5
53 #define UGOLD_TYPE_DS75		6
54 
55 /*
56  * This driver uses three known commands for the TEMPer and TEMPerHUM
57  * devices.
58  *
59  * The first byte of the answer corresponds to the command and the
60  * second one seems to be the size (in bytes) of the answer.
61  *
62  * The device always sends 8 bytes and if the length of the answer
63  * is less than that, it just leaves the last bytes untouched.  That
64  * is why most of the time the last n bytes of the answers are the
65  * same.
66  *
67  * The type command below seems to generate two answers with a
68  * string corresponding to the device, for example:
69  *	'TEMPer1F' and '1.1Per1F' (here Per1F is repeated).
70  */
71 static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 };
72 static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 };
73 static uint8_t cmd_type[8] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 };
74 /*
75  * The following command is also recognized and reports some kind of status
76  * byte (i.e. 87 xx 00 00 00 00 00 00).
77 			     { 0x01, 0x87, 0xee, 0x01, 0x00, 0x00, 0x00, 0x00 };
78  */
79 
80 struct ugold_softc;
81 
82 struct ugold_softc {
83 	struct uhidev		 sc_hdev;
84 	struct usbd_device	*sc_udev;
85 
86 	int			 sc_num_sensors;
87 	int			 sc_type;
88 
89 	char			 sc_model[16 + 1];
90 	unsigned int		 sc_model_len;
91 
92 	struct ksensor		 sc_sensor[UGOLD_MAX_SENSORS];
93 	struct ksensordev	 sc_sensordev;
94 	struct sensor_task	*sc_sensortask;
95 
96 	void		(*sc_intr)(struct ugold_softc *, uint8_t *, u_int);
97 };
98 
99 const struct usb_devno ugold_devs[] = {
100 	{ USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER },
101 	{ USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPERHUM },
102 	{ USB_VENDOR_PCSENSORS, USB_PRODUCT_PCSENSORS_TEMPER },
103 	{ USB_VENDOR_RDING, USB_PRODUCT_RDING_TEMPER },
104 	{ USB_VENDOR_WCH2, USB_PRODUCT_WCH2_TEMPER },
105 };
106 
107 int 	ugold_match(struct device *, void *, void *);
108 void	ugold_attach(struct device *, struct device *, void *);
109 int 	ugold_detach(struct device *, int);
110 
111 void	ugold_setup_sensors(struct ugold_softc *);
112 void	ugold_intr(struct uhidev *, void *, u_int);
113 void	ugold_ds75_intr(struct ugold_softc *, uint8_t *, u_int);
114 void	ugold_si700x_intr(struct ugold_softc *, uint8_t *, u_int);
115 void	ugold_refresh(void *);
116 
117 int	ugold_issue_cmd(struct ugold_softc *, uint8_t *, int);
118 
119 struct cfdriver ugold_cd = {
120 	NULL, "ugold", DV_DULL
121 };
122 
123 const struct cfattach ugold_ca = {
124 	sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach,
125 };
126 
127 int
128 ugold_match(struct device *parent, void *match, void *aux)
129 {
130 	struct uhidev_attach_arg *uha = aux;
131 	int size;
132 	void *desc;
133 
134 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
135 		return (UMATCH_NONE);
136 
137 	if (usb_lookup(ugold_devs, uha->uaa->vendor, uha->uaa->product) == NULL)
138 		return (UMATCH_NONE);
139 
140 	/*
141 	 * XXX Only match the sensor interface.
142 	 *
143 	 * Does it make sense to attach various uhidev(4) to these
144 	 * non-standard HID devices?
145 	 */
146 	uhidev_get_report_desc(uha->parent, &desc, &size);
147 	if (hid_is_collection(desc, size, uha->reportid,
148 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
149 		return (UMATCH_NONE);
150 
151 	return (UMATCH_VENDOR_PRODUCT);
152 
153 }
154 
155 void
156 ugold_attach(struct device *parent, struct device *self, void *aux)
157 {
158 	struct ugold_softc *sc = (struct ugold_softc *)self;
159 	struct uhidev_attach_arg *uha = aux;
160 	int size, repid;
161 	void *desc;
162 
163 	sc->sc_udev = uha->parent->sc_udev;
164 	sc->sc_hdev.sc_parent = uha->parent;
165 	sc->sc_hdev.sc_report_id = uha->reportid;
166 	sc->sc_hdev.sc_intr = ugold_intr;
167 	switch (uha->uaa->product) {
168 	case USB_PRODUCT_MICRODIA_TEMPER:
169 		sc->sc_intr = ugold_ds75_intr;
170 		break;
171 	case USB_PRODUCT_MICRODIA_TEMPERHUM:
172 	case USB_PRODUCT_PCSENSORS_TEMPER:
173 	case USB_PRODUCT_RDING_TEMPER:
174 	case USB_PRODUCT_WCH2_TEMPER:
175 		sc->sc_intr = ugold_si700x_intr;
176 		break;
177 	default:
178 		printf(", unknown product\n");
179 		return;
180 	}
181 
182 	uhidev_get_report_desc(uha->parent, &desc, &size);
183 	repid = uha->reportid;
184 	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
185 	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
186 	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
187 
188 	if (uhidev_open(&sc->sc_hdev)) {
189 		printf(", unable to open interrupt pipe\n");
190 		return;
191 	}
192 
193 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
194 	    sizeof(sc->sc_sensordev.xname));
195 
196 	/* 0.166Hz */
197 	sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6);
198 	if (sc->sc_sensortask == NULL) {
199 		printf(", unable to register update task\n");
200 		return;
201 	}
202 	printf("\n");
203 
204 	/* speed up sensor identification */
205 	ugold_refresh(sc);
206 
207 	sensordev_install(&sc->sc_sensordev);
208 }
209 
210 int
211 ugold_detach(struct device *self, int flags)
212 {
213 	struct ugold_softc *sc = (struct ugold_softc *)self;
214 	int i;
215 
216 	if (sc->sc_sensortask != NULL) {
217 		sensor_task_unregister(sc->sc_sensortask);
218 		sensordev_deinstall(&sc->sc_sensordev);
219 	}
220 
221 	if (sc->sc_type != UGOLD_TYPE_INVALID) {
222 		for (i = 0; i < sc->sc_num_sensors; i++)
223 			sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]);
224 	}
225 
226 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
227 		uhidev_close(&sc->sc_hdev);
228 
229 	return (0);
230 }
231 
232 void
233 ugold_setup_sensors(struct ugold_softc *sc)
234 {
235 	int i;
236 
237 	switch (sc->sc_type) {
238 	default:
239 		return;
240 	case UGOLD_TYPE_SI7005:
241 	case UGOLD_TYPE_SI7006:
242 	case UGOLD_TYPE_SHT1X:
243 	case UGOLD_TYPE_TEMPERX:
244 		/* 1 temperature and 1 humidity sensor */
245 		sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
246 		strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
247 		    sizeof(sc->sc_sensor[UGOLD_INNER].desc));
248 		sc->sc_sensor[UGOLD_HUM].type = SENSOR_HUMIDITY;
249 		strlcpy(sc->sc_sensor[UGOLD_HUM].desc, "RH",
250 		    sizeof(sc->sc_sensor[UGOLD_HUM].desc));
251 		break;
252 	case UGOLD_TYPE_GOLD:
253 	case UGOLD_TYPE_DS75:
254 		/* up to 2 temperature sensors */
255 		sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
256 		strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
257 		    sizeof(sc->sc_sensor[UGOLD_INNER].desc));
258 		sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP;
259 		strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer",
260 		    sizeof(sc->sc_sensor[UGOLD_OUTER].desc));
261 		break;
262 	}
263 	for (i = 0; i < sc->sc_num_sensors; i++) {
264 		sc->sc_sensor[i].flags |= SENSOR_FINVALID;
265 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
266 	}
267 }
268 
269 static void
270 strnvis(char *dst, const char *src, size_t siz)
271 {
272 	char *start, *end;
273 	int c;
274 
275 	for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
276 		if (c >= 0x20 && c <= 0x7f) {
277 			if (c == '\\') {
278 				/* need space for the extra '\\' */
279 				if (dst + 2 > end)
280 					break;
281 				*dst++ = '\\';
282 			}
283 			*dst++ = c;
284 		} else {
285 			if (dst + 4 > end)
286 				break;
287 			*dst++ = '\\';
288 			*dst++ = ((u_char)c >> 6 & 07) + '0';
289 			*dst++ = ((u_char)c >> 3 & 07) + '0';
290 			*dst++ = ((u_char)c & 07) + '0';
291 		}
292 		src++;
293 	}
294 	if (siz > 0)
295 		*dst = '\0';
296 }
297 
298 static int
299 ugold_ds75_temp(uint8_t msb, uint8_t lsb)
300 {
301 	/* DS75 12bit precision mode: 0.0625 degrees Celsius ticks */
302 	return (((msb * 100) + ((lsb >> 4) * 25 / 4)) * 10000) + 273150000;
303 }
304 
305 static void
306 ugold_ds75_type(struct ugold_softc *sc)
307 {
308 	char model[4 * sizeof(sc->sc_model) + 1];
309 
310 	strnvis(model, sc->sc_model, sizeof model);
311 
312 	if (memcmp(sc->sc_model, "TEMPer1F", 8) == 0 ||
313 	    memcmp(sc->sc_model, "TEMPer2F", 8) == 0 ||
314 	    memcmp(sc->sc_model, "TEMPerF1", 8) == 0) {
315 		sc->sc_type = UGOLD_TYPE_DS75;
316 		ugold_setup_sensors(sc);
317 		printf("%s: \"%s\", %d sensor%s"
318 		       " type ds75/12bit (temperature)\n",
319 		    sc->sc_hdev.sc_dev.dv_xname, model, sc->sc_num_sensors,
320 		    (sc->sc_num_sensors == 1) ? "" : "s");
321 		ugold_refresh(sc);
322 		return;
323 	}
324 
325 	printf("%s: unknown model \"%s\"\n",
326 	    sc->sc_hdev.sc_dev.dv_xname, model);
327 	sc->sc_num_sensors = 0;
328 	sc->sc_type = UGOLD_TYPE_INVALID;
329 }
330 
331 void
332 ugold_ds75_intr(struct ugold_softc *sc, uint8_t *buf, u_int len)
333 {
334 	int temp;
335 
336 	switch (buf[0]) {
337 	case UGOLD_CMD_INIT:
338 		if (sc->sc_num_sensors != 0)
339 			break;
340 		sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS) /* XXX */;
341 		ugold_refresh(sc);
342 		break;
343 	case UGOLD_CMD_DATA:
344 		switch (buf[1]) {
345 		case 4:
346 			temp = ugold_ds75_temp(buf[4], buf[5]);
347 			sc->sc_sensor[UGOLD_OUTER].value = temp;
348 			sc->sc_sensor[UGOLD_OUTER].flags &= ~SENSOR_FINVALID;
349 			/* FALLTHROUGH */
350 		case 2:
351 			temp = ugold_ds75_temp(buf[2], buf[3]);
352 			sc->sc_sensor[UGOLD_INNER].value = temp;
353 			sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID;
354 			break;
355 		default:
356 #ifdef UGOLD_DEBUG
357 			printf("%s: invalid data length (%d bytes)\n",
358 				sc->sc_hdev.sc_dev.dv_xname, buf[1]);
359 #endif
360 			break;
361 		}
362 		break;
363 	default:
364 		ugold_ds75_type(sc);
365 		break;
366 	}
367 }
368 
369 static int
370 ugold_si700x_temp(int type, uint8_t msb, uint8_t lsb)
371 {
372 	int temp = msb * 256 + lsb;
373 
374 	switch (type) { /* convert to mdegC */
375 	case UGOLD_TYPE_SI7005: /* 14bit 32 codes per degC 0x0000 = -50 degC */
376 		temp = (((temp & 0x3fff) * 1000) / 32) - 50000;
377 		break;
378 	case UGOLD_TYPE_SI7006: /* 14bit and status bit */
379 		temp = (((temp & ~3) * 21965) / 8192) - 46850;
380 		break;
381 	case UGOLD_TYPE_SHT1X:
382 		temp = (temp * 1000) / 256;
383 		break;
384 	case UGOLD_TYPE_GOLD:
385 	case UGOLD_TYPE_TEMPERX:
386 		/* temp = temp / 100 to get degC, then * 1000 to get mdegC */
387 		temp = temp * 10;
388 		break;
389 	default:
390 		temp = 0;
391 	}
392 
393 	return temp;
394 }
395 
396 static int
397 ugold_si700x_rhum(int type, uint8_t msb, uint8_t lsb, int temp)
398 {
399 	int rhum = msb * 256 + lsb;
400 
401 	switch (type) { /* convert to m%RH */
402 	case UGOLD_TYPE_SI7005: /* 12bit 16 codes per %RH 0x0000 = -24 %RH */
403 		rhum = (((rhum & 0x0fff) * 1000) / 16) - 24000;
404 #if 0		/* todo: linearization and temperature compensation */
405 		rhum -= -0.00393 * rhum * rhum + 0.4008 * rhum - 4.7844;
406 		rhum += (temp - 30) * (0.00237 * rhum + 0.1973);
407 #endif
408 		break;
409 	case UGOLD_TYPE_SI7006: /* 14bit and status bit */
410 		rhum = (((rhum & ~3) * 15625) / 8192) - 6000;
411 		break;
412 	case UGOLD_TYPE_SHT1X: /* 16 bit */
413 		rhum = rhum * 32;
414 		break;
415 	case UGOLD_TYPE_TEMPERX:
416 		rhum = rhum * 10;
417 		break;
418 	default:
419 		rhum = 0;
420 	}
421 
422 	/* limit the humidity to valid values */
423 	if (rhum < 0)
424 		rhum = 0;
425 	else if (rhum > 100000)
426 		rhum = 100000;
427 	return rhum;
428 }
429 
430 static void
431 ugold_si700x_type(struct ugold_softc *sc)
432 {
433 	char model[4 * sizeof(sc->sc_model) + 1];
434 	const char *descr;
435 	int nsensors = 0;
436 
437 	strnvis(model, sc->sc_model, sizeof model);
438 
439 	/* TEMPerHUM prefix */
440 	if (sc->sc_model_len >= 9 &&
441 	    memcmp(sc->sc_model, "TEMPerHum", 9) == 0) {
442 		if (memcmp(sc->sc_model + 9, "M12V1.0", 16 - 9) == 0) {
443 			sc->sc_type = UGOLD_TYPE_SI7005;
444 			descr = "si7005 (temperature and humidity)";
445 			goto identified;
446 		}
447 		if (memcmp(sc->sc_model + 9, "M12V1.2", 16 - 9) == 0) {
448 			sc->sc_type = UGOLD_TYPE_SI7006;
449 			descr = "si7006 (temperature and humidity)";
450 			goto identified;
451 		}
452 	}
453 	if (sc->sc_model_len >= 9 &&
454 	    memcmp(sc->sc_model, "TEMPerHUM", 9) == 0) {
455 		if (memcmp(sc->sc_model + 9, "_V3.9  ", 16 - 9) == 0 ||
456 		    memcmp(sc->sc_model + 9, "_V4.0  ", 16 - 9) == 0) {
457 			sc->sc_type = UGOLD_TYPE_TEMPERX;
458 			descr = "temperx (temperature and humidity)";
459 			goto identified;
460 		}
461 	}
462 
463 	/* TEMPerX prefix */
464 	if (sc->sc_model_len >= 8 &&
465 	    memcmp(sc->sc_model, "TEMPerX_", 8) == 0) {
466 		if (memcmp(sc->sc_model + 8, "V3.1    ", 16 - 8) == 0 ||
467 		    memcmp(sc->sc_model + 8, "V3.3    ", 16 - 8) == 0) {
468 			sc->sc_type = UGOLD_TYPE_TEMPERX;
469 			descr = "temperx (temperature and humidity)";
470 			goto identified;
471 		}
472 	}
473 
474 	/* TEMPer1F or TEMPer2_ prefixes */
475 	if (sc->sc_model_len >= 16 &&
476 	    memcmp(sc->sc_model, "TEMPer1F_H1V1.5F", 16) == 0) {
477 		sc->sc_type = UGOLD_TYPE_SHT1X;
478 		descr = "sht1x (temperature and humidity)";
479 		goto identified;
480 	}
481 	if (sc->sc_model_len >= 16 &&
482 	    (memcmp(sc->sc_model, "TEMPer1F_V4.1\0\0\0", 16) == 0 ||
483 	     memcmp(sc->sc_model, "TEMPer2_V4.1\0\0\0\0", 16) == 0)) {
484 		sc->sc_type = UGOLD_TYPE_GOLD;
485 		/*
486 		 * TEMPer1F devices lack the internal sensor, but will never
487 		 * report data for it, so it will never gets marked as valid.
488 		 * We thus keep the value of sc_num_sensors unchanged at 2,
489 		 * and make sure we will only report one single sensor below.
490 		 */
491 		if (sc->sc_model[6] == '1')
492 			nsensors = 1;
493 		descr = "gold (temperature only)";
494 		goto identified;
495 	}
496 
497 	/* TEMPerGold prefix */
498 	if (sc->sc_model_len >= 11 &&
499 	    memcmp(sc->sc_model, "TEMPerGold_", 11) == 0) {
500 		/*
501 		 * All V3.something models ought to work, but better be
502 		 * safe than sorry, and TEMPerHum models have been known
503 		 * to use slightly different sensors between models.
504 		 */
505 		if (memcmp(sc->sc_model + 11, "V3.1 ", 16 - 11) == 0 ||
506 		    memcmp(sc->sc_model + 11, "V3.4 ", 16 - 11) == 0 ||
507 		    memcmp(sc->sc_model + 11, "V3.5 ", 16 - 11) == 0) {
508 			sc->sc_type = UGOLD_TYPE_GOLD;
509 			sc->sc_num_sensors = 1;
510 			descr = "gold (temperature only)";
511 			goto identified;
512 		}
513 	}
514 
515 	printf("%s: unknown model \"%s\"\n",
516 	    sc->sc_hdev.sc_dev.dv_xname, model);
517 	sc->sc_num_sensors = 0;
518 	sc->sc_type = UGOLD_TYPE_INVALID;
519 	return;
520 
521  identified:
522 	ugold_setup_sensors(sc);
523 	if (nsensors == 0)
524 		nsensors = sc->sc_num_sensors;
525 	printf("%s: \"%s\", %d sensor%s type %s\n", sc->sc_hdev.sc_dev.dv_xname,
526 	    model, nsensors, (nsensors == 1) ? "" : "s", descr);
527 	ugold_refresh(sc);
528 }
529 
530 void
531 ugold_si700x_intr(struct ugold_softc *sc, uint8_t *buf, u_int len)
532 {
533 	int temp, sensor, rhum;
534 
535 	switch (buf[0]) {
536 	case UGOLD_CMD_INIT:
537 		if (sc->sc_num_sensors != 0)
538 			break;
539 		/* XXX some devices report 0x04 here */
540 		sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS);
541 		ugold_refresh(sc);
542 		break;
543 	case UGOLD_CMD_DATA:
544 		if (sc->sc_type == UGOLD_TYPE_GOLD) {
545 			if (buf[1] == 0x80)
546 				sensor = UGOLD_INNER;
547 			else if (buf[1] == 0x01)
548 				sensor = UGOLD_OUTER;
549 			else
550 				sensor = -1;
551 		} else {
552 			if (buf[1] == 0x04 || buf[1] == 0x20 ||
553 			    buf[1] == 0x40 || buf[1] == 0x80)
554 				sensor = UGOLD_INNER;
555 			else
556 				sensor = -1;
557 		}
558 		if (sensor < 0) {
559 			/* unexpected data, ignore */
560 #ifdef UGOLD_DEBUG
561 			printf("%s: unexpected sensor id %02x\n",
562 			    sc->sc_hdev.sc_dev.dv_xname, buf[1]);
563 #endif
564 			break;
565 		}
566 
567 		temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]);
568 		sc->sc_sensor[sensor].value = (temp * 1000) + 273150000;
569 		/*
570 		 * TEMPer1F and TEMPer2 report 200C when the sensor probe is
571 		 * missing or not plugged correctly.
572 		 */
573 		if (sc->sc_type == UGOLD_TYPE_GOLD && temp == 200000)
574 			sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
575 		else
576 			sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID;
577 
578 		if (sc->sc_type != UGOLD_TYPE_GOLD) {
579 			rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp);
580 			sc->sc_sensor[UGOLD_HUM].value = rhum;
581 			sc->sc_sensor[UGOLD_HUM].flags &= ~SENSOR_FINVALID;
582 		}
583 		break;
584 	default:
585 		ugold_si700x_type(sc);
586 		break;
587 	}
588 }
589 
590 void
591 ugold_intr(struct uhidev *addr, void *ibuf, u_int len)
592 {
593 	struct ugold_softc *sc = (struct ugold_softc *)addr;
594 	uint8_t *buf = ibuf;
595 	unsigned long chunk;
596 
597 #ifdef UGOLD_DEBUG
598 	{
599 		printf("%s: %u bytes\n", sc->sc_hdev.sc_dev.dv_xname, len);
600 		u_int i;
601 		for (i = 0; i < len; i++) {
602 			if (i != 0 && (i % 8) == 0)
603 				printf("\n");
604 			printf("%02x ", buf[i]);
605 		}
606 		printf("\n");
607 	}
608 #endif
609 
610 	switch (buf[0]) {
611 	case UGOLD_CMD_INIT:
612 	case UGOLD_CMD_DATA:
613 		(*sc->sc_intr)(sc, buf, len);
614 		break;
615 	default:
616 		if (!sc->sc_type) {
617 			/*
618 			 * During initialization, some devices need a bit
619 			 * more time to submit their identification string.
620 			 */
621 			if (len == sc->sc_model_len &&
622 			    !memcmp(sc->sc_model, buf, len)) {
623 #ifdef UGOLD_DEBUG
624 				printf("%s: duplicate string component\n",
625 				    sc->sc_hdev.sc_dev.dv_xname);
626 #endif
627 				break;
628 			}
629 			/*
630 			 * Exact sensor type is not known yet, type command
631 			 * returns arbitrary string.
632 			 */
633 			chunk = ulmin(len,
634 			    sizeof(sc->sc_model) - 1 - sc->sc_model_len);
635 			if (chunk != 0) {
636 				memcpy(sc->sc_model + sc->sc_model_len, buf,
637 				    chunk);
638 				sc->sc_model_len += chunk;
639 			}
640 			if (sc->sc_model_len > 8) {
641 				/* should have enough data now */
642 				(*sc->sc_intr)(sc, buf, len);
643 			}
644 			break;
645 		}
646 		printf("%s: unknown command 0x%02x\n",
647 		    sc->sc_hdev.sc_dev.dv_xname, buf[0]);
648 		break;
649 	}
650 }
651 
652 void
653 ugold_refresh(void *arg)
654 {
655 	struct ugold_softc *sc = arg;
656 	int i;
657 
658 	/*
659 	 * Don't waste time talking to the device if we don't understand
660 	 * its language.
661 	 */
662 	if (sc->sc_type == UGOLD_TYPE_INVALID)
663 		return;
664 
665 	if (!sc->sc_num_sensors) {
666 		ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
667 		return;
668 	}
669 	if (!sc->sc_type) {
670 		ugold_issue_cmd(sc, cmd_type, sizeof(cmd_type));
671 		return;
672 	}
673 
674 	if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) {
675 		for (i = 0; i < sc->sc_num_sensors; i++)
676 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
677 	}
678 }
679 
680 int
681 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
682 {
683 	int actlen;
684 
685 	actlen = uhidev_set_report_async(sc->sc_hdev.sc_parent,
686 	    UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmd, len);
687 	return (actlen != len);
688 }
689