/* * dht11.c - Using GPIO to read temperature and humidity from DHT11 sensor. */ #include #include #include #include #include #include #include #include #include #include #include #include #define GPIO_PIN_4 575 #define DEVICE_NAME "dht11" #define DEVICE_CNT 1 static char msg[64]; struct dht11_dev { dev_t dev_num; int major_num, minor_num; struct cdev cdev; struct class *cls; struct device *dev; }; static struct dht11_dev dht11_device; /* Define GPIOs for LEDs. * TODO: According to the requirements, search /sys/kernel/debug/gpio to * find the corresponding GPIO location. */ static struct gpio dht11[] = { { GPIO_PIN_4, GPIOF_OUT_INIT_HIGH, "Signal" } }; static int dht11_read_data(void) { int timeout; uint8_t sensor_data[5] = { 0 }; uint8_t i, j; gpio_set_value(dht11[0].gpio, 0); mdelay(20); gpio_set_value(dht11[0].gpio, 1); udelay(30); gpio_direction_input(dht11[0].gpio); udelay(2); timeout = 300; while (gpio_get_value(dht11[0].gpio) && timeout--) udelay(1); if (timeout == -1) return -ETIMEDOUT; timeout = 300; while (!gpio_get_value(dht11[0].gpio) && timeout--) udelay(1); if (timeout == -1) return -ETIMEDOUT; timeout = 300; while (gpio_get_value(dht11[0].gpio) && timeout--) udelay(1); if (timeout == -1) return -ETIMEDOUT; for (j = 0; j < 5; j++) { uint8_t byte = 0; for (i = 0; i < 8; i++) { timeout = 300; while (gpio_get_value(dht11[0].gpio) && timeout--) udelay(1); if (timeout == -1) return -ETIMEDOUT; timeout = 300; while (!gpio_get_value(dht11[0].gpio) && timeout--) udelay(1); if (timeout == -1) return -ETIMEDOUT; udelay(50); byte <<= 1; if (gpio_get_value(dht11[0].gpio)) byte |= 0x01; } sensor_data[j] = byte; } if (sensor_data[4] != (uint8_t)(sensor_data[0] + sensor_data[1] + sensor_data[2] + sensor_data[3])) return -EIO; gpio_direction_output(dht11[0].gpio, 1); sprintf(msg, "Humidity: %d%%\nTemperature: %d deg C\n", sensor_data[0], sensor_data[2]); return 0; } static int device_open(struct inode *inode, struct file *file) { int ret, retry; for (retry = 0; retry < 5; ++retry) { ret = dht11_read_data(); if (ret == 0) return 0; msleep(10); } gpio_direction_output(dht11[0].gpio, 1); return ret; } static int device_release(struct inode *inode, struct file *file) { return 0; } static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { int msg_len = strlen(msg); if (*offset >= msg_len) return 0; size_t remain = msg_len - *offset; size_t bytes_read = min(length, remain); if (copy_to_user(buffer, msg + *offset, bytes_read)) return -EFAULT; *offset += bytes_read; return bytes_read; } static struct file_operations fops = { #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) .owner = THIS_MODULE, #endif .open = device_open, .release = device_release, .read = device_read }; /* Initialize the module - Register the character device */ static int __init dht11_init(void) { int ret = 0; /* Determine whether dynamic allocation of the device number is needed. */ if (dht11_device.major_num) { dht11_device.dev_num = MKDEV(dht11_device.major_num, dht11_device.minor_num); ret = register_chrdev_region(dht11_device.dev_num, DEVICE_CNT, DEVICE_NAME); } else { ret = alloc_chrdev_region(&dht11_device.dev_num, 0, DEVICE_CNT, DEVICE_NAME); } /* Negative values signify an error */ if (ret < 0) { pr_alert("Failed to register character device, error: %d\n", ret); return ret; } pr_info("Major = %d, Minor = %d\n", MAJOR(dht11_device.dev_num), MINOR(dht11_device.dev_num)); /* Prevents module unloading while operations are in use */ #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) dht11_device.cdev.owner = THIS_MODULE; #endif cdev_init(&dht11_device.cdev, &fops); ret = cdev_add(&dht11_device.cdev, dht11_device.dev_num, 1); if (ret) { pr_err("Failed to add the device to the system\n"); goto fail1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) dht11_device.cls = class_create(DEVICE_NAME); #else dht11_device.cls = class_create(THIS_MODULE, DEVICE_NAME); #endif if (IS_ERR(dht11_device.cls)) { pr_err("Failed to create class for device\n"); ret = PTR_ERR(dht11_device.cls); goto fail2; } dht11_device.dev = device_create(dht11_device.cls, NULL, dht11_device.dev_num, NULL, DEVICE_NAME); if (IS_ERR(dht11_device.dev)) { pr_err("Failed to create the device file\n"); ret = PTR_ERR(dht11_device.dev); goto fail3; } pr_info("Device created on /dev/%s\n", DEVICE_NAME); ret = gpio_request(dht11[0].gpio, dht11[0].label); if (ret) { pr_err("Unable to request GPIOs for dht11: %d\n", ret); goto fail4; } ret = gpio_direction_output(dht11[0].gpio, 1); if (ret) { pr_err("Failed to set GPIO %d direction\n", dht11[0].gpio); goto fail5; } return 0; fail5: gpio_free(dht11[0].gpio); fail4: device_destroy(dht11_device.cls, dht11_device.dev_num); fail3: class_destroy(dht11_device.cls); fail2: cdev_del(&dht11_device.cdev); fail1: unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT); return ret; } static void __exit dht11_exit(void) { gpio_set_value(dht11[0].gpio, 0); gpio_free(dht11[0].gpio); device_destroy(dht11_device.cls, dht11_device.dev_num); class_destroy(dht11_device.cls); cdev_del(&dht11_device.cdev); unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT); } module_init(dht11_init); module_exit(dht11_exit); MODULE_LICENSE("GPL");