LCOV - code coverage report
Current view: top level - dev/usb - ugold.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 190 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 12 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: ugold.c,v 1.14 2017/10/05 17:29:00 stsp 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             :  *
       8             :  * Permission to use, copy, modify, and distribute this software for any
       9             :  * purpose with or without fee is hereby granted, provided that the above
      10             :  * copyright notice and this permission notice appear in all copies.
      11             :  *
      12             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
      13             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      14             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      15             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      16             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      17             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      18             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      19             :  */
      20             : 
      21             : /*
      22             :  * Driver for Microdia's HID base TEMPer and TEMPerHUM temperature and
      23             :  * humidity sensors
      24             :  */
      25             : 
      26             : #include <sys/param.h>
      27             : #include <sys/systm.h>
      28             : #include <sys/kernel.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_SI7005       1
      48             : #define UGOLD_TYPE_SI7006       2
      49             : #define UGOLD_TYPE_SHT1X        3
      50             : 
      51             : /*
      52             :  * This driver uses three known commands for the TEMPer and TEMPerHUM
      53             :  * devices.
      54             :  *
      55             :  * The first byte of the answer corresponds to the command and the
      56             :  * second one seems to be the size (in bytes) of the answer.
      57             :  *
      58             :  * The device always sends 8 bytes and if the length of the answer
      59             :  * is less than that, it just leaves the last bytes untouched.  That
      60             :  * is why most of the time the last n bytes of the answers are the
      61             :  * same.
      62             :  *
      63             :  * The type command below seems to generate two answers with a
      64             :  * string corresponding to the device, for example:
      65             :  *      'TEMPer1F' and '1.1Per1F' (here Per1F is repeated).
      66             :  */
      67             : static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 };
      68             : static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 };
      69             : static uint8_t cmd_type[8] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 };
      70             : 
      71             : struct ugold_softc {
      72             :         struct uhidev            sc_hdev;
      73             :         struct usbd_device      *sc_udev;
      74             : 
      75             :         int                      sc_num_sensors;
      76             :         int                      sc_type;
      77             : 
      78             :         struct ksensor           sc_sensor[UGOLD_MAX_SENSORS];
      79             :         struct ksensordev        sc_sensordev;
      80             :         struct sensor_task      *sc_sensortask;
      81             : };
      82             : 
      83             : const struct usb_devno ugold_devs[] = {
      84             :         { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER },
      85             :         { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPERHUM },
      86             : };
      87             : 
      88             : int     ugold_match(struct device *, void *, void *);
      89             : void    ugold_attach(struct device *, struct device *, void *);
      90             : int     ugold_detach(struct device *, int);
      91             : 
      92             : void    ugold_ds75_intr(struct uhidev *, void *, u_int);
      93             : void    ugold_si700x_intr(struct uhidev *, void *, u_int);
      94             : void    ugold_refresh(void *);
      95             : 
      96             : int     ugold_issue_cmd(struct ugold_softc *, uint8_t *, int);
      97             : 
      98             : struct cfdriver ugold_cd = {
      99             :         NULL, "ugold", DV_DULL
     100             : };
     101             : 
     102             : const struct cfattach ugold_ca = {
     103             :         sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach,
     104             : };
     105             : 
     106             : int
     107           0 : ugold_match(struct device *parent, void *match, void *aux)
     108             : {
     109           0 :         struct uhidev_attach_arg *uha = aux;
     110           0 :         int size;
     111           0 :         void *desc;
     112             : 
     113           0 :         if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
     114           0 :                 return (UMATCH_NONE);
     115             : 
     116           0 :         if (usb_lookup(ugold_devs, uha->uaa->vendor, uha->uaa->product) == NULL)
     117           0 :                 return (UMATCH_NONE);
     118             : 
     119             :         /*
     120             :          * XXX Only match the sensor interface.
     121             :          *
     122             :          * Does it makes sense to attach various uhidev(4) to these
     123             :          * non-standard HID devices?
     124             :          */
     125           0 :         uhidev_get_report_desc(uha->parent, &desc, &size);
     126           0 :         if (hid_is_collection(desc, size, uha->reportid,
     127             :             HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
     128           0 :                 return (UMATCH_NONE);
     129             : 
     130           0 :         return (UMATCH_VENDOR_PRODUCT);
     131             : 
     132           0 : }
     133             : 
     134             : void
     135           0 : ugold_attach(struct device *parent, struct device *self, void *aux)
     136             : {
     137           0 :         struct ugold_softc *sc = (struct ugold_softc *)self;
     138           0 :         struct uhidev_attach_arg *uha = aux;
     139           0 :         int size, repid;
     140           0 :         void *desc;
     141             : 
     142           0 :         sc->sc_udev = uha->parent->sc_udev;
     143           0 :         sc->sc_hdev.sc_parent = uha->parent;
     144           0 :         sc->sc_hdev.sc_report_id = uha->reportid;
     145           0 :         switch (uha->uaa->product) {
     146             :         case USB_PRODUCT_MICRODIA_TEMPER:
     147           0 :                 sc->sc_hdev.sc_intr = ugold_ds75_intr;
     148           0 :                 break;
     149             :         case USB_PRODUCT_MICRODIA_TEMPERHUM:
     150           0 :                 sc->sc_hdev.sc_intr = ugold_si700x_intr;
     151           0 :                 break;
     152             :         default:
     153           0 :                 printf(", unknown product\n");
     154           0 :                 return;
     155             :         }
     156             : 
     157           0 :         uhidev_get_report_desc(uha->parent, &desc, &size);
     158           0 :         repid = uha->reportid;
     159           0 :         sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
     160           0 :         sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
     161           0 :         sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
     162             : 
     163           0 :         if (uhidev_open(&sc->sc_hdev)) {
     164           0 :                 printf(", unable to open interrupt pipe\n");
     165           0 :                 return;
     166             :         }
     167             : 
     168           0 :         strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
     169             :             sizeof(sc->sc_sensordev.xname));
     170             : 
     171           0 :         switch (uha->uaa->product) {
     172             :         case USB_PRODUCT_MICRODIA_TEMPER:
     173             :                 /* 2 temperature sensors */
     174           0 :                 sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
     175           0 :                 strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
     176             :                     sizeof(sc->sc_sensor[UGOLD_INNER].desc));
     177           0 :                 sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP;
     178           0 :                 strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer",
     179             :                     sizeof(sc->sc_sensor[UGOLD_OUTER].desc));
     180           0 :                 break;
     181             :         case USB_PRODUCT_MICRODIA_TEMPERHUM:
     182             :                 /* 1 temperature and 1 humidity sensor */
     183           0 :                 sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP;
     184           0 :                 strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner",
     185             :                     sizeof(sc->sc_sensor[UGOLD_INNER].desc));
     186           0 :                 sc->sc_sensor[UGOLD_HUM].type = SENSOR_HUMIDITY;
     187           0 :                 strlcpy(sc->sc_sensor[UGOLD_HUM].desc, "RH",
     188             :                     sizeof(sc->sc_sensor[UGOLD_HUM].desc));
     189           0 :                 break;
     190             :         default:
     191           0 :                 printf(", unknown product\n");
     192           0 :                 return;
     193             :         }
     194             : 
     195             :         /* 0.1Hz */
     196           0 :         sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6);
     197           0 :         if (sc->sc_sensortask == NULL) {
     198           0 :                 printf(", unable to register update task\n");
     199           0 :                 return;
     200             :         }
     201           0 :         printf("\n");
     202             : 
     203           0 :         sensordev_install(&sc->sc_sensordev);
     204           0 : }
     205             : 
     206             : int
     207           0 : ugold_detach(struct device *self, int flags)
     208             : {
     209           0 :         struct ugold_softc *sc = (struct ugold_softc *)self;
     210             :         int i;
     211             : 
     212           0 :         if (sc->sc_sensortask != NULL) {
     213           0 :                 sensor_task_unregister(sc->sc_sensortask);
     214           0 :                 sensordev_deinstall(&sc->sc_sensordev);
     215           0 :         }
     216             : 
     217           0 :         for (i = 0; i < sc->sc_num_sensors; i++)
     218           0 :                 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]);
     219             : 
     220           0 :         if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
     221           0 :                 uhidev_close(&sc->sc_hdev);
     222             : 
     223           0 :         return (0);
     224             : }
     225             : 
     226             : static int
     227           0 : ugold_ds75_temp(uint8_t msb, uint8_t lsb)
     228             : {
     229             :         /* DS75 12bit precision mode: 0.0625 degrees Celsius ticks */
     230           0 :         return (((msb * 100) + ((lsb >> 4) * 25 / 4)) * 10000) + 273150000;
     231             : }
     232             : 
     233             : static void
     234           0 : ugold_ds75_type(struct ugold_softc *sc, uint8_t *buf, u_int len)
     235             : {
     236           0 :         if (memcmp(buf, "TEMPer1F", len) == 0 ||
     237           0 :             memcmp(buf, "TEMPer2F", len) == 0 ||
     238           0 :             memcmp(buf, "TEMPerF1", len) == 0)
     239             :                 return; /* skip first half of the answer */
     240             : 
     241           0 :         printf("%s: %d sensor%s type ds75/12bit (temperature)\n",
     242           0 :             sc->sc_hdev.sc_dev.dv_xname, sc->sc_num_sensors,
     243           0 :             (sc->sc_num_sensors == 1) ? "" : "s");
     244             : 
     245           0 :         sc->sc_type = -1; /* ignore type */
     246           0 : }
     247             : 
     248             : void
     249           0 : ugold_ds75_intr(struct uhidev *addr, void *ibuf, u_int len)
     250             : {
     251           0 :         struct ugold_softc *sc = (struct ugold_softc *)addr;
     252             :         uint8_t *buf = ibuf;
     253             :         int i, temp;
     254             : 
     255           0 :         switch (buf[0]) {
     256             :         case UGOLD_CMD_INIT:
     257           0 :                 if (sc->sc_num_sensors)
     258             :                         break;
     259             : 
     260           0 :                 sc->sc_num_sensors = min(buf[1], UGOLD_MAX_SENSORS) /* XXX */;
     261             : 
     262           0 :                 for (i = 0; i < sc->sc_num_sensors; i++) {
     263           0 :                         sc->sc_sensor[i].flags |= SENSOR_FINVALID;
     264           0 :                         sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
     265             :                 }
     266             : 
     267             :                 break;
     268             :         case UGOLD_CMD_DATA:
     269           0 :                 switch (buf[1]) {
     270             :                 case 4:
     271           0 :                         temp = ugold_ds75_temp(buf[4], buf[5]);
     272           0 :                         sc->sc_sensor[UGOLD_OUTER].value = temp;
     273           0 :                         sc->sc_sensor[UGOLD_OUTER].flags &= ~SENSOR_FINVALID;
     274             :                         /* FALLTHROUGH */
     275             :                 case 2:
     276           0 :                         temp = ugold_ds75_temp(buf[2], buf[3]);
     277           0 :                         sc->sc_sensor[UGOLD_INNER].value = temp;
     278           0 :                         sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID;
     279           0 :                         break;
     280             :                 default:
     281           0 :                         printf("%s: invalid data length (%d bytes)\n",
     282           0 :                                 sc->sc_hdev.sc_dev.dv_xname, buf[1]);
     283           0 :                 }
     284             :                 break;
     285             :         default:
     286           0 :                 if (!sc->sc_type) { /* type command returns arbitrary string */
     287           0 :                         ugold_ds75_type(sc, buf, len);
     288           0 :                         break;
     289             :                 }
     290           0 :                 printf("%s: unknown command 0x%02x\n",
     291           0 :                     sc->sc_hdev.sc_dev.dv_xname, buf[0]);
     292           0 :         }
     293           0 : }
     294             : 
     295             : static int
     296           0 : ugold_si700x_temp(int type, uint8_t msb, uint8_t lsb)
     297             : {
     298           0 :         int temp = msb * 256 + lsb;
     299             : 
     300           0 :         switch (type) { /* convert to mdegC */
     301             :         case UGOLD_TYPE_SI7005: /* 14bit 32 codes per degC 0x0000 = -50 degC */
     302           0 :                 temp = (((temp & 0x3fff) * 1000) / 32) - 50000;
     303           0 :                 break;
     304             :         case UGOLD_TYPE_SI7006: /* 14bit and status bit */
     305           0 :                 temp = (((temp & ~3) * 21965) / 8192) - 46850;
     306           0 :                 break;
     307             :         case UGOLD_TYPE_SHT1X:
     308           0 :                 temp = (temp * 1000) / 256;
     309           0 :                 break;
     310             :         default:
     311             :                 temp = 0;
     312           0 :         }
     313           0 :         return temp;
     314             : }
     315             : 
     316             : static int
     317           0 : ugold_si700x_rhum(int type, uint8_t msb, uint8_t lsb, int temp)
     318             : {
     319           0 :         int rhum = msb * 256 + lsb;
     320             : 
     321           0 :         switch (type) { /* convert to m%RH */
     322             :         case UGOLD_TYPE_SI7005: /* 12bit 16 codes per %RH 0x0000 = -24 %RH */
     323           0 :                 rhum = (((rhum & 0x0fff) * 1000) / 16) - 24000;
     324             : #if 0           /* todo: linearization and temperature compensation */
     325             :                 rhum -= -0.00393 * rhum * rhum + 0.4008 * rhum - 4.7844;
     326             :                 rhum += (temp - 30) * (0.00237 * rhum + 0.1973);
     327             : #endif
     328           0 :                 break;
     329             :         case UGOLD_TYPE_SI7006: /* 14bit and status bit */
     330           0 :                 rhum = (((rhum & ~3) * 15625) / 8192) - 6000;
     331           0 :                 break;
     332             :         case UGOLD_TYPE_SHT1X: /* 16 bit */
     333           0 :                 rhum = rhum * 32;
     334           0 :                 break;
     335             :         default:
     336             :                 rhum = 0;
     337           0 :         }
     338             : 
     339             :         /* limit the humidity to valid values */
     340           0 :         if (rhum < 0)
     341           0 :                 rhum = 0;
     342           0 :         else if (rhum > 100000)
     343           0 :                 rhum = 100000;
     344           0 :         return rhum;
     345             : }
     346             : 
     347             : static void
     348           0 : ugold_si700x_type(struct ugold_softc *sc, uint8_t *buf, u_int len)
     349             : {
     350           0 :         if (memcmp(buf, "TEMPerHu", len) == 0 ||
     351           0 :             memcmp(buf, "TEMPer1F", len) == 0)
     352             :                 return; /* skip equal first half of the answer */
     353             : 
     354           0 :         printf("%s: %d sensor%s type ", sc->sc_hdev.sc_dev.dv_xname,
     355           0 :             sc->sc_num_sensors, (sc->sc_num_sensors == 1) ? "" : "s");
     356             : 
     357           0 :         if (memcmp(buf, "mM12V1.0", len) == 0) {
     358           0 :                 sc->sc_type = UGOLD_TYPE_SI7005;
     359           0 :                 printf("si7005 (temperature and humidity)\n");
     360           0 :         } else if (memcmp(buf, "mM12V1.2", len) == 0) {
     361           0 :                 sc->sc_type = UGOLD_TYPE_SI7006;
     362           0 :                 printf("si7006 (temperature and humidity)\n");
     363           0 :         } else if (memcmp(buf, "_H1V1.5F", len) == 0) {
     364           0 :                 sc->sc_type = UGOLD_TYPE_SHT1X;
     365           0 :                 printf("sht1x (temperature and humidity)\n");
     366           0 :         } else {
     367           0 :                 sc->sc_type = -1;
     368           0 :                 printf("unknown\n");
     369             :         }
     370           0 : }
     371             : 
     372             : void
     373           0 : ugold_si700x_intr(struct uhidev *addr, void *ibuf, u_int len)
     374             : {
     375           0 :         struct ugold_softc *sc = (struct ugold_softc *)addr;
     376             :         uint8_t *buf = ibuf;
     377             :         int i, temp, rhum;
     378             : 
     379           0 :         switch (buf[0]) {
     380             :         case UGOLD_CMD_INIT:
     381           0 :                 if (sc->sc_num_sensors)
     382             :                         break;
     383             : 
     384           0 :                 sc->sc_num_sensors = min(buf[1], UGOLD_MAX_SENSORS) /* XXX */;
     385             : 
     386           0 :                 for (i = 0; i < sc->sc_num_sensors; i++) {
     387           0 :                         sc->sc_sensor[i].flags |= SENSOR_FINVALID;
     388           0 :                         sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
     389             :                 }
     390             :                 break;
     391             :         case UGOLD_CMD_DATA:
     392           0 :                 if (buf[1] != 4)
     393           0 :                         printf("%s: invalid data length (%d bytes)\n",
     394           0 :                             sc->sc_hdev.sc_dev.dv_xname, buf[1]);
     395           0 :                 temp = ugold_si700x_temp(sc->sc_type, buf[2], buf[3]);
     396           0 :                 sc->sc_sensor[UGOLD_INNER].value = (temp * 1000) + 273150000;
     397           0 :                 sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID;
     398           0 :                 rhum = ugold_si700x_rhum(sc->sc_type, buf[4], buf[5], temp);
     399           0 :                 sc->sc_sensor[UGOLD_HUM].value = rhum;
     400           0 :                 sc->sc_sensor[UGOLD_HUM].flags &= ~SENSOR_FINVALID;
     401           0 :                 break;
     402             :         default:
     403           0 :                 if (!sc->sc_type) { /* type command returns arbitrary string */
     404           0 :                         ugold_si700x_type(sc, buf, len);
     405           0 :                         break;
     406             :                 }
     407           0 :                 printf("%s: unknown command 0x%02x\n",
     408           0 :                     sc->sc_hdev.sc_dev.dv_xname, buf[0]);
     409           0 :         }
     410           0 : }
     411             : 
     412             : void
     413           0 : ugold_refresh(void *arg)
     414             : {
     415           0 :         struct ugold_softc *sc = arg;
     416             :         int i;
     417             : 
     418           0 :         if (!sc->sc_num_sensors) {
     419           0 :                 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init));
     420           0 :                 return;
     421             :         }
     422           0 :         if (!sc->sc_type) {
     423           0 :                 ugold_issue_cmd(sc, cmd_type, sizeof(cmd_type));
     424           0 :                 return;
     425             :         }
     426             : 
     427           0 :         if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) {
     428           0 :                 for (i = 0; i < sc->sc_num_sensors; i++)
     429           0 :                         sc->sc_sensor[i].flags |= SENSOR_FINVALID;
     430             :         }
     431           0 : }
     432             : 
     433             : int
     434           0 : ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len)
     435             : {
     436             :         int actlen;
     437             : 
     438           0 :         actlen = uhidev_set_report_async(sc->sc_hdev.sc_parent,
     439           0 :             UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmd, len);
     440           0 :         return (actlen != len);
     441             : }

Generated by: LCOV version 1.13