Board logo

标题: IO端口与IO内存详解(1) [打印本页]

作者: look_w    时间: 2017-9-23 14:32     标题: IO端口与IO内存详解(1)

1.3、从用户空间访问I/O端口

1.2节介绍的函数主要是提供给驱动使用,但它们也可在用户空间使用,至少在PC机上可以。GNU C 库在 <sys/io.h> 中定义它们。如果在用户空间使用这些函数,必须满足下列条件:

1)、程序必须使用-O选项编译来强制扩展内联函数

2)、必须使用ioperm和iopl系统调用(#include <sys/perm.h>) 来获得进行操作I/O端口的权限。ioperm 为获取单个端口的操作许可,iopl 为获取整个I/O空间许可。这2个函数都是x86特有的

3)、程序必须以root来调用ioperm或者iopl,或者其父进程(祖先)必须以root获得的端口操作权限

如果平台不支持ioperm和iopl系统调用,通过使用/dev/prot设备文件,用户空间仍然可以存取I/O 端口。但是要注意的是,这个文件的定义也是依赖平台的。

1.4、字串操作

除了一次传递一个数据的I/O操作,某些处理器实现了一次传递一序列数据(单位可以是字节、字和双字)的特殊指令。这些所谓的字串指令,它们完成任务比一个C语言循环更快。下列宏定义实现字串操作,在某些体系上,它们通过使用单个机器指令实现;但如果目标处理器没有进行字串I/O指令,则通过执行一个紧凑的循环实现。

字串函数的原型是:

/* insb:从I/O端口port读取count个数据(单位字节)到以内存地址addr为开始的内存空间 */
void insb(unsigned port, void *addr, unsigned long count);
/* outsb:将内存地址addr开始的count个数据(单位字节)写到I/O端口port */
void outsb(unsigned port, void *addr, unsigned long count);
/* insw:从I/O端口port读取count个数据(单位字)到以内存地址addr为开始的内存空间 */
void insw(unsigned port, void *addr, unsigned long count);
/* outsw:将内存地址addr开始的count个数据(单位字)写到I/O端口port */
void outsw(unsigned port, void *addr, unsigned long count);
/* insl:从I/O端口port读取count个数据(单位双字)到以内存地址addr为开始的内存空间 */
void insl(unsigned port, void *addr, unsigned long count);
/* outsl:将内存地址addr开始的count个数据(单位双字)写到I/O端口port */
void outsl(unsigned port, void *addr, unsigned long count);

注意:使用字串函数时,它们直接将字节流从端口中读取或写入。当端口和主机系统有不同的字节序时,会导致不可预期的结果。使用 inw读取端口应在必要时自行转换字节序,以匹配主机字节序。

1.5、暂停式I/O操作函数

由于处理器的速率可能与外设(尤其是低速设备)的并不匹配,当处理器过快地传送数据到或自总线时,这时可能就会引起问题。解决方法是:如果在I/O 指令后面紧跟着另一个相似的I/O 指令,就必须插入一个小的延时。为此,Linux提供了暂停式I/O操作函数,这些函数的名子只是在非暂停式I/O操作函数(前面提到的那些I/O操作函数都是非暂停式的)名后加上_p ,如inb_p、outb_p等。大部分体系都支持这些函数,尽管它们常常被扩展为与非暂停 I/O 同样的代码,因为如果体系使用一个合理的现代外设总线,没有必要额外暂停。

以下是ARM体系暂停式I/O宏的定义:

#define outb_p(val,port)    outb((val),(port))
#define outw_p(val,port)    outw((val),(port))
#define outl_p(val,port)    outl((val),(port))
#define inb_p(port)        inb((port))
#define inw_p(port)        inw((port))
#define inl_p(port)        inl((port))
#define outsb_p(port,from,len)    outsb(port,from,len)
#define outsw_p(port,from,len)    outsw(port,from,len)
#define outsl_p(port,from,len)    outsl(port,from,len)
#define insb_p(port,to,len)    insb(port,to,len)
#define insw_p(port,to,len)    insw(port,to,len)
#define insl_p(port,to,len)    insl(port,to,len)

因为ARM使用内部总线,就没有必要额外暂停,所以暂停式的I/O函数被扩展为与非暂停式I/O同样的代码。

1.6、平台依赖性

由于自身的特性,I/O指令高度依赖于处理器,非常难以隐藏各体系间的不同。因此,大部分的关于端口 I/O的源码是平台依赖的。以下是x86和ARM所使用函数的总结:

IA-32 (x86)

x86_64

这个体系支持本章介绍的所有函数;port参数的类型为unsigned short。

ARM

端口映射到内存,并且支持本章介绍的所有函数;port参数的类型为unsigned int;字串函数用C语言实现。






欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0