linux irq 自动探测
前言
编写驱动的时候,经常会用到中断,这时候我们在驱动初始化时就得申请中断,那么问题来了,中断号是多少呢?以前的中断号在板级相关的头文件里面已经静态定义好了,bsp的代码在内核启动过程也会根据那个帮我们建立好hw irq到irq的映射,我们直接用它静态定义的irq就可以了。但是在硬件越来越复杂的今天,经常会碰到一个系统里同时有几个中断控制器同时存在的情况(级联),内核因此实现了另一套处理机制(irq domain),这个时候,我们通过翻看datasheet,也仅仅能知道hw irq,最终具体映射到哪个irq上了呢,还真不是那么容易知道的,运气好的呢,在sdk开发文档里面已经标明清楚了,否则我们就只能read the fucking source code了。当然,我们还有另一种尝试方法,就是内核提供的irq探测机制。
irq探测机制使用
以下是LDD3中的并口示例代码:
int count = 0;
do
{
unsigned long mask;
mask = probe_irq_on();
outb_p(0x10,short_base+2); /* enable reporting */
outb_p(0x00,short_base); /* clear the bit */
outb_p(0xFF,short_base); /* set the bit: interrupt! */
outb_p(0x00,short_base+2); /* disable reporting */
udelay(5); /* give it some time */
short_irq = probe_irq_off(mask);
if (short_irq == 0) { /* none of them? */
printk(KERN_INFO "short: no irq reported by probe\n");
short_irq = -1;
}
} while (short_irq < 0 && count++ < 5);
if (short_irq < 0)
printk("short: probe failed %i times, giving up\n", count);
调用probe_irq_on
函数之后,驱动程序要安排设备产生至少一次中断,上面的例子就是通过控制寄存器来使设备触发一次中断,在设备产生中断之后,驱动程序要调用probe_irq_off
,并将前面probe_irq_on
返回的位掩码作为参数传递给它(其实该掩码暂时无意义,后面分析内部实现的时候会发现这点)。probe_irq_off
返回probe_irq_on
之后发生的中断编号。如果没有中断发生,就返回0。如果产生了多次中断,出现了二义性,就返回负数(该负数是最小中断号的负数)。
使用内核提供的接口探测中断号时,需要注意在调用probe_irq_on
之后启用设备中断,在调用probe_irq_off
之前禁用中断。另外,在probe_irq_off
之后,需要处理设备上待处理的中断,同时,irq探测机制是不适合中断共享方式的。
irq 探测原理
需要注意的是,只有在配置了CONFIG_GENERIC_IRQ_PROBE
的时候,irq探测机制才会生效。下面看probe_irq_on
内部实现:
/*
* Autodetection depends on the fact that any interrupt that
* comes in on to an unassigned handler will get stuck with
* "IRQS_WAITING" cleared and the interrupt disabled.
*/
unsigned long probe_irq_on(void)
{
struct irq_desc *desc;
unsigned long mask = 0;
int i;
/*
* quiesce the kernel, or at least the asynchronous portion
*/
async_synchronize_full();
mutex_lock(&probing_active);
/*
* something may have generated an irq long ago and we want to
* flush such a longstanding irq before considering it as spurious.
*/
for_each_irq_desc_reverse(i, desc) {
raw_spin_lock_irq(&desc->lock);
if (!desc->action && irq_settings_can_probe(desc)) {
/*
* Some chips need to know about probing in
* progress:
*/
if (desc->irq_data.chip->irq_set_type)
desc->irq_data.chip->irq_set_type(&desc->irq_data,
IRQ_TYPE_PROBE);
irq_startup(desc, false);
}
raw_spin_unlock_irq(&desc->lock);
}
/* Wait for longstanding interrupts to trigger. */
msleep(20);
/*
* enable any unassigned irqs
* (we must startup again here because if a longstanding irq
* happened in the previous stage, it may have masked itself)
*/
for_each_irq_desc_reverse(i, desc) {
raw_spin_lock_irq(&desc->lock);
if (!desc->action && irq_settings_can_probe(desc)) {
desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;
if (irq_startup(desc, false))
desc->istate |= IRQS_PENDING;
}
raw_spin_unlock_irq(&desc->lock);
}
/*
* Wait for spurious interrupts to trigger
*/
msleep(100);
/*
* Now filter out any obviously spurious interrupts
*/
for_each_irq_desc(i, desc) {
raw_spin_lock_irq(&desc->lock);
if (desc->istate & IRQS_AUTODETECT) {
/* It triggered already - consider it spurious. */
if (!(desc->istate & IRQS_WAITING)) {
desc->istate &= ~IRQS_AUTODETECT;
irq_shutdown(desc);
} else
if (i < 32)
mask |= 1 << i;
}
raw_spin_unlock_irq(&desc->lock);
}
return mask;
}
三次循环遍历irq_desc
,第一次反向遍历,如果对应的irq还没有注册,即desc->action为NULL,且该irq允许探测irq_settings_can_probe(desc)
,那么使能该中断;第二次还是反向遍历,如果对应的irq还没有注册且允许探测,那么就在该irq对应的desc上的istate里添加IRQS_AUTODETECT | IRQS_WAITING
标志;第三次,正向遍历,如果发现irq对应的desc上的istate的IRQS_AUTODETECT
存在,但是它的IRQS_WAITING
被清除了,那么说明运行到这里,发生了莫名其妙的中断,这个中断当然不是我们将要探测的中断,毕竟我们都还没触发它产生中断,于是就清除掉IRQS_AUTODETECT
并禁用它的中断。从上面的分析可以看出,probe_irq_on
就是在适合的中断上添加了IRQS_AUTODETECT | IRQS_WAITING
标志,并暂时开启了它们的中断能力。
分析完probe_irq_on
,现在继续分析probe_irq_off
:
int probe_irq_off(unsigned long val)
{
int i, irq_found = 0, nr_of_irqs = 0;
struct irq_desc *desc;
for_each_irq_desc(i, desc) {
raw_spin_lock_irq(&desc->lock);
if (desc->istate & IRQS_AUTODETECT) {
if (!(desc->istate & IRQS_WAITING)) {
if (!nr_of_irqs)
irq_found = i;
nr_of_irqs++;
}
desc->istate &= ~IRQS_AUTODETECT;
irq_shutdown(desc);
}
raw_spin_unlock_irq(&desc->lock);
}
mutex_unlock(&probing_active);
if (nr_of_irqs > 1)
irq_found = -irq_found;
return irq_found;
}
注意,在probe_irq_off
前,需要我们自己触发中断,这个请参考前面的例子。probe_irq_off
里面就是一次遍历,寻找有设置IRQS_AUTODETECT
,但是无IRQS_WAITING
的,这种情况,意味着该irq号对应的设备有中断产生(后面我会分析具体清除IRQS_WAITING
的地方)。另外,我们也会发现,probe_irq_off
只会记录第一个发现的中断,并且在发现有多个中断产生时,会将第一个发现的中断号取反并返回。
下面分析下具体清除IRQS_WAITING
的地方,以以前的中断处理为例,毕竟现在的中断控制器都会定义自己控制器的中断处理,各式各样,这里面的思想是一样的。当一个中断产生时,调用的逻辑如下:
irq_handler-->arch_irq_handler_default-->asm_do_IRQ-->handle_IRQ-->generic_handle_irq-->generic_handle_irq_desc-->desc->handle_irq-->handle_level_irq|handle_edge_irq
在handle_level_irq
中:
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
...
...
...
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
...
...
...
}
这说明,只要某个irq有中断产生,它对应的desc里面的IRQS_WAITING
就会被清除。
总结
本文讨论的中断探测机制在我们不知道中断号的情况时,也不失为一种获取中断号的方法,对吧!
完!
2013年7月
linux irq 自动探测的更多相关文章
- Linux轻量级自动运维工具-Ansible浅析【转】
转自 Linux轻量级自动运维工具-Ansible浅析 - ~微风~ - 51CTO技术博客http://weiweidefeng.blog.51cto.com/1957995/1895261 Ans ...
- Linux轻量级自动运维工具-Ansible浅析 转
转自 Linux轻量级自动运维工具-Ansible浅析 - ~微风~ - 51CTO技术博客http://weiweidefeng.blog.51cto.com/1957995/1895261 Ans ...
- Linux 每天自动备份mysql数据库的方法
Linux 每天自动备份mysql数据库的方法 作者: 字体:[增加 减小] 类型:转载 linux下为了安全有时候需要自动备份mysql数据库,下面是具体的实现步骤. /usr/bin为my ...
- linux - 怎么自动填写有交互的shell脚本 - SegmentFault
linux - 怎么自动填写有交互的shell脚本 - SegmentFault TCL/Expect交互式自动化测试概要 - - ITeye技术网站 expect是一种基于TCL,能与交互式程序进行 ...
- Linux Makefile自动生成--config.h
Linux Makefile自动生成--config.h http://blog.csdn.net/spch2008/article/details/12510805
- 在 Linux 中自动生成 Cordova/Phonegap for Android 的 APK 安装程序
在 Linux 中自动生成 Cordova/Phonegap for Android 的 APK 安装程序 本贴首发于: http://xuekaiyuan.com/forum.php?mod=vie ...
- Linux项目自动部署
场景:linux中自动部署项目在工作中经常遇到,快速高效的部署项目能够大幅提高工作效率.现在将项目部署的过程记录下来,以供参考,其中用到的知识点现在还有很多不很清楚,后面要好好琢磨琢磨! 1 项目部署 ...
- Linux下自动备份MySQL数据库并上传到远程FTP服务器
Linux下自动备份MySQL数据库并上传到远程FTP服务器且删除指定日期前的备份Shell脚本 说明: 1.备份MySQL数据库存放目录/var/lib/mysql下面的xshelldata数据库 ...
- 在 Linux 中自动配置 IPv6 地址
在 Linux 中自动配置 IPv6 地址 在本文中,我们将学习如何为 ULA 自动配置 IP 地址. 何时使用唯一本地地址 唯一本地地址unique local addresses(ULA)使用 f ...
随机推荐
- javabean 参数收集 设置属性 设置不同级别的域对象的属性 默认存储在pagecontext中
javabean 参数收集 设置属性 设置不同级别的域对象的属性 默认存储在pagecontext中
- [洛谷P3224][HNOI2012]永无乡
题目大意:给你$n$个点,每个点有权值$k$,现有两种操作: 1. $B\;x\;y:$将$x,y$所在联通块合并2. $Q\;x\;k:$查询第$x$个点所在联通块权值第$k$小是哪个数 题解:线段 ...
- C++分离字符串中的数字和字符 转
#include <iostream> #include <string> #include <vector> using namespace std; void ...
- HDU2841 (队列容斥)
Visible TreesTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- Sql Server 2008 R2 数据库脚本导出方法
经常忘记怎么搞,截几张图记录一下. 1 选中要导出的数据库,右键—>任务—>生成脚本 2 3 4 查看保存的脚本
- Cows POJ - 2481 树状数组
Farmer John's cows have discovered that the clover growing along the ridge of the hill (which we can ...
- Android JUnit test
Android单元测试步骤 1.修改AndroidManifest.xml文件. 添加instrumentation节点.其中name是固定值,targetPackage为需要测试的类所在的包.如: ...
- bzoj1177 [Apio2009]Oil 二维前缀最大值,和
[Apio2009]Oil Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 2300 Solved: 932[Submit][Status][Disc ...
- C#学习之泛型准备
想要把泛型搞明白,最好先弄明白下面的代码实例 本实例是建立了两个类,然后在类中可以添加任意类型的值,并且可以利用foreach语句读出 //第一个节点类,放在一个文件中 using System; u ...
- 一图看懂深度学习框架对比----Caffe Torch Theano TensorFlow
Caffe Torch Theano TensorFlow Language C++, Python Lua Python Python Pretrained Yes ++ Yes ++ Yes ...