通过上一节我们知道,在内核中有一个irq_desc数组,数组里面的每一项对应一个中断,数组的下标就是对应中断的虚拟中断号(virq)。

假设只有一个中断控制器,有32个中断,那么中断和irq_desc数组可以一一对应,每一个数组项对应一个中断。

因为第0项一般不用,所以是从第1项开始,一一对应。

此时虚拟中断号和硬件中断号的对应关系为:virq = hwirq + 1。

如果再加一个中断控制器sub_intc,它也会发出中断,并且sub_intc发出的中断会触发上一级的中断控制器的n号中断。

也就是说,sub_intc的0,1,2,3号中断,都会触发上一级中断控制器的n号中断。

根据上一节的说明,sub_intc的0,1,2,3号中断,在irq_desc数组中也会有对应的单独项和它们一一对应。

假设irq_desc数组项中的第36,37,38,39项,分别对应sub_intc的0,1,2,3号中断。

sub_intc的硬件中断号称为hwirq',那么就可以得到hwirq'和virq的转换公式。

virq = hwirq' + 36

也就是说,不论是intc还是sub_intc,都可以根据硬件中断号获得对应的虚拟中断号,并且这些中断号对应的数组项,并不重合

再增加一个外部中断控制器external intc,让系统更复杂一点。

与sub_intc类似,external intc对应intc的m号中断,我们让external intc的0号中断对应数组项的第48项。

那么,也可以得到一个硬件中断号和虚拟中断之间的转换公式:virq = hwirq'' + 48。

以前, 对于每一个硬件中断(hwirq)都预先确定它的中断号(virq),这些中断号一般都写在一个头文件里, 比如:arch\arm\mach-s3c24xx\include\mach\irqs.h。

这里的每一个宏,就是一个虚拟中断号。

使用时:

  1. 执行 request_irq(virq, my_handler) :内核根据virq可以知道对应的硬件中断号,然后去设置、使能中断等;
  2. 发生硬件中断时,内核读取硬件信息,确定hwirq,反算出virq,然后调用 irq_desc[virq].handle_irq,最终会用到 my_handler;

问:前面说了三个中断控制器,intc,sub_intc,external intc,在这三个中断控制器中,不同的硬件中断号对应的虚拟中断号是不同。

但是,intc,sub_intc,external intc都有各自的0号,1号中断等,内核怎么根据这些硬件中断号,推算出对应的虚拟中断号?

答:需要引入了一个新的概念——域(irq_domain),intc,sub_intc,external intc分别有自己的域(irq_domain)。

不同的域(irq_domain)中,相同的硬件中断号(hwirq)对应的虚拟中断号(virq)是不同的

所以,在描述hwirq时,应该注意“是哪个域的hwirq”。

那么,怎么使用域将硬件中断号,转化为虚拟中断号?稍后再说。

之前,virq和硬件的对应关系是固定的,比如virq 38固定对应串口3的接收中断,virq 56固定对应GPIO外部中断等。

但现在的趋势是,virq跟硬件无关,仅仅是一个标号(中断描述数组的标号)而已。

问:为什么会变成这样呢?

答:如果只有几个中断,那么可以事先确定好中断号,并且只要几个宏就可以让中断和数组项一一对应。

但是如果有成百上千个中断,就需要成百上千个宏,并且这些数组项要各自独立互不影响,工作量就变得多得多。(想想要定义上千个宏,很恐怖的。。。)

为了避免这种复杂的情况,就将硬件中断号和虚拟中断号之间固定的关系取消掉它们依旧是一一对应,但是对应关系不再固定了

当需要使用某个硬件中断(hwirq)时,来查找irq_desc数组,在数组中查找到一个空余项,这个空余项的下标就是这个硬件中断号对应的virq

我们在这个空余项中存放对应的处理函数就可以了。

假设,要使用inc的INT2。

那么,先要在 irq_desc 数组中,找到一个空余项。

问:怎么查找空余项呢?

答:最笨的方法,就是从下标0开始依次查找。这当然也是一种方法,但是效率可能不好,这种方法的时间复杂度应该是O(n)。

内核使用的是另一种方法。在内核中定义了一个位图,用来记录哪些空余项被使用了。

这个位图其实就是一个数组——allocated_irqs

allocated_irqs的bit0对应下标0,bitn对应下标n。当某一位等于1时,表示这一项被占用了。

那么,比如硬件ID为2,那么就从bit2开始,bit2,bit3依次查找,直到找到空余项。

这样做的效率应该是比从左到右一个一个找要快。

假设,要设置2号中断,并且allocated_irqs的bit2为0,那么它的virq就等于2。

问:以后处理2号中断时,我们可以从中断控制器里面获得hwirq为2,但是怎么知道对应的virq呢?

答:这就需要在设置中断时,将中断的virq保存下来了。

事实上,这个virq保存在对应的irq_domain里面。

@linear_revmap: Linear table of hwirq->virq reverse mappings
struct irq_domain {......unsigned int linear_revmap[];
};

irq_domain里面有一个数组linear_revmap,叫做反向映射数组

为什么叫反向映射数组呢?

以前,我们使用中断时,是在驱动程序里面执行 request_irq,通过virq找到对应的hwirq。

现在呢,反过来,使用hwirq找到virq。

把hwirq对应的virq,保存在对应的irq_domainlinear_revmap数组中,也就是 linear_revmap[hwirq] = virq

对于本例,hwirq=2,virq=2,所以就是linear_revmap[2] = 2。

这样,后续发生2号中断时:

  1. 首先根据中断向量进入到指定地址执行中断处理流程,将会调用到C语言的中断处理函数
  2. 然后,在中断处理函数中读取中断控制器,得到硬件中断号
  3. 之后,再根据这个中断控制器,得到对应的irq_domain
  4. 通过irq_domain的linear_revmap数组以及硬件中断号,就可以得到一个virq
  5. 最后,在irq_desc数组中,根据virq,找到对应的那一项,把其中的handle_irq拿出来执行。

假设要使用子中断控制器(subintc)的n号中断, 它发生时会导致父中断控制器(intc)的m号中断:

  1. 设备树表明要使用<subintc n>,subintc表示要使用<intc m>
  2. 解析设备树时,会为<subintc n>找到空闲项 irq_desc[virq'],
    sub irq_domain.linear_revmap[n] = virq';
    会为<intc m>   找到空闲项 irq_desc[virq],
    irq_domain.linear_revmap[m] = virq;
    并且设置它的handle_irq为某个分析函数demux_func
  3. 设置驱动程序 request_irq(virq', my_handler);
  4. 发生硬件中断时,内核读取intc硬件信息, 确定hwirq = m, 确定 virq =  irq_domain.linear_revmap[m];
    然后调用 irq_desc[m].handle_irq, 即demux_func
  5. demux_func:读取sub intc硬件信息, 确定hwirq = n, 确定 virq' =  sub irq_domain.linear_revmap[n];
  6. 然后调用 irq_desc[n].handle_irq, 即my_handler。

在旧的中断配置方法中,irq_domain也有linear_revmap成员,只是它的linear_revmap数组都预先设置好了,只有新的中断配置方法中,linear_revmap才是设置了才不为空(0)。

也就是说,新旧配置方法是兼容的,只是配置方法略有不同

旧的配置方法是通过固定配置,可以直接通过 request_irq 函数配置中断(因为virq是已知的,固定的),而新的配置方法在一开始并不知道virq,需要先在设备树表明要使用哪个中断(hwirq),然后程序会将这个hwirq和某个virq绑定,确定virq后,才可以调用request_irq函数设置中断。

那么,要怎么在设备树中表明要使用哪个中断?这个下节再说明。

在设备树中表明要使用的中断信息后,会通过xlate函数对设备树进行解析,获得对应的hwirqirq_type中断触发方法)。

然后,再把hwirq映射得到virq,之后驱动程序才能来设置和使用中断。

还有一个map函数,用来建立hwirq和virq之间的映射关系的,比如,若配置的是子中断,那么map函数还要去设置父中断。

xlatemap都是irq_domain.ops的成员,他们都是函数指针。

struct irq_domain {......const struct irq_domain_ops *ops;......
};struct irq_domain_ops {int (*match)(struct irq_domain *d, struct device_node *node,enum irq_domain_bus_token bus_token);int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,enum irq_domain_bus_token bus_token);int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);void (*unmap)(struct irq_domain *d, unsigned int virq);int (*xlate)(struct irq_domain *d, struct device_node *node,const u32 *intspec, unsigned int intsize,unsigned long *out_hwirq, unsigned int *out_type);
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY/* extended V2 interfaces to support hierarchy irq_domains */int (*alloc)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs, void *arg);void (*free)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs);int (*activate)(struct irq_domain *d, struct irq_data *irqd, bool reserve);void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,unsigned long *out_hwirq, unsigned int *out_type);
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSvoid (*debug_show)(struct seq_file *m, struct irq_domain *d,struct irq_data *irqd, int ind);
#endif
};

关于 xlatemap 的更详细的说明,会在后面的文章中说明。

5.3中断系统中的设备树——中断号的演变与irq_domain相关推荐

  1. 韦东山 IMX6ULL和正点原子_【预习】韦东山:剥丝抽茧分析Linux中断系统中的重要数据结构...

    导语: 众所周知,目前升级版视频正在录中断系统,已经录到[Linux系统对中断处理的演进],配套文档发布后,颇受学员好评,知乎文章: https://zhuanlan.zhihu.com/p/1130 ...

  2. zynq开发中的设备树

    在zynq开发中经常会修改设备树,每次遇到这种情况都有点发愁,今天把设备树相关的知识点总结一下,希望以后遇到设备树时,能够自如应对. 什么是设备树 设备树时描述硬件的数据结构,Linux系统可以通过设 ...

  3. 07 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(上)

    上一节,我用一个 Nginx+PHP 的案例,给你讲了服务器 CPU 使用率高的分析和应对方法.这里你一定要记得,当碰到无法解释的 CPU 使用率问题时,先要检查一下是不是短时应用在捣鬼. 短时应用的 ...

  4. Linux驱动开发中与设备树相关的6种debug方法

    整理出了6种驱动开发时与设备注册.设备树相关的调试方法,彼此间没有优先级之分,每种方法不一定是最优解,但可以作为一种debug查找问题的手段,快速定位问题原因.例如在芯片验证时,不同时钟频率下系统启动 ...

  5. 08 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(下)

    上一节,我给你讲了 Linux 进程状态的含义,以及不可中断进程和僵尸进程产生的原因,我们先来简单复习下. 使用 ps 或者 top 可以查看进程的状态,这些状态包括运行.空闲.不可中断睡眠.可中断睡 ...

  6. 在Linux系统中存储设备的两种表示方法

    作者:北南南北 来自:LinuxSir.Org 摘要: 硬盘和硬盘分区在Linux都表示为设备,按我们通俗的说法来说,就是怎么来表示或描述硬盘和或硬盘分区,但这种描述应该是科学和具体的:比如IDE硬盘 ...

  7. 在WindowsMobil系统中实现透明树组件

    因一个工程中需要使用WM来开发一款IM软件,后期实现界面的时候很费了一翻功夫,主要是在WM系统下TreeView的功能太有限了,不行添加背景,也不能实现行选择,这样效果就很差,单击事件也没有,后来通过 ...

  8. Mtk android中的设备树,msm8909+android5.1 device tree(dt) 设备树组成和编译

    1.Device tree设备树概述 设备树包含DTC(device treecompiler),DTS(device treesource和DTB(device treeblob).其对应关系如图1 ...

  9. 中断系统详解、外部中断

    51单片机各个引脚功能 IO口引脚: 中断系统的主要功能:处理随机突发事件 中断系统结构: 什么是中断系统: 数据的输入/输出传送方式: 中断传送方式特点: 51系统允许的5个中断源: 51单片机中断 ...

最新文章

  1. 虚拟化--015 配置VMware View Event database失败:
  2. javascript 与vbscript 互相调用
  3. 02 | 系统可用性:没有故障,系统就一定是稳定的吗?
  4. 将字符转换成数字(atoi),将数字转换成字符(itoa)
  5. json模拟数据怎么用_在使用axios获取自己模拟的json数据是踩到的坑
  6. 把一个数组的值赋给另一个数组(VB.NET)
  7. 【js高三】---js模块模式
  8. 计算机老年学校讲义,天津老年大学计算机类教学大纲
  9. 微信客服我是这样管理的
  10. Fiddler对安卓模拟器里的APP抓包(步骤详细,各种抓包工具总结)
  11. iOS开发之定位神器-超简单方式解决iOS后台定时定位
  12. 详解 Flutter engine多线程、Dart isolate和异步
  13. 柔光混合模式android,滤色与柔光两种图层混合模式的理解和应用实例
  14. 我的世界服务器连接协议,go-mc: Minecraft(我的世界)各种协议的Go实现
  15. 你看到的都是错的!——虚拟化技术的真相
  16. thinkphp3.2 微信 Native扫码支付功能
  17. PS:“内容识别填充”去水印
  18. Java 基础——HashMap 遍历方式
  19. 《詹姆斯·高斯林Java白皮书1996自译》00:概览
  20. Java性能调优杀手锏JMH

热门文章

  1. Rosalind第五题:计算GC内容
  2. Python Qt5 入门教程
  3. R read.table Error:appears to contain embedded nulls
  4. mysql list dbs 代替_mysql_list_dbs函数的用法实例汇总
  5. Multiple users(Guest mode) 多用户或访客模式调试
  6. 如何挑选自己喜欢的colormap样式
  7. ffmpeg视频剪切与拼接
  8. 反编译工具ILSpy
  9. Linux内核中的IPSEC实现(3)
  10. 【001】光学系统的像质评价方法