xref: /openbsd/sys/dev/usb/ugold.c (revision 6bc6e70e)
1 /*	$OpenBSD: ugold.c,v 1.29 2024/07/27 17:31:49 miod 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
ugold_match(struct device * parent,void * match,void * aux)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
ugold_attach(struct device * parent,struct device * self,void * aux)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
ugold_detach(struct device * self,int flags)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
ugold_setup_sensors(struct ugold_softc * sc)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
strnvis(char * dst,const char * src,size_t siz)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
ugold_ds75_temp(uint8_t msb,uint8_t lsb)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
ugold_ds75_type(struct ugold_softc * sc)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
ugold_ds75_intr(struct ugold_softc * sc,uint8_t * buf,u_int len)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
ugold_si700x_temp(int type,uint8_t msb,uint8_t lsb)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
ugold_si700x_rhum(int type,uint8_t msb,uint8_t lsb,int temp)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
ugold_si700x_type(struct ugold_softc * sc)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 		    memcmp(sc->sc_model + 9, "_V4.1\0\0", 16 - 9) == 0) {
458 			sc->sc_type = UGOLD_TYPE_TEMPERX;
459 			descr = "temperx (temperature and humidity)";
460 			goto identified;
461 		}
462 	}
463 
464 	/* TEMPerX prefix */
465 	if (sc->sc_model_len >= 8 &&
466 	    memcmp(sc->sc_model, "TEMPerX_", 8) == 0) {
467 		if (memcmp(sc->sc_model + 8, "V3.1    ", 16 - 8) == 0 ||
468 		    memcmp(sc->sc_model + 8, "V3.3    ", 16 - 8) == 0) {
469 			sc->sc_type = UGOLD_TYPE_TEMPERX;
470 			descr = "temperx (temperature and humidity)";
471 			goto identified;
472 		}
473 	}
474 
475 	/* TEMPer1F or TEMPer2_ prefixes */
476 	if (sc->sc_model_len >= 16 &&
477 	    memcmp(sc->sc_model, "TEMPer1F_H1V1.5F", 16) == 0) {
478 		sc->sc_type = UGOLD_TYPE_SHT1X;
479 		descr = "sht1x (temperature and humidity)";
480 		goto identified;
481 	}
482 	if (sc->sc_model_len >= 16 &&
483 	    (memcmp(sc->sc_model, "TEMPer1F_V4.1\0\0\0", 16) == 0 ||
484 	     memcmp(sc->sc_model, "TEMPer2_V4.1\0\0\0\0", 16) == 0)) {
485 		sc->sc_type = UGOLD_TYPE_GOLD;
486 		/*
487 		 * TEMPer1F devices lack the internal sensor, but will never
488 		 * report data for it, so it will never get marked as valid.
489 		 * We thus keep the value of sc_num_sensors unchanged at 2,
490 		 * and make sure we will only report one single sensor below.
491 		 */
492 		if (sc->sc_model[6] == '1')
493 			nsensors = 1;
494 		descr = "gold (temperature only)";
495 		goto identified;
496 	}
497 
498 	/* TEMPerGold prefix */
499 	if (sc->sc_model_len >= 11 &&
500 	    memcmp(sc->sc_model, "TEMPerGold_", 11) == 0) {
501 		/*
502 		 * All V3.something models ought to work, but better be
503 		 * safe than sorry, and TEMPerHum models have been known
504 		 * to use slightly different sensors between models.
505 		 */
506 		if (memcmp(sc->sc_model + 11, "V3.1 ", 16 - 11) == 0 ||
507 		    memcmp(sc->sc_model + 11, "V3.4 ", 16 - 11) == 0 ||
508 		    memcmp(sc->sc_model + 11, "V3.5 ", 16 - 11) == 0) {
509 			sc->sc_type = UGOLD_TYPE_GOLD;
510 			sc->sc_num_sensors = 1;
511 			descr = "gold (temperature only)";
512 			goto identified;
513 		}
514 	}
515 
516 	printf("%s: unknown model \"%s\"\n",
517 	    sc->sc_hdev.sc_dev.dv_xname, model);
518 	sc->sc_num_sensors = 0;
519 	sc->sc_type = UGOLD_TYPE_INVALID;
520 	return;
521 
522  identified:
523 	ugold_setup_sensors(sc);
524 	if (nsensors == 0)
525 		nsensors = sc->sc_num_sensors;
526 	printf("%s: \"%s\", %d sensor%s type %s\n", sc->sc_hdev.sc_dev.dv_xname,
527 	    model, nsensors, (nsensors == 1) ? "" : "s", descr);
528 	ugold_refresh(sc);
529 }
530 
531 void
ugold_si700x_intr(struct ugold_softc * sc,uint8_t * buf,u_int len)532 ugold_si700x_intr(struct ugold_softc *sc, uint8_t *buf, u_int len)
533 {
534 	int temp, sensor, rhum;
535 
536 	switch (buf[0]) {
537 	case UGOLD_CMD_INIT:
538 		if (sc->sc_num_sensors != 0)
539 			break;
540 		/* XXX some devices report 0x04 here */
541 		sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS);
542 		ugold_refresh(sc);
543 		break;
544 	case UGOLD_CMD_DATA:
545 		if (sc->sc_type == UGOLD_TYPE_GOLD) {
546 			if (buf[1] == 0x80)
547 				sensor = UGOLD_INNER;
548 			else if (buf[1] == 0x01)
549 				sensor = UGOLD_OUTER;
550 			else
551 				sensor = -1;
552 		} else {
553 			if (buf[1] == 0x04 || buf[1] == 0x20 ||
554 			    buf[1] == 0x40 || buf[1] == 0x80)
555 				sensor = UGOLD_INNER;
556 			else
557 				sensor = -1;
558 		}
559 		if (sensor < 0) {
560 			/* unexpected data, ignore */
561 #ifdef UGOLD_DEBUG
562 			printf("%s: unexpected sensor id %02x\n",
563 			    sc->sc_hdev.sc_dev.dv_xname, buf[1]);
564 #endif
565 			break;
566 		}
567 
568 		temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]);
569 		sc->sc_sensor[sensor].value = (temp * 1000) + 273150000;
570 		/*
571 		 * TEMPer1F and TEMPer2 report 200C when the sensor probe is
572 		 * missing or not plugged correctly.
573 		 */
574 		if (sc->sc_type == UGOLD_TYPE_GOLD && temp == 200000)
575 			sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
576 		else
577 			sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID;
578 
579 		if (sc->sc_type != UGOLD_TYPE_GOLD) {
580 			rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp);
581 			sc->sc_sensor[UGOLD_HUM].value = rhum;
582 			sc->sc_sensor[UGOLD_HUM].flags &= ~SENSOR_FINVALID;
583 		}
584 		break;
585 	default:
586 		ugold_si700x_type(sc);
587 		break;
588 	}
589 }
590 
591 void
ugold_intr(struct uhidev * addr,void * ibuf,u_int len)592 ugold_intr(struct uhidev *addr, void *ibuf, u_int len)
593 {
594 	struct ugold_softc *sc = (struct ugold_softc *)addr;
595 	uint8_t *buf = ibuf;
596 	unsigned long chunk;
597 
598 #ifdef UGOLD_DEBUG
599 	{
600 		printf("%s: %u bytes\n", sc->sc_hdev.sc_dev.dv_xname, len);
601 		u_int i;
602 		for (i = 0; i < len; i++) {
603 			if (i != 0 && (i % 8) == 0)
604 				printf("\n");
605 			printf("%02x ", buf[i]);
606 		}
607 		printf("\n");
608 	}
609 #endif
610 
611 	switch (buf[0]) {
612 	case UGOLD_CMD_INIT:
613 	case UGOLD_CMD_DATA:
614 		(*sc->sc_intr)(sc, buf, len);
615 		break;
616 	default:
617 		if (!sc->sc_type) {
618 			/*
619 			 * During initialization, some devices need a bit
620 			 * more time to submit their identification string.
621 			 */
622 			if (len == sc->sc_model_len &&
623 			    !memcmp(sc->sc_model, buf, len)) {
624 #ifdef UGOLD_DEBUG
625 				printf("%s: duplicate string component\n",
626 				    sc->sc_hdev.sc_dev.dv_xname);
627 #endif
628 				break;
629 			}
630 			/*
631 			 * Exact sensor type is not known yet, type command
632 			 * returns arbitrary string.
633 			 */
634 			chunk = ulmin(len,
635 			    sizeof(sc->sc_model) - 1 - sc->sc_model_len);
636 			if (chunk != 0) {
637 				memcpy(sc->sc_model + sc->sc_model_len, buf,
638 				    chunk);
639 				sc->sc_model_len += chunk;
640 			}
641 			if (sc->sc_model_len > 8) {
642 				/* should have enough data now */
643 				(*sc->sc_intr)(sc, buf, len);
644 			}
645 			break;
646 		}
647 		printf("%s: unknown command 0x%02x\n",
648 		    sc->sc_hdev.sc_dev.dv_xname, buf[0]);
649 		break;
650 	}
651 }
652 
653 void
ugold_refresh(void * arg)654 ugold_refresh(void *arg)
655 {
656 	struct ugold_softc *sc = arg;
657 	int i;
658 
659 	/*
660 	 * Don't waste time talking to the device if we don't understand
661 	 * its language.
662 	 */
663 	if (sc->sc_type == UGOLD_TYPE_INVALID)
664 		return;
665 
666 	if (!sc->sc_num_sensors) {
667 		ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
668 		return;
669 	}
670 	if (!sc->sc_type) {
671 		ugold_issue_cmd(sc, cmd_type, sizeof(cmd_type));
672 		return;
673 	}
674 
675 	if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) {
676 		for (i = 0; i < sc->sc_num_sensors; i++)
677 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
678 	}
679 }
680 
681 int
ugold_issue_cmd(struct ugold_softc * sc,uint8_t * cmd,int len)682 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
683 {
684 	int actlen;
685 
686 	actlen = uhidev_set_report_async(sc->sc_hdev.sc_parent,
687 	    UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmd, len);
688 	return (actlen != len);
689 }
690