wplhd的笔记 https://bbs.21ic.com/?599312 [收藏] [复制] [RSS]

日志

linux如何访问i/o 端口

已有 1275 次阅读2009-11-24 02:47 |系统分类:ARM| linux如何访问i/o, 端口


 





arm ,Linux里面,全部都会做phy->virt的映射。映射方式中的一种是静态映射,ioremap是动态映射。在静态映射之后,仍然可以通过ioremap动态映射,也就是一个IO物理地址可以映射到多个虚拟地址。

(1)关于IO与内存空间:
     在X86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言的,它通过特定的指令in、out来访问。端口号标识了外设的寄存器地址。Intel语法的in、out指令格式为:
     IN 累加器, {端口号│DX}
     OUT {端口号│DX},累加器
     目前,大多数嵌入式微控制器如ARM、PowerPC等中并不提供I/O空间,而仅存在内存空间。内存空间可以直接通过地址、指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。
     即便是在X86处理器中,虽然提供了I/O空间,如果由我们自己设计电路板,外设仍然可以只挂接在内存空间。此时,CPU可以像访问一个内存单元那样访问外设I/O端口,而不需要设立专门的I/O指令。因此,内存空间是必须的,而I/O空间是可选的。


(2)inb和outb:


在Linux设备驱动中,宜使用Linux内核提供的函数来访问定位于I/O空间的端口,这些函数包括:
· 读写字节端口(8位宽)
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
· 读写字端口(16位宽)
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
· 读写长字端口(32位宽)
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
· 读写一串字节
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
· insb()从端口port开始读count个字节端口,并将读取结果写入addr指向的内存;outsb()将addr指向的内存的count个字节连续地写入port开始的端口。
· 读写一串字
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
· 读写一串长字
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
上述各函数中I/O端口号port的类型高度依赖于具体的硬件平台,因此,只是写出了unsigned。


(3)readb和writeb:
在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是工程师宜使用Linux内核的如下一组函数来完成设备内存映射的虚拟地址的读写,这些函数包括:
· 读I/O内存
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
· 写I/O内存
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);


(4)把I/O端口映射到“内存空间”:
void *ioport_map(unsigned long port, unsigned int count);
通过这个函数,可以把port开始的count个连续的I/O端口重映射为一段“内存空间”。然后就可以在其返回的地址上像访问I/O内存一样访问这些I/O端口。当不再需要这种映射时,需要调用下面的函数来撤消:
void ioport_unmap(void *addr);
实际上,分析ioport_map()的源代码可发现,所谓的映射到内存空间行为实际上是给开发人员制造的一个“假象”,并没有映射到内核虚拟地址,仅仅是为了让工程师可使用统一的I/O内存访问接口访问I/O端口。


路过

鸡蛋

鲜花

握手

雷人

全部作者的其他最新日志

评论 (0 个评论)