Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @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 <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/gpio.h> // Required for the GPIO functions
- #include <linux/interrupt.h> // Required for the IRQ code
- #include <linux/ktime.h>
- #include <asm/uaccess.h>
- 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);
Advertisement
Add Comment
Please, Sign In to add comment