嵌入式linux的中断函数实现和分析

2019-07-12 22:04发布

分析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的结构体,这个结构体中有maskunmask等函数,这些函数是在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运行完毕。
irqdeschandle函数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结构体中的irqchipchip,就是这一步实现了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发到邮箱里。