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 */
71static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 };
72static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 };
73static 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
80struct ugold_softc;
81
82struct 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
99const 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
107int 	ugold_match(struct device *, void *, void *);
108void	ugold_attach(struct device *, struct device *, void *);
109int 	ugold_detach(struct device *, int);
110
111void	ugold_setup_sensors(struct ugold_softc *);
112void	ugold_intr(struct uhidev *, void *, u_int);
113void	ugold_ds75_intr(struct ugold_softc *, uint8_t *, u_int);
114void	ugold_si700x_intr(struct ugold_softc *, uint8_t *, u_int);
115void	ugold_refresh(void *);
116
117int	ugold_issue_cmd(struct ugold_softc *, uint8_t *, int);
118
119struct cfdriver ugold_cd = {
120	NULL, "ugold", DV_DULL
121};
122
123const struct cfattach ugold_ca = {
124	sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach,
125};
126
127int
128ugold_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
155void
156ugold_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
210int
211ugold_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
232void
233ugold_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
269static void
270strnvis(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
298static int
299ugold_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
305static void
306ugold_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
331void
332ugold_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
369static int
370ugold_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
396static int
397ugold_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
430static void
431ugold_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
530void
531ugold_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
590void
591ugold_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
652void
653ugold_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
680int
681ugold_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