florianscholz icon

Untitled

florianscholz | PRO | 05/16/17 04:51:03 PM UTC | 0 ⭐ | 175 👁️ | Never ⏰ | []
C |

7.95 KB

|

None

|

0 👍

/

0 👎

/**
 * @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);

Comments