与Linux设备驱动中中断处理相关的首先是申请与释放IRQ的API request_irq()和free_irq(),request_irq()的原型为:
int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs), unsigned long irqflags, const char * devname, void *dev_id);
irq是要申请的硬件中断号;
handler是向系统登记的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递;
irqflags是中断处理的属性,若设置SA_INTERRUPT,标明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置SA_SHIRQ,则多个设备共享中断;
dev_id在中断共享时会用到,一般设置为这个设备的device结构本身或者NULL。
free_irq()的原型为:
void free_irq(unsigned int irq,void *dev_id);
另外,与Linux中断息息相关的一个重要概念是Linux中断分为两个半部:上半部(top half)和下半部(bottom half)。上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部执行的速度就会很快,可以服务更多的中断请求。但是,仅有"登记中断"是远远不够的,因为中断的事件可能很复杂。因此,Linux引入了一个下半部,来完 成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断!下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。
Linux实现下半部的机制主要有tasklet和工作队列。
tasklet基于Linux softirq,其使用相当简单,我们只需要定义tasklet及其处理函数并将二者关联:
void my_tasklet_func(unsigned long); //定义一个处理函数:
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data); //定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联;然后,在需要调度tasklet的时候引用一个简单的API就能使系统在适当的时候进行调度运行:
tasklet_schedule(&my_tasklet);
此外,Linux还提供了另外一些其它的控制tasklet调度与运行的API:
DECLARE_TASKLET_DISABLED(name,function,data); //与DECLARE_TASKLET类似,但等待tasklet被使能
tasklet_enable(struct tasklet_struct *); //使能tasklet
tasklet_disble(struct tasklet_struct *); //禁用tasklet
tasklet_init(struct tasklet_struct *,void (*func)(unsigned long),unsigned long); //类似DECLARE_TASKLET()
tasklet_kill(struct tasklet_struct *); // 清除指定tasklet的可调度位,即不允许调度该tasklet
我们先来看一个tasklet的运行实例,这个实例没有任何实际意义,仅仅为了演示。它的功能是:在globalvar被写入一次后,就调度一个tasklet,函数中输出"tasklet is executing":
代码:
tasklet使用模板
//定义tasklet和底半部函数并关联
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);
//中断底半部处理
void xxx_do_tasklet(unsigned long)
{
……………
}
//中断处理顶半部
irqreturn_t xxx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
{
……
tasklet_schedule(&xxx_tasklet);
……
}
//设备驱动模块加载函数
int __init xxx_init(void)
{
…………
//申请中断
result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, “xxx”, NULL);
…………
}
//设备驱动模块卸载函数
void __exit xxx_exit(void)
{
……………
//释放中断
free_irq(xxx_irq, xxx_interrupt);
……………
}
工作队列使用模板
//定义工作队列和关联函数
struct work_struct xxx_wq;
void xxx_do_work(unsigned long);
//中断处理底半部
void xxx_do_work(unsigned long)
{
………………
}
//中断处理顶半部
irqreturn_t xxx_interrupt( int irq, void * dev_id, struct pt_regs * regs)
{
…………
schedule_work(&xxx_wq);
…………
}
//设备驱动模块加载函数
int xxx_int(void)
{
…………
result = request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT, “xxx”, NULL);
//初始化工作队列
INIT_WORK(&xxx_wq, (void (*) (void *)) xxx_do_work, NULL);
…………….
}
//设备驱动模块卸载函数
void xxx_exit(void)
{
……………
free_irq(xxx_irq, xxx_interrupt);
………….
}
共享中断编程模板
//中断处理顶半部
irqreturn_t xxx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
{
………………
int status = read_int_status();//获知中断源
if(!(is_myint(dev_id, status))//判断时候是本设备中断
{
return IRQ_NONE;
}
………….
return IRQ_HANDLED;
}
//设备驱动模块加载函数
int xxx_init(void)
{
……………
//申请共享中断
result = request_irq( sh_irq, xxx_interrupt, SA_SHIRQ, “xxx”, xxx_dev);
………………
}
//卸载函数
void xxx_exit(void)
{
………………
free_irq(xxx_irq, xxx_interrupt);
……………
}