/** * @file movementsensor.c * @author Florian Scholz * @date 11. May 2017 * @brief A kernel module for handling our movement sensor * @see http://www.derekmolloy.ie/ */ #include #include #include #include // Required for the GPIO functions #include // Required for the IRQ code #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Florian Scholz"); MODULE_DESCRIPTION("A driver for the movement sensor"); MODULE_VERSION("0.1"); #define DEVICE_NAME "movementsensorfs" #define CLASS_NAME "mvsfs" static unsigned int gpioSensor = 16; ///< hard coding the LED gpio for this example to P9_23 (GPIO49) static unsigned int irqNumber; ///< Used to share the IRQ number within this file static int majorNumber; static ktime_t diff_firing; static ktime_t last_firing; // Function prototype for the custom IRQ handler function -- see below for the implementation static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs); static struct class* ebbcharClass = NULL; ///< The device-driver class struct pointer static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int dev_open(struct inode *inodep, struct file *filep){ return 0; } static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){ char pbuf[64]; short value; struct timespec ts_last = ktime_to_timespec(ktime_sub(ktime_get(), last_firing)); struct timespec ts_diff = ktime_to_timespec(diff_firing); int cnt = snprintf(pbuf, len-1, "%d %d\n", ts_last.tv_sec, ts_diff.tv_sec); pbuf[cnt] = '\0'; len -= copy_to_user(buffer,pbuf,cnt); *offset += len; return len; } static struct file_operations fops = { .owner = THIS_MODULE, .open = dev_open, .read = dev_read }; static int __init ebbgpio_init(void){ last_firing = diff_firing = ktime_set(0,0); int result = 0; printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n"); // Is the GPIO a valid GPIO number (e.g., the BBB has 4x32 but not all available) if (!gpio_is_valid(gpioSensor)){ printk(KERN_INFO "GPIO_TEST: invalid LED GPIO\n"); return -ENODEV; } majorNumber = register_chrdev(0, DEVICE_NAME, &fops); if (majorNumber<0){ printk(KERN_ALERT "EBBChar failed to register a major number\n"); return majorNumber; } // Register the device class ebbcharClass = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Failed to register device class\n"); return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer } printk(KERN_INFO "EBBChar: device class registered correctly\n"); ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Failed to create the device\n"); return PTR_ERR(ebbcharDevice); } // Going to set up the LED. It is a GPIO in output mode and will be on by default gpio_request(gpioSensor, "sysfs"); // Set up the gpioButton gpio_direction_input(gpioSensor); // Set the button GPIO to be an input gpio_set_debounce(gpioSensor, 20); // Debounce the button with a delay of 200ms gpio_export(gpioSensor, false); // Causes gpio115 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed // Perform a quick test to see that the button is working as expected on LKM load printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpioSensor)); last_firing = ktime_get(); // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us irqNumber = gpio_to_irq(gpioSensor); printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irqNumber); // This next call requests an interrupt line result = request_irq(irqNumber, // The interrupt number requested (irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, // Interrupt on rising edge (button press, not release) "ebb_gpio_handler", // Used in /proc/interrupts to identify the owner NULL); // The *dev_id for shared interrupt lines, NULL is okay printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result); return result; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. Used to release the * GPIOs and display cleanup messages. */ static void __exit ebbgpio_exit(void) { free_irq(irqNumber, 0); gpio_unexport(gpioSensor); // Unexport the Button GPIO gpio_free(gpioSensor); // Free the Button GPIO device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device class_unregister(ebbcharClass); // unregister the device class class_destroy(ebbcharClass); // remove the device class unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number printk(KERN_INFO "GPIO_TEST: Goodbye from the LKM!\n"); } /** @brief The GPIO IRQ Handler function * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. * This function is static as it should not be invoked directly from outside of this file. * @param irq the IRQ number that is associated with the GPIO -- useful for logging. * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt * Not used in this example as NULL is passed. * @param regs h/w specific register values -- only really ever used for debugging. * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. */ static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){ // printk("interrupt fired\n"); int gpio_number = gpioSensor; if (gpio_number == gpioSensor) { int value = gpio_get_value(gpioSensor); if(value == 1) { // printk ("movement started\n"); last_firing = ktime_get(); diff_firing = ktime_set(0,0); } else { ktime_t current_firing = ktime_get(); diff_firing = ktime_sub(current_firing, last_firing); // struct timespec ts = ktime_to_timespec(diff_firing); // printk("movement stopped - duration: %ld\n", ts.tv_sec); } } return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly } /// This next calls are mandatory -- they identify the initialization function /// and the cleanup function (as above). module_init(ebbgpio_init); module_exit(ebbgpio_exit);