【轉】linux中斷流程詳解

參考 :http://blog.csdn.net/yimu13/article/details/6803957

中斷早期初始化
1. irq_desc[]

struct irq_desc {
    struct irq_data     irq_data;
    struct timer_rand_state *timer_rand_state;
    unsigned int __percpu   *kstat_irqs;
    irq_flow_handler_t  handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
    irq_preflow_handler_t   preflow_handler;
#endif
    struct irqaction    *action;    /* IRQ action list */
    unsigned int        status_use_accessors;
    unsigned int        core_internal_state__do_not_mess_with_it;
    unsigned int        depth;      /* nested irq disables */
    unsigned int        wake_depth; /* nested wake enables */
    unsigned int        irq_count;  /* For detecting broken IRQs */
    unsigned long       last_unhandled; /* Aging timer for unhandled count */
    unsigned int        irqs_unhandled;
    raw_spinlock_t      lock;
#ifdef CONFIG_SMP
    const struct cpumask    *affinity_hint;
    struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
    cpumask_var_t       pending_mask;
#endif
#endif
    unsigned long       threads_oneshot;
    atomic_t        threads_active;
    wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry   *dir;
#endif
    const char      *name;
}
extern struct irq_desc irq_desc[NR_IRQS];
#define NR_IRQS         (GODNET_IRQ_START + 96)
#define GODNET_IRQ_START    (32)

GIC控制器,有PPI、SGI、SPI
SPI 共享外設中斷號大於32.

2.
在內核早期初始化的時候,machine_desc會初始化gic_irq。
並且在別的函數(irq_set_handler)裏設置irq_desc[]–>handle_irq。這個handle_irq後面會用得到。

==========================
下面是中斷執行流程:

中斷異常向量表:
1 在 arch/arm/kernel/entry-armv.S 中定義了宏 irq_handler
irq_handler裏面會執行arch_irq_handler_default–》asm_do_IRQ

  6         .macro  arch_irq_handler_default
  7         get_irqnr_preamble r5, lr
  8 1:      get_irqnr_and_base r0, r6, r5, lr
  9         movne   r1, sp
 10         @
 11         @ routine called with r0 = irq number, r1 = struct pt_regs *
 12         @
 13         adrne   lr, BSYM(1b)
 14         bne     asm_do_IRQ

無論是gic 控制器,還是別的中斷控制器,都會執行到asm_do_IRQ
下面分析asm_do_IRQ 不

  1. `asmlinkage void __exception_irq_entry
    asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
    {
    struct pt_regs *old_regs = set_irq_regs(regs);//18個寄存器,保存參數

    irq_enter();

    /*

    • Some hardware gives randomly wrong interrupts. Rather
    • than crashing, do something sensible.
      */
      if (unlikely(irq >= nr_irqs)) {
      if (printk_ratelimit())
      printk(KERN_WARNING “Bad IRQ%u\n”, irq);
      ack_bad_irq(irq);
      } else {
      generic_handle_irq(irq);
      }

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
    }`

int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(irq, desc);
    return 0;
}
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

這裏的desc_handle_rq就是內核早期初始化的時候設置的函數。
如有的平臺是handle_level_irq
這函數會調用 handle_irq_event(desc);

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    struct irqaction *action = desc->action;
    irqreturn_t ret;

    desc->istate &= ~IRQS_PENDING;
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    raw_spin_unlock(&desc->lock);

    ret = handle_irq_event_percpu(desc, action);

    raw_spin_lock(&desc->lock);
    irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    return ret;
}
irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int random = 0, irq = desc->irq_data.irq;

    do {
        irqreturn_t res;

        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);
        trace_irq_handler_exit(irq, action, res);

        if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
                  irq, action->handler))
            local_irq_disable();

        switch (res) {
        case IRQ_WAKE_THREAD:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }

            irq_wake_thread(desc, action);

            /* Fall through to add to randomness */
        case IRQ_HANDLED:
            random |= action->flags;
            break;

        default:
            break;
        }

        retval |= res;
        action = action->next;
    } while (action);

    if (random & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);

    if (!noirqdebug)
        note_interrupt(irq, desc, retval);
    return retval;
}

由此找到了action->handler.
裏面還會根據handler的返回值,如果是IRQ_WAKE_THREAD則喚醒中斷線程。
那麼這個handler是什麼?
request_irq->request_threaded_irq-

    if (!action)
        return -ENOMEM;

    action->handler = handler;
    action->thread_fn = thread_fn;
    action->flags = irqflags;
    action->name = devname;
    action->dev_id = dev_id;

    chip_bus_lock(desc);
    retval = __setup_irq(irq, desc, action);
    chip_bus_sync_unlock(desc);

在__setup_irq裏添加action到desc[]的action鏈表中。
然後遍歷這個表,最終執行的就是中斷申請的時候使用的handler。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章