#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
//#include <mach/irqs.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("farsight");
#define GPH0CON 0xe0300c00
static int key_major = 250;
static int key_minor = 0;
struct key_device
{
int keyval;
struct cdev cdev;
struct semaphore sem;
wait_queue_head_t rwait;
} key_device;
void *gph0con;
static int s5pc100_key_open(struct inode *inode, struct file *filp)
{
struct key_device *dev = container_of(inode->i_cdev, struct key_device, cdev);
filp->private_data = dev;
printk("s5pc100_key is opened\n");
return 0;
}
static int s5pc100_key_release(struct inode *inode, struct file *filp)
{
printk("s5pc100_key is closed\n");
return 0;
}
static int s5pc100_key_read(struct file *filp, char __user *buf, size_t size, loff_t *off)
{
struct key_device *dev = filp->private_data;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
while (dev->keyval == 0)
{
up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(dev->rwait, dev->keyval != 0))
return -ERESTARTSYS;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
if (copy_to_user(buf, &dev->keyval, sizeof(int)))
{
up(&dev->sem);
return -EFAULT;
}
dev->keyval = 0;
up(&dev->sem);
return sizeof(int);
}
static struct file_operations s5pc100_key_ops = {
.owner = THIS_MODULE,
.open = s5pc100_key_open,
.release = s5pc100_key_release,
.read = s5pc100_key_read,
};
static irqreturn_t handler(int irqno, void *dev_id)
{
static long old_jiffies = 0;
if (jiffies - old_jiffies < 20) return IRQ_HANDLED;
old_jiffies = jiffies;
printk("irq: interrupt %d\n", irqno);
switch (irqno)
{
case IRQ_EINT(1) :
key_device.keyval = 1;
break;
case IRQ_EINT(2) :
key_device.keyval = 2;
break;
case IRQ_EINT(3) :
key_device.keyval = 3;
break;
case IRQ_EINT(4) :
key_device.keyval = 4;
break;
case IRQ_EINT(6) :
key_device.keyval = 5;
break;
case IRQ_EINT(7) :
key_device.keyval = 6;
break;
}
wake_up(&key_device.rwait);
return IRQ_HANDLED;
}
static int key_request_irq(void)
{
int result;
result = request_irq(IRQ_EINT(1), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_1", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(1));
return result;
}
result = request_irq(IRQ_EINT(2), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_2", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(2));
goto _error_irq1;
}
result = request_irq(IRQ_EINT(3), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_3", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(3));
goto _error_irq2;
}
result = request_irq(IRQ_EINT(4), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_4", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(4));
goto _error_irq3;
}
result = request_irq(IRQ_EINT(6), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_6", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(6));
goto _error_irq4;
}
result = request_irq(IRQ_EINT(7), handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key_7", NULL);
if (result) {
printk("key: request irq %d failed!\n", IRQ_EINT(7));
goto _error_irq6;
}
return 0;
_error_irq6 :
free_irq(IRQ_EINT(6), NULL);
_error_irq4 :
free_irq(IRQ_EINT(4), NULL);
_error_irq3 :
free_irq(IRQ_EINT(3), NULL);
_error_irq2 :
free_irq(IRQ_EINT(2), NULL);
_error_irq1 :
free_irq(IRQ_EINT(1), NULL);
return result;
}
static void init_key(void)
{
gph0con = ioremap(GPH0CON, 4);
writel((readl(gph0con) & ~0xff0ffff0) | (0x22022220), gph0con);
}
static void key_free_irq(void)
{
free_irq(IRQ_EINT(1), NULL);
free_irq(IRQ_EINT(2), NULL);
free_irq(IRQ_EINT(3), NULL);
free_irq(IRQ_EINT(4), NULL);
free_irq(IRQ_EINT(6), NULL);
free_irq(IRQ_EINT(7), NULL);
}
static int key_setup_cdev(struct cdev *cdev, struct file_operations *ops)
{
int result;
dev_t devno = MKDEV(key_major, key_minor);
cdev_init(cdev, ops);
cdev->owner = THIS_MODULE;
result = cdev_add(cdev, devno, 1);
if (result)
{
printk("key: fail to add cdev\n");
return result;
}
return 0;
}
static int __init s5pc100_key_init(void)
{
int result;
dev_t devno = MKDEV(key_major, key_minor);
result = register_chrdev_region(devno, 1, "s5pc100_key");
if (result)
{
printk("key: unable to get major %d\n", key_major);
return result;
}
result = key_setup_cdev(&key_device.cdev, &s5pc100_key_ops);
if (result)
goto _error1;
result = key_request_irq();
if (result)
goto _error2;
init_key();
key_device.keyval = 0;
init_MUTEX(&key_device.sem); // can used in kernel which version is no more than 2.6.35
init_waitqueue_head(&key_device.rwait);
printk("key : init_module\n");
return 0;
_error2:
cdev_del(&key_device.cdev);
_error1:
unregister_chrdev_region(devno, 1);
return result;
}
static void __exit s5pc100_key_exit(void)
{
dev_t devno = MKDEV(key_major, key_minor);
key_free_irq();
cdev_del(&key_device.cdev);
unregister_chrdev_region(devno, 1);
printk("key: cleanup_module!\n");
}
module_init(s5pc100_key_init);
module_exit(s5pc100_key_exit);