#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/cdev.h> //与cdev操作相关
#include <linux/sched.h> //与wait等操作相关
#include <linux/interrupt.h> //irq函数操作
#include <linux/device.h> //与cdev操作相关
#include <linux/poll.h>
#include <linux/semaphore.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/signal.h> //SA_SHIRQ
#include <mach/irqs.h>
#define DEV_NAME "irqs"
dev_t devid;
struct workqueue_struct *myworkq;
static struct delayed_work irq_delay_work;
static struct work_struct irq_work;
static struct tasklet_struct irqtask;
static DEFINE_SEMAPHORE(irq_lock); //声明信号量,用于锁定设备
static DECLARE_WAIT_QUEUE_HEAD(irq_waitq); //声明等待队列
struct irq_dev {
struct cdev *irq_cdev;
struct class *irq_class;
};
struct irq_dev mydev; //这里的结构设计的不好,这样做的意义不明显
static volatile int press[4] = {0,0,0,0}; //按键按下次数统计
static volatile int ev_press = 0; //按键按下事件标志
static const int *irqid[4] = {1,2,3,4};//用于申请中断时的dev_id
volatile int *irqid_tmp;
static irqreturn_t irq1(int irq, void *dev_id)
{
volatile int *irqid_tmp = (volatile int *)dev_id;
printk("key1 dev_id:%d\n",*irqid_tmp);
press[0]++;
ev_press = 1;
wake_up_interruptible(&irq_waitq);
printk("key1 pressed %d times\n",press[0]);
return IRQ_HANDLED;
}
void irq2_tasklet(unsigned long arg)
{
press[1]++;
ev_press = 1;
wake_up_interruptible(&irq_waitq);
printk("key2 pressed %d times\n",press[1]);
}
static irqreturn_t irq2(int irq, void *dev_id)
{
volatile int *irqid_tmp = (volatile int *)dev_id;
printk("key2 dev_id:%d\n",*irqid_tmp);
tasklet_schedule(&irqtask); //调度tasklet
return IRQ_HANDLED;
}
void irq3_work(void *arg)
{
press[2]++;
ev_press = 1;
wake_up_interruptible(&irq_waitq);
printk("key3 pressed %d times\n",press[2]);
}
static irqreturn_t irq3(int irq, void *dev_id)
{
int ret;
volatile int *irqid_tmp = (volatile int *)dev_id;
printk("key3 dev_id:%d\n",*irqid_tmp);
ret = schedule_work(&irq_work); //调度工作队列
if(ret!=1) {
printk("irq3 work schedule error\n");
printk("ret=%d\n",ret);
}
return IRQ_HANDLED;
}
void irq4_work_delay(void *arg)
{
press[3]++;
ev_press = 1;
wake_up_interruptible(&irq_waitq);
printk("key4 pressed %d times\n",press[2]);
}
static irqreturn_t irq4(int irq, void *dev_id)
{
int ret;
volatile int *irqid_tmp = (volatile int *)dev_id;
printk("key4 dev_id:%d\n",*irqid_tmp);
ret = queue_delayed_work(myworkq,&irq_delay_work,10);
if(ret!=1) {
printk("irq4 delayed_work schedule error\n");
printk("ret=%d\n",ret);
}
return IRQ_HANDLED;
}
static int irq_open(struct inode *inode, struct file *file)
{
int ret[4];
down(&irq_lock);
ret[0] = request_irq(IRQ_EINT(0),irq1,IRQF_TRIGGER_FALLING,"irq_1",&irqid[0]);
ret[1] = request_irq(IRQ_EINT(1),irq2,IRQF_TRIGGER_FALLING,"irq_2",&irqid[1]);
ret[2] = request_irq(IRQ_EINT(2),irq3,IRQF_TRIGGER_FALLING,"irq_3",&irqid[2]);
ret[3] = request_irq(IRQ_EINT(3),irq4,IRQF_TRIGGER_FALLING,"irq_4",&irqid[3]);
if(ret[0]||ret[1]||ret[2]||ret[3]) {
if(!ret[0]) free_irq(IRQ_EINT(0),&irqid[0]);
if(!ret[1]) free_irq(IRQ_EINT(1),&irqid[1]);
if(!ret[2]) free_irq(IRQ_EINT(2),&irqid[2]);
if(!ret[3]) free_irq(IRQ_EINT(3),&irqid[3]);
printk("irq request error\n");
}
tasklet_init(&irqtask,irq2_tasklet,0);
myworkq = create_workqueue("irqwork");
INIT_DELAYED_WORK(&irq_delay_work,irq4_work_delay);
INIT_WORK(&irq_work,irq3_work);
memset(press,0,sizeof(press));
printk("opened irq \n");
return 0;
}
static int irq_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT(0),&irqid[0]);
free_irq(IRQ_EINT(1),&irqid[1]);
free_irq(IRQ_EINT(2),&irqid[2]);
free_irq(IRQ_EINT(3),&irqid[3]);
ev_press = 0;
tasklet_kill(&irqtask);
if(!cancel_delayed_work(&irq_delay_work)) flush_workqueue(myworkq);
destroy_workqueue(myworkq);
memset(press,0,sizeof(press));
printk("closed\n");
up(&irq_lock);
return 0;
}
static int irq_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
int err;
wait_event_interruptible(irq_waitq,ev_press); //等待按键按下的事件,如果没有则休眠
ev_press = 0;
err = copy_to_user(buff,(const void *)press,min(sizeof(press),count));
if(err) printk("cpy to user err\n");
printk("read\n");
return 0;
}
static unsigned irq_poll(struct file *file, poll_table *wait)
{
unsigned int mask=0;
poll_wait(file,&irq_waitq,wait); //开始就会进入poll,调入等待队列,但是没有马上进入睡眠,只是加入等待队列
if(ev_press) mask |= POLL_IN | POLLRDNORM;
// printk("poll\n");
return mask;
}
static struct file_operations irqop = {
.owner = THIS_MODULE,
.open = irq_open,
.read = irq_read,
.poll = irq_poll,
.release = irq_close,
};
static int __init irq_init(void)
{
int ret;
ret = alloc_chrdev_region(&devid,0,1,DEV_NAME);
if(ret==-1) {
printk("alloc chardev error\n");
return -1;
}
mydev.irq_cdev = cdev_alloc();
cdev_init(mydev.irq_cdev,&irqop);
mydev.irq_cdev->owner = THIS_MODULE;
mydev.irq_cdev->ops = &irqop;
ret = cdev_add(mydev.irq_cdev,devid,1);
if(ret==-1) {
printk("cdev add error\n");
return -1;
}
mydev.irq_class = class_create(THIS_MODULE,DEV_NAME);
device_create(mydev.irq_class,NULL,devid,NULL,"%s",DEV_NAME);
printk("initlized\n");
return 0;
}
static void __exit irqexit(void)
{
cdev_del(mydev.irq_cdev);
device_destroy(mydev.irq_class,devid);
class_destroy(mydev.irq_class);
unregister_chrdev_region(devid,1);
}
module_init(irq_init);
module_exit(irqexit);
MODULE_LICENSE("GPL");