在响应一个特定的中断的时候,内核会执行一个函数,该函数叫做中断处理程序(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