Linux驱动之中断处理体系结构简析

S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ,目标是推导出中断的处理过程。

看到asm_do_irq函数,它位于archarmkernelIrq.c中。它先根据irq中断号从irq_desc 数组中取出这个中断对应的desc结构体,irq中断号是根据INTOFFSET寄存器的值来确定的,这个寄存器里的值根据中断的来源不同会置位相应的位,它在调用C函数asm_do_IRQ之前被存放在r0中,在C函数中即是irq。

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;//根据irq中断号求出当前中断的desc结构

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (irq >= NR_IRQS)
        desc = &bad_irq_desc;

    irq_enter();//中断次数的计数

    desc_handle_irq(irq, desc);//处理函数

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

    irq_exit();//退出中断
    set_irq_regs(old_regs);
}

然后再调用中断处理函数为desc_handle_irq,接着看到desc_handle_irq函数,它是一个内联函数,它位于includeasm-armmachirq.h

/*
 * Obsolete inline function for calling irq descriptor handlers.
 */
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);//调用handle_irq处理中断
}

从中可以得出一个很重要的数据结构irq_desc ,它的结构如下

struct irq_desc {
    irq_flow_handler_t    handle_irq;//handle_irq处理函数
    struct irq_chip        *chip;
    struct msi_desc        *msi_desc;
    void            *handler_data;
    void            *chip_data;
    struct irqaction    *action;    /* IRQ action list */action链表
    unsigned int        status;        /* IRQ status */

    unsigned int        depth;        /* nested irq disables */
    unsigned int        wake_depth;    /* nested wake enables */
    unsigned int        irq_count;    /* For detecting broken IRQs */
    unsigned int        irqs_unhandled;
    spinlock_t        lock;
#ifdef CONFIG_SMP
    cpumask_t        affinity;
    unsigned int        cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    cpumask_t        pending_mask;
#endif
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry    *dir;
#endif
    const char        *name;
} ____cacheline_internodealigned_in_smp;

接着搜索handle_irq函数看看它是在哪里被定义的,经过层层分析,最终找到了调用它的顶层函数init_IRQ,下面列出调用过程:其中init_arch_irq函数是由MACHINE_START这个宏定义的,它在start_kernel->setup_arch(initMain.c)时已经初始化。它是跟单板结构相关的,详情参考Linux移植之内核启动过程引导阶段分析

init_IRQ
    init_arch_irq();//init_arch_irq = mdesc->init_irq,这个是在machine_desc结构中定义的
        s3c24xx_init_irq
            set_irq_handler
                __set_irq_handler
                    desc->handle_irq = handle;

接着看到init_arch_irq,即s3c24xx_init_irq(archarmplat-s3c24xxIrq.c)函数:这个函数设置了irq_desc的一些变量,后面就可以调用这些变量。比如说handle_edge_irq即是最终的中断处理函数

660    void __init s3c24xx_init_irq(void)
661    {
    ...
    ...
751        /* external interrupts */
752
753        for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
754            irqdbf("registering irq %d (ext int)
", irqno);
755            set_irq_chip(irqno, &s3c_irq_eint0t4);  //设置irq_desc->chip = &s3c_irq_eint0t4
756            set_irq_handler(irqno, handle_edge_irq);//设置irq_desc->handler = handle_edge_irq,处理入口函数
757            set_irq_flags(irqno, IRQF_VALID);       //设置irq_desc->status为IRQF_VALID表示可以使用前面定义的这几个函数
758        }
    ...
    ...
800    }

接着看到handle_edge_irq,它位于kernelirqChip.c中,它的主要功能是首先是清0中断标志,然后运行中断处理函数handle_IRQ_event

445    void fastcall
446    handle_edge_irq(unsigned int irq, struct irq_desc *desc)
447    {
    ...
    ...
468        /* Start handling the irq */
469        desc->chip->ack(irq);//清0中断标志,响应中断
    ...
    ...
      
488                    if (unlikely((desc->status &
489                    (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
490                    (IRQ_PENDING | IRQ_MASKED))) {
491                        desc->chip->unmask(irq);//开启中断
492                        desc->status &= ~IRQ_MASKED;
493                    }


497        action_ret = handle_IRQ_event(irq, action);//中断处理函数

        ...
        ...
507    }

 继续往下看handle_IRQ_event,它位于kernelirqHandle.c,它会不断的检查desc结构里的action结构链表,然后调用里面的处理函数action->handler,函数的参数为irq、atcion->dev_id,这个函数就是真正的处理函数,而这个函数可以由用户定义。

129    irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
130    {
        ...
        ...

139        do {
140            ret = action->handler(irq, action->dev_id);//运行handler函数,这个函数是自己构建的
141        if (ret == IRQ_HANDLED)
142            status |= action->flags;//设置状态
143        retval |= ret;
144        action = action->next;
145        } while (action);//检查action链表上是否还有函数未执行

        ...
        ...
152}

那么action->handler这个函数在哪里被定义呢,接着搜索action->handler被定义的地方,最终发现在request_irq,这个函数位于kernelirqManage.c,

500    int request_irq(unsigned int irq, irq_handler_t handler,
501            unsigned long irqflags, const char *devname, void *dev_id)
502    {
            ...
            ...
527        action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);//分配一个action结构体
528        if (!action)
529            return -ENOMEM;
530
531        action->handler = handler;//设置处理函数handler
532        action->flags = irqflags; //设置标志irqflags。中断方式
533        cpus_clear(action->mask); 
534        action->name = devname;   //设置设备名称
535        action->next = NULL;      //设置下一个action为空
536        action->dev_id = dev_id;  //设置设备id
            ...
            ...

559        retval = setup_irq(irq, action);//将上面设备的action结构放入action链表
560        if (retval)
561            kfree(action);
562
563        return retval;
564    }

这个函数的主要作用是构建一个ation结构,然后用request_irq传入的四个参数初始化它,ation结构如下:

struct irqaction {
    irq_handler_t handler;
    unsigned long flags;
    cpumask_t mask;
    const char *name;
    void *dev_id;
    struct irqaction *next;
    int irq;
    struct proc_dir_entry *dir;
};

最终会调用setup_irq,将初始化的内容放入action链表,这个函数同样位于kernelirqManage.c。它的功能简述为

1、将新的action放入链表

2、设置中断触发方式

3、启动中断

255    int setup_irq(unsigned int irq, struct irqaction *new)
256    {
            ...
            ...
285        /*
286         * The following block of code has to be executed atomically
287         */
288        spin_lock_irqsave(&desc->lock, flags);
289        p = &desc->action;//获得当前desc结构的action结构
290        old = *p;
291        if (old) {//如果已经存在了action结构
298            if (!((old->flags & new->flags) & IRQF_SHARED) ||
299                ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {//判断新的action结构与现有的action结构是否使用相同的触发方式,是否是可共享的
300                old_name = old->name;
301                goto mismatch;//如果不一样,则跳转到mismatch
302            }

        ...
        ...
        
311            /* add new interrupt at end of irq queue */
312            do {//腾出空间,准备将新的结构放入当前的action链表
313                p = &old->next;
314                old = *p;
315            } while (old);
316            shared = 1;
317        }
318    
319        *p = new;//将新的结构放入当前的action链表

            ...
            ...

    if (!shared) {
326            irq_chip_set_defaults(desc->chip);//设置一些chip结构还没设置的指针

#if defined(CONFIG_IRQ_PER_CPU)
        if (new->flags & IRQF_PERCPU)
            desc->status |= IRQ_PER_CPU;
#endif

        /* Setup the type (level, edge polarity) if configured: */
        if (new->flags & IRQF_TRIGGER_MASK) {
            if (desc->chip && desc->chip->set_type)
                desc->chip->set_type(irq,
337                        new->flags & IRQF_TRIGGER_MASK);//设置中断的触发方式,配置EXTINTX寄存器以及GPXCON寄存器
        ...
        ...

        if (!(desc->status & IRQ_NOAUTOEN)) {
            desc->depth = 0;
            desc->status &= ~IRQ_DISABLED;
            if (desc->chip->startup)
357                    desc->chip->startup(irq);//开始中断
            else
359                    desc->chip->enable(irq);//开始中断
        } else
            /* Undo nested disables: */
            desc->depth = 1;
    }
    ...
    ...
387    }

所有如果要注册一个新的中断,那么可以调用request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)来注册。

同样的,如果要消除已经注册的中断,需要调用free_irq(unsigned int irq,void *dev_id)函数来注销,它的执行过程与request刚好相反

1、根据中断号irq、dev_iq从action链表中找到表项,然后移除它

2、如果它是唯一的表项,,则调用desc->chip->shutdown(irq);或desc->chip->disable(irq);来关闭irq号的中断。

以上就是整个linux的中断体系结构的简单描述,更复杂的下半部机制后面深入了解后再去描述。

原文地址:https://www.cnblogs.com/andyfly/p/9475730.html