Linux内核
源码
:2.6.35
irq_chip
irqaction
early_trap_init函数是将异常向量和异常处理的汇编代码拷贝到 内存 中,其中异常向量是0xffff0000处,关键代码
这里,异常向量地址在arch/arm/kernel/entry-armv.S中,代码摘抄如下
设置个中断号对应的中断状态,关键代码
中断处理流程
一 发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理 程序
二 在vector_irq里面,最终会调用中断处理的总入口函数
asm_do_IRQ,调用代码
generic_handle_irq(irq)函数实现在这
再进一步跟踪下去
里面的desc->handle_irq(irq, desc);即调用irq_desc中的handle_irq(不同的中断会有与其对应的irq_desc结构,所以asm_do_IRQ会根据不同的中断号来调用相应的handle_irq),接下去handle_irq会调用chip成员中的函数来进行一些 硬件 设置,在调用 用户 在action链表中注册的处理函数
这个函数的实现如下
调用irqaction中的handler,set_irq_handler(timer_irq, handle_level_irq);/*用户注册的中断处理函数*/
函数功能实现代码
里面会根据中断号调用到desc中的handle_irq,desc->handle_irq = handle;
[irq].handle_irq,对于电平触发中断,这个入口通常为 handle_level_irq,对于边沿触发中断,这个入口通常为 handle_edge_irq
里面 action_ret = handle_IRQ_event(irq, action);
handle_IRQ_event(irq, action)函数里面实现:
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action )
工具:source insight 3
内核中断机制三大数据结构
irq_desc
struct irq_desc {
unsigned int irq;
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct irqaction *action; /* IRQ action list */
irq_chip
struct irq_chip {
const char *name;
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
irqaction
struct irqaction {
irq_handler_t handler;
unsigned long flags;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
中断处理中,内核会在start_kernel
函数
中调用init_IRQ(arch/arm/kernel/irq.c)、和early_trap_init(start_kernel内通过调用setup_arch)来设置异常处理函数
,
early_trap_init函数功能
early_trap_init函数是将异常向量和异常处理的汇编代码拷贝到 内存 中,其中异常向量是0xffff0000处,关键代码
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
这里,异常向量地址在arch/arm/kernel/entry-armv.S中,代码摘抄如下
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
.globl __vectors_start
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
init_IRQ函数功能
设置个中断号对应的中断状态,关键代码
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;\\设置中断状态
init_arch_irq();/*对应芯片结构的中断初始化函数,每个架构不同*/
例如s3c64xx中的初始化函数
void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
{
printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);
/* initialise the pair of VICs */
vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0);
vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0);
/* add the timer sub-irqs */
s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0);
s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1);
s3c_init_vic_timer_irq(IRQ_TIMER2_VIC, IRQ_TIMER2);
s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3);
s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4);
s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs));
}
中断处理流程
一 发生中断时,CPU执行异常向量vector_irq的代码, 即异常向量表中的中断异常的代码,它是一个跳转指令,跳去执行真正的中断处理 程序
二 在vector_irq里面,最终会调用中断处理的总入口函数
asm_do_IRQ,调用代码
adrne lr, BSYM(1b)
bne asm_do_IRQ
三 asm_do_IRQ根据中断号irq来调用相应的irq_desc
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{ 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);
}
}
generic_handle_irq(irq)函数实现在这
static inline void generic_handle_irq(unsigned int irq)
{
generic_handle_irq_desc(irq, irq_to_desc(irq));
}
再进一步跟踪下去
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq, desc);
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
else
__do_IRQ(irq);
#endif
}
里面的desc->handle_irq(irq, desc);即调用irq_desc中的handle_irq(不同的中断会有与其对应的irq_desc结构,所以asm_do_IRQ会根据不同的中断号来调用相应的handle_irq),接下去handle_irq会调用chip成员中的函数来进行一些 硬件 设置,在调用 用户 在action链表中注册的处理函数
s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0);
这个函数的实现如下
void __init s3c_init_vic_timer_irq(unsigned int parent_irq,
unsigned int timer_irq)
{
struct irq_desc *desc = irq_to_desc(parent_irq);
set_irq_chained_handler(parent_irq, s3c_irq_demux_vic_timer);
set_irq_chip(timer_irq, &s3c_irq_timer);
set_irq_handler(timer_irq, handle_level_irq);
set_irq_flags(timer_irq, IRQF_VALID);
desc->handler_data = (void *)timer_irq;
}
调用irqaction中的handler,set_irq_handler(timer_irq, handle_level_irq);/*用户注册的中断处理函数*/
函数功能实现代码
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc) {
printk(KERN_ERR
"Trying to install type control for IRQ%d\n", irq);
return;
}
if (!handle)
handle = handle_bad_irq;
else if (desc->chip == &no_irq_chip) {
printk(KERN_WARNING "Trying to install %sinterrupt handler "
"for IRQ%d\n", is_chained ? "chained " : "", irq);
/*
* Some ARM implementations install a handler for really dumb
* interrupt hardware without setting an irq_chip. This worked
* with the ARM no_irq_chip but the check in setup_irq would
* prevent us to setup the interrupt at all. Switch it to
* dummy_irq_chip for easy transition.
*/
desc->chip = &dummy_irq_chip;
}
chip_bus_lock(irq, desc);
raw_spin_lock_irqsave(&desc->lock, flags);
/* Uninstall? */
if (handle == handle_bad_irq) {
if (desc->chip != &no_irq_chip)
mask_ack_irq(desc, irq);
desc->status |= IRQ_DISABLED;
desc->depth = 1;
}
desc->handle_irq = handle;
desc->name = name;
if (handle != handle_bad_irq && is_chained) {
desc->status &= ~IRQ_DISABLED;
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
desc->depth = 0;
desc->chip->startup(irq);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
}
里面会根据中断号调用到desc中的handle_irq,desc->handle_irq = handle;
[irq].handle_irq,对于电平触发中断,这个入口通常为 handle_level_irq,对于边沿触发中断,这个入口通常为 handle_edge_irq
handle_level_irq函数实现
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret;
raw_spin_lock(&desc->lock);
mask_ack_irq(desc, irq);
if (unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
/*
* If its disabled or no action available
* keep it masked and get out of here
*/
action = desc->action;
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock;
desc->status |= IRQ_INPROGRESS;
raw_spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
raw_spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS;
if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))
unmask_irq(desc, irq);
out_unlock:
raw_spin_unlock(&desc->lock);
}
里面 action_ret = handle_IRQ_event(irq, action);
handle_IRQ_event(irq, action)函数里面实现:
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action )
do {
trace_irq_handler_entry(irq, action);
ret = action->handler(irq, action->dev_id);/*调用用户注册函数,action是用irqaction类型,等同于irqaction->handler*/
trace_irq_handler_exit(irq, action, ret);
switch (ret) {
case IRQ_WAKE_THREAD:
ret = IRQ_HANDLED;
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
if (likely(!test_bit(IRQTF_DIED,
&action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
wake_up_process(action->thread);
}
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;
default:
break;
}
retval |= ret;
action = action->next; /*依次调用用户注册的函数,进入下一个*/
} while (action);