1 /*- 2 * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 13 * 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 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/bus.h> 33 #include <sys/module.h> 34 #include <sys/errno.h> 35 #include <sys/systm.h> 36 #include <sys/sysctl.h> 37 38 #include <machine/bus.h> 39 #include <sys/rman.h> 40 #include <sys/gpio.h> 41 #include <machine/resource.h> 42 43 #include "gpiobus_if.h" 44 45 /* 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. 49 * 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> 54 */ 55 56 #define GPIOTHS_POLLTIME 5 /* in seconds */ 57 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 64 65 struct gpioths_softc { 66 device_t dev; 67 int temp; 68 int hum; 69 int fails; 70 struct sysctl_oid *temp_oid; 71 struct sysctl_oid *hum_oid; 72 struct sysctl_oid *fails_oid; 73 struct callout callout; 74 }; 75 76 static devclass_t gpioths_devclass; 77 78 /* Prototypes */ 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); 86 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); 92 93 /* Implementation */ 94 static int 95 gpioths_probe(device_t dev) 96 { 97 device_set_desc(dev, "Temperature and Humidity Sensor over GPIO"); 98 return (0); 99 } 100 101 static int 102 gpioths_dht_timeuntil(device_t bus, device_t dev, uint32_t lev, uint32_t *time) 103 { 104 uint32_t cur_level; 105 int i; 106 107 for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) { 108 GPIOBUS_PIN_GET(bus, dev, 0, &cur_level); 109 if (cur_level == lev) { 110 if (time != NULL) 111 *time = i; 112 return (0); 113 } 114 DELAY(1); 115 } 116 117 /* Timeout */ 118 return (ETIMEDOUT); 119 } 120 121 static int 122 gpioths_dht_initread(device_t bus, device_t dev) 123 { 124 int err; 125 126 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT); 127 if (err != 0) { 128 device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, OUT) = %d\n", err); 129 return (err); 130 } 131 DELAY(1); 132 133 err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_LOW); 134 if (err != 0) { 135 device_printf(dev, "err(GPIOBUS_PIN_SET, LOW) = %d\n", err); 136 return (err); 137 } 138 139 /* 140 * According to specifications we need to wait no more than 18ms 141 * to start data transfer 142 */ 143 DELAY(GPIOTHS_DHT_STARTCYCLE); 144 err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_HIGH); 145 if (err != 0) { 146 device_printf(dev, "err(GPIOBUS_PIN_SET, HIGH) = %d\n", err); 147 return (err); 148 } 149 150 DELAY(1); 151 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_INPUT) ; 152 if (err != 0) { 153 device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, IN) = %d\n", err); 154 return (err); 155 } 156 157 DELAY(1); 158 return (0); 159 } 160 161 static int 162 gpioths_dht_readbytes(device_t bus, device_t dev) 163 { 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; 168 uint8_t crc, calc; 169 int i, offset, size; 170 171 sc = device_get_softc(dev); 172 173 err = gpioths_dht_initread(bus,dev); 174 if (err) { 175 device_printf(dev, "gpioths_dht_initread error = %d\n", err); 176 goto error; 177 } 178 179 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, NULL); 180 if (err) { 181 device_printf(dev, "err(START) = %d\n", err); 182 goto error; 183 } 184 185 /* reading - 41 cycles */ 186 for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) { 187 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_HIGH, 188 &calibrations[i]); 189 if (err) { 190 device_printf(dev, "err(CAL, %d) = %d\n", i, err); 191 goto error; 192 } 193 err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, 194 &intervals[i]); 195 if (err) { 196 device_printf(dev, "err(INTERVAL, %d) = %d\n", i, err); 197 goto error; 198 } 199 } 200 201 err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT); 202 if (err != 0) { 203 device_printf(dev, "err(FINAL_SETFLAGS, OUT) = %d\n", err); 204 goto error; 205 } 206 DELAY(1); 207 208 /* Calculate average data calibration cycle length */ 209 avglen = 0; 210 for (i = 1; i < GPIOTHS_DHT_CYCLES; i++) 211 avglen += calibrations[i]; 212 213 avglen = avglen / (GPIOTHS_DHT_CYCLES - 1); 214 215 /* Calculate data */ 216 value = 0; 217 offset = 1; 218 size = sizeof(value) * 8; 219 for (i = offset; i < size + offset; i++) { 220 value <<= 1; 221 if (intervals[i] > avglen) 222 value += 1; 223 } 224 225 /* Calculate CRC */ 226 crc = 0; 227 offset = sizeof(value) * 8 + 1; 228 size = sizeof(crc) * 8; 229 for (i = offset; i < size + offset; i++) { 230 crc <<= 1; 231 if (intervals[i] > avglen) 232 crc += 1; 233 } 234 235 calc = 0; 236 for (i = 0; i < sizeof(value); i++) 237 calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK; 238 239 #ifdef GPIOTHS_DEBUG 240 /* Debug bits */ 241 for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) 242 device_printf(dev, "%d: %d %d\n", i, calibrations[i], 243 intervals[i]); 244 245 device_printf(dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc, 246 calc); 247 #endif /* GPIOTHS_DEBUG */ 248 249 /* CRC check */ 250 if (calc != crc) { 251 err = -1; 252 goto error; 253 } 254 255 sc->fails = 0; 256 sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK; 257 sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK; 258 259 #ifdef GPIOTHS_DEBUG 260 /* Debug bits */ 261 device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails, 262 sc->temp, sc->hum); 263 #endif /* GPIOTHS_DEBUG */ 264 265 return (0); 266 error: 267 sc->fails++; 268 return (err); 269 } 270 271 static void 272 gpioths_poll(void *arg) 273 { 274 struct gpioths_softc *sc; 275 device_t dev; 276 277 dev = (device_t)arg; 278 sc = device_get_softc(dev); 279 280 gpioths_dht_readbytes(device_get_parent(dev), dev); 281 callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz); 282 } 283 284 static int 285 gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS) 286 { 287 struct gpioths_softc *sc; 288 int value; 289 290 sc = (struct gpioths_softc*)arg1; 291 value = sc->temp; 292 293 return (sysctl_handle_int(oidp, &value, 0, req)); 294 } 295 296 static int 297 gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS) 298 { 299 struct gpioths_softc *sc; 300 int value; 301 302 sc = (struct gpioths_softc*)arg1; 303 value = sc->hum; 304 305 return (sysctl_handle_int(oidp, &value, 0, req)); 306 } 307 308 309 static int 310 gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS) 311 { 312 struct gpioths_softc *sc; 313 int value; 314 315 sc = (struct gpioths_softc*)arg1; 316 value = sc->fails; 317 318 return (sysctl_handle_int(oidp, &value, 0, req)); 319 } 320 321 static int 322 gpioths_attach(device_t dev) 323 { 324 struct gpioths_softc *sc; 325 struct sysctl_ctx_list *ctx; 326 struct sysctl_oid *tree; 327 328 sc = device_get_softc(dev); 329 ctx = device_get_sysctl_ctx(dev); 330 tree = device_get_sysctl_tree(dev); 331 332 sc->dev = dev; 333 334 callout_init(&sc->callout, 1); 335 callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev); 336 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)"); 340 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(%)"); 344 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"); 348 349 return (0); 350 } 351 352 static int 353 gpioths_detach(device_t dev) 354 { 355 356 return (0); 357 } 358 359 /* DDB bits */ 360 #include "opt_ddb.h" 361 #ifdef DDB 362 #include <ddb/ddb.h> 363 #include <ddb/db_lex.h> 364 #include <sys/cons.h> 365 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); 368 369 DB_FUNC(read, db_show_gpiothsread, db_gpioths_table, CS_OWN, NULL) 370 { 371 device_t dev; 372 int t; 373 int init; 374 375 init = 0; 376 t = db_read_token(); 377 if (t == tIDENT) { 378 dev = device_lookup_by_name(db_tok_string); 379 init = 1; 380 } 381 382 db_skip_to_eol(); 383 384 if (init) 385 db_printf("read: 0x%x\n", 386 gpioths_dht_readbytes(dev, device_get_parent(dev))); 387 else 388 db_printf("usage: show gpioths read <gpiothsdevice>\n"); 389 390 return; 391 } 392 #endif /* DDB */ 393 394 /* Driver bits */ 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), 400 401 DEVMETHOD_END 402 }; 403 404 DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc)); 405 DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0); 406