1. 共享中断的注册和处理
注册中断时,在setup_irq中,
struct irq_desc *desc = irq_desc + irq;...
p = &desc->action; old = *p; if (old) {如果已注册中断或者本次注册中断不是共享中断,则
if (!((old->flags & new->flags) & IRQF_SHARED) || ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) { old_name = old->name; goto mismatch; }
如果是共享中断,则
do { p = &old->next; old = *p; } while (old); shared = 1;
然后将本次注册中断添加到desc->action的链表上。 *p = new;
2. 执行中断时共享中断相关代码
执行handle_IRQ_event时,会依次执行所有注册到当前中断上的action->handler,所以当多个中断共享同一中断号时,每次中断都会依次执行所有注册的ISR。另外在执行action->handler之前,如果不是独占中断(IRQF_DISABLED),会重新使能中断,action->handler执行完毕再关闭中断。
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action){ irqreturn_t ret, retval = IRQ_NONE; unsigned int status = 0;
handle_dynamic_tick(action);
if (!(action->flags & IRQF_DISABLED)) local_irq_enable_in_hardirq();
do { ret = action->handler(irq, action->dev_id); if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action);
if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable();
return retval;}
3. 中断处理期间又来了一次当前正处理的中断
在执行驱动中断处理函数过程中,如果又来了一次与当前正执行中断类型相同的中断:
A. 离开当前中断处理,重新进入中断处理,如果正在处理(IRQ_INPROGRESS),则action为0直接返回,而status被设置了IRQ_PENDING。参考代码:
status |= IRQ_PENDING; /* we _want_ to handle it */ action = NULL; if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; if (unlikely(!action)) goto out;
B. 从新中断返回后,会回到前面正在执行的中断处理,此时,如果发现有IRQ_PENDING,会再次执行一遍中断处理流程,知道没有IRQ_PENDING被设置为止。
可参考在函数unsigned int __do_IRQ(unsigned int irq)中的代码:
/* * Edge triggered interrupts need to remember * pending events. * This applies to any hw interrupts that allow a second * instance of the same irq to arrive while we are in do_IRQ * or in the handler. But the code here only handles the _second_ * instance of the irq, not the third or fourth. So it is mostly * useful for irq hardware that does not mask cleanly in an * SMP environment. */ for (;;) { irqreturn_t action_ret;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action); if (!noirqdebug) note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock); if (likely(!(desc->status & IRQ_PENDING))) break; desc->status &= ~IRQ_PENDING; }
注意:如果在一次中断执行过程中来了多次(n>1)同一种中断,则只会执行一次,有(n-1)个该类中断被“无视”了。