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

发布时间:2024-08-20  

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)n', 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链表


            ...

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>