分析linux中断中常用的两个中断函数:
Disable_irq(int irq)
Enable_irq(int irq)
第一步:
●对于关中断
跟踪代码到arch/arm/kernel/irq.c
void disable_irq(unsigned int irq)
{
struct irqdesc *desc = irq_desc + irq;
disable_irq_nosync(irq);
if (desc->action)
synchronize_irq(irq);
}
Irqdesc结构体中有一个irqchip的结构体,这个结构体中有mask,unmask等函数,这些函数是在arch/arm/mach-sep4020/irq.c中注册进去的,继续跟踪disable_irq_nosync(irq);
void disable_irq_nosync(unsigned int irq)
{
struct irqdesc *desc = irq_desc + irq;
unsigned long flags;
spin_lock_irqsave(&irq_controller_lock, flags); //获得自旋锁,并保存中断标志位
//仅仅是增加相应中断线的irqdesc结构中的disable_depth值,把自己从irq_pending链表中摘除,再等待现在正在执行的hangdler运行完毕。
考irqdesc的handle函数do_level_IRQ(),标记triggered,调用chip->ack()应答一下后,如果disable_depth不为0,说明该中断线已经disable,不做其他处理,但每次该中断线有中断触发,还是会应答一下,只是不做__do_irq()函数做的真正的工作,
desc->disable_depth++;
list_del_init(&desc->pend);
spin_unlock_irqrestore(&irq_controller_lock, flags); //释放自旋锁,并返回之前保存的中断位
}
disable_irq 不但会禁止给定的中断,而且也会等待当前正在执行的中断处理程序完成。另一方面,disable_irq_nosync 是立即返回的。这样,使用后者将会更快,但是可能会让你的驱动程序处于竞态下——这是百度搜索的结果。
但我们可以看到其实disable_irq也是使用的disable_irq_nosync的函数体,这里纳闷!
●对于开中断
void enable_irq(unsigned int irq)
{
struct irqdesc *desc = irq_desc + irq;
unsigned long flags;
spin_lock_irqsave(&irq_controller_lock, flags); //获得自旋锁,并保存中断标志位
if (unlikely(!desc->disable_depth)) {
printk("enable_irq(%u) unbalanced from %p/n", irq,
__builtin_return_address(0));
} else if (!--desc->disable_depth) {
desc->probing = 0;
desc->chip->unmask(irq);//实现了开中断
//如果中断正在等待处理,尝试重新运行它;我们不能直接从这里运行,因为caller可能在一个中断保护的区域
if (desc->pending && list_empty(&desc->pend)) {
desc->pending = 0;
if (!desc->chip->retrigger ||
desc->chip->retrigger(irq))
list_add(&desc->pend, &irq_pending);
}
}
spin_unlock_irqrestore(&irq_controller_lock, flags); //释放自旋锁,并返回之前保存的中断位
}
第二步:
●在arch/arm/mach-sep4020/irq.c中对irq进行初始化,其中有部分重要代码如下:
void __init sep4020_init_irq(void)
{
……
for(i = 0; i < NR_IRQS; i++)
{
set_irq_handler(i, do_level_IRQ); //先初始化所有的中断的处理函数为一个简单的,低级的handler
set_irq_chip(i, &sep4020_chip);//将我们所写的irqchip注册到Irqdesc结构体中的irqchip的chip,就是这一步实现了arch/arm/mach-sep4020/irq.c中的函数到系统函数的注册
set_irq_flags(i, IRQF_VALID | IRQF_PROBE);//如果设置为IRQF_VALID,则表示此中断可用,设置为IRQF_PROBE,则表示此中断可探测,如果设置为IRQF_NOAUTOEN则表示不能自动enable此中断
}
}
通过这几个函数就能实现了中断的整个过程,分析完毕,呵呵。
ps:这些都是基于sep4020的linux2.6.16实现的,可能有了基于sep4020的linux源代码更方便看懂,如果有兴趣,可以留下电子邮箱,我会把相应的patch发到邮箱里。