前面为了写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);
转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/47111647
如果有理解不正确的地方,欢迎指正!!
版权声明:本文为博主原创文章,未经博主允许不得转载。