微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口

时间:11-23 来源:互联网 点击:

,使其对驱动程序可见,这就意味着在进行任何I/O操作之前,你必须调用ioremap;如果不需要页表,I/O内存区域就类似于I/O端口,你可以直接使用适当的I/O函数读写它们。

由于边际效应的缘故,不管是否需要ioremap,都不鼓励直接使用I/O内存指针,而应使用专门的I/O内存操作函数。这些I/O内存操作函数不仅在所有平台上是安全,而且对直接使用指针操作I/O内存的情况进行了优化。

2.1、I/O内存分配和映射

I/O内存区在使用前必须先分配。分配内存区的函数接口在定义中:

/* request_mem_region分配一个开始于start,len字节的I/O内存区。分配成功,返回一个非NULL指针;否则返回NULL。系统当前所有I/O内存分配信息都在/proc/iomem文件中列出,你分配失败时,可以看看该文件,看谁先占用了该内存区 */
structresource*request_mem_region(unsignedlongstart,unsignedlonglen,char*name);
/* release_mem_region用于释放不再需要的I/O内存区*/
voidrelease_mem_region(unsignedlongstart,unsignedlonglen);
/* check_mem_region用于检查I/O内存区的可用性。同样,该函数不安全,不推荐使用 */
intcheck_mem_region(unsignedlongstart,unsignedlonglen);

在访问I/O内存之前,分配I/O内存并不是唯一要求的步骤,你还必须保证内核可存取该I/O内存。访问I/O内存并不只是简单解引用指针,在许多体系中,I/O内存无法以这种方式直接存取。因此,还必须通过ioremap函数设置一个映射。

#include
/*ioremap用于将I/O内存区映射到虚拟地址。参数phys_addr为要映射的I/O内存起始地址,参数size为要映射的I/O内存的大小,返回值为被映射到的虚拟地址 */
void*ioremap(unsignedlongphys_addr,unsignedlongsize);

/* ioremap_nocache为ioremap的无缓存版本。实际上,在大部分体系中,ioremap与ioremap_nocache的实现一样的,因为所有 I/O 内存都是在无缓存的内存地址空间中 */
void*ioremap_nocache(unsignedlongphys_addr,unsignedlongsize);
/* iounmap用于释放不再需要的映射 */
voidiounmap(void*addr);

经过ioremap(和iounmap)之后,设备驱动就可以存取任何I/O内存地址。注意,ioremap返回的地址不可以直接解引用;相反,应当使用内核提供的访问函数。

2.2、访问I/O内存

访问I/O内存的正确方式是通过一系列专门用于实现此目的的函数:

#include
/*I/O内存读函数。参数addr应当是从ioremap获得的地址(可能包含一个整型偏移); 返回值是从给定I/O内存读取到的值 */
unsignedintioread8(void*addr);
unsignedintioread16(void*addr);
unsignedintioread32(void*addr);
/*I/O内存写函数。参数addr同I/O内存读函数,参数value为要写的值 */
voidiowrite8(u8 value,void*addr);
voidiowrite16(u16 value,void*addr);
voidiowrite32(u32 value,void*addr);
/* 以下这些函数读和写一系列值到一个给定的 I/O 内存地址,从给定的buf读或写count个值到给定的addr。参数count表示要读写的数据个数,而不是字节大小 */
voidioread8_rep(void*addr,void*buf,unsignedlongcount);
voidioread16_rep(void*addr,void*buf,unsignedlongcount);
voidioread32_rep(void*addr,void*buf,unsignedlongcount);
voidiowrite8_rep(void*addr,constvoid*buf,unsignedlongcount);
voidiowrite16_rep(void*addr,constvoid*buf,unsignedlongcount);
voidiowrite32_rep(void*addr,,onstvoid*buf,,nsignedlongcount);
/* 需要操作一块I/O地址时,使用下列函数(这些函数的行为类似于它们的C库类似函数): */
voidmemset_io(void*addr,u8 value,unsignedintcount);
voidmemcpy_fromio(void*dest,void*source,unsignedintcount);
voidmemcpy_toio(void*dest,void*source,unsignedintcount);
/* 旧的I/O内存读写函数,不推荐使用 */
unsignedreadb(address);
unsignedreadw(address);
unsignedreadl(address);
voidwriteb(unsignedvalue,address);
voidwritew(unsignedvalue,address);
voidwritel(unsignedvalue,address);

2.3、像I/O内存一样使用端口

一些硬件有一个有趣的特性:有些版本使用I/O端口;而有些版本则使用I/O内存。不管是I/O端口还是I/O内存,处理器见到的设备寄存器都是相同的,只是访问方法不同。为了统一编程接口,使驱动程序易于编写,2.6内核提供了一个ioport_map函数:

/*ioport_map重新映射count个I/O端口,使它们看起来I/O内存。此后,驱动程序可以在ioport_map返回的地址上使用ioread8和同类函数。这样,就可以在编程时,消除了I/O端口和I/O 内存的区别*/
void*ioport_map(unsignedlongport,unsignedintcount);
/* ioport_unmap用于释放不再需要的映射 */
voidioport_unmap(void*addr);

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top