Friday, 4 September 2015

Switch_LED.c complete Interrupt based program to switch ON LED when BUTTON is pressed

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/sched.h> /* current and everything */
#include <linux/types.h> /* size_t */
#include <linux/wait.h>     /* Wait Q, ... */
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>

#define FIRST_MINOR 0
#define MINOR_CNT 1

static dev_t dev;
static struct cdev c_dev;
static struct class *cl;

static unsigned int sw_gpio_num = 72; // GPIO 2_8
static unsigned int led_gpio_num = 56; // GPIO 1_24
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
static unsigned char c;

static irqreturn_t sw_irq_handler(int irq, void *data)
{
    int val;

    val = !gpio_get_value(sw_gpio_num);
    gpio_set_value(led_gpio_num, val);
    c = '0' + val;

    //printk(KERN_INFO "Awakening the readers...\n");
    flag = 1;
    wake_up_interruptible(&wq);

    return IRQ_HANDLED;
}

static int swl_char_open(struct inode *i, struct file *f)
{
    int ret;
    int irq;

    if ((ret = gpio_request_one(sw_gpio_num, GPIOF_IN, "sw_int")) < 0)
    {
        printk(KERN_ERR "GPIO %d is already in use\n", sw_gpio_num);
        return ret;
    }
    if ((irq = gpio_to_irq(sw_gpio_num)) < 0)
    {
        printk(KERN_ALERT "GPIO %d cannot be used as interrupt", sw_gpio_num);
        gpio_free(sw_gpio_num);
        return irq;
    }
    if ((ret = request_irq(irq, sw_irq_handler,
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "sw_int", NULL)) < 0)
    {
        printk(KERN_ERR "Registering irq %d failed\n", irq);
        gpio_free(sw_gpio_num);
        return ret;
    }
    return 0;
}

static int swl_char_close(struct inode *i, struct file *f)
{
    free_irq(gpio_to_irq(sw_gpio_num), NULL);
    gpio_free(sw_gpio_num);
    return 0;
}

static ssize_t swl_char_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    //printk(KERN_INFO "Process %i (%s) going to sleep\n", current->pid, current->comm);
    wait_event_interruptible(wq, flag != 0);
    flag = 0;
    //printk(KERN_INFO "Awoken %i (%s)\n", current->pid, current->comm);
    if (copy_to_user(buf, &c, 1))
    {
        return -EFAULT;
    }
    return 1;
}

static struct file_operations swl_char_fops =
{
    .owner = THIS_MODULE,
    .open = swl_char_open,
    .release = swl_char_close,
    .read = swl_char_read,
};

static int __init swl_char_init(void)
{
    int ret;
    struct device *dev_ret;

    if ((ret = alloc_chrdev_region(&dev, FIRST_MINOR, MINOR_CNT, "switch")) < 0)
    {
        return ret;
    }

    cdev_init(&c_dev, &swl_char_fops);

    if ((ret = cdev_add(&c_dev, dev, MINOR_CNT)) < 0)
    {
        unregister_chrdev_region(dev, MINOR_CNT);
        return ret;
    }
   
    if (IS_ERR(cl = class_create(THIS_MODULE, "char")))
    {
        cdev_del(&c_dev);
        unregister_chrdev_region(dev, MINOR_CNT);
        return PTR_ERR(cl);
    }
    if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "switch%d", 0)))
    {
        class_destroy(cl);
        cdev_del(&c_dev);
        unregister_chrdev_region(dev, MINOR_CNT);
        return PTR_ERR(dev_ret);
    }

    return 0;
}

static void __exit swl_char_exit(void)
{
    device_destroy(cl, dev);
    class_destroy(cl);
    cdev_del(&c_dev);
    unregister_chrdev_region(dev, MINOR_CNT);
}

module_init(swl_char_init);
module_exit(swl_char_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <anil@sysplay.in>");
MODULE_DESCRIPTION("Switch Char Driver Demo");

No comments:

Post a Comment