📄 linux设备驱动程序学习(11)-中断处理 - linux设备驱动程序 - tekkaman ninja.htm
字号:
style="COLOR: #000000"><FONT face=新宋体><SPAN
style="COLOR: #0000ff">int</SPAN>
can_request_irq<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">int</SPAN> irq<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> flags<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <SPAN
style="COLOR: #ff9900">/*当能够成功分配给定中断,则返回非零值。但注意,在
can_request_irq 和 request_irq
的调用之间给定中断可能被占用*/</SPAN></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P><FONT color=#0000ff
size=3><STRONG>快速和慢速处理例程</STRONG></FONT></P>
<P>快速中断是那些能够很快处理的中断,而处理慢速中断会花费更长的时间。在处理慢速中断时处理器重新使能中断,避免快速中断被延时过长。在现代内核中,快速和慢速中断的区别已经消失,剩下的只有一个:快速中断(使用
SA_INTERRUPT
)执行时禁止所有在当前处理器上的其他中断。注意:其他的处理器仍然能够处理中断。</P>
<P> 除非你充足的理由在禁止其他中断情况下来运行中断处理例程,否则不应当使用SA_INTERRUPT.</P>
<P><FONT color=#0000ff
size=3><STRONG> x86中断处理内幕</STRONG></FONT></P>
<P>这个描述是从 2.6 内核 arch/i386/kernel/irq.c,
arch/i386/kernel/ apic.c,
arch/i386/kernel/entry.S,
arch/i386/kernel/i8259.c, 和
include/asm-i386/hw_irq.h
中得出,尽管基本概念相同,硬件细节与其他平台上不同。</P>
<P>底层中断处理代码在汇编语言文件
entry.S。在所有情况下,这个代码将中断号压栈并且跳转到一个公共段,公共段会调用
do_IRQ(在 irq.c 中定义)。do_IRQ
做的第一件事是应答中断以便中断控制器能够继续其他事情。它接着获取给定 IRQ
号的一个自旋锁,阻止其他 CPU 处理这个 IRQ,然后清除几个状态位(包括IRQ_WAITING
)然后查找这个 IRQ
的处理例程。若没有找到,什么也不做;释放自旋锁,处理任何待处理的软件中断,最后 do_IRQ
返回。从中断中返回的最后一件事可能是一次处理器的重新调度。</P>
<P> IRQ的探测是通过为每个缺乏处理例程的IRQ设置 IRQ_WAITING
状态位来完成。当中断发生, 因为没有注册处理例程,do_IRQ 清除这个位并且接着返回。
当probe_irq_off被一个函数调用,只需搜索没有设置 IRQ_WAITING 的
IRQ。</P>
<P><FONT color=#0000ff size=3><STRONG>/proc
接口</STRONG></FONT></P>
<P>当硬件中断到达处理器时, 内核提供的一个内部计数器会递增,产生的中断报告显示在文件
/proc/interrupts中。这一方法可以用来检查设备是否按预期地工作。此文件只显示当前已安装处理例程的中断的计数。若以前<FONT
face=新宋体>request_irq的一个中断,现在已经free_irq了,那么就不会显示在这个文件中,但是它可以显示终端共享的情况。</FONT></P>
<P><FONT face=新宋体>/proc/stat记录了几个关于系统活动的底层统计信息,
包括(但不仅限于)自系统启动以来收到的中断数。</FONT><FONT
face=新宋体> stat 的每一行以一个字符串开始, 是该行的关键词:intr
标志是中断计数。第一个数是所有中断的总数, 而其他每一个代表一个单独的中断线的计数, 从中断 0
开始(包括当前没有安装处理例程的中断),无法显示终端共享的情况。</FONT></P>
<P><FONT face=新宋体>以上两个文件的一个不同是:<FONT
face="Courier New">/proc/interrupts几乎</FONT>不依赖体系,而/proc/stat的字段数依赖内核下的硬件中断,</FONT><FONT
face=新宋体>其定义在<FONT
color=#0000ff><asm/irq.h></FONT>中。ARM的定义为:</FONT>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000"><FONT face=新宋体><SPAN
style="COLOR: #0000cc">#</SPAN><SPAN
style="COLOR: #ff0000">define</SPAN>
NR_IRQS 128</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P>
<HR id=null>
<P><FONT color=#0000ff size=4><STRONG>自动检测 IRQ
号</STRONG></FONT></P></DIV><FONT color=#0000ff
size=4>
<P><FONT color=#000000>驱动初始化时最迫切的问题之一是决定设备要使用的IRQ
线,驱动需要信息来正确安装处理例程。自动检测中断号对驱动的可用性来说是一个基本需求。</FONT><FONT
color=#000000>有时自动探测依赖一些设备具有的默认特性,以下是典型的并口中断探测程序:</P>
<P>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000"><FONT face=新宋体><SPAN
style="COLOR: #0000ff">if</SPAN> <SPAN
style="COLOR: #0000cc">(</SPAN>short_irq <SPAN
style="COLOR: #0000cc"><</SPAN> 0<SPAN
style="COLOR: #0000cc">)</SPAN> <SPAN
style="COLOR: #ff9900">/*
依靠使并口的端口号,确定中断*/</SPAN><BR> <SPAN
style="COLOR: #0000ff">switch</SPAN><SPAN
style="COLOR: #0000cc">(</SPAN>short_base<SPAN
style="COLOR: #0000cc">)</SPAN> <SPAN
style="COLOR: #0000cc">{</SPAN><BR> <SPAN
style="COLOR: #0000ff">case</SPAN> 0x378<SPAN
style="COLOR: #0000cc">:</SPAN> short_irq <SPAN
style="COLOR: #0000cc">=</SPAN> 7<SPAN
style="COLOR: #0000cc">;</SPAN> <SPAN
style="COLOR: #0000ff">break</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN><BR> <SPAN
style="COLOR: #0000ff">case</SPAN> 0x278<SPAN
style="COLOR: #0000cc">:</SPAN> short_irq <SPAN
style="COLOR: #0000cc">=</SPAN> 2<SPAN
style="COLOR: #0000cc">;</SPAN> <SPAN
style="COLOR: #0000ff">break</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN><BR> <SPAN
style="COLOR: #0000ff">case</SPAN> 0x3bc<SPAN
style="COLOR: #0000cc">:</SPAN> short_irq <SPAN
style="COLOR: #0000cc">=</SPAN> 5<SPAN
style="COLOR: #0000cc">;</SPAN> <SPAN
style="COLOR: #0000ff">break</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN><BR> <SPAN
style="COLOR: #0000cc">}</SPAN>
</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P>有的驱动允许用户在加载时覆盖默认值:</P>
<P>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000"><FONT face=新宋体>insmod
xxxxx<SPAN style="COLOR: #0000cc">.</SPAN>ko
irq<SPAN style="COLOR: #0000cc">=</SPAN>x
</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P>当目标设备有能力告知驱动它要使用的中断号时,自动探测中断号只是意味着探测设备,无需做额外的工作探测中断。</P>
<P>但不是每个设备都对程序员友好,对于他们还是需要一些探测工作。这个工作技术上非常简单:
驱动告知设备产生中断并且观察发生了什么。如果一切顺利,则只有一个中断信号线被激活。尽管探测在理论上简单,但实现可能不简单。有
2 种方法来进行探测中断: <FONT
color=#0000ff><STRONG>调用内核定义的辅助函数</STRONG><FONT
color=#000000>和</FONT><STRONG>DIY探测</STRONG></FONT>。</P>
<P><STRONG><FONT color=#0000ff>(1)</FONT><FONT
color=#0000ff>调用内核定义的辅助函数</FONT></STRONG><BR></P></FONT><PRE class=programlisting></FONT>Linux 内核提供了一个底层设施来探测中断号,且只能在非共享中断模式下工作,它包括 2 个函数, 在<linux/interrupt.h> 中声明( 也描述了探测机制 ):<BR></PRE>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: #000000"><SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN>
probe_irq_on<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">void</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #ff9900">/*这个函数返回一个未分配中断的位掩码。驱动必须保留返回的位掩码,
并在后面传递给 probe_irq_off。在调用probe_irq_on之后,
驱动应当安排它的设备产生至少一次中断*/</SPAN><BR><BR><SPAN
style="COLOR: #0000ff">int</SPAN>
probe_irq_off<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #ff9900">/*在请求设备产生一个中断后, 驱动调用这个函数,
并将 probe_irq_on
返回的位掩码作为参数传递给probe_irq_off。probe_irq_off
返回在"probe_on"之后发生的中断号。如果没有中断发生, 返回 0
;如果产生了多次中断,probe_irq_off
返回一个负值*/</SPAN></SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P>程序员应当注意在调用 probe_irq_on 之后启用设备上的中断, 并在调用
probe_irq_off 前禁用。此外还必须记住在 probe_irq_off
之后服务设备中待处理的中断。<BR>以下是LDD3中的并口示例代码,(并口的管脚 9 和 10
连接在一起,探测五次失败后放弃):</P>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -