Overhaul device identification logic in order to make matching on
authormiod <miod@openbsd.org>
Thu, 30 Nov 2023 20:08:23 +0000 (20:08 +0000)
committermiod <miod@openbsd.org>
Thu, 30 Nov 2023 20:08:23 +0000 (20:08 +0000)
device-provided information easier.

Add support for a few more devices.

Trigger state machine updates quickly so as not to have to wait 6 seconds to
get the device identified, then 6 more seconds to get the first sensor data.

Tested on:
TEMPerX_V3.3 by landry@
TEMPerF1.4M by sthen@
TEMPerHUM_V4.0, TEMPer2_V4.1, TEMPer1F_V4.1 and TEMPerGold_V3.4 by yours truly

share/man/man4/ugold.4
sys/dev/usb/ugold.c

index d2f4b30..78e328a 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ugold.4,v 1.7 2023/04/02 17:03:14 miod Exp $
+.\"    $OpenBSD: ugold.4,v 1.8 2023/11/30 20:08:23 miod Exp $
 .\"
 .\" Copyright (c) 2013 Takayoshi SASANO <sasano@openbsd.org>
 .\" Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
@@ -16,7 +16,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 2 2023 $
+.Dd $Mdocdate: November 30 2023 $
 .Dt UGOLD 4
 .Os
 .Sh NAME
@@ -35,10 +35,13 @@ driver:
 .It Em "Device" Ta Em "Sensors"
 .It Li "RDing TEMPer1V1.2" Ta "1 Temperature"
 .It Li "RDing TEMPerV1.4" Ta "1 Temperature"
+.It Li "RDing TEMPer1F_V4.1" Ta "1 Temperature (external)"
+.It Li "RDing TEMPer2_V4.1" Ta "2 Temperature (internal/external)"
 .It Li "RDing TEMPerGold_V3.1" Ta "1 Temperature"
 .It Li "RDing TEMPerGold_V3.4" Ta "1 Temperature"
-.It Li "RDing TEMPerHUM1V1.0" Ta "1 Temperature and 1 Humidity"
-.It Li "RDing TEMPerHUM1V1.2" Ta "1 Temperature and 1 Humidity"
+.It Li "RDing TEMPerHum1V1.0" Ta "1 Temperature and 1 Humidity"
+.It Li "RDing TEMPerHum1V1.2" Ta "1 Temperature and 1 Humidity"
+.It Li "RDing TEMPerHUM_V4.0" Ta "1 Temperature and 1 Humidity"
 .It Li "RDing TEMPer1F_H1V1.5F" Ta "1 Temperature and 1 Humidity"
 .It Li "RDing TEMPerX_V3.1" Ta "1 Temperature and 1 Humidity"
 .It Li "RDing TEMPerX_V3.3" Ta "1 Temperature and 1 Humidity"
index bfc155b..a5bd162 100644 (file)
@@ -1,9 +1,10 @@
-/*     $OpenBSD: ugold.c,v 1.23 2023/04/19 04:51:53 miod Exp $   */
+/*     $OpenBSD: ugold.c,v 1.24 2023/11/30 20:08:23 miod Exp $   */
 
 /*
  * Copyright (c) 2013 Takayoshi SASANO <uaa@openbsd.org>
  * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org>
  * Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
+ * Copyright (c) 2023 Miodrag Vallat.
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #define UGOLD_CMD_DATA         0x80
 #define UGOLD_CMD_INIT         0x82
 
+#define UGOLD_TYPE_INVALID     -1
 #define UGOLD_TYPE_SI7005      1
 #define UGOLD_TYPE_SI7006      2
 #define UGOLD_TYPE_SHT1X       3
 #define UGOLD_TYPE_GOLD                4
 #define UGOLD_TYPE_TEMPERX     5
+#define UGOLD_TYPE_DS75                6
 
 /*
  * This driver uses three known commands for the TEMPer and TEMPerHUM
 static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 };
 static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 };
 static uint8_t cmd_type[8] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 };
+/*
+ * The following command is also recognized and reports some kind of status
+ * byte (i.e. 87 xx 00 00 00 00 00 00).
+                            { 0x01, 0x87, 0xee, 0x01, 0x00, 0x00, 0x00, 0x00 };
+ */
+
+struct ugold_softc;
 
 struct ugold_softc {
        struct uhidev            sc_hdev;
@@ -77,15 +87,21 @@ struct ugold_softc {
        int                      sc_num_sensors;
        int                      sc_type;
 
+       char                     sc_model[16 + 1];
+       unsigned int             sc_model_len;
+
        struct ksensor           sc_sensor[UGOLD_MAX_SENSORS];
        struct ksensordev        sc_sensordev;
        struct sensor_task      *sc_sensortask;
+
+       void            (*sc_intr)(struct ugold_softc *, uint8_t *, u_int);
 };
 
 const struct usb_devno ugold_devs[] = {
        { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER },
        { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPERHUM },
        { USB_VENDOR_PCSENSORS, USB_PRODUCT_PCSENSORS_TEMPER },
+       { USB_VENDOR_RDING, USB_PRODUCT_RDING_TEMPER },
        { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_TEMPER },
 };
 
@@ -93,8 +109,10 @@ int         ugold_match(struct device *, void *, void *);
 void   ugold_attach(struct device *, struct device *, void *);
 int    ugold_detach(struct device *, int);
 
-void   ugold_ds75_intr(struct uhidev *, void *, u_int);
-void   ugold_si700x_intr(struct uhidev *, void *, u_int);
+void   ugold_setup_sensors(struct ugold_softc *);
+void   ugold_intr(struct uhidev *, void *, u_int);
+void   ugold_ds75_intr(struct ugold_softc *, uint8_t *, u_int);
+void   ugold_si700x_intr(struct ugold_softc *, uint8_t *, u_int);
 void   ugold_refresh(void *);
 
 int    ugold_issue_cmd(struct ugold_softc *, uint8_t *, int);
@@ -146,14 +164,16 @@ ugold_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_udev = uha->parent->sc_udev;
        sc->sc_hdev.sc_parent = uha->parent;
        sc->sc_hdev.sc_report_id = uha->reportid;
+       sc->sc_hdev.sc_intr = ugold_intr;
        switch (uha->uaa->product) {
        case USB_PRODUCT_MICRODIA_TEMPER:
-               sc->sc_hdev.sc_intr = ugold_ds75_intr;
+               sc->sc_intr = ugold_ds75_intr;
                break;
        case USB_PRODUCT_MICRODIA_TEMPERHUM:
        case USB_PRODUCT_PCSENSORS_TEMPER:
+       case USB_PRODUCT_RDING_TEMPER:
        case USB_PRODUCT_WCH2_TEMPER:
-               sc->sc_hdev.sc_intr = ugold_si700x_intr;
+               sc->sc_intr = ugold_si700x_intr;
                break;
        default:
                printf(", unknown product\n");
@@ -174,33 +194,7 @@ ugold_attach(struct device *parent, struct device *self, void *aux)
        strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
            sizeof(sc->sc_sensordev.xname));
 
-       switch (uha->uaa->product) {
-       case USB_PRODUCT_MICRODIA_TEMPER:
-               /* 2 temperature sensors */
-               sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
-               strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
-                   sizeof(sc->sc_sensor[UGOLD_INNER].desc));
-               sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP;
-               strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer",
-                   sizeof(sc->sc_sensor[UGOLD_OUTER].desc));
-               break;
-       case USB_PRODUCT_MICRODIA_TEMPERHUM:
-       case USB_PRODUCT_PCSENSORS_TEMPER:
-       case USB_PRODUCT_WCH2_TEMPER:
-               /* 1 temperature and 1 humidity sensor */
-               sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
-               strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
-                   sizeof(sc->sc_sensor[UGOLD_INNER].desc));
-               sc->sc_sensor[UGOLD_HUM].type = SENSOR_HUMIDITY;
-               strlcpy(sc->sc_sensor[UGOLD_HUM].desc, "RH",
-                   sizeof(sc->sc_sensor[UGOLD_HUM].desc));
-               break;
-       default:
-               printf(", unknown product\n");
-               return;
-       }
-
-       /* 0.1Hz */
+       /* 0.166Hz */
        sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6);
        if (sc->sc_sensortask == NULL) {
                printf(", unable to register update task\n");
@@ -208,6 +202,9 @@ ugold_attach(struct device *parent, struct device *self, void *aux)
        }
        printf("\n");
 
+       /* speed up sensor identification */
+       ugold_refresh(sc);
+
        sensordev_install(&sc->sc_sensordev);
 }
 
@@ -222,8 +219,10 @@ ugold_detach(struct device *self, int flags)
                sensordev_deinstall(&sc->sc_sensordev);
        }
 
-       for (i = 0; i < sc->sc_num_sensors; i++)
-               sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]);
+       if (sc->sc_type != UGOLD_TYPE_INVALID) {
+               for (i = 0; i < sc->sc_num_sensors; i++)
+                       sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]);
+       }
 
        if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
                uhidev_close(&sc->sc_hdev);
@@ -231,6 +230,72 @@ ugold_detach(struct device *self, int flags)
        return (0);
 }
 
+void
+ugold_setup_sensors(struct ugold_softc *sc)
+{
+       int i;
+
+       switch (sc->sc_type) {
+       default:
+               return;
+       case UGOLD_TYPE_SI7005:
+       case UGOLD_TYPE_SI7006:
+       case UGOLD_TYPE_SHT1X:
+       case UGOLD_TYPE_TEMPERX:
+               /* 1 temperature and 1 humidity sensor */
+               sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
+               strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
+                   sizeof(sc->sc_sensor[UGOLD_INNER].desc));
+               sc->sc_sensor[UGOLD_HUM].type = SENSOR_HUMIDITY;
+               strlcpy(sc->sc_sensor[UGOLD_HUM].desc, "RH",
+                   sizeof(sc->sc_sensor[UGOLD_HUM].desc));
+               break;
+       case UGOLD_TYPE_GOLD:
+       case UGOLD_TYPE_DS75:
+               /* up to 2 temperature sensors */
+               sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
+               strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
+                   sizeof(sc->sc_sensor[UGOLD_INNER].desc));
+               sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP;
+               strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer",
+                   sizeof(sc->sc_sensor[UGOLD_OUTER].desc));
+               break;
+       }
+       for (i = 0; i < sc->sc_num_sensors; i++) {
+               sc->sc_sensor[i].flags |= SENSOR_FINVALID;
+               sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
+       }
+}
+
+static void
+strnvis(char *dst, const char *src, size_t siz)
+{
+       char *start, *end;
+       int c;
+
+       for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
+               if (c >= 0x20 && c <= 0x7f) {
+                       if (c == '\\') {
+                               /* need space for the extra '\\' */
+                               if (dst + 2 > end)
+                                       break;
+                               *dst++ = '\\';
+                       }
+                       *dst++ = c;
+               } else {
+                       if (dst + 4 > end)
+                               break;
+                       *dst++ = '\\';
+                       *dst++ = ((u_char)c >> 6 & 07) + '0';
+                       *dst++ = ((u_char)c >> 3 & 07) + '0';
+                       *dst++ = ((u_char)c & 07) + '0';
+               }
+               src++;
+       }
+       if (siz > 0)
+               *dst = '\0';
+}
+
 static int
 ugold_ds75_temp(uint8_t msb, uint8_t lsb)
 {
@@ -239,39 +304,42 @@ ugold_ds75_temp(uint8_t msb, uint8_t lsb)
 }
 
 static void
-ugold_ds75_type(struct ugold_softc *sc, uint8_t *buf, u_int len)
+ugold_ds75_type(struct ugold_softc *sc)
 {
-       if (memcmp(buf, "TEMPer1F", len) == 0 ||
-           memcmp(buf, "TEMPer2F", len) == 0 ||
-           memcmp(buf, "TEMPerF1", len) == 0)
-               return; /* skip first half of the answer */
-
-       printf("%s: %d sensor%s type ds75/12bit (temperature)\n",
-           sc->sc_hdev.sc_dev.dv_xname, sc->sc_num_sensors,
-           (sc->sc_num_sensors == 1) ? "" : "s");
+       char model[4 * sizeof(sc->sc_model) + 1];
+
+       strnvis(model, sc->sc_model, sizeof model);
+
+       if (memcmp(sc->sc_model, "TEMPer1F", 8) == 0 ||
+           memcmp(sc->sc_model, "TEMPer2F", 8) == 0 ||
+           memcmp(sc->sc_model, "TEMPerF1", 8) == 0) {
+               sc->sc_type = UGOLD_TYPE_DS75;
+               ugold_setup_sensors(sc);
+               printf("%s: \"%s\", %d sensor%s"
+                      " type ds75/12bit (temperature)\n",
+                   sc->sc_hdev.sc_dev.dv_xname, model, sc->sc_num_sensors,
+                   (sc->sc_num_sensors == 1) ? "" : "s");
+               ugold_refresh(sc);
+               return;
+       }
 
-       sc->sc_type = -1; /* ignore type */
+       printf("%s: unknown model \"%s\"\n",
+           sc->sc_hdev.sc_dev.dv_xname, model);
+       sc->sc_num_sensors = 0;
+       sc->sc_type = UGOLD_TYPE_INVALID;
 }
 
 void
-ugold_ds75_intr(struct uhidev *addr, void *ibuf, u_int len)
+ugold_ds75_intr(struct ugold_softc *sc, uint8_t *buf, u_int len)
 {
-       struct ugold_softc *sc = (struct ugold_softc *)addr;
-       uint8_t *buf = ibuf;
-       int i, temp;
+       int temp;
 
        switch (buf[0]) {
        case UGOLD_CMD_INIT:
-               if (sc->sc_num_sensors)
+               if (sc->sc_num_sensors != 0)
                        break;
-
-               sc->sc_num_sensors = min(buf[1], UGOLD_MAX_SENSORS) /* XXX */;
-
-               for (i = 0; i < sc->sc_num_sensors; i++) {
-                       sc->sc_sensor[i].flags |= SENSOR_FINVALID;
-                       sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
-               }
-
+               sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS) /* XXX */;
+               ugold_refresh(sc);
                break;
        case UGOLD_CMD_DATA:
                switch (buf[1]) {
@@ -286,17 +354,16 @@ ugold_ds75_intr(struct uhidev *addr, void *ibuf, u_int len)
                        sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID;
                        break;
                default:
+#ifdef UGOLD_DEBUG
                        printf("%s: invalid data length (%d bytes)\n",
                                sc->sc_hdev.sc_dev.dv_xname, buf[1]);
+#endif
+                       break;
                }
                break;
        default:
-               if (!sc->sc_type) { /* type command returns arbitrary string */
-                       ugold_ds75_type(sc, buf, len);
-                       break;
-               }
-               printf("%s: unknown command 0x%02x\n",
-                   sc->sc_hdev.sc_dev.dv_xname, buf[0]);
+               ugold_ds75_type(sc);
+               break;
        }
 }
 
@@ -362,74 +429,146 @@ ugold_si700x_rhum(int type, uint8_t msb, uint8_t lsb, int temp)
 }
 
 static void
-ugold_si700x_type(struct ugold_softc *sc, uint8_t *buf, u_int len)
+ugold_si700x_type(struct ugold_softc *sc)
 {
-       if (memcmp(buf, "TEMPerHu", len) == 0 ||
-           memcmp(buf, "TEMPer1F", len) == 0 ||
-           memcmp(buf, "TEMPerX_", len) == 0 ||
-           memcmp(buf, "TEMPerGo", len) == 0)
-               return; /* skip equal first half of the answer */
-
-       printf("%s: %d sensor%s type ", sc->sc_hdev.sc_dev.dv_xname,
-           sc->sc_num_sensors, (sc->sc_num_sensors == 1) ? "" : "s");
-
-       if (memcmp(buf, "mM12V1.0", len) == 0) {
-               sc->sc_type = UGOLD_TYPE_SI7005;
-               printf("si7005 (temperature and humidity)\n");
-       } else if (memcmp(buf, "mM12V1.2", len) == 0) {
-               sc->sc_type = UGOLD_TYPE_SI7006;
-               printf("si7006 (temperature and humidity)\n");
-       } else if (memcmp(buf, "_H1V1.5F", len) == 0) {
+       char model[4 * sizeof(sc->sc_model) + 1];
+       const char *descr;
+       int nsensors = 0;
+
+       strnvis(model, sc->sc_model, sizeof model);
+
+       /* TEMPerHUM prefix */
+       if (sc->sc_model_len >= 9 &&
+           memcmp(sc->sc_model, "TEMPerHum", 9) == 0) {
+               if (memcmp(sc->sc_model + 9, "M12V1.0", 16 - 9) == 0) {
+                       sc->sc_type = UGOLD_TYPE_SI7005;
+                       descr = "si7005 (temperature and humidity)";
+                       goto identified;
+               }
+               if (memcmp(sc->sc_model + 9, "M12V1.2", 16 - 9) == 0) {
+                       sc->sc_type = UGOLD_TYPE_SI7006;
+                       descr = "si7006 (temperature and humidity)";
+                       goto identified;
+               }
+       }
+       if (sc->sc_model_len >= 9 &&
+           memcmp(sc->sc_model, "TEMPerHUM", 9) == 0) {
+               if (memcmp(sc->sc_model + 9, "_V4.0  ", 16 - 9) == 0) {
+                       sc->sc_type = UGOLD_TYPE_TEMPERX;
+                       descr = "temperx (temperature and humidity)";
+                       goto identified;
+               }
+       }
+
+       /* TEMPerX prefix */
+       if (sc->sc_model_len >= 8 &&
+           memcmp(sc->sc_model, "TEMPerX_", 8) == 0) {
+               if (memcmp(sc->sc_model + 8, "V3.1    ", 16 - 8) == 0 ||
+                   memcmp(sc->sc_model + 8, "V3.3    ", 16 - 8) == 0) {
+                       sc->sc_type = UGOLD_TYPE_TEMPERX;
+                       descr = "temperx (temperature and humidity)";
+                       goto identified;
+               }
+       }
+
+       /* TEMPer1F or TEMPer2_ prefixes */
+       if (sc->sc_model_len >= 16 &&
+           memcmp(sc->sc_model, "TEMPer1F_H1V1.5F", 16) == 0) {
                sc->sc_type = UGOLD_TYPE_SHT1X;
-               printf("sht1x (temperature and humidity)\n");
-       } else if (memcmp(buf, "V3.1    ", len) == 0) {
-               sc->sc_type = UGOLD_TYPE_TEMPERX;
-               printf("temperx (temperature and humidity)\n");
-       } else if (memcmp(buf, "V3.3    ", len) == 0) {
-               sc->sc_type = UGOLD_TYPE_TEMPERX;
-               printf("temperx (temperature and humidity)\n");
-       } else if (memcmp(buf, "ld_V3.1 ", len) == 0) {
-               sc->sc_type = UGOLD_TYPE_GOLD;
-               printf("gold (temperature only)\n");
-       } else if (memcmp(buf, "ld_V3.4 ", len) == 0) {
+               descr = "sht1x (temperature and humidity)";
+               goto identified;
+       }
+       if (sc->sc_model_len >= 16 &&
+           (memcmp(sc->sc_model, "TEMPer1F_V4.1\0\0\0", 16) == 0 ||
+            memcmp(sc->sc_model, "TEMPer2_V4.1\0\0\0\0", 16) == 0)) {
                sc->sc_type = UGOLD_TYPE_GOLD;
-               printf("gold (temperature only)\n");
-       } else {
-               sc->sc_type = -1;
-               printf("unknown\n");
+               /*
+                * TEMPer1F devices lack the internal sensor, but will never
+                * report data for it, so it will never gets marked as valid.
+                * We thus keep the value of sc_num_sensors unchanged at 2,
+                * and make sure we will only report one single sensor below.
+                */
+               if (sc->sc_model[6] == '1')
+                       nsensors = 1;
+               descr = "gold (temperature only)";
+               goto identified;
        }
+
+       /* TEMPerGold prefix */
+       if (sc->sc_model_len >= 11 &&
+           memcmp(sc->sc_model, "TEMPerGold_", 11) == 0) {
+               if (memcmp(sc->sc_model + 11, "V3.1 ", 16 - 11) == 0 ||
+                   memcmp(sc->sc_model + 11, "V3.4 ", 16 - 11) == 0) {
+                       sc->sc_type = UGOLD_TYPE_GOLD;
+                       sc->sc_num_sensors = 1;
+                       descr = "gold (temperature only)";
+                       goto identified;
+               }
+       }
+       
+       printf("%s: unknown model \"%s\"\n",
+           sc->sc_hdev.sc_dev.dv_xname, model);
+       sc->sc_num_sensors = 0;
+       sc->sc_type = UGOLD_TYPE_INVALID;
+       return;
+
+ identified:
+       ugold_setup_sensors(sc);
+       if (nsensors == 0)
+               nsensors = sc->sc_num_sensors;
+       printf("%s: \"%s\", %d sensor%s type %s\n", sc->sc_hdev.sc_dev.dv_xname,
+           model, nsensors, (nsensors == 1) ? "" : "s", descr);
+       ugold_refresh(sc);
 }
 
 void
-ugold_si700x_intr(struct uhidev *addr, void *ibuf, u_int len)
+ugold_si700x_intr(struct ugold_softc *sc, uint8_t *buf, u_int len)
 {
-       struct ugold_softc *sc = (struct ugold_softc *)addr;
-       uint8_t *buf = ibuf;
-       int i, temp, rhum;
+       int temp, sensor, rhum;
 
        switch (buf[0]) {
        case UGOLD_CMD_INIT:
-               if (sc->sc_num_sensors)
+               if (sc->sc_num_sensors != 0)
                        break;
-
-               if (sc->sc_type == UGOLD_TYPE_GOLD)
-                       sc->sc_num_sensors = 1;
-               else
-                       sc->sc_num_sensors = min(buf[1],
-                           UGOLD_MAX_SENSORS) /* XXX */;
-
-               for (i = 0; i < sc->sc_num_sensors; i++) {
-                       sc->sc_sensor[i].flags |= SENSOR_FINVALID;
-                       sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
-               }
+               /* XXX some devices report 0x04 here */
+               sc->sc_num_sensors = imin(buf[1], UGOLD_MAX_SENSORS);
+               ugold_refresh(sc);
                break;
        case UGOLD_CMD_DATA:
-               if (buf[1] != 4 && buf[1] != 64 && buf[1] != 128)
-                       printf("%s: invalid data length (%d bytes)\n",
+               if (sc->sc_type == UGOLD_TYPE_GOLD) {
+                       if (buf[1] == 0x80)
+                               sensor = UGOLD_INNER;
+                       else if (buf[1] == 0x01)
+                               sensor = UGOLD_OUTER;
+                       else
+                               sensor = -1;
+               } else {
+                       if (buf[1] == 0x04 || buf[1] == 0x20 ||
+                           buf[1] == 0x40 || buf[1] == 0x80)
+                               sensor = UGOLD_INNER;
+                       else
+                               sensor = -1;
+               }
+               if (sensor < 0) {
+                       /* unexpected data, ignore */
+#ifdef UGOLD_DEBUG
+                       printf("%s: unexpected sensor id %02x\n",
                            sc->sc_hdev.sc_dev.dv_xname, buf[1]);
+#endif
+                       break;
+               }
+
                temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]);
-               sc->sc_sensor[UGOLD_INNER].value = (temp * 1000) + 273150000;
-               sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID;
+               sc->sc_sensor[sensor].value = (temp * 1000) + 273150000;
+               /*
+                * TEMPer1F and TEMPer2 report 200C when the sensor probe is
+                * missing or not plugged correctly.
+                */
+               if (sc->sc_type == UGOLD_TYPE_GOLD && temp == 200000)
+                       sc->sc_sensor[sensor].flags |= SENSOR_FINVALID;
+               else
+                       sc->sc_sensor[sensor].flags &= ~SENSOR_FINVALID;
+
                if (sc->sc_type != UGOLD_TYPE_GOLD) {
                        rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp);
                        sc->sc_sensor[UGOLD_HUM].value = rhum;
@@ -437,12 +576,58 @@ ugold_si700x_intr(struct uhidev *addr, void *ibuf, u_int len)
                }
                break;
        default:
-               if (!sc->sc_type) { /* type command returns arbitrary string */
-                       ugold_si700x_type(sc, buf, len);
+               ugold_si700x_type(sc);
+               break;
+       }
+}
+
+void
+ugold_intr(struct uhidev *addr, void *ibuf, u_int len)
+{
+       struct ugold_softc *sc = (struct ugold_softc *)addr;
+       uint8_t *buf = ibuf;
+       unsigned long chunk;
+
+#ifdef UGOLD_DEBUG
+       {
+               printf("%s: %u bytes\n", sc->sc_hdev.sc_dev.dv_xname, len);
+               u_int i;
+               for (i = 0; i < len; i++) {
+                       if (i != 0 && (i % 8) == 0)
+                               printf("\n");
+                       printf("%02x ", buf[i]);
+               }
+               printf("\n");
+       }
+#endif
+
+       switch (buf[0]) {
+       case UGOLD_CMD_INIT:
+       case UGOLD_CMD_DATA:
+               (*sc->sc_intr)(sc, buf, len);
+               break;
+       default:
+               if (!sc->sc_type) {
+                       /*
+                        * Exact sensor type is not known yet, type command
+                        * returns arbitrary string.
+                        */
+                       chunk = ulmin(len,
+                           sizeof(sc->sc_model) - 1 - sc->sc_model_len);
+                       if (chunk != 0) {
+                               memcpy(sc->sc_model + sc->sc_model_len, buf,
+                                   chunk);
+                               sc->sc_model_len += chunk;
+                       }
+                       if (sc->sc_model_len > 8) {
+                               /* should have enough data now */
+                               (*sc->sc_intr)(sc, buf, len);
+                       }
                        break;
                }
                printf("%s: unknown command 0x%02x\n",
                    sc->sc_hdev.sc_dev.dv_xname, buf[0]);
+               break;
        }
 }
 
@@ -452,6 +637,13 @@ ugold_refresh(void *arg)
        struct ugold_softc *sc = arg;
        int i;
 
+       /*
+        * Don't waste time talking to the device if we don't understand
+        * its language.
+        */
+       if (sc->sc_type == UGOLD_TYPE_INVALID)
+               return;
+
        if (!sc->sc_num_sensors) {
                ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
                return;