四、中断处理模型
要想弄清楚desc->handle_irq(irq, desc)和我们注册的中断有什么关联,就要了解中断处理模型了。
4.1 中断处理模型结构
中断处理模型如下图所示,
其中NR_IRQS表示最大的中断号,在include/asm/arch/irq.h中定义。
irq_desc[]是一个指向irq_desc_t结构的数组, irq_desc_t结构是各个设备中断服务例程的描述符。Irq_desc_t结构体中的成员action指向该中断号对应的irqaction结构体链表。Irqaction结构体定义在include/linux/interrupt.h中,如下:
truct irqaction {
irq_handler_t handler; //中断处理函数,注册时提供
unsigned long flags; //中断标志,注册时提供
cpumask_t mask; //中断掩码
const char *name; //中断名称
void *dev_id; //设备id,本文后面部分介绍中断共享时会详细说明这个参数的作用
struct irqaction *next; //如果有中断共享,则继续执行,
int irq; //中断号,注册时提供
struct proc_dir_entry *dir; //指向IRQn相关的/proc/irq/n目录的描述符
};
在注册中断号为irq的中断服务程序时,系统会根据注册参数封装相应的irqaction结构体。并把中断号为irq的irqaction结构体写入irq_desc [irq]->action。这样就把设备的中断请求号与该设备的中断服务例程irqaction联系在一起了。样当CPU接收到中断请求后,就可以根据中断号通过irq_desc []找到该设备的中断服务程序。
4.2 中断共享的处理模型
共享中断的不同设备的iqraction结构体都会添加进该中断号对应的irq_desc结构体的action成员所指向的irqaction链表内。当内核发生中断时,它会依次调用该链表内所有的handler函数。因此,若驱动程序需要使用共享中断机制,其中断处理函数必须有能力识别是否是自己的硬件产生了中断。通常是通过读取该硬件设备提供的中断flag标志位进行判断。也就是说不是任何设备都可以做为中断共享源的,它必须能够通过的它的中断flag判断出是否发生了中断。
中断共享的注册方法是:
int request_irq(unsigned int irq, irq_handler_t handler,
IRQF_SHARED, const char *devname, void *dev_id)
很多权威资料中都提到,中断共享注册时的注册函数中的dev_id参数是必不可少的,并且dev_id的值必须唯一。那么这里提供唯一的dev_id值的究竟是做什么用的?
根据我们前面中断模型的知识,可以看出发生中断时,内核并不判断究竟是共享中断线上的哪个设备产生了中断,它会循环执行所有该中断线上注册的中断处理函数(即irqaction->handler函数)。因此irqaction->handler函数有责任识别出是否是自己的硬件设备产生了中断,然后再执行该中断处理函数。通常是通过读取该硬件设备提供的中断flag标志位进行判断。那既然kernel循环执行该中断线上注册的所有irqaction->handler函数,把识别究竟是哪个硬件设备产生了中断这件事交给中断处理函数本身去做,那request_irq的dev_id参数究竟是做什么用的?
很多资料中都建议将设备结构指针作为dev_id参数。在中断到来时,迅速地根据硬件寄存器中的信息比照传入的dev_id参数判断是否是本设备的中断,若不是,应迅速返回。这样的说法没有问题,也是我们编程时都遵循的方法。但事实上并不能够说明为什么中断共享必须要设置dev_id。
下面解释一下dev_id参数为什么必须的,而且是必须唯一的。
当调用free_irq注销中断处理函数时(通常卸载驱动时其中断处理函数也会被注销掉),因为dev_id是唯一的,所以可以通过它来判断从共享中断线上的多个中断处理程序中删除指定的一个。如果没有这个参数,那么kernel不可能知道给定的中断线上到底要删除哪一个处理程序。
注销函数定义在Kernel/irq/manage.c中定义:
void free_irq(unsigned int irq, void *dev_id)
五、S3C2410子中断的注册的实现
5.1 S3C2410子中断注册问题的提出
参看3.5节中判断中断号的方法,可以看到只是通过S3C2410中断控制器中的INTOFFSET寄存器来判断的。对于INTPND中的EINT4_7、EINT8_23、INT_UART0、INT_ADC 等带有子中断的向量,INTOFFSET无法判断出具体的中断号。平台留给我们的注册方法如下:
在include/asm/arch/irqs.h中有类似如下定义:
/* interrupts generated from the external interrupts sources */
#define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */
#define IRQ_EINT5 S3C2410_IRQ(33)
#define IRQ_EINT6 S3C2410_IRQ(34)
#define IRQ_EINT7 S3C2410_IRQ(35)
#define IRQ_EINT8 S3C2410_IRQ(36)
#define IRQ_EINT9 S3C2410_IRQ(37)
#define IRQ_EINT10 S3C2410_IRQ(38)
#define IRQ_EINT11 S3C2410_IRQ(39)
#define IRQ_EINT12 S3C2410_IRQ(40)
#define IRQ_EINT13 S3C2410_IRQ(41)
#define IRQ_EINT14 S3C2410_IRQ(42)
#define IRQ_EINT15 S3C2410_IRQ(43)
#define IRQ_EINT16 S3C2410_IRQ(44)
#define IRQ_EINT17 S3C2410_IRQ(45)
#define IRQ_EINT18 S3C2410_IRQ(46)
#define IRQ_EINT19 S3C2410_IRQ(47)
#define IRQ_EINT20 S3C2410_IRQ(48) /* 64 */
#define IRQ_EINT21 S3C2410_IRQ(49)
#define IRQ_EINT22 S3C2410_IRQ(50)
#define IRQ_EINT23 S3C2410_IRQ(51)
可以看到平台为每种子中断都定义了中断号,如果你想实现EINT10的中断注册,直接按照IRQ_EINT10这个中断号注册都可以了。那么平台代码是如何实现这部分中断注册的呢?
5.2 S3C2410子中断注册问题的解决
/*arch/arm/plat-s3c24xx/irq.c*/
void __init s3c24xx_init_irq(void)
{……
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
……
}
平台在初始化时会调用到s3c24xx_init_irq,在此函数中实现了对EINT4_7、EINT8_23、INT_UART0、INT_ADC等中断的注册。下面看看这些带有子中断的中断号对应的处理函数的内容。以IRQ_EINT4t7为例,其它情况类似。
/*arch/arm/plat-s3c24xx/irq.c*/
s3c_irq_demux_extint4t7(unsigned int irq,
struct irq_desc *desc)
{
unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);
eintpnd &= ~eintmsk;
eintpnd &= 0xff; /* only lower irqs */
/* eintpnd中可以有多个位同时置1,这一点和intpnd的只能有1个位置1是不一样的 */
while (eintpnd) { //循环执行所有置位的子中断
irq = __ffs(eintpnd); //算出第一个不为0的位,类似arm v5后的clz前导0的作用
eintpnd &= ~(1<<irq);//清除相应的位
irq += (IRQ_EINT4 - 4);//算出对应的中断号
desc_handle_irq(irq, irq_desc + irq);//执行对应子中断的注册函数
}
}
从上面的函数可以看出子中断是如何注册及被调用到的。有人可能会问为何不在include/asm/arch-s3c2410/entry-macro.s 文件中get_irqnr_and_base函数判断中断号时,直接算出对应的子中断号,就可以直接找到子中断处理了呢?
原因是: get_irqnr_and_base是平台给系统提供的函数,对于多个子中断同时置位的情况无法通过一个值返回(因为子中断中,如eintpnd是可以多个位同时置位的))。而intpnd则没有这个问题。 (作者:刘洪涛,华清远见嵌入式学院金牌讲师)
分享到:
相关推荐
### Linux 2.6.26 内核中 ARM 中断实现详解 #### 一、中断注册方法 在深入探讨 Linux 内核中 ARM 架构的中断机制之前,我们首先来了解一下中断注册的核心——`request_irq()` 函数。这个函数在 `Kernel/irq/manage...
### Linux 2.6.26 内核中 ARM 中断实现详解 #### 一、中断注册方法 在 Linux 内核中,处理中断的核心函数是 `request_irq()`,该函数用于请求并注册一个硬件中断。`request_irq()` 的定义位于 `Kernel/irq/manage....
在Linux内核中,特别是针对ARM架构的实现,有一系列的方法和机制来处理中断。 首先,中断注册是整个中断处理流程中的第一步。在Linux内核中,申请中断通常使用request_irq()函数。该函数的定义位于Kernel/irq/...
这一节将以S3C2410为例,描述linux-2.6.26内核中,从中断开始,中断是如何一步一步执行到我们注册函数的。 3.1 中断向量表 arch\arm\kernel\entry-armv.S __vectors_start: swi SYS_ERROR0 b vector_...
在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devnam
作者:刘洪涛,华清远见嵌入式学院金牌讲师。 四、中断处理模型 要想弄清楚desc->handle_irq(irq, desc)和我们注册的中断有...Irq_desc_t结构体中的成员action指向该中断号对应的irqaction结构体链表。Irqaction
12. **虚拟化技术**:在2.6.26版本中,内核支持KVM(Kernel-based Virtual Machine),这是Linux平台上的全虚拟化解决方案。 通过研究`linux-2.6.26`源码,开发者可以学习到操作系统设计的精髓,对计算机系统有更...
linux-2.6.26.tar 安装 Iso 格式
内核ARM中断是嵌入式系统中...提供的文档《linux-2.6.26内核ARM中断实现过程详解》系列可能涵盖了中断处理的具体细节,包括中断初始化、中断注册、中断处理流程以及相关的数据结构和函数,对于学习这一主题非常有帮助。
linux-2.6.26 原始代码修改了大小写重命名问题,并修正了相关引用。
学习linux下的libxml编程是必须的两个包 libxml2-2.6.26-2.1.2.8.i386.rpm libxml2-devel-2.6.26-2.1.2.8.i386.rpm
Linux 2.6.26 下开发 Marvell 88W8686 WiFi 驱动 本文档详细介绍了在 Linux 2.6.26 平台下开发 Marvell 88W8686 WiFi 驱动的过程。该驱动程序基于 PXA270 平台的 SPI 接口驱动程序,并移植到 2440 平台上。整个移植...
Linux 2.6.26 POWER驱动包是一个针对Power架构计算机系统的内核模块集合,它包含了一系列用于优化和增强Power架构处理器性能的驱动程序。在Linux操作系统中,驱动程序是连接硬件设备与操作系统内核的桥梁,使得操作...
There is 3 standalone tools runtest.c runsuite.c testapi.c, which should compile as part of the build or as any application would. Launch them from this directory to get results, runtest checks ...
学习linux下的libxml编程是必须的两个包 libxml2-2.6.26-2.1.2.8.i386.rpm libxml2-devel-2.6.26-2.1.2.8.i386.rpm
标题中的"Python库 | discovery-transition-ds-2.6.26.tar.gz"指的是一个针对Python编程语言的特定库,该库的版本为2.6.26,并且以tar.gz格式的压缩包形式存在。在软件开发中,库是一组预先编写好的函数或模块,...
Smarty是一个知名的PHP模板引擎,它的版本为2.6.26,封装在名为"Smarty-2.6.26.zip"的压缩包中。这款模板引擎的出现旨在将设计人员和开发人员的工作分离,使他们可以更加专注于各自的专业领域,提高工作效率。下面,...
总的来说,libxml2-2.6.26-x86-64.rpm是一个重要的软件包,为64位的CentOS 5系统提供了XML处理能力,确保了许多依赖XML的应用能够正常运行。安装此RPM包时,应确保系统满足所有依赖关系,并遵循正确的RPM安装流程。
在本文中,我们将深入探讨Linux内核2.6.26中的关键知识点。 首先,让我们关注内核调度器。在2.6.26版本中,Linux内核采用了新的完全公平调度器( Completely Fair Scheduler, CFS),这是由Ingo Molnar设计的。CFS...