linux中断机制学习整理

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);





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值