在中断的调用流程中讲到Linux中对中断的处理最终会调用到在驱动程序中注册的中断处理函数。中断处理函数(interrupt handle / interruptservice routine)是设备驱动程序的一部分,内核通过它来回应特定的中断。中断处理函数同普通的函数并无太大的区别,但由于它是运行在中断上下文(interrupt context)中的,所以他不能休眠和引起阻塞。
中断处理函数的原型在linux/interrupt.h中:
typedef irqreturn_t (*irq_handler_t)(int, void *);
如我们可以定义一个中断处理函数:
static irqreturn_t m_key_irq_handle(int irq, void *dev)
{
… …
return IRQ_HANDLED;
}
其中第一个参数是中断号。第二个参数是我们注册中断时传入的一个指针通常我们传入的是我们的设备结构体。我们通过request_irq函数来注册我们的中断处理函数,他的原型是:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id);
irq是要申请的中断号。
handler即使我们的中断处理函数,dev_id即是要传给中断处理函数的第二个参数。
irqflags是中断处理的属性,可以指定中断的出发方式及处理方式。这里列出几个比较重要的flag:
IRQF_DISABLED-当该位被设置时在执行相应的中断处理函数时内核会关掉所有中断。
IRQF_SAMPLE_RANDOM-会将产生该中断的时间加入系统熵池,以便产生真正的随机数。如果你的中断时可预期或有规律的不要设置该项。
IRQF_SHARED-表明多个设备可以共享这个驱动。
另外还有一些处理方式的标志位:IRQF_TRIGGER_RISING/ IRQF_TRIGGER_FALLING/ IRQF_TRIGGER_HIGH/ IRQF_TRIGGER_LOW等这些都是指定中断的触发方式的。
在函数request_irq中,会对action进行初始化:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
int retval;
…
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
if (irq >= NR_IRQS)
return -EINVAL;
if (irq_desc[irq].status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler)
return -EINVAL;
action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
if (!action)
return -ENOMEM;
action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
select_smp_affinity(irq);
…
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
其中struct irqaction的定义如下:
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(irq, action)将已经初始化好的action同irq联系起来:
int setup_irq(unsigned int irq, struct irqaction *new)
{
struct irq_desc *desc = irq_desc + irq;
struct irqaction *old, **p;
…
p = &desc->action;
…
*p = new;
…
return 0;
}
主要通过上面的语句,将request_irq中初始化好的action赋值给desc->action,通过desc的定义我们可以知道它指向irq_desc全局数组的第irq个元素。这个全局数组在handle.c中定义:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
}
};
具体的过程我们会在下一篇文章中介绍。在处理中断时会通过:
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
…
struct irq_desc *desc = irq_desc + irq;
…
}
将我们此处的调注册的信息同中断的调用信息联系起来了。至此中断的注册流程就已经介绍完成了。