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