Author: mizhka
Date: Mon Jan 16 15:36:36 2017
New Revision: 312290
URL: https://svnweb.freebsd.org/changeset/base/312290

Log:
  [gpioths] new driver for temperature/humidity sensor DHT11
  
  This patch adds driver for temperature/humidity sensor connected via GPIO.
  To compile it into kernel add "device gpioths". To activate driver, use
  hints (.at and .pins) for gpiobus. As result it will provide temperature &
  humidity values via sysctl.
  
  DHT11 is cheap & popular temperature/humidity sensor used via GPIO on ARM
  or MIPS devices like Raspberry Pi or Onion Omega.
  
  Reviewed by:  adrian
  Approved by:  adrian (mentor)
  Differential Revision:        https://reviews.freebsd.org/D9185

Added:
  head/sys/dev/gpio/gpioths.c   (contents, props changed)
Modified:
  head/sys/conf/files

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Mon Jan 16 15:23:55 2017        (r312289)
+++ head/sys/conf/files Mon Jan 16 15:36:36 2017        (r312290)
@@ -1714,6 +1714,7 @@ dev/gpio/gpioled_fdt.c            optional gpioled
 dev/gpio/gpiopower.c           optional gpiopower fdt
 dev/gpio/gpioregulator.c       optional gpioregulator fdt ext_resources
 dev/gpio/gpiospi.c             optional gpiospi
+dev/gpio/gpioths.c             optional gpioths
 dev/gpio/gpio_if.m             optional gpio
 dev/gpio/gpiobus_if.m          optional gpio
 dev/gpio/gpiopps.c             optional gpiopps

Added: head/sys/dev/gpio/gpioths.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/gpio/gpioths.c Mon Jan 16 15:36:36 2017        (r312290)
@@ -0,0 +1,405 @@
+/*-
+ * Copyright (c) 2016 Michael Zhilin <miz...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/gpio.h>
+#include <machine/resource.h>
+
+#include "gpiobus_if.h"
+
+/*
+ * GPIOTHS - Temp/Humidity sensor over GPIO, e.g. DHT11/DHT22
+ * This is driver for Temperature & Humidity sensor which provides digital
+ * output over single-wire protocol from embedded 8-bit microcontroller.
+ * 
+ * Temp/Humidity sensor can't be discovered automatically, please specify hints
+ * as part of loader or kernel configuration:
+ *     hint.gpioths.0.at="gpiobus0"
+ *     hint.gpioths.0.pins=<PIN>
+ */
+
+#define        GPIOTHS_POLLTIME        5       /* in seconds */
+
+#define        GPIOTHS_DHT_STARTCYCLE  20000   /* 20ms = 20000us */
+#define        GPIOTHS_DHT_TIMEOUT     1000    /* 1ms = 1000us */
+#define        GPIOTHS_DHT_CYCLES      41
+#define        GPIOTHS_DHT_ONEBYTEMASK 0xFF
+#define        GPIOTHS_DHT_TEMP_SHIFT  8
+#define        GPIOTHS_DHT_HUM_SHIFT   24
+
+struct gpioths_softc {
+       device_t                 dev;
+       int                      temp;
+       int                      hum;
+       int                      fails;
+       struct sysctl_oid       *temp_oid;
+       struct sysctl_oid       *hum_oid;
+       struct sysctl_oid       *fails_oid;
+       struct callout           callout;
+};
+
+static devclass_t gpioths_devclass;
+
+/* Prototypes */
+static int             gpioths_probe(device_t dev);
+static int             gpioths_attach(device_t dev);
+static int             gpioths_detach(device_t dev);
+static void            gpioths_poll(void *arg);
+static int             gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS);
+static int             gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS);
+static int             gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS);
+
+/* DHT-specific methods */
+static int             gpioths_dht_initread(device_t bus, device_t dev);
+static int             gpioths_dht_readbytes(device_t bus, device_t dev);
+static int             gpioths_dht_timeuntil(device_t bus, device_t dev,
+                           uint32_t lev, uint32_t *time);
+
+/* Implementation */
+static int
+gpioths_probe(device_t dev)
+{
+       device_set_desc(dev, "Temperature and Humidity Sensor over GPIO");
+       return (0);
+}
+
+static int
+gpioths_dht_timeuntil(device_t bus, device_t dev, uint32_t lev, uint32_t *time)
+{
+       uint32_t        cur_level;
+       int             i;
+
+       for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) {
+               GPIOBUS_PIN_GET(bus, dev, 0, &cur_level);
+               if (cur_level == lev) {
+                       if (time != NULL)
+                               *time = i;
+                       return (0);
+               }
+               DELAY(1);
+       }
+
+       /* Timeout */
+       return (ETIMEDOUT);
+}
+
+static int
+gpioths_dht_initread(device_t bus, device_t dev)
+{
+       int     err;
+
+       err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
+       if (err != 0) {
+               device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, OUT) = %d\n", 
err);
+               return (err);
+       }
+       DELAY(1);
+
+       err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_LOW);
+       if (err != 0) {
+               device_printf(dev, "err(GPIOBUS_PIN_SET, LOW) = %d\n", err);
+               return (err);
+       }
+
+       /*
+        * According to specifications we need to wait no more than 18ms
+        * to start data transfer
+        */
+       DELAY(GPIOTHS_DHT_STARTCYCLE);
+       err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_HIGH);
+       if (err != 0) {
+               device_printf(dev, "err(GPIOBUS_PIN_SET, HIGH) = %d\n", err);
+               return (err);
+       }
+
+       DELAY(1);
+       err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_INPUT) ;
+       if (err != 0) {
+               device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, IN) = %d\n", err);
+               return (err);
+       }
+
+       DELAY(1);
+       return (0);
+}
+
+static int
+gpioths_dht_readbytes(device_t bus, device_t dev)
+{
+       struct gpioths_softc    *sc;
+       uint32_t                 calibrations[GPIOTHS_DHT_CYCLES];
+       uint32_t                 intervals[GPIOTHS_DHT_CYCLES];
+       uint32_t                 err, avglen, value;
+       uint8_t                  crc, calc;
+       int                      i, offset, size;
+
+       sc = device_get_softc(dev);
+
+       err = gpioths_dht_initread(bus,dev);
+       if (err) {
+               device_printf(dev, "gpioths_dht_initread error = %d\n", err);
+               goto error;
+       }
+
+       err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, NULL);
+       if (err) {
+               device_printf(dev, "err(START) = %d\n", err);
+               goto error;
+       }
+
+       /* reading - 41 cycles */
+       for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) {
+               err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_HIGH,
+                         &calibrations[i]);
+               if (err) {
+                       device_printf(dev, "err(CAL, %d) = %d\n", i, err);
+                       goto error;
+               }
+               err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW,
+                         &intervals[i]);
+               if (err) {
+                       device_printf(dev, "err(INTERVAL, %d) = %d\n", i, err);
+                       goto error;
+               }
+       }
+
+       err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
+       if (err != 0) {
+               device_printf(dev, "err(FINAL_SETFLAGS, OUT) = %d\n", err);
+               goto error;
+       }
+       DELAY(1);
+
+       /* Calculate average data calibration cycle length */
+       avglen = 0;
+       for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
+               avglen += calibrations[i];
+
+       avglen = avglen / (GPIOTHS_DHT_CYCLES - 1);
+
+       /* Calculate data */
+       value = 0;
+       offset = 1;
+       size = sizeof(value) * 8;
+       for (i = offset; i < size + offset; i++) {
+               value <<= 1;
+               if (intervals[i] > avglen)
+                       value += 1;
+       }
+
+       /* Calculate CRC */
+       crc = 0;
+       offset = sizeof(value) * 8 + 1;
+       size = sizeof(crc) * 8;
+       for (i = offset;  i < size + offset; i++) {
+               crc <<= 1;
+               if (intervals[i] > avglen)
+                       crc += 1;
+       }
+
+       calc = 0;
+       for (i = 0; i < sizeof(value); i++)
+               calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK;
+
+#ifdef GPIOTHS_DEBUG
+       /* Debug bits */
+       for (i = 0; i < GPIOTHS_DHT_CYCLES; i++)
+               device_printf(dev, "%d: %d %d\n", i, calibrations[i],
+                   intervals[i]);
+
+       device_printf(dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc,
+           calc);
+#endif /* GPIOTHS_DEBUG */
+
+       /* CRC check */
+       if (calc != crc) {
+               err = -1;
+               goto error;
+       }
+
+       sc->fails = 0;
+       sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
+       sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
+
+#ifdef GPIOTHS_DEBUG
+       /* Debug bits */
+       device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails,
+           sc->temp, sc->hum);
+#endif /* GPIOTHS_DEBUG */
+
+       return (0);
+error:
+       sc->fails++;
+       return (err);
+}
+
+static void
+gpioths_poll(void *arg)
+{
+       struct gpioths_softc    *sc;
+       device_t                 dev;
+
+       dev = (device_t)arg;
+       sc = device_get_softc(dev);
+
+       gpioths_dht_readbytes(device_get_parent(dev), dev);
+       callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz);
+}
+
+static int
+gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct gpioths_softc    *sc;
+       int                      value;
+
+       sc = (struct gpioths_softc*)arg1;
+       value = sc->temp;
+
+       return (sysctl_handle_int(oidp, &value, 0, req));
+}
+
+static int
+gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct gpioths_softc    *sc;
+       int                      value;
+
+       sc = (struct gpioths_softc*)arg1;
+       value = sc->hum;
+
+       return (sysctl_handle_int(oidp, &value, 0, req));
+}
+
+
+static int
+gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct gpioths_softc    *sc;
+       int                      value;
+
+       sc = (struct gpioths_softc*)arg1;
+       value = sc->fails;
+
+       return (sysctl_handle_int(oidp, &value, 0, req));
+}
+
+static int
+gpioths_attach(device_t dev)
+{
+       struct gpioths_softc    *sc;
+       struct sysctl_ctx_list  *ctx;
+       struct sysctl_oid       *tree;
+
+       sc = device_get_softc(dev);
+       ctx = device_get_sysctl_ctx(dev);
+       tree = device_get_sysctl_tree(dev);
+
+       sc->dev = dev;
+
+       callout_init(&sc->callout, 1);
+       callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev);
+
+       sc->temp_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+           "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
+           gpioths_temp_sysctl, "I", "temperature(C)");
+
+       sc->hum_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+           "humidity", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
+           gpioths_hum_sysctl, "I", "humidity(%)");
+
+       sc->fails_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+           "fails", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
+           gpioths_fails_sysctl, "I", "fails since last successful read");
+
+       return (0);
+}
+
+static int
+gpioths_detach(device_t dev)
+{
+
+       return (0);
+}
+
+/* DDB bits */
+#include "opt_ddb.h"
+#ifdef DDB
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+#include <sys/cons.h>
+
+static struct command_table db_gpioths_table = 
LIST_HEAD_INITIALIZER(db_t4_table);
+_DB_SET(_show, gpioths, NULL, db_show_table, 0, &db_gpioths_table);
+
+DB_FUNC(read, db_show_gpiothsread, db_gpioths_table, CS_OWN, NULL)
+{
+       device_t        dev;
+       int             t;
+       int             init;
+
+       init = 0;
+       t = db_read_token();
+       if (t == tIDENT) {
+               dev = device_lookup_by_name(db_tok_string);
+               init = 1;
+       }
+
+       db_skip_to_eol();
+
+       if (init)
+               db_printf("read: 0x%x\n",
+                   gpioths_dht_readbytes(dev, device_get_parent(dev)));
+       else
+               db_printf("usage: show gpioths read <gpiothsdevice>\n");
+
+return;
+}
+#endif /* DDB */
+
+/* Driver bits */
+static device_method_t gpioths_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,                 gpioths_probe),
+       DEVMETHOD(device_attach,                gpioths_attach),
+       DEVMETHOD(device_detach,                gpioths_detach),
+
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct 
gpioths_softc));
+DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to