linux中断机制学习整理

来源:互联网 发布:javascript购物车代码 编辑:程序博客网 时间:2024/05/12 21:47
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();/*对应芯片结构的中断初始化函数,每个架构不同*/
例如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);





原创粉丝点击