2 * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/errno.h>
35 #include <sys/systm.h>
36 #include <sys/sysctl.h>
38 #include <machine/bus.h>
41 #include <machine/resource.h>
43 #include "gpiobus_if.h"
46 * GPIOTHS - Temp/Humidity sensor over GPIO, e.g. DHT11/DHT22
47 * This is driver for Temperature & Humidity sensor which provides digital
48 * output over single-wire protocol from embedded 8-bit microcontroller.
50 * Temp/Humidity sensor can't be discovered automatically, please specify hints
51 * as part of loader or kernel configuration:
52 * hint.gpioths.0.at="gpiobus0"
53 * hint.gpioths.0.pins=<PIN>
56 #define GPIOTHS_POLLTIME 5 /* in seconds */
58 #define GPIOTHS_DHT_STARTCYCLE 20000 /* 20ms = 20000us */
59 #define GPIOTHS_DHT_TIMEOUT 1000 /* 1ms = 1000us */
60 #define GPIOTHS_DHT_CYCLES 41
61 #define GPIOTHS_DHT_ONEBYTEMASK 0xFF
62 #define GPIOTHS_DHT_TEMP_SHIFT 8
63 #define GPIOTHS_DHT_HUM_SHIFT 24
65 struct gpioths_softc {
70 struct sysctl_oid *temp_oid;
71 struct sysctl_oid *hum_oid;
72 struct sysctl_oid *fails_oid;
73 struct callout callout;
76 static devclass_t gpioths_devclass;
79 static int gpioths_probe(device_t dev);
80 static int gpioths_attach(device_t dev);
81 static int gpioths_detach(device_t dev);
82 static void gpioths_poll(void *arg);
83 static int gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS);
84 static int gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS);
85 static int gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS);
87 /* DHT-specific methods */
88 static int gpioths_dht_initread(device_t bus, device_t dev);
89 static int gpioths_dht_readbytes(device_t bus, device_t dev);
90 static int gpioths_dht_timeuntil(device_t bus, device_t dev,
91 uint32_t lev, uint32_t *time);
95 gpioths_probe(device_t dev)
97 device_set_desc(dev, "Temperature and Humidity Sensor over GPIO");
102 gpioths_dht_timeuntil(device_t bus, device_t dev, uint32_t lev, uint32_t *time)
107 for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) {
108 GPIOBUS_PIN_GET(bus, dev, 0, &cur_level);
109 if (cur_level == lev) {
122 gpioths_dht_initread(device_t bus, device_t dev)
126 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
128 device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, OUT) = %d\n", err);
133 err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_LOW);
135 device_printf(dev, "err(GPIOBUS_PIN_SET, LOW) = %d\n", err);
140 * According to specifications we need to wait no more than 18ms
141 * to start data transfer
143 DELAY(GPIOTHS_DHT_STARTCYCLE);
144 err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_HIGH);
146 device_printf(dev, "err(GPIOBUS_PIN_SET, HIGH) = %d\n", err);
151 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_INPUT) ;
153 device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, IN) = %d\n", err);
162 gpioths_dht_readbytes(device_t bus, device_t dev)
164 struct gpioths_softc *sc;
165 uint32_t calibrations[GPIOTHS_DHT_CYCLES];
166 uint32_t intervals[GPIOTHS_DHT_CYCLES];
167 uint32_t err, avglen, value;
171 sc = device_get_softc(dev);
173 err = gpioths_dht_initread(bus,dev);
175 device_printf(dev, "gpioths_dht_initread error = %d\n", err);
179 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, NULL);
181 device_printf(dev, "err(START) = %d\n", err);
185 /* reading - 41 cycles */
186 for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) {
187 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_HIGH,
190 device_printf(dev, "err(CAL, %d) = %d\n", i, err);
193 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW,
196 device_printf(dev, "err(INTERVAL, %d) = %d\n", i, err);
201 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
203 device_printf(dev, "err(FINAL_SETFLAGS, OUT) = %d\n", err);
208 /* Calculate average data calibration cycle length */
210 for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
211 avglen += calibrations[i];
213 avglen = avglen / (GPIOTHS_DHT_CYCLES - 1);
218 size = sizeof(value) * 8;
219 for (i = offset; i < size + offset; i++) {
221 if (intervals[i] > avglen)
227 offset = sizeof(value) * 8 + 1;
228 size = sizeof(crc) * 8;
229 for (i = offset; i < size + offset; i++) {
231 if (intervals[i] > avglen)
236 for (i = 0; i < sizeof(value); i++)
237 calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK;
241 for (i = 0; i < GPIOTHS_DHT_CYCLES; i++)
242 device_printf(dev, "%d: %d %d\n", i, calibrations[i],
245 device_printf(dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc,
247 #endif /* GPIOTHS_DEBUG */
256 sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
257 sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
261 device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails,
263 #endif /* GPIOTHS_DEBUG */
272 gpioths_poll(void *arg)
274 struct gpioths_softc *sc;
278 sc = device_get_softc(dev);
280 gpioths_dht_readbytes(device_get_parent(dev), dev);
281 callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz);
285 gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS)
287 struct gpioths_softc *sc;
290 sc = (struct gpioths_softc*)arg1;
293 return (sysctl_handle_int(oidp, &value, 0, req));
297 gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS)
299 struct gpioths_softc *sc;
302 sc = (struct gpioths_softc*)arg1;
305 return (sysctl_handle_int(oidp, &value, 0, req));
310 gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS)
312 struct gpioths_softc *sc;
315 sc = (struct gpioths_softc*)arg1;
318 return (sysctl_handle_int(oidp, &value, 0, req));
322 gpioths_attach(device_t dev)
324 struct gpioths_softc *sc;
325 struct sysctl_ctx_list *ctx;
326 struct sysctl_oid *tree;
328 sc = device_get_softc(dev);
329 ctx = device_get_sysctl_ctx(dev);
330 tree = device_get_sysctl_tree(dev);
334 callout_init(&sc->callout, 1);
335 callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev);
337 sc->temp_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
338 "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
339 gpioths_temp_sysctl, "I", "temperature(C)");
341 sc->hum_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
342 "humidity", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
343 gpioths_hum_sysctl, "I", "humidity(%)");
345 sc->fails_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
346 "fails", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
347 gpioths_fails_sysctl, "I", "fails since last successful read");
353 gpioths_detach(device_t dev)
363 #include <ddb/db_lex.h>
364 #include <sys/cons.h>
366 static struct command_table db_gpioths_table = LIST_HEAD_INITIALIZER(db_t4_table);
367 _DB_SET(_show, gpioths, NULL, db_show_table, 0, &db_gpioths_table);
369 DB_FUNC(read, db_show_gpiothsread, db_gpioths_table, CS_OWN, NULL)
378 dev = device_lookup_by_name(db_tok_string);
385 db_printf("read: 0x%x\n",
386 gpioths_dht_readbytes(dev, device_get_parent(dev)));
388 db_printf("usage: show gpioths read <gpiothsdevice>\n");
395 static device_method_t gpioths_methods[] = {
396 /* Device interface */
397 DEVMETHOD(device_probe, gpioths_probe),
398 DEVMETHOD(device_attach, gpioths_attach),
399 DEVMETHOD(device_detach, gpioths_detach),
404 DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc));
405 DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0);