The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/gpio/gpioths.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause
    3  *
    4  * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org> All rights reserved.
    5  * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 /*
   30  * GPIOTHS - Temp/Humidity sensor over GPIO.
   31  *
   32  * This is driver for Temperature & Humidity sensor which provides digital
   33  * output over single-wire protocol from embedded 8-bit microcontroller.
   34  * Note that it uses a custom single-wire protocol, it is not 1-wire(tm).
   35  * 
   36  * This driver supports the following chips:
   37  *   DHT11:  Temp   0c to 50c +-2.0c, Humidity 20% to  90% +-5%
   38  *   DHT12:  Temp -20c to 60c +-0.5c, Humidity 20% to  95% +-5%
   39  *   DHT21:  Temp -40c to 80c +-0.3c, Humidity  0% to 100% +-3%
   40  *   DHT22:  Temp -40c to 80c +-0.3c, Humidity  0% to 100% +-2%
   41  *   AM2301: Same as DHT21, but also supports i2c interface.
   42  *   AM2302: Same as DHT22, but also supports i2c interface.
   43  *
   44  * Temp/Humidity sensor can't be discovered automatically, please specify hints
   45  * as part of loader or kernel configuration:
   46  *      hint.gpioths.0.at="gpiobus0"
   47  *      hint.gpioths.0.pins=<PIN>
   48  *
   49  * Or configure via FDT data.
   50  */
   51 
   52 #include <sys/cdefs.h>
   53 __FBSDID("$FreeBSD$");
   54 
   55 #include <sys/param.h>
   56 #include <sys/kernel.h>
   57 #include <sys/bus.h>
   58 #include <sys/gpio.h>
   59 #include <sys/module.h>
   60 #include <sys/errno.h>
   61 #include <sys/systm.h>
   62 #include <sys/sysctl.h>
   63 #include <sys/taskqueue.h>
   64 
   65 #include <dev/gpio/gpiobusvar.h>
   66 
   67 #ifdef FDT
   68 #include <dev/ofw/ofw_bus.h>
   69 #include <dev/ofw/ofw_bus_subr.h>
   70 
   71 static struct ofw_compat_data compat_data[] = {
   72         {"dht11",  true},
   73         {NULL,     false}
   74 };
   75 OFWBUS_PNP_INFO(compat_data);
   76 SIMPLEBUS_PNP_INFO(compat_data);
   77 #endif /* FDT */
   78 
   79 #define PIN_IDX 0                       /* Use the first/only configured pin. */
   80 
   81 #define GPIOTHS_POLLTIME        5       /* in seconds */
   82 
   83 #define GPIOTHS_DHT_STARTCYCLE  20000   /* 20ms = 20000us */
   84 #define GPIOTHS_DHT_TIMEOUT     1000    /* 1ms = 1000us */
   85 #define GPIOTHS_DHT_CYCLES      41
   86 #define GPIOTHS_DHT_ONEBYTEMASK 0xFF
   87 
   88 struct gpioths_softc {
   89         device_t                 dev;
   90         gpio_pin_t               pin;
   91         int                      temp;
   92         int                      hum;
   93         int                      fails;
   94         struct timeout_task      task;
   95         bool                     detaching;
   96 };
   97 
   98 static int
   99 gpioths_probe(device_t dev)
  100 {
  101         int rv;
  102 
  103         /*
  104          * By default we only bid to attach if specifically added by our parent
  105          * (usually via hint.gpioths.#.at=busname).  On FDT systems we bid as
  106          * the default driver based on being configured in the FDT data.
  107          */
  108         rv = BUS_PROBE_NOWILDCARD;
  109 
  110 #ifdef FDT
  111         if (ofw_bus_status_okay(dev) &&
  112             ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  113                 rv = BUS_PROBE_DEFAULT;
  114 #endif
  115 
  116         device_set_desc(dev, "DHT11/DHT22 Temperature and Humidity Sensor");
  117 
  118         return (rv);
  119 }
  120 
  121 static int
  122 gpioths_dht_timeuntil(struct gpioths_softc *sc, bool lev, uint32_t *time)
  123 {
  124         bool            cur_level;
  125         int             i;
  126 
  127         for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) {
  128                 gpio_pin_is_active(sc->pin, &cur_level);
  129                 if (cur_level == lev) {
  130                         if (time != NULL)
  131                                 *time = i;
  132                         return (0);
  133                 }
  134                 DELAY(1);
  135         }
  136 
  137         /* Timeout */
  138         return (ETIMEDOUT);
  139 }
  140 
  141 static void
  142 gpioths_dht_initread(struct gpioths_softc *sc)
  143 {
  144 
  145         /*
  146          * According to specifications we need to drive the data line low for at
  147          * least 20ms then drive it high, to wake up the chip and signal it to
  148          * send a measurement. After sending this start signal, we switch the
  149          * pin back to input so the device can begin talking to us.
  150          */
  151         gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT);
  152         gpio_pin_set_active(sc->pin, false);
  153         pause_sbt("gpioths", ustosbt(GPIOTHS_DHT_STARTCYCLE), C_PREL(2), 0);
  154         gpio_pin_set_active(sc->pin, true);
  155         gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT);
  156 }
  157 
  158 static int
  159 gpioths_dht_readbytes(struct gpioths_softc *sc)
  160 {
  161         uint32_t                 calibrations[GPIOTHS_DHT_CYCLES];
  162         uint32_t                 intervals[GPIOTHS_DHT_CYCLES];
  163         uint32_t                 err, avglen, value;
  164         uint8_t                  crc, calc;
  165         int                      i, negmul, offset, size, tmphi, tmplo;
  166 
  167         gpioths_dht_initread(sc);
  168         
  169         err = gpioths_dht_timeuntil(sc, false, NULL);
  170         if (err) {
  171                 device_printf(sc->dev, "err(START) = %d\n", err);
  172                 goto error;
  173         }
  174 
  175         /* reading - 41 cycles */
  176         for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) {
  177                 err = gpioths_dht_timeuntil(sc, true, &calibrations[i]);
  178                 if (err) {
  179                         device_printf(sc->dev, "err(CAL, %d) = %d\n", i, err);
  180                         goto error;
  181                 }
  182                 err = gpioths_dht_timeuntil(sc, false, &intervals[i]);
  183                 if (err) {
  184                         device_printf(sc->dev, "err(INTERVAL, %d) = %d\n", i, err);
  185                         goto error;
  186                 }
  187         }
  188 
  189         /* Calculate average data calibration cycle length */
  190         avglen = 0;
  191         for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
  192                 avglen += calibrations[i];
  193 
  194         avglen = avglen / (GPIOTHS_DHT_CYCLES - 1);
  195 
  196         /* Calculate data */
  197         value = 0;
  198         offset = 1;
  199         size = sizeof(value) * 8;
  200         for (i = offset; i < size + offset; i++) {
  201                 value <<= 1;
  202                 if (intervals[i] > avglen)
  203                         value += 1;
  204         }
  205 
  206         /* Calculate CRC */
  207         crc = 0;
  208         offset = sizeof(value) * 8 + 1;
  209         size = sizeof(crc) * 8;
  210         for (i = offset;  i < size + offset; i++) {
  211                 crc <<= 1;
  212                 if (intervals[i] > avglen)
  213                         crc += 1;
  214         }
  215 
  216         calc = 0;
  217         for (i = 0; i < sizeof(value); i++)
  218                 calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK;
  219 
  220 #ifdef GPIOTHS_DEBUG
  221         /* Debug bits */
  222         for (i = 0; i < GPIOTHS_DHT_CYCLES; i++)
  223                 device_printf(dev, "%d: %d %d\n", i, calibrations[i],
  224                     intervals[i]);
  225 
  226         device_printf(dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc,
  227             calc);
  228 #endif /* GPIOTHS_DEBUG */
  229 
  230         /* CRC check */
  231         if (calc != crc) {
  232                 err = -1;
  233                 goto error;
  234         }
  235 
  236         /*
  237          * For DHT11/12, the values are split into 8 bits of integer and 8 bits
  238          * of fractional tenths.  On DHT11 the fraction bytes are always zero.
  239          * On DHT12 the sign bit is in the high bit of the fraction byte.
  240          *  - DHT11: 0HHHHHHH 00000000 00TTTTTT 00000000
  241          *  - DHT12: 0HHHHHHH 0000hhhh 00TTTTTT s000tttt
  242          *
  243          * For DHT21/21, the values are are encoded in 16 bits each, with the
  244          * temperature sign bit in the high bit.  The values are tenths of a
  245          * degree C and tenths of a percent RH.
  246          *  - DHT21: 000000HH HHHHHHHH s00000TT TTTTTTTT
  247          *  - DHT22: 000000HH HHHHHHHH s00000TT TTTTTTTT
  248          *
  249          * For all devices, some bits are always zero because of the range of
  250          * values supported by the device.
  251          *
  252          * We figure out how to decode things based on the high byte of the
  253          * humidity.  A DHT21/22 cannot report a value greater than 3 in
  254          * the upper bits of its 16-bit humidity.  A DHT11/12 should not report
  255          * a value lower than 20.  To allow for the possibility that a device
  256          * could report a value slightly out of its sensitivity range, we split
  257          * the difference and say if the value is greater than 10 it must be a
  258          * DHT11/12 (that would be a humidity over 256% on a DHT21/22).
  259          */
  260 #define DK_OFFSET 2731 /* Offset between K and C, in decikelvins. */
  261         if ((value >> 24) > 10) {
  262                 /* DHT11 or DHT12 */
  263                 tmphi = (value >> 8) & 0x3f;
  264                 tmplo = value & 0x0f;
  265                 negmul = (value & 0x80) ? -1 : 1;
  266                 sc->temp = DK_OFFSET + (negmul * (tmphi * 10 + tmplo));
  267                 sc->hum = (value >> 24) & 0x7f;
  268         } else {
  269                 /* DHT21 or DHT22 */
  270                 negmul = (value & 0x8000) ? -1 : 1;
  271                 sc->temp = DK_OFFSET + (negmul * (value & 0x03ff));
  272                 sc->hum = ((value >> 16) & 0x03ff) / 10;
  273         }
  274 
  275         sc->fails = 0;
  276 
  277 #ifdef GPIOTHS_DEBUG
  278         /* Debug bits */
  279         device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails,
  280             sc->temp, sc->hum);
  281 #endif /* GPIOTHS_DEBUG */
  282 
  283         return (0);
  284 error:
  285         sc->fails++;
  286         return (err);
  287 }
  288 
  289 static void
  290 gpioths_poll(void *arg, int pending __unused)
  291 {
  292         struct gpioths_softc    *sc;
  293 
  294         sc = (struct gpioths_softc *)arg;
  295 
  296         gpioths_dht_readbytes(sc);
  297         if (!sc->detaching)
  298                 taskqueue_enqueue_timeout_sbt(taskqueue_thread, &sc->task,
  299                     GPIOTHS_POLLTIME * SBT_1S, 0, C_PREL(3));
  300 }
  301 
  302 static int
  303 gpioths_attach(device_t dev)
  304 {
  305         struct gpioths_softc    *sc;
  306         struct sysctl_ctx_list  *ctx;
  307         struct sysctl_oid       *tree;
  308         int err;
  309 
  310         sc = device_get_softc(dev);
  311         ctx = device_get_sysctl_ctx(dev);
  312         tree = device_get_sysctl_tree(dev);
  313 
  314         sc->dev = dev;
  315 
  316         TIMEOUT_TASK_INIT(taskqueue_thread, &sc->task, 0, gpioths_poll, sc);
  317 
  318 #ifdef FDT
  319         /* Try to configure our pin from fdt data on fdt-based systems. */
  320         err = gpio_pin_get_by_ofw_idx(dev, ofw_bus_get_node(dev), PIN_IDX,
  321             &sc->pin);
  322 #else
  323         err = ENOENT;
  324 #endif
  325         /*
  326          * If we didn't get configured by fdt data and our parent is gpiobus,
  327          * see if we can be configured by the bus (allows hinted attachment even
  328          * on fdt-based systems).
  329          */
  330         if (err != 0 &&
  331             strcmp("gpiobus", device_get_name(device_get_parent(dev))) == 0)
  332                 err = gpio_pin_get_by_child_index(dev, PIN_IDX, &sc->pin);
  333 
  334         /* If we didn't get configured by either method, whine and punt. */
  335         if (err != 0) {
  336                 device_printf(sc->dev,
  337                     "cannot acquire gpio pin (config error)\n");
  338                 return (err);
  339         }
  340 
  341         /*
  342          * Ensure we have control of our pin, and preset the data line to its
  343          * idle condition (high).  Leave the line in input mode, relying on the
  344          * external pullup to keep the line high while idle.
  345          */
  346         err = gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT);
  347         if (err != 0) {
  348                 device_printf(dev, "gpio_pin_setflags(OUT) = %d\n", err);
  349                 return (err);
  350         }
  351         err = gpio_pin_set_active(sc->pin, true);
  352         if (err != 0) {
  353                 device_printf(dev, "gpio_pin_set_active(false) = %d\n", err);
  354                 return (err);
  355         }
  356         err = gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT);
  357         if (err != 0) {
  358                 device_printf(dev, "gpio_pin_setflags(IN) = %d\n", err);
  359                 return (err);
  360         }
  361 
  362         /* 
  363          * Do an initial read so we have correct values for reporting before
  364          * registering the sysctls that can access those values.  This also
  365          * schedules the periodic polling the driver does every few seconds to
  366          * update the sysctl variables.
  367          */
  368         gpioths_poll(sc, 0);
  369 
  370         sysctl_add_oid(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "temperature",                             \
  371             CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE,
  372             &sc->temp, 0, sysctl_handle_int, "IK", "temperature", NULL);
  373 
  374         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "humidity",
  375             CTLFLAG_RD, &sc->hum, 0, "relative humidity(%)");
  376 
  377         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fails",
  378             CTLFLAG_RD, &sc->fails, 0,
  379             "failures since last successful read");
  380 
  381         return (0);
  382 }
  383 
  384 static int
  385 gpioths_detach(device_t dev)
  386 {
  387         struct gpioths_softc    *sc;
  388 
  389         sc = device_get_softc(dev);
  390         gpio_pin_release(sc->pin);
  391         sc->detaching = true;
  392         while (taskqueue_cancel_timeout(taskqueue_thread, &sc->task, NULL) != 0)
  393                 taskqueue_drain_timeout(taskqueue_thread, &sc->task);
  394 
  395         return (0);
  396 }
  397 
  398 /* Driver bits */
  399 static device_method_t gpioths_methods[] = {
  400         /* Device interface */
  401         DEVMETHOD(device_probe,                 gpioths_probe),
  402         DEVMETHOD(device_attach,                gpioths_attach),
  403         DEVMETHOD(device_detach,                gpioths_detach),
  404 
  405         DEVMETHOD_END
  406 };
  407 
  408 static devclass_t gpioths_devclass;
  409 
  410 DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc));
  411 
  412 #ifdef FDT
  413 DRIVER_MODULE(gpioths, simplebus, gpioths_driver, gpioths_devclass, 0, 0);
  414 #endif
  415 
  416 DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0);
  417 MODULE_DEPEND(gpioths, gpiobus, 1, 1, 1);

Cache object: 0fe8667334d5e658deb540c0413496d7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.