zoukankan      html  css  js  c++  java
  • Linux中断体系结构

    1.中断处理体系结构

    Linux内核将所有中断统一编号,使用一个irq_desc结构数组来描述这些中断。

    数组声明在/linux/kernel/irq/handle.c中,其中#define NR_IRQS 128,定义在/linux/include/asm/irq.h

     1 /*
     2  * Linux has a controller-independent interrupt architecture.
     3  * Every controller has a 'controller-template', that is used
     4  * by the main code to do the right thing. Each driver-visible
     5  * interrupt source is transparently wired to the appropriate
     6  * controller. Thus drivers need not be aware of the
     7  * interrupt-controller.
     8  *
     9  * The code is designed to be easily extended with new/different
    10  * interrupt controllers, without having to do assembly magic or
    11  * having to touch the generic code.
    12  *
    13  * Controller mappings for all interrupt sources:
    14  */
    15 struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
    16     [0 ... NR_IRQS-1] = {
    17         .status = IRQ_DISABLED,
    18         .chip = &no_irq_chip,
    19         .handle_irq = handle_bad_irq,
    20         .depth = 1,
    21         .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
    22 #ifdef CONFIG_SMP
    23         .affinity = CPU_MASK_ALL
    24 #endif
    25     }
    26 };
    irq_desc结构的数据类型在/linuxinclude/linux/irq.h中定义,
     1 struct irq_desc {
     2     irq_flow_handler_t    handle_irq;
     3     struct irq_chip        *chip;
     4     struct msi_desc        *msi_desc;
     5     void            *handler_data;
     6     void            *chip_data;
     7     struct irqaction    *action;    /* IRQ action list */
     8     unsigned int        status;        /* IRQ status */
     9 
    10     unsigned int        depth;        /* nested irq disables */
    11     unsigned int        wake_depth;    /* nested wake enables */
    12     unsigned int        irq_count;    /* For detecting broken IRQs */
    13     unsigned int        irqs_unhandled;
    14     spinlock_t        lock;
    15 #ifdef CONFIG_SMP
    16     cpumask_t        affinity;
    17     unsigned int        cpu;
    18 #endif
    19 #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    20     cpumask_t        pending_mask;
    21 #endif
    22 #ifdef CONFIG_PROC_FS
    23     struct proc_dir_entry    *dir;
    24 #endif
    25     const char        *name;
    26 } ____cacheline_internodealigned_in_smp;

    handle_irq是这个或这组中断的处理函数入口。发生中断时,总入口函数asm_do_IRQ将根据中断号调用相应irq_desc数组项中handle_irq.

    typedef    void fastcall (*irq_flow_handler_t)(unsigned int irq,                struct irq_desc *desc);

    handle_irq使用chip结构中的函数清除、屏蔽或者重新使能中断,还要调用用户在action链表中注册的中断处理函数。irq_chip结构类型也是在include/linux/irq.h中定义,其中的成员大多用于操作底层硬件,比如设置寄存器以屏蔽中断,使能中断,清除中断等。注意这里的成员name会出现在/proc/interrupts中。

    struct irq_chip {
        const char    *name;
        unsigned int    (*startup)(unsigned int irq);
        void        (*shutdown)(unsigned int irq);
        void        (*enable)(unsigned int irq);
        void        (*disable)(unsigned int irq);
    
        void        (*ack)(unsigned int irq);
        void        (*mask)(unsigned int irq);
        void        (*mask_ack)(unsigned int irq);
        void        (*unmask)(unsigned int irq);
        void        (*eoi)(unsigned int irq);
    
        void        (*end)(unsigned int irq);
        void        (*set_affinity)(unsigned int irq, cpumask_t dest);
        int        (*retrigger)(unsigned int irq);
        int        (*set_type)(unsigned int irq, unsigned int flow_type);
        int        (*set_wake)(unsigned int irq, unsigned int on);
    
        /* Currently used only by UML, might disappear one day.*/
    #ifdef CONFIG_IRQ_RELEASE_METHOD
        void        (*release)(unsigned int irq, void *dev_id);
    #endif
        /*
         * For compatibility, ->typename is copied into ->name.
         * Will disappear.
         */
        const char    *typename;
    };

    irq_desc结构中的irqaction结构类型在include/linux/iterrupt.h中定义。用户注册的每个中断处理函数用一个irqaction结构来表示,一个中断比如共享中断可以有多个处理函数,它们的irqaction结构链接成一个链表,以action为表头。irqation结构在linux/include/linux/interrupt.h中定义如下:

    typedef irqreturn_t (*irq_handler_t)(int, void *);
    
    struct irqaction {
        irq_handler_t handler;
        unsigned long flags;
        cpumask_t mask;
        const char *name;
        void *dev_id;
        struct irqaction *next;
        int irq;
        struct proc_dir_entry *dir;
    };

     irq_desc结构数组、它的成员“struct irq_chip *chip” "struct irqaction *action",这3种数据结构构成了中断处理体系的框架。下图中描述了Linxu中断处理体系结构的关系图:

    中断处理流程如下
    (1)发生中断时,CPU执行异常向量vector_irq的代码
    (2)在vector_irq里面,最终会调用中断处理的总入口函数asm_do_IRQ
    (3)asm_do_IRQ根据中断号调用irq_desc数组项中的handle_irq。
    (4)handle_irq会使用chip成员中的函数来设置硬件,比如清除中断、禁止中断、重新使能中断等
    (5)handle_irq逐个调用用户在aciton链表中注册的处理函数
       中断体系结构的初始化就是构造这些数据结构,比如irq_desc数组项中的handle_irq、chip等成员;用户注册中断时就是构造action链表;用户卸载中断时就是从action链表中去除不需要的项。
     
    2.中断处理体系结构的初始化
    init_IRQ函数被用来初始化中断处理体系结构,代码在arch/arm/kernel/irq.c中
    void __init init_IRQ(void)
    {
        int irq;
    
        for (irq = 0; irq < NR_IRQS; irq++)
            irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
    
    #ifdef CONFIG_SMP
        bad_irq_desc.affinity = CPU_MASK_ALL;
        bad_irq_desc.cpu = smp_processor_id();
    #endif
        init_arch_irq();
    }

    下面简单分析下init_arch_irq();的获取过程及调用顺序

    1 /*
    2 init_arch_irq()的由来
    3 定义一个空函数指针void (*init_arch_irq)(void) __initdata = NULL;
    4 */
    5 asmlinkage void __init start_kernel(void)
    6     -->setup_arch(&command_line);
    7         -->init_arch_irq = mdesc->init_irq;
    8     -->init_IRQ();    
    9         -->init_arch_irq()//即mdesc->init_irq=s3c24xx_init_irq

    上述machine_desc结构在/linux/arch/arm/mach-s3c2410/mach-bast.c如下宏中获得,后面会分析machine_desc结构。

    MACHINE_START(BAST, "Simtec-BAST")
        //Maintainer: Ben Dooks <ben@simtec.co.uk> 
        .phys_io    = S3C2410_PA_UART,
        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
        .boot_params    = S3C2410_SDRAM_PA + 0x100,
        .map_io        = bast_map_io,
        .init_irq    = s3c24xx_init_irq,
        .init_machine    = bast_init,
        .timer        = &s3c24xx_timer,
    MACHINE_END

    对于S3C2440开发板,这个函数就是s3c24xx_init_irq,移植machine_desc结构中的init_irq成员就指向这个函数s3c24xx_init_irq函数在arch/arm/plat-s3c24xx/irq.c中定义,它为所有中断设置了芯片相关的数据结构(irq_desc[irq].chip),设置了处理函数入口(irq_desc[irq].handle_irq)。

     以外部中断EINT4-EINT23为例,用来设置它们的代码如下:
    void __init s3c24xx_init_irq(void)
    {
        ...
        
        for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
            irqdbf("registering irq %d (extended s3c irq)
    ", irqno);
            set_irq_chip(irqno, &s3c_irqext_chip);
            set_irq_handler(irqno, handle_edge_irq);
            set_irq_flags(irqno, IRQF_VALID);
        }
        ...    
        
    }

    set_irq_chip函数的作用就是“irq_desc[irno].chip = &s3c_irqext_chip”,以后就可能通过irq_desc[irqno].chip结构中的函数指针设置这些外部中断的触发方式(电平触发,边沿触发),使能中断,禁止中断。

    set_irq_chip函数的作用就是“irq_desc[irno].handler_irq = handle_edge_irq”,设置这些中断的处理函数入口为handle_edge_irq。发生中断时handle_edge_irq函数会调用用户注册的具体处理函数;

    set_irq_flags设置中断标志为“IRQF_VALID”,表示可以使用它们。

    init_IRQ函数执行完后,irq_desc数组项的chip,handl_irq成员都被设置。

    3.用户注册中断过程分析

    调用/linux/kernel/irq/manage.c中的request_irq函数。

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
        -->struct irqaction *action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
        -->action->handler = handler;
        -->action->flags = irqflags;
        -->cpus_clear(action->mask);
        -->action->name = devname;
        -->action->next = NULL;
        -->action->dev_id = dev_id;
        -->retval = setup_irq(irq, action);
            -->request_irq函数根据中断号找到irq_desc数组项,然后在它的action链表添加一个表
            -->irq_chip_set_defaults(desc->chip);//chip成员在init_IRQ()中已经设置,这里设置其中还没有设置的。
            -->设置中断触发方式
            -->启动中断

    3.1将新建的irqaction结构链入irq_desc[irq]结构的action链表中

    这有两种可能。如果action链表为空,则直接链入;否则先判断新建的irqaction结构和链表中的irqaction结构所表示的中断类型是否一致,即是否都声明为"可共享的"(IRQF_SHARED)、是否都使用相同的触发方式,如果一致,则将新建的irqation结构链入。
    3.2设置irq_desc[irq]结构中chip成员的还没设置的指针

    让它们指向一些默认函数。chip成员在init_IRQ函数初始化中断体系结构的时候已经设置了,这里只是设置其中还没设置的指针这通过irq_chip_set_defaults函数来完成,它在kernel/irq/chip.c中定义

    3.3设置中断的触发方式

    如果requestt_irq传入的irqflags参数表示中断的触发方式为高低电平触发/边沿触发,则调用irq_desc[irq]结构中的chip-.set_type成员函数来进行设置:设置引脚功能为外部中断,设置中断触发方式。

    3.4启动中断

    如果irq_desc[irq]结构中status成员没有被指明IRQ_NOAUTOEN(表示注册中断时不要使用中断),还要调用chip->startup或chip->enable来启动中断,所谓启动中断通常就是使用中断。一般情况下,只有那些“可以自动使能的”中断对应的irq_desc[irq].status才会被指明为IRQ_NOAUTOEN,所以,无论哪种情况,执行request_irq注册中断之后,这个中断就已经被使能了。

    //-----------------------------------------------

    /*
    NR_IRQS定义in linux/arch/arm/plat-s3c64xx/include/mach/irqs.h
    */
    #define NR_IRQS (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1)


    asmlinkage void __init start_kernel(void)
    -->early_irq_init();
    -->init_IRQ();
    -->init_arch_irq();

    /*
    arch/arm/kernel/irq.c中声明init_arch_irq函数指针
    */
    void (*init_arch_irq)(void) __initdata = NULL; /*全局函数指针*/
    void __init setup_arch(char **cmdline_p)
    -->init_arch_irq = mdesc->init_irq;//s3c6410_init_irq

    /*
    linux/arch/arm/mach-s3c6410/mach-smdk6410.c中定义machine_desc结构体
    */
    MACHINE_START(SMDK6410, "SMDK6410")
    /* Maintainer: Ben Dooks <ben@fluff.org> */
    .phys_io = S3C_PA_UART & 0xfff00000,
    .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
    .boot_params = S3C64XX_PA_SDRAM + 0x100,

    .init_irq = s3c6410_init_irq,
    .map_io = smdk6410_map_io,
    .init_machine = smdk6410_machine_init,
    .timer = &s3c24xx_timer,
    MACHINE_END

    /*
    s3c6410_init_irq in linux/arch/arm/mach-s3c6410/cpu.c
    */
    void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
    {
    int uart, irq;

    printk(KERN_DEBUG "%s: initialising interrupts ", __func__);

    /* initialise the pair of VICs */
    vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid);
    vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid);

    /* add the timer sub-irqs */

    set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0);
    set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1);
    set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2);
    set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3);
    set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4);

    for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
    set_irq_chip(irq, &s3c_irq_timer);
    set_irq_handler(irq, handle_level_irq);
    set_irq_flags(irq, IRQF_VALID);
    }

    for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
    s3c64xx_uart_irq(&uart_irqs[uart]);
    }

    #define IRQ_EINT_GROUP(group, no) (IRQ_EINT_GROUP##group##_BASE + (no))
    /*
    IRQ_EINT_GROUP(1, 3)展开为
    IRQ_EINT_GROUP1_BASE + 3
    */

  • 相关阅读:
    HDU 2844 Coins(多重背包)
    HDU 4540 威威猫系列故事——打地鼠(DP)
    Codeforces Round #236 (Div. 2)
    FZU 2140 Forever 0.5
    HDU 1171 Big Event in HDU(DP)
    HDU 1160 FatMouse's Speed(DP)
    ZOJ 3490 String Successor
    ZOJ 3609 Modular Inverse
    ZOJ 3603 Draw Something Cheat
    ZOJ 3705 Applications
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/7631539.html
Copyright © 2011-2022 走看看