Linux内核源码:2.6.35
工具: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();<span style="color: rgb(34, 34, 34); font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 22.383333206176758px;">/* 对应</span><span href="tag.php?name=%E8%8A%AF%E7%89 %87" class="t_tag" style="color: rgb(34, 34, 34); font- family: Helvetica, Arial, sans-serif; font-size: 14px; line- height: 22.366666793823242px; word-wrap: break- word; cursor: pointer; border-bottom-width: 1px; border-bottom- style: solid; border-bottom-color: rgb(255, 0, 0); white- space: nowrap;">芯 片</span><span style="color: rgb(34, 34, 34); font- family: Helvetica, Arial, sans-serif; font-size: 14px; line- height: 22.383333206176758px;">结构的中断初始化函数,每个架构不同*/</span>
例如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);
浙公网安备 33010602011771号