一,难点
1,注册用户中断函数
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *devname,void *dev_id);
用户(即驱动程序)通过request_irq函数向内核注册中断处理函数,request_irq函数根据中断号找到irq_desc数组项,然后在它的action链表中添加一个表项。
第一个参数irq为中断好,第二个参数handler为用户编写的中断处理函数(见第2点),第三个参数flags为中断触发模式,第四个参数devname为中断名称(方便用户查看而已),第五个参数dev_id为一个空类型的指针,传递给中断处理函数供用户自定义使用等
2,卸载中断处理函数
void free_irq(unsigned int irq, void *dev_id)
两个参数对应request_irq函数中的中断号irq和dev_id
3,用户编写的中断处理函数
static irqreturn_t irqButton_handle(int irq, void *dev_id)
{
char **name = (char **)dev_id;
printk("%d press %s\n",cnt++,*name);
eventPress = 1;
wake_up_interruptible(&buttonWaitQueue);
return IRQ_RETVAL(IRQ_HANDLED);
}
2个参数irq,dev_id由request_irq函数传递过来的,用户在这个函数里做中断发生时候需要做的事情,注意这个函数的返回值为return IRQ_RETVAL(IRQ_HANDLED);
4,有关使进程睡眠的函数
(1)static DECLARE_WAIT_QUEUE_HEAD(buttonWaitQueue);
创建一个睡眠队列,队列头为buttonWaitQueue,这个队头在睡眠和唤醒函数里用到
(2)wait_event_interruptible(buttonWaitQueue,eventPress);
当eventPress为0时候进程会进入睡眠
(3)wake_up_interruptible(&button_waitq);
唤醒睡眠的进程
二,使用中断的方法
(1)编写中断处理函数
(2)在open函数里注册中断函数,此时中断就打开了
(3)在close函数里卸载中断函数
三,Linux中断处理流程
(1)中断时CPU执行异常向量vector_irq的代码
(2)在vector_irq里,最终会调用外部中断处理的总入口函数asm_do_IRQ
(3)在asm_do_IRQ根据中断号调用irq_desc 结构体数组项中的handle_irq
(4)handle_irq会调用irq_desc 结构体数组项中的chip成员中的函数来设置硬件,比如清楚中断,禁止中断等
(5)handle_irq会逐个调用在irq_desc结构体数组项中的action链表中注册的中断处理函数
可见,操作系统的中断体系结构的初始化是构造这些数据结构。用户(我们)注册中断时就是构造action链表,卸载中断就是从action链表里去除不需要的项
四,代码
(1)驱动程序
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static struct class *irqButton_class;
static struct class_device *irqButton_class_device;
int major;
int cnt = 0;
int eventPress = 0;
//声明一个等待队列,队头为buttonWaitQueue
static DECLARE_WAIT_QUEUE_HEAD(buttonWaitQueue);
struct IrqButtonInfo
{
int irqno;
char *name;
};
struct IrqButtonInfo keys[6]=
{
{IRQ_EINT8,"K1"},
{IRQ_EINT11,"K2"},
{IRQ_EINT13,"K3"},
{IRQ_EINT14,"K4"},
{IRQ_EINT15,"K5"},
{IRQ_EINT19,"K6"}
};
static irqreturn_t irqButton_handle(int irq, void *dev_id)
{
char **name = (char **)dev_id;
printk("%d press %s\n",cnt++,*name);
eventPress = 1;
wake_up_interruptible(&buttonWaitQueue);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int irqButton_open(struct inode *inode, struct file *file)
{
//初始化中断
int i;
for(i=0;i<6;i++)
{
request_irq(keys[i].irqno,irqButton_handle,IRQT_FALLING,keys[i].name,&keys[i].name);
}
return 0;
}
static int irqButton_read (struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
wait_event_interruptible(buttonWaitQueue,eventPress);
eventPress = 0;
return 0;
}
static int irqButton_close(struct inode *inode, struct file *file)
{
int i;
for(i=0;i<6;i++)
{
free_irq(keys[i].irqno,&keys[i].name);
}
return 0;
}
static struct file_operations irqButton_ops =
{
.owner = THIS_MODULE,
.open = irqButton_open,
.read = irqButton_read,
.release = irqButton_close
};
static int irqButton_init(void)
{
major = register_chrdev(0,"irqButton_driver",&irqButton_ops);
irqButton_class = class_create(THIS_MODULE,"irqButton_class");
irqButton_class_device = class_device_create(irqButton_class,NULL,MKDEV(major,0),NULL,"irqButton");
return 0;
}
static void irqButton_exit(void)
{
unregister_chrdev(major,"irqButton_driver");
class_device_unregister(irqButton_class_device);
class_destroy(irqButton_class);
}
module_init(irqButton_init);
module_exit(irqButton_exit);
MODULE_LICENSE("GPL");
(2)测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fid;
int i;
char filename[40] = "/dev/irqButton";
fid = open(filename,O_RDWR);
if(fid < 0)
{
printf("%s Open error!\n",argv[1]);
return -1;
}
while(1)
{
read(fid,&i,sizeof(i));
}
close(fid);
return 0;
}