linux中断流程详解

Posted 飞雪天龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux中断流程详解相关的知识,希望对你有一定的参考价值。

异常体系比较复杂,但是linux已经准备了很多的函数和框架,但是因为中断是和具体的开发板相关,所以中断需要我们自己来处理一些方面,但是这也是很少的一部分,很多公用的处理函数内核已经实现,linux内核搭建了一个非常容易扩充的中断处理体系。

中 断系统结构涉及的方面很多,而且分布在很多的函数中,这里我主要理清一些结构和流程顺序已经在哪些函数中实现,我不知道其他人怎么样?但是我自己一开始怎 是找不到linux内核是怎么把GPIO设置成中断的,我找了很久都找不到,还有我们很多的设置,初始化等等东西好像都没有实现,清除中断寄存器也不知道 是怎么实现的,只是知道使用中断,差不多用request_irq函数就差不多了。下面我就把这些涉及的方方面面的流程整理出来。

一、重要结构

  /include/linux/irq.h 

irq_desc 内核中记录一个irq_desc的数组,数组的每一项对应一个中断或者一组中断使用同一个中断号,一句话irq_desc几乎记录所有中断相关的东西,这个结构是中断的核心。其中包括俩个重要的结构irq_chip irqaction

irq_chip  里面基本上是一些回调函数,其中大多用于操作底层硬件,设置寄存器,其中包括设置GPIO为中断输入就是其中的一个回调函数,分析一些源代码

/include/linux/irq.h

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

        int            (*set_affinity)(unsigned int irq,

                                      const struct cpumask *dest);

        int            (*retrigger)(unsigned int irq);

        int            (*set_type)(unsigned int irq, unsigned int flow_type); 设置中断类型,其中包括设置GPIO口为中断输入

        int            (*set_wake)(unsigned int irq, unsigned int on);

 

        void           (*bus_lock)(unsigned int irq);  上锁函数

        void           (*bus_sync_unlock)(unsigned int irq); 解锁

 

        /* 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;

;

我们可以看到这里实现的是一个框架,需要我们进一步的填充里面的函数。我们在分析另一个结构irqaction

include/linux/interrupt.h

struct irqaction

        irq_handler_t handler;  用户注册的中断处理函数

        unsigned long flags;    中断标识

        const char *name;       用户注册的中断名字,cat/proc/interrupts时可以看到

        void *dev_id;           可以是用户传递的参数或者用来区分共享中断

        struct irqaction *next; irqaction结构链,一个共享中断可以有多个中断处理函数

        int irq;                中断号

        struct proc_dir_entry *dir;

        irq_handler_t thread_fn;

        struct task_struct *thread;

        unsigned long thread_flags;

;

我们用irq_request函数注册中断时,主要做俩个事情,根据中断号生成一个irqaction结构并添加到irq_desc中的 action结构链表,另一发面做一些初始化的工作,其中包括设置中断触发方式,设置一些irq_chip结构中没有初始化的函数为默认,开启中断,设置 GPIO口为中断输入模式(这里后面有详细流程分析)。

二、中断流程

整个中断可以分为几个大的流程

1. 中断初始化流程  注意这个阶段就是我非常迷惑的一个阶段,很多初始化,设置寄存器等等问题都是内核启动的时候就已经初始化了,这个阶段做很多工作,其中最重要的就是初始化 了irq_chip结构。使得其中的众多函数已经设置好了,可以被调用了。注意这里只是实现了irq_chip结构的函数,要响应中断还有很多事情要做。

2. 中断注册流程    这个流程是我们比较熟悉的,因为我们每次用中断的时候就是注册一个中断函数。request_irq首先生成一个irqaction结构,其次根据中断号 找到irq_desc数组项(还记得吧,内核中irq_desc是一个数组,没一项对应一个中断号),然后将irqaction结构添加到 irq_desc中的action链表中。当然还做一些其他的工作,注册完成后,中断函数就可以发生并被处理了。

3.中断的处理流程     这个流程主要是产生中断后调用irq_chip中的函数来屏蔽,清除中断等等,然后调用irqaction结构中用户注册的中断函数处理中断,当然还有很多其他的事情要做,不过主要流程是这样。

中断流程应该还包括中断卸载,不过内容比较简单这里就不过啰唆了,下面我们俩详细分析一下这些具体的流程。

<>中断初始化流程

下面内容转载他人博客,原文地址   http://blog.chinaunix.net/space.php?uid=15193587&do=blog&cuid=2194431

下面我们分析内核中断初始化的过程以及如何调用到一个新平台的irq初始化函数。
这里我们以s3c2410平台为例,他的中断初始化函数定义在:

/* arch/arm/mach-s3c2410/irq.c */
void __init s3c24xx_init_irq(void)

……

 

arch/arm/mach-s3c2410/mach-smdk2410.c内通过MACHINE_START宏将s3c24xx_init_irq赋值给mach_desc结构体的.init_irq成员。

 

MACHINE_START(SMDK2410,"SMDK2410")/* @TODO: request a new identifier and switch
        * to SMDK2410 */

 
/* Maintainer: Jonas Dietsche */
 
.phys_io = S3C2410_PA_UART,
 
.io_pg_offst =(((u32)S3C24XX_VA_UART)>> 18)& 0xfffc,
 
.boot_params = S3C2410_SDRAM_PA+ 0x100,
 
.map_io = smdk2410_map_io,
 
.init_irq = s3c24xx_init_irq,
 
.init_machine = smdk_machine_init,
 
.timer = &s3c24xx_timer,
MACHINE_END


注:MACHINE_START宏的作用是对mach_desc结构体进行初始化。mach_desc里定义了一些关键的体系架构相关的函数。Porting kernel到新平台时,这个结构体是非常关键的。

 

init_irq这个成员在系统初始化的时候会被赋值给init_arch_irq全局变量,如下:

/* arch/arm/kernel/setup.c */
void __init setup_arch(char**cmdline_p)

 ……
 cpu_init
();
 
/*
  * Set up various architecture-specific pointers
  */

 init_arch_irq
= mdesc->init_irq;
 system_timer
= mdesc->timer;
 init_machine
= mdesc->init_machine;
 ……


注: 可以看到这里不仅初始化了init_arch_irq 全局变量,同时初始化了system_timer,init_machine等全局变量。这是kernel支持多平台的一种机制。当然这里 system_timerinit_machine我不多描述,有兴趣的可以大家自己去看。机制和init_arch_irq大同小异。

 

init_arch_irq函数指针定义在体系架构无关的arch/arm/kernel/irq.c
/* arch/arm/kernel/irq.c */
void (*init_arch_irq)(void) __initdata = NULL;

 

并且在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_DELAYED_DISABLE|
   IRQ_NOPROBE
;
#ifdef CONFIG_SMP
 bad_irq_desc
.affinity = CPU_MASK_ALL;
 bad_irq_desc
.cpu = smp_processor_id();
#中断程序详解(附例题)

Exynos4412 中断处理流程详解

嵌入式Linux9.U-Boot启动流程详解

s3c2440裸机-异常中断(四. irq之外部中断)

中断唤醒系统流程

Linux编写一个Linux按键中断Demo

(c)2006-2024 SYSTEM All Rights Reserved IT常识