#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");