与硬件通讯---IO内存

来源:互联网 发布:网络文字顶格 编辑:程序博客网 时间:2024/06/08 18:52
一、简述
尽管IO端口在x86平台上流行,但是用来与设备通讯的主要机制仍然是通过内存映射的寄存器和设备内存;这两者都称为IO内存,因为寄存器与内存之间的区别对软件是透明的;
IO内存就是一个像RAM一样的简单区域,它被处理器用来跨过总线存取设备;这几个内存可用作几个目的:例如持有视频数据或者以太网报文,同时实现设备寄存器就像IO端口一样的行为(即:它们有读和写相关联的边际效果).
存取IO内存的方式依赖于计算机体系、总线、以及所使用的设备;
依赖于计算机平台和所使用的总线,IO内存可以或者不可以通过页表来存取;当通过页表来存取时,内核必须首先安排(知道)从驱动中可见的物理地址,并且这常常意味着在做任何IO之前,必须先调用ioremap();当不需要通过页表来存取时,IO内存位置看起来就像IO端口,可以直接使用,并且只可以使用正确的包装函数读写它们;
不管是否需要ioremap()来存取IO内存,都不鼓励直接使用IO内存指针,尽管IO内存如同在硬件级别的正常RAM一样寻址;用来存取IO内存的包装函数在所有平台上是安全的并且在任何时候直接的指针解引用能够进行操作时,会被优化掉;
二、IO内存分配和映射
IO内存区必须在使用之前分配;分配IO内存区的接口函数定义在<linux/ioport.h>文件中;
struct resource* request_mem_region(unsigned long start, unsigned long len, char* name);
该函数分配一个大小为len字节的IO内存区,并且这个新分配的IO内存区从start处开始;如果分配成功,则返回一个非NULL指针;如果分配失败,则返回一个NULL指针;所有的IO内存分配都在/proc/iomem中列出;
IO内存区在不再需要的时候应当释放:
void release_mem_region(unsigned long start, unsigned long len);
还有一个旧的检查IO内存区可用性的函数:
int check_mem_region(unsigned long start, unsigned long len);
但是这个检验函数是不安全的;
在存取IO内存之前,分配IO内存不是唯一要求的步骤,必须保证这个IO内存区已经对内核是可存取的;
在许多系统中,IO内存区根本不是可以直接存取的,而是通过设置一个映射之后,才能使用;函数ioremap()就是用来完成IO内存区重映射功能的,它是专门用于给IO内存区安排虚拟地址的;
一旦对IO内存区调用了ioremap()和iounmap(),一个设备驱动程序就可以存取任何IO内存区地址,而不管它是否直接映射到虚拟地址空间;
注意:从ioremap()函数返回的内存地址不应当直接解引用,而是应当使用内核提供的存取函数来解引用;
#include <asm/io.h>
void* ioremap(unsigned long phys_addr, unsigned long size);
void* ioremap_nocache(unsigned long phys_addr, unsigned long size);
void iounmap(void* addr);
其中,函数ioremap_nocache()是硬件相关的,因为硬件是禁止缓冲的;
三、存取IO内存
在一些平台上,直接使用ioremap()的返回值可能会带来一些移植性问题;因此,内核提供了一些列的函数来存取IO内存,以避免移植性问题;
1、读IO内存:
unsigned int ioread8(void* addr);
unsigned int ioread16(void* addr);
unsigned int ioread32(void* addr);
其中,参数addr应当是从ioremap()函数获得的地址(也许与一个整型偏移);返回值是从给定IO内存中读取的数据;
2、写IO内存:
void iowrite8(u8 value, void* addr);
void iowrite16(u16 value, void* addr);
void iowrite32(u32 value, void* addr);
3、读写一系列值到一个给定的IO内存区地址:
void ioread8_rep(void* addr, void* buf, unsigned long count);
void ioread16_rep(void* addr, void* buf, unsigned long count);
void ioread32_rep(void* addr, void* buf, unsigned long count);
void iowrite8_rep(void* addr, const void* buf, unsigned long count);
void iowrite16_rep(void* addr, const void* buf, unsigned long count);
void iowrite32_rep(void* addr, const void* buf, unsigned long count);
其中,参数count表示为要读写的8-bit、16-bit或32-bit数据值的个数,即:数据总大小;参数addr是IO内存区的首地址;参数buf是数据缓存的首地址;
比如:ioread32_rep()函数的意思就是从IO内存区地址addr处开始,读取count个32-bit数据值到缓存buf中;iowrite32_rep()函数的意思就是把缓存buf中的count个32-bit数据值写到以地址addr处开始的IO内存区中;
4、存取IO内存块:
void memset_io(void* addr, u8 value, unsigned int count);
void memcpy_fromio(void* dst, void* src, unsigned int count);
void memcpy_toio(void* dst, void* src, unsigned int count);
5、旧版的IO内存读写:
u8 readb(void* addr);
u16 readw(void* addr);
u32 readl(void* addr);
void writeb(u8 value, void* addr);
void writew(u16 value, void* addr);
void writel(u32 value, void* addr);
另外,还有一些64位平台上也提供了函数readq()和writeq(),用于为PCI总线上的4字(8字节)数据的IO内存操作;
四、映射IO端口为IO内存:
一些硬件有一个有趣的特性:一些版本使用了IO端口,而其它版本的使用了IO内存;输出给处理器的寄存器在任一种情况中相同,但是存取方法不同;内核提供了一些函数用于使IO端口和IO内存存取的区别最小化;
void* ioport_map(unsigned long start, unsigned int count);
该函数用于从端口start开始重新映射count个端口到一块IO内存中,以让这个IO端口组能够像一块IO内存那样使用;映射之后,就可以使用ioread8()之类的函数对这个IO端口组进行存取了;
void ioport_unmap(void* addr);
该函数用于解除ioport_map()函数所做的映射;
这两个函数可以使IO端口看起来像IO内存;但是要注意,需要被映射的IO端口任然是通过request_region()函数申请,并通过release_region()函数释放的;
0 0
原创粉丝点击