本笔记摘自《深入linux设备驱动程序内核机制》
1、硬件中断框架
其中PIC为中断控制器,现在的ARM SOC处理器已经集成了PIC
irq是发生中断后,从中断寄存器中读出的中断号码,用于识别是发生了哪个中断
INT代表触发CUP的统一外部中断中断信号,进过一些汇编代码处理后,调用统一的外部中断处理函数asm_do_IRQ
2、中断软件处理流程
2.1、重要数据结构irq_desc irqaction
struct irq_desc {
unsigned int irq;
struct irq_data irq_data;
irq_flow_handler_t handle_irq;
struct irqaction *action; /* IRQ action list */
} ____cacheline_internodealigned_in_smp;
struct irqaction {
irq_handler_t handler;
struct irqaction *next;
int irq;
};
1、发生中断时,内核会根据中断号irq查询irq_desc表,
2、然后从中irqaction里面调用驱动程序用irq_request()函数注册到系统的具体的设备中断处理函数action->hendler。
2.2、中断的注册irq_request
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);
}
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; //定义两个结构体成员并填充
struct irq_desc *desc;
int retval;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler; //填充中断处理函数
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action); //将两个结构体成员关联起来,注册到内核系统中
chip_bus_sync_unlock(desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (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;
}
2.3、中断顶半部和底半部
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
handle_IRQ(irq, regs);
}
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq);
}
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
1、顶半部在irq_enter();和irq_exit();之间调用generic_handle_irq(irq);完成,此时关闭了中断,因此处理时间应该尽可能短,否则因为时间过长导致其他中断得不到相应,严重可导致内核崩溃
2、底半部在irq_exit();函数中一直不断调用函数最终调用到__do_softirq函数,此时中断时开启的,因此处理时间可以较长
3、总结:在处理中断时,可以把较短的紧急事情放到中断上半部处理,而不紧急的事情可以放到底半部处理