当前位置:首页 > 嵌入式 > 嵌入式软件

作者:刘洪涛,华清远见嵌入式学院金牌讲师。

四、中断处理模型

要想弄清楚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则没有这个问题。

“本文由华清远见http://www.embedu.org/index.htm提供”



华清远见

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

为了满足日益增长的数据处理需求,铁威马NAS推出了全新的性能巅峰2024年旗舰之作F4-424 Pro,并搭载了最新的操作系统--TOS 6。这款高效办公神器的问世,无疑将为企业和专业人士带来前所未有的便捷与效率。

关键字: 存储 Linux 服务器

Arm CPU正在从根本上推动AI变革,并造福地球。Arm架构是未来AI计算的基石。​

关键字: ARM AI

近日,Arm推出了Arm® Ethos™-U85神经网络处理器(NPU)和Arm Corstone™-320物联网参考设计平台,旨在满足海量的数据处理和大规模计算,加速推进边缘AI的发展进程。

关键字: ARM

为了赶超云计算市场上的竞争对手,谷歌正试图通过定制的Arm服务器芯片降低云计算服务成本。

关键字: 谷歌 ARM 定制芯片

嵌入式开发作为一个融合了计算机软硬件和系统工程的综合性领域,其成功与否往往取决于三个核心要素的有效整合与协调。这三个要素分别是:硬件平台的选择与设计、软件开发及其优化、以及系统级的设计与集成。深入理解并熟练掌握这三个方面...

关键字: 嵌入式开发 ARM

随着汽车软件数量爆发式的增长,整个行业都需要重新思考汽车产品的开发流程。为此,Arm推出了丰富的硬件IP、新的系统IP,以及全新的汽车计算与计算子系统产品路线图,旨在为各种汽车应用实现性能、功能安全、可扩展等方面的支持。

关键字: ARM 汽车电子

双系统将是下述内容的主要介绍对象,通过这篇文章,小编希望大家可以对双系统的相关情况以及信息有所认识和了解,详细内容如下。

关键字: 双系统 Windows Linux

知名移动芯片设计公司ARM最近迈出重要一步,它正式推出汽车芯片设计。ARM推出的芯片设计方案名叫Neoverse,随同芯片一起推出的还有面向汽车制造商、汽车供应商的新系统。

关键字: ARM 汽车芯片 芯片

随着通用人工智能的发展,数据中心的计算需求逐步提高。针对多模态数据、大模型的推理和训练需要更高的算力支持,而随着算力提升与之而来的还需更关注在功耗方面的优化。对于头部云计算和服务厂商而言,针对专门用例提高每瓦性能变得至关...

关键字: ARM 服务器 AI Neoverse CSS

一直以来,riscv架构都是大家的关注焦点之一。因此针对大家的兴趣点所在,小编将为大家带来riscv架构的相关介绍,详细内容请看下文。

关键字: riscv ARM riscv架构
关闭
关闭