中断代码阅读(1)

    技术2025-01-29  58

     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)个该类中断被“无视”了。

     

    最新回复(0)