• 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的中断体系结构的简单描述,更复杂的下半部机制后面深入了解后再去描述。

  • 相关阅读:
    前端模板Nunjucks简介
    git提交时支持文件名大小写的修改
    多行文本加省略号的处理方法
    前端性能优化实践方案总结
    使用gulp工具生成svgsprites
    koa简介
    JSX语法简介
    踩坑所引发出的appendChild方法的介绍
    React业务实践
    javascript--数组
  • 原文地址:https://www.cnblogs.com/andyfly/p/9475730.html
Copyright © 2020-2023  润新知