中断触发流程

自古美人都是妖i 提交于 2020-04-06 19:31:16

    在响应一个特定的中断的时候,内核会执行一个函数,该函数叫做中断处理程序(interrupt handler)或中断服务例程(interrupt service routine ,ISP).产生中断的每个设备都有一个相应的中断处理程序,中断处理程序通常不和特定的设备关联,而是和特定的中断关联的,也就是说,如果一个设备可以 产生多种不同的中断,那么该就可以对应多个中断处理程序,相应的,该设备的驱动程序也就要准备多个这样的函数。在Linux内核中处理中断是分为上半部 (top half),和下半部(bottom half)之分的。上半部只做有严格时限的工作,例如对接收到的中断进行应答或复位硬件,这些工作是在所有的中断被禁止的情况下完成的,能够被允许稍后完 成的工作会推迟到下半部去。

其实中断的整个过程分为2个部分:

1 注册

2 执行或者叫触发

如上图:首先中断触发,cpu响应,去执行IRQ中断总的服务子程序(就是所有的IRQ中断都经过这一步),去读两个寄存器,确定中断号,再根据中断号,在子程序链表中找到对应的中断服务子程序,结束了。为了达到这样的目的,把中断号与中断子程序联系起来。request_irq()做的就只是这个工作。而GPIO与中断号的联系是定死的,或者这两个中断号还不一样。后面讲吧。

先说说request_irq(),就是把中断例程添加到中断子程序链表中去。

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,                                                                                    
            const char *name, void *dev)                                                                                                                    
{                                                                                                                                                            
        return request_threaded_irq(irq, handler, NULL, flags, name, dev);     }include/linux/interrupt.h                                                                         
kernel/kernel/irq/manage.c
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
                         irq_handler_t thread_fn, unsigned long irqflags,
                         const char *devname, void *dev_id)
{
        struct irqaction *action;//每一个action结构体中存放着一个中断子程序的入口地址及相关信息
        struct irq_desc *desc;//每一个desc结构体中存放着一个中断号的相关信息,一个中断号可以有多个子程序,以链表形式存在,以dev_id名字区别
        int retval;//IRQF_DISABLED:快速中断标志,对应老版本中的SA_INTERRUPT标志,表明在处理本中断时屏蔽CPU所有中断(屏蔽其他中断线上的中断,本中断线上本来就是屏蔽的),//而在没设置此标志位的程序中,都是开中断处理的,可以进行中断嵌套。嵌套与中断线优先级有关,有中断控制器控制。

  //  IRQF_SAMPLE_RANDOM:用于随机数据产生;告诉内核,本中断源可以用作随机数生成器的熵池(这个不太理解)

  //  IRQF_SHARED:用于共享中断,设置此标志时,request_irq最后一个参数dev不能为NULL,对应老版本内核的SA_SHIRQ;表示共享中断线。


  //   IRQF_PROBE_SHARED:探测共享中断;


  //  IRQF_TIMER:专用于定时中断;

        if ((irqflags & (IRQF_SHARED|IRQF_DISABLED)) ==
                                        (IRQF_SHARED|IRQF_DISABLED)) {//共享中断不能做快速中断
                pr_warning(
                  "IRQ %d/%s: IRQF_DISABLED is not guaranteed on shared IRQs\n",
                        irq, devname);
        }

#ifdef CONFIG_LOCKDEP    //若定义此宏,则禁止中断嵌套,所有中断都关中断运行。
        /*
         * Lockdep wants atomic interrupt handlers:
         */
        irqflags |= IRQF_DISABLED;
#endif

 if ((irqflags & IRQF_SHARED) && !dev_id)//如果状态是共享,那么就必须要有dev_id,而且还不能与已经存在的重合,否则无法区别
                return -EINVAL;

        desc = irq_to_desc(irq);//根据中断号,找到对应的desc结构体,应该也包括创建新的desc结构体吧?
        if (!desc)              //所有irq_desc在系统启动时,通过init_IRQ初始化。
                return -EINVAL;

        if (desc->status & IRQ_NOREQUEST) //若此中断禁止响应,则返回。
                return -EINVAL;

        if (!handler) {      //没有处理函数也不行
                if (!thread_fn)
                        return -EINVAL;
                handler = irq_default_primary_handler;
        }

        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);//在内核空间分配一个action,用于存放一个中断子程序的信息        //中断的入口是irq_desc->handle_irq,这只是电平触发或者边沿触发的处理,但我们注册的中断处理程序都是irq_desc下的一个action。
        if (!action)
                return -ENOMEM;

        action->handler = handler;    //我们定义的中断处理子程序
        action->thread_fn = thread_fn;
        action->flags = irqflags;  //中断的属性
        action->name = devname;//与该中断相关联的名称,在/proc/interrupt中可看到。
        action->dev_id = dev_id;//中断服务程序的参数,可以为NULL,但在注册共享中断时,此参数不能为NULL。

        chip_bus_lock(irq, desc);
        retval = __setup_irq(irq, desc, action);   //添加进action链表,上下还锁起来了。
        chip_bus_sync_unlock(irq, desc);

        if (retval)
                kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ   //调试用的
        if (irqflags & IRQF_SHARED) {
                /*
                 * It's a shared IRQ -- the driver ought to be prepared for it
                 * to happen immediately, so let's make sure....
                 * We disable the irq to make sure that a 'real' IRQ doesn't
                 * run in parallel with our fake.
                 */
                unsigned long flags;

                disable_irq(irq);
                local_irq_save(flags);

                handler(irq, dev_id);

                local_irq_restore(flags);
                enable_irq(irq);
        }
#endif
        return retval;
}
EXPORT_SYMBOL(request_threaded_irq);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!