📄 (ldd) ch09-中断处理(上)(转载).txt
字号:
第一个数是总的中断次数,而其它每个数都代表一个中断信号,从0号中断开始
。上面的快照显示4号中断被使用了4907次,虽然当前它的处理程序没有安装上。如果你
测试的驱动程序是在每次打开和关闭设备的循环中获取和释放中断的话,那么你会发现/
proc/stat文件要比/proc/interrupts文件更有用。
两个文件另一处不同是interrupts文件与体系结构无关,而stat文件则与体系结
构有关:其字段的个数取决于内核之下的硬件。可以获取的中断个数在Sparc上只有15个
,而在Atari(M68k处理器)上则多达72个。
下面的快照给出我的Alpha工作站(共有16个中断,和x86机器一样)上的文件内容:
1: 2 keyboard
5: 4641 NE2000
5: 4641 NE2000
15: 22909 + 53c7,8xx
intr 27555 0 2 0 1 1 4642 0 0 0 0 0 0 0 0 0 22909
这个输出的最值得注意的地方是不出现时钟中断。在Alpha机器上,时钟中断与
其它中断到达处理器的方式不同,没有分配IRQ中断号。
自动检测中断号
驱动程序初始化时最迫切的问题之一就是如何决定设备要使用哪条中断信号线。
驱动程序需要该信息以便安装正确的处理程序。虽然程序员可以要求用户在装载是指定
中断号,但这并不好,因为一般用户并不知道中断号,或者是因为他没有配置跳线或者
因为该设备根本就没有跳线。自动检测中断号是对驱动程序使用的基本要求。
有时自动检测依赖于一些设备拥有的较少改变的缺省特性。此时,驱动程序可以
就假定设备使用了这些缺省值。short在检测并口时就正是这么作的。正如short的代码
就假定设备使用了这些缺省值。short在检测并口时就正是这么作的。正如short的代码
中所给出的,实现起来相当简明:
if (short_irq<0) /* 尚未指定:强制为缺省的 */
switch(short_base){
case 0x378: short_irq=7; break;
case 0x278: short_irq=2; break;
case 0x3bc: short_irq=5; break;
}
这段代码根据选定的I/O地址来分配中断号,但也允许用户在装载驱动程序时通
过调用insmod short short_irq=x来覆盖缺省值。short_base缺省为0x378,因此short_
irq缺省为7。
有些设备设计得更为先进,会简单地“声明”它们要使用那个中断。此时,驱动
程序可以通过读设备的某个I/O端口的一个状态字节来获得中断号。当目标设备能告述设
备要使用哪个中断时,那么自动检测中断号就是探测设备,不需要额外工作来探测中断
。
值得注意的是,现代的设备能提供自己的中断配置信息。PCI标准通过要求外围
设备声明要使用的中断信号线的方法来解决这个问题。关于PCI标准的讨论可参见第15章
“外设总线概貌”。
遗憾的是,不是所有设备都对程序员友好,自动检测可能还是需要一些探测的。
技术很简单:驱动程序告诉设备产生中断,然后观察会发生些什么。如果一切正常,那
么只有一条中断信号线被激活了。
尽管探测在理论上很简单,实际的实现则并不那么简明。下面我们看看执行该任
尽管探测在理论上很简单,实际的实现则并不那么简明。下面我们看看执行该任
务的两种方法:调用内核定义的帮助函数和实现我们自己的版本。
核心帮助下的检测
主流的内核版本都提供探测中断号的底层工具。这种工具包括两个函数,都在头
文件<linux/interrupt.h>中声明(该头文件也描述了探测的机制):
unsigned long probe_irq_on(void);
这个函数返回尚未分配的中断的位掩码。驱动程序必须保留返回的位掩码以便随后能将
它传递给probe_irq_off函数。调用该函数后,驱动程序要安排相应设备至少产生一次中
断。
int probe_irq_off(unsigned long);
在设备已经申请了中断之后,驱动程序要调用这个函数,传递给它的参数是先前调用pro
be_irq_on返回的位掩码。probe_irq_off返回“启动探测”后发出的中断次数。如果没
有发生任何中断,就返回0(因此无法探测0号中断,但在能支持的所有体系结构上也没有
什么定制设备能使用它)。如果产生了多次中断(二义性检测),probe_irq_off将返回一
什么定制设备能使用它)。如果产生了多次中断(二义性检测),probe_irq_off将返回一
个负值。
程序员要注意在调用probe_irq_on后启动设备,并在调用probe_irq_off后关闭
它。此外,在调用probe_irq_off之后,不要忘了处理你的设备尚未处理的那些中断。
short模块演示了如何进行这样的探测。如果你在装载模块时指定probe=1并且并
口插座的9号和10号引脚相连,就会执行下面的代码进行中断信号线的检测。
int count=0;
do {
unsigned long mask;
mask=probe_irq_on();
outb_p(0x10, short_base+2); /* 启动中断报告 */
outb_p(0x00,short_base); /* 清位 */
outb_p(0xFF, short_base); /* 置位:中断!*/
outb_p(0x00, short_base+2); /* 关闭中断报告 */
short_irq=probe_irq_off(mask);
if (short_irq==0){ /* 没有探测到中断报告?*/
printk(KERN_INFO "short: no irq reported by probe\n");
short_irq=-1;
}
/*
* 如果激活了一个以上的中断,结果就是负的。我们将为中断提供服务(除非是lpt
* 端口)并且再次进行循环。最多循环5次,然后放弃
*/
} while (short_irq<0 && count++<5);
if (short_irq<0)
printk("short: probe failed %i times, giving up\n",count);
探测很耗时。尽管short的探测很快,但象探测帧捕捉卡,就至少需要延迟20ms(
相对处理器时间就太长了),而探测其它设备可能会更花时间。因此,最好就只在模块初
始化时探测中断信号线一次,不管你是在打开设备时(你应该这样做)或者在init_module
中(你无论如何不应该这样做)安装你的中断处理程序的。
值得注意的是,在Sparc和M68k上,中断探测全无必要,因此也不必实现。探测
是种“黑客”行为,象PCI这样的成熟的体系结构会提供所有必要的信息。实际上,M68k
和Sparc的内核开放给模块桩(stub)的探测函数总是返回0——每种体系结构都必须定义
这些函数,因为它们是由体系结构无关的源文件来开放的。所有其它的体系结构都允许
使用上面给出的探测技术。
probe_irq_on和probe_irq_off的问题是早期的内核版本并不开放这两个函数。
因此,如果你希望写的模块能移植到1.2版的内核,你必须自己做中断探测。
DIY(Do It Yourself自己做)检测
探测也可以有驱动程序自己较容易地实现。如果装载是指定probe=2,short模块
将对中断信号线进行DIY检测。
实现机制和前面讨论的内核帮助下的检测是一样的:启动所有未被占用的中断,
然后等着看会发生些什么。但我们可以利用拥有的对设备的一些知识。通常一个设备可
以配置成使用3或4个中断号中的一个;只需要探测这些中断号,这使我们不必测试所有
可能的中断号就可以检测到正确的中断号。
可能的中断号就可以检测到正确的中断号。
在short的实现中假定可能的中断号只有3,5,7和9。这些数值实际上是一些并
口允许你选取的值的范围。
下面的代码通过测试所有“可能的”中断和会观察发生什么来进行中断探测。tr
ials数组列出所有要尝试的中断号,0是该列表的结束标志;trials数组用于记录实际上
哪个处理程序被驱动程序注册了。
int trials[]={3,5,7,9,0};
int tried[]={0,0,0,0,0};
int i,count=0;
/*
*为所有可能的中断信号线安装探测处理程序。记录下结果(0表示成功,-EBUSY
*表示失败)以便只释放申请的中断
*/
for (i=0; trials[i]; i++)
tried[i]=request_irq(trials[i], short_probing, SA_INTERRUPT, "short
probe", NULL);
do {
short_irq=0; /* 尚未取得中断号 */
outb_p(0x10, short_base+2); /* 启动 */
outb_p(0x00, short_base);
outb_p(0x00, short_base);
outb_p(0xFF, short_base); /* 置位 */
outb_p(0x10, short_base+2); /* 关闭 */
/* 处理程序已经设置了这个值 */
if (short_irq==0) { /*
printk(KERN_INFO "short: no irq reported by probe\n");
}
/*
* 如果激活了一个以上的中断,结果就是负的。我们将为中断提供服务(除非是lpt
* 端口)并且再次进行循环。最多这样做5次
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -