linux interrupt management (a)

First, the interrupt action

Linux kernel needs to manage all the hardware devices connected to the computer. If you want to manage these devices, first of all, and they communicate with each other for the job.
Generally there are two options available to achieve this functionality:

  • Poll (polling) kernel to regularly query the state of the device, and then make the appropriate treatment;
  • Interrupt (interrupt) so that hardware signals (kernel becomes active initiative for the hardware) to the kernel when needed.

Use a polling way will take up more CPU time and low efficiency. For example: To read when there is a button is not pressed, a process needs to continue to query the keys have not been pressed. So this task was a lot of CPU time, CPU makes a lot of wasted effort. Use interrupt provide such a mechanism. When the button is not pressed, suspends the current process, the control to the other processes. When the button is pressed when the operating system is set to the current process activity, allowing the process to continue.

Two, linux interrupt management

linux kernel interrupt all uniform numbers, using a irq_descstructure array describing interrupted. An array entry (or a set of interrupt, which share the interrupt number) to use an interrupt.
struct irq_descA record structure, the name of the interrupt, the interrupt status, to access the underlying hardware interfaces (enabled interrupt, maskable interrupt, clearing the interrupt), the interrupt handler of the inlet,
through which the user can call the interrupt handler registration.

1、struct irq_desc

struct irq_descIn the include\linux\irq.hdefinition file inside

struct irq_desc {
    irq_flow_handler_t  handle_irq;    /* 当前中断的处理函数入口 */
    struct irq_chip     *chip;         /* 底层硬件访问 */
        ...
    struct irqaction    *action;    /* 用户注册的中断处理函数链表 */
    unsigned int        status;     /* IRQ状态 */

    ...

    const char      *name;         /* 中断函数名 */
} ____cacheline_internodealigned_in_smp;

a. handle_irq

handle_irqIs this or is this group of interrupt handler entrance. When an interrupt occurs, it will call asm_do_IRQthe function. In this function call inside the interrupt number corresponding irq_descarray item handle_irq.
In handle_irquse the inside chipinterface member to enable the shield, clear the interrupt. Eleven will call the user registered in actionthe list inside the handler.

b. struct irq_chip

struct irq_chip in the include\linux\irq.hdefinition file inside

struct irq_chip {
    const char  *name;
    /* 启动中断,如果不设置则缺省为 "enable" */
    unsigned int    (*startup)(unsigned int irq);       
    /* 关闭中断,如果不设置则缺省为 "disable" */
    void        (*shutdown)(unsigned int irq);      
    /* 使能中断,如果不设置则缺省为"unmask" */  
    void        (*enable)(unsigned int irq);
    /* 禁止中断,如果不设置则缺省为"mask" */  
    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);
  ....
};

c. struct irqaction

struct irqactionIn the structure include\linux\interrupt.hdefinition files inside.
Each interrupt handler are registered users with a irqactionstructure to describe an interrupt (e.g. shared interrupts) may have a plurality of handlers.
Their irqactionstructure actioninto a linked list of chain headers

struct irqaction {
    /* 用户注册的中断处理函数 */
    irq_handler_t handler;
    /* 中断的标志,是否是共享中断,中断的触发方式是电平触发,还是边沿触发 */
    unsigned long flags;

    cpumask_t mask;
    /* 用户注册时,给的中断的名字 */
    const char *name;
    /* handler 中断函数的参数,也可以用来区分共享中断 */
    void *dev_id;
    /* 链表的指针 */
    struct irqaction *next;
    /* 中断号 */
    int irq;
    struct proc_dir_entry *dir;
};

2. Summary

For struct irq_descarrays, struct irq_chipstructures, and struct irqactionthe relationship between the three, as shown below:

Third, the interrupt handler initialization

1, the interrupt handler initialization

In the init\Main.cdocument start_kernelfunction which calls init_IRQ()function it is to interrupt initialization architecture

2, init_IRQ function

init_IRQIt used to initialize the interrupt handling architecture, the arch\arm\kernel\irq.cfile inside

void __init init_IRQ(void)
{
    int irq;
        /* 初始化irq_desc[] 每一项的中断状态 */
    for (irq = 0; irq < NR_IRQS; irq++)
        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
    ...
        /* 架构相关的中断初始化函数 */
    init_arch_irq();
}

3、 init_arch_irq

init_arch_irqIs a function pointer, arch\arm\kernel\setup.cthe file setup_arch()function is initialized

void __init setup_arch(char **cmdline_p)
{
    ...
    init_arch_irq = mdesc->init_irq;
    ...
}

mdesc->init_irqPoints to the arch\arm\plat-s3c24xx\irq.cfile s3c24xx_init_irq()functions.
MACHINE_START(S3C2440, "SMDK2440")A macro is used to define the struct machine_descstructure of
the structures in arch\arm\mach-s3c2440\Mach-smdk2440.cthe definition file and initializes inside init_irqpointing s3c24xx_init_irq()function

MACHINE_START(S3C2440, "SMDK2440")
    /* Maintainer: Ben Dooks <[email protected]> */
    .phys_io    = S3C2410_PA_UART,
    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S3C2410_SDRAM_PA + 0x100,
         /* init_irq成员在这里初始化 */
    .init_irq   = s3c24xx_init_irq,
    .map_io     = smdk2440_map_io,
    .init_machine   = smdk2440_machine_init,
    .timer      = &s3c24xx_timer,
MACHINE_END

4, s3c24xx_init_irq function

s3c24xx_init_irq()Function arch\arm\plat-s3c24xx\irq.cdefinition (as part of the code block of code), for all his chip operating all associated data structures (irq_desc [irq] .chip) ,, and the initialization handler entry (irq_desc [irq] .handle_irq).
In IRQ_EINT0and IRQ_EINT3, for example, set_irq_chipthe function that will irq_desc[irqno].chip = &s3c_irq_eint0t4, after it can irq_desc[irqno].chipbe provided a touch-way function pointers structure, enabling the interrupt, the interrupt mask
set_irq_handlerfunction is provided to the interrupt handler entry irq_desc[irqno].handle_irq = handle_edge_irqinterrupt occurs, handle_edge_irqit calls the handler registered user specific
irq_desc[irqno].falgssettings forIRQF_VALID

void __init s3c24xx_init_irq(void)
{
    ...
    /* external interrupts */
    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
        irqdbf("registering irq %d (ext int)\n", irqno);
        set_irq_chip(irqno, &s3c_irq_eint0t4);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }
    for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
        irqdbf("registering irq %d (extended s3c irq)\n", irqno);
        set_irq_chip(irqno, &s3c_irqext_chip);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }
    ...
}

5, set_irq_chip function

int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
    struct irq_desc *desc;
    unsigned long flags;
        /* 判断是否超过最大的中断号 */
    if (irq >= NR_IRQS) {
        printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
        WARN_ON(1);
        return -EINVAL;
    }
         
    if (!chip)
        chip = &no_irq_chip;
        /* 通过中断号找到irq_desc数组对应的数组项 */
    desc = irq_desc + irq;
    spin_lock_irqsave(&desc->lock, flags);
        /* 判断 chip 的成员即&s3c_irq_eint0t4的成员是否为空,如果为空就设置为默认的操作函数 */
    irq_chip_set_defaults(chip);
        /* 设置irq_desc[].chip成员 */
    desc->chip = chip;
    spin_unlock_irqrestore(&desc->lock, flags);
    return 0;

5, set_irq_handler function

set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    __set_irq_handler(irq, handle, 0, NULL);
}


/***************************************************************************************/


__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
          const char *name)
{
    struct irq_desc *desc;
    unsigned long flags;
    /* 通过中断号找到irq_desc数组对应的数组项 */
    desc = irq_desc + irq;
   
   ...
    /*  中间还会做一些判断 */
   ...
    /* 设置中断处理函数,名字*/
    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->unmask(irq);
    }

}

6, set_irq_flags function

void set_irq_flags(unsigned int irq, unsigned int iflags)
{
    struct irq_desc *desc;
    unsigned long flags;

    if (irq >= NR_IRQS) {
        printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
        return;
    }
        /* 找到数组项 */
    desc = irq_desc + irq;
    spin_lock_irqsave(&desc->lock, flags);

       /* 设置中断状态 */
    desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
    if (iflags & IRQF_VALID)
        desc->status &= ~IRQ_NOREQUEST;
    if (iflags & IRQF_PROBE)
        desc->status &= ~IRQ_NOPROBE;
    if (!(iflags & IRQF_NOAUTOEN))
        desc->status &= ~IRQ_NOAUTOEN;
    spin_unlock_irqrestore(&desc->lock, flags);
}

IV Summary

Initialization process interrupt processing architecture of the fact that for irq_desc[]each of the initialization of the array is initialized.
A break or a set of interrupt by irq_desc[]an array of items to manage.
An array of items inside the handle_irq chip actionthree important structure members.

  • handle_irq is the current interrupt handler
  • chip associated underlying hardware handler (interrupt trigger mode, maskable interrupt, the interrupt enable)
  • action list head, user registration processing function which are linked to this list, when an interrupt occurs, it calls registered users come from inside the interrupt service function

Guess you like

Origin www.cnblogs.com/gulan-zmc/p/11823660.html