与硬件的通信:IO端口与IO内存

来源:互联网 发布:国家地名数据库 编辑:程序博客网 时间:2024/05/20 15:38

不同的体系架构对IO空间的编址不一样,有的是与内存空间同意编址,如arm,有些采用独立编址,如x86。

对外设的操作一般是通过寄存器来控制的,外设寄存器也称为IO端口。对于统一编址的架构,我们称外设寄存器为IO端口,独立编址的称为IO内存,对其操作有专用的指令。

1 使用IO端口

1 .1  分配IO端口

在驱动还没独占设备之前,不应对端口进行操作。内核提供了一个注册接口,以允许驱动声明其需要的端口:

#include <linux/ioport.h>

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

request_region告诉内核:要使用first开始的n个端口。参数name为设备名。如果分配成功返回值是非NULL;否则无法使用需要的端口(/proc/ioports包含了系统当前所有端口的分配信息,若request_region分配失败时,可以查看该文件,看谁先用了你要的端口。

用完I/O端口后(可能在模块卸载时),应当调用release_region将I/O端口返还给系统。参数start和n应与之前传递给request_region一致

void release_region(unsigned long start, unsigned long n); 

1.2 操作IO端口

在驱动成功请求到I/O 端口后,就可以读写这些端口了。大部分硬件会将8位、16位和32位端口区分开,无法像访问内存那样混淆使用。驱动程序必须调用不同的函数来访问不同大小的端口。


1.3 像IO内存一样使用IO端口

为了统一编程接口,使驱动程序易于编写,2.6内核提供了一个ioport_map函数:

oid *ioport_map(unsigned long port, unsigned int count); 

 ioport_map重新映射countI/O端口,使它们看起来I/O内存。此后,驱动程序可以在ioport_map返回的地址上使用ioread8和同类函数。这样,就可以在编程时,消除了I/O端口和I/O 内存的区别 。

ioport_unmap用于释放不再需要的映射
void ioport_unmap(void *addr);

注意,I/O 端口在重新映射前必须使用request_region分配所需的I/O 端口。

2 操作IO内存

尽管 I/O 端口在x86世界中非常流行,但是用来和设备通讯的主要机制是通过内存映射的寄存器和设备内存,两者都称为I/O 内存,因为寄存器和内存之间的区别对软件是透明的。

I/O 内存仅仅是一个类似于RAM 的区域,处理器通过总线访问该区域,以实现对设备的访问。同样,读写这个区域是有边际效应

根据计算机体系和总线不同,I/O 内存可分为可以或者不可以通过页表来存取。若通过页表存取,内核必须先重新编排物理地址,使其对驱动程序可见,这就意味着在进行任何I/O操作之前,你必须调用ioremap;如果不需要页表,I/O内存区域就类似于I/O端口,你可以直接使用适当的I/O函数读写它们。

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

2.1 分配IO内存

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

 request_mem_region分配一个开始于start,len字节的I/O内存区。分配成功,返回一个非NULL指针;否则返回NULL。系统当前所有I/O内存分配信息都在/proc/iomem文件中列出,你分配失败时,可以看看该文件,看谁先占用了该内存区 

release_mem_region用于释放不再需要的I/O内存区 *
void release_mem_region(unsigned long start, unsigned long len); 

2.2 映射IO内存

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

void *ioremap(unsigned long phys_addr, unsigned long size);

ioremap用于将I/O内存区映射到虚拟地址。参数phys_addr为要映射的I/O内存起始地址,参数size为要映射的I/O内存的大小,返回值为被映射到的虚拟地址,经过 ioremap (iounmap)之后,设备驱动就可以存取任何I/O内存地址。

2.3 访问IO内存

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



注意:

1)、所有的读写指令(I/O操作函数)所赋的地址必须都是虚拟地址,你有两种选择:使用内核已经定义好的地址,如在include/asm-arm/arch-s3c2410/regs-xxx.h中定义了s3c2410处理器各外设寄存器地址(其他处理器芯片也可在类似路径找到内核定义好的外设寄存器的虚拟地址;另一种方法就是使用自己用ioremap映射的虚拟地址。绝对不能使用实际的物理地址,否则会因为内核无法处理地址而出现oops。

2)、在使用I/O指令时,可以不使用request_region和request_mem_region,而直接使用outb、ioread等指令。因为request的功能只是告诉内核端口被谁占用了,如再次request,内核会制止(资源busy)。但是不推荐这么做,这样的代码也不规范,可能会引起并发问题(很多时候我们都需要独占设备)。

3)、在使用I/O指令时,所赋的地址数据有时必须通过强制类型转换为 unsigned long,不然会有警告。

4)、在include\asm-arm\arch-s3c2410\hardware.h中定义了很多io口的操作函数,有需要可以在驱动中直接使用,很方便。


0 0
原创粉丝点击