📄 linux设备驱动程序学习(9)-与硬件通信 - linux设备驱动程序 - tekkaman ninja.htm
字号:
端口</STRONG></FONT></P>
<P>在驱动程序注册I/O
端口后,就可以读/写这些端口。大部分硬件会把8、16和32位端口区分开,不能像访问系统内存那样混淆使用。驱动必须调用不同的函数来存取不同大小的端口。</P>
<P>只支持内存映射的 I/O
寄存器的计算机体系通过重新映射I/O端口到内存地址来伪装端口I/O。为了提高移植性,内核向驱动隐藏了这些细节。Linux
内核头文件(体系依赖的头文件 <FONT
color=#0000ff><asm/io.h></FONT> )
定义了下列内联函数(有的体系是宏,有的不存在)来访问 I/O 端口:</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"><SPAN
style="COLOR: #0000ff">unsigned</SPAN> inb<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outb<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">char</SPAN> byte<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #ff9900">/*读/写字节端口( 8 位宽 )。port
参数某些平台定义为 unsigned long ,有些为 unsigned short 。
inb 的返回类型也体系而不同。*/</SPAN><BR><BR><SPAN
style="COLOR: #0000ff">unsigned</SPAN> inw<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outw<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">short</SPAN> word<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #ff9900">/*访问 16位 端口( 一个字宽
)*/</SPAN><BR><BR><SPAN
style="COLOR: #0000ff">unsigned</SPAN> inl<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outl<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN>
longword<SPAN style="COLOR: #0000cc">,</SPAN>
<SPAN style="COLOR: #0000ff">unsigned</SPAN>
port<SPAN style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #ff9900">/*访问 32位 端口。 longword
声明有的平台为 unsigned long ,有的为 unsigned
int。*/</SPAN></SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P><STRONG><FONT color=#0000ff size=3>在用户空间访问 I/O
端口</FONT></STRONG></P>
<P>以上函数主要提供给设备驱动使用,但它们也可在用户空间使用,至少在 PC上可以。 GNU C
库在 <sys/io.h>
中定义了它们。如果在用户空间代码中使用必须满足以下条件:</P>
<P>(1)程序必须使用 -O 选项编译来强制扩展内联函数。</P>
<P>(2)必须用ioperm 和 iopl 系统调用<FONT
color=#660099>(#include <sys/perm.h>)</FONT>
来获得对端口 I/O 操作的权限。ioperm 为获取单独端口操作权限,而 iopl 为整个 I/O
空间的操作权限。<FONT color=#660099> (x86 特有的)</FONT></P>
<P>(3)程序以 root 来调用 ioperm 和 iopl,或是其父进程必须以
root 获得端口操作权限。<FONT color=#660099>(x86
特有的)</FONT></P>
<P><FONT color=#0000ff>若平台没有 ioperm 和 iopl
系统调用,用户空间可以仍然通过使用 /dev/prot 设备文件访问 I/O
端口。注意:这个文件的定义是体系相关的,并且I/O 端口必须先被注册。</FONT></P>
<P><FONT color=#0000ff
size=3><STRONG>串操作</STRONG></FONT></P>
<P>除了一次传输一个数据的I/O操作,一些处理器实现了一次传输一个数据序列的特殊指令,序列中的数据单位可以是字节、字或双字,这是所谓的串操作指令。它们完成任务比一个
C 语言循环更快。下列宏定义实现了串I/O,它们有的通过单个机器指令实现;但如果目标处理器没有进行串
I/O 的指令,则通过执行一个紧凑的循环实现。 有的体系的原型如下:</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"><SPAN
style="COLOR: #0000ff">void</SPAN> insb<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outsb<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><BR><SPAN
style="COLOR: #0000ff">void</SPAN> insw<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outsw<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><BR><SPAN
style="COLOR: #0000ff">void</SPAN> insl<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN> <BR><SPAN
style="COLOR: #0000ff">void</SPAN> outsl<SPAN
style="COLOR: #0000cc">(</SPAN><SPAN
style="COLOR: #0000ff">unsigned</SPAN> port<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">void</SPAN> <SPAN
style="COLOR: #0000cc">*</SPAN>addr<SPAN
style="COLOR: #0000cc">,</SPAN> <SPAN
style="COLOR: #0000ff">unsigned</SPAN> <SPAN
style="COLOR: #0000ff">long</SPAN> <SPAN
style="COLOR: #ff0000">count</SPAN><SPAN
style="COLOR: #0000cc">)</SPAN><SPAN
style="COLOR: #0000cc">;</SPAN>
</SPAN></CODE></P></TD></TR></TBODY></TABLE>
<P>使用时注意:
它们直接将字节流从端口中读取或写入。当端口和主机系统有不同的字节序时,会导致不可预期的结果。 使用
inw 读取端口应在必要时自行转换字节序,以匹配主机字节序。</P>
<P><FONT color=#0000ff
size=3><STRONG>暂停式 I/O</STRONG></FONT></P>
<P><FONT color=#000000>为了匹配低速外设的速度,有时若 I/O
指令后面还紧跟着另一个类似的I/O指令,就必须在 I/O
指令后面插入一个小延时。</FONT><FONT
color=#000000>在这种情况下,可以使用暂停式的I/O函数代替通常的I/O函数,它们的名字以
_p 结尾,如 inb_p、outb_p等等。
这些函数定义被大部分体系支持,尽管它们常常被扩展为与非暂停式I/O
同样的代码。因为如果体系使用一个合理的现代外设总线,就没有必要额外暂停。细节可参考平台的 asm
子目录的 io.h
文件。以下是include\asm-arm\io.h中的宏定义:</P></FONT>
<TABLE st
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -