您的位置:首页 > 其它

访问I/O内存和I/O端口设备

2015-08-04 22:25 260 查看
前面为了写pwm驱动,仔细研究了下I/O内存和I/O端口设备的区别,以及访问方式。不过,其实也没必要纠结这个了,因为现在绝大部分设备都使用I/O内存映射的。

I/O独立编址和I/O统一编址

首先有两个概念:I/O独立编址和I/O统一编址;记住这两种编址方式都是由CPU架构决定的。

I/O独立编址:应该只有X86处理器才是I/O独立编址,其他的处理器基本都是统一编址了。所谓的I/O独立编址就是处理器上的所有物理地址(如果开启了MMU则为所有虚拟地址)都分配给总线和内存(可以理解为内存条上)。而 不 分配地址给外设,外设有自己的一套编址方式。所以有内存空间和I/O空间的叫法;

I/O统一编址:就是在分配地址时会留一部分地址给外设用。

内存空间和I/O空间

每种外设都是通过读写寄存器进行控制的,大部分外设都有几个寄存器,不管是在内存地址空间还是在I/O地址空间,这些寄存器的访问地址都是连续的。在硬件层,内存区域和I/O区域没有概念上的区别:他们都是通过向地址总线和控制总线发送电平信号进行访问,再通过数据总线读写数据;




上面简单的图说明了内存区域(memory)和I/O区域(I/O),他们都是通过地址总线(address)、控制总线(controller)、数据总线(data)发送信号来控制外设的。还有些处理器为I/O空间读写提供了独立的总线(上图是I/O空间没有独立的总线,和内存区域共用总线);

I/O端口映射和内存映射

I/O端口映射:这是在I/O独立编址处理器上映射的,把I/O端口映射到I/O空间,然后通过特殊指令去访问该空间的数据。系统会专门定义一些特殊指令来访问I/O空间:in、out;而系统也为这些指令封装成一些简单的函数:inb()、inw()、outb()、outw()。。。等;

内存映射:这是在统一编址的处理器上操作的。把外设寄存器和内存映射到系统的内存中,外设内存映射到系统内存中有点不一样,可以看看PCI设备,后期会分析下。

对外设的访问操作

端口映射访问:

头文件:#include <linux/ioport.h>

struct resource *request_region(unsigned long first, unsigned long n, const char* name);

向内核注册需要使用的接口,first 使用端口的起始地址;n 注册n个端口;name 设备名称;可以从/proc/ioports中查看到端口分配的情况;

不再使用该I/O端口:void release_region(unsigned long start, unsigned long n);

#######################################################################################################

对端口的访问不能向内存那样使用一样的访问方式,硬件把8位、16位、32位端口区分开来;

头文件: #include<asm/io.h>

unsigned inb(unsigned port); ===== void outb(unsigned char byte, unsigned port);

unsigned inw(unsigned port); ===== void outw(unsinged short word, unsigned port);

内存映射访问:

头文件:#include<linux/ioport.h>

struct resource* request_mem_region(unsigned long start, unsigned long len, char *name);表示从start开始分配len个字节长的内存区域。I/O内存分配情况可以从/proc/iomem中得到;

void release_mem_region(unsigned long start, unsigned long len);释放接口;

######################################################################################################

注册完后,接下来就是映射到内存中和访问该段内存了。其实有的系统支持直接访问注册时得到的内存地址,但是这样不安全,也不利用代码移植,所以有些系统干脆就直接报错来阻止这种访问方式;

映射到内存中,通过下面函数:

void *ioremap(unsigned long phys_addr, unsigned long size);这个函数就是把虚拟地址和物理地址映射起来,一般会动用页表。这个后面linux内核内存会分析下。这里不一样的就是ioremap映射的物理地址不是系统的,而是外设的。释放映射:void iounmap(void *addr);

访问函数:

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);



转载地址:/article/1530561.html

如果有理解不正确的地方,欢迎指正!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: