在响应一个特定的中断的时候,内核会执行一个函数,该函数叫做中断处理程序(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);
来源:https://www.cnblogs.com/autum/archive/2012/07/23/IRQ.html