Linux 内核访问外设IO资源的方式

来源:互联网 发布:手机有些什么编程软件 编辑:程序博客网 时间:2024/05/22 03:00
首先介绍一下I/O端口和I/O内存。
1. I/O端口:当一个寄存器或内存位于I/O空间时,称其为I/O端口。
2. I/O内存:当一个寄存器或内存位于内存空间时,称其为I/O内存。
再来看一下I/O寄存器和常规内存的区别:I/O寄存器具有边际效应(side 
effect),而内存操作则没有,内存写操作的唯一结果就是在指定位置存贮一个
数值;内存读操作则仅仅是返回指定位置最后一次写入的数值。何为边际效应
呢?就是读取某个地址时可能导致该地址内容发生变化。比如很多设备的中断
状态寄存器只要一读取,便自动清零。
现在来看一看如何在Linux驱动程序中使用I/O端口和I/O内存。

使用I/O端口的步骤:

1.  申请
2.  访问
3.  释放
申请I/O端口:
在尚未对这些端口进行申请之前我们是不应该对这些端口进行操作的。内核为
我们提供了一个注册用的接口:

1. #include <linux/ioport.h>  
2. struct resource *request_region(unsigned long first, unsigned long n, const char *name);  
这个函数告诉内核,我们要使用起始于first的n个端口,参数name应该是设
备的名称。如果分配成功,则返回非NULL。如果request_region返回NULL,
那么我们就不能使用这些期望的端口。
访问I/O端口:
访问I/O端口时,多数硬件都会把8位,16位和32位的端口区分开。因此,C
语言程序中必须调用不同的函数来访问大小不同的端口。
1. unsigned inb(unsigned port);  
2. void outb(unsigned char byte, unsigned port);  
字节(8位宽度)读写端口。
1. unsigned inw(unsigned port);  
2. void outw(unsigned short word, unsigned port);  
读写16位端口。

1. unsigned inl(unsigned port);  
2. void outl(unsigned longword, unsigned port);  
  
读写32位端口。
释放I/O端口:
如果不在使用某组I/O端口(可能在卸载模块时),则应该使用下面的函数将
这些端口返回给系统:
 
1. void release_region(unsigned long start, unsigned long n);  
使用I/O内存的步骤:
1. 申请
2. 映射
3. 访问
4. 释放
根据计算机平台和所使用总线的不同,I/O内存可能是,也可能不是通过页表
访问的。如果访问是经由页表进行的,内核必须首先安排物理地址使其对设备
驱动程序可见(这通常意味着在进行任何I/O之前必须先调用ioremap)。如果
访问无需页表,那么I/O内存区域就非常类似于I/O端口,可以使用适当形式
的函数读写它们。
I/O内存申请:

1. struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);  
该函数从start开始分配len字节长的内存区域。如果成功,返回非NULL指针;
否则返回NULL值。
I/O内存映射:
1. #include<asm/io.h>  
2. void *ioremap(unsigned long phys_addr, unsigned long size);  
把物理地址转化成虚拟地址,返回值是虚拟地址。
1. void *iounmap(void *addr);  
解除映射。  
I/O内存访问:
从内存中读取:
 
1. unsigned int ioread8(void *addr);  
2. unsigned int ioread16(void *addr);  
3. unsigned int ioread32(void *addr);  

其中addr应该是从ioremap获得的地址。
还有一组写入I/O内存类似函数:
1. void iowrite8(u8 value, void *addr);   
2. void iowrite16(u16 value, void *addr);  
3. void iowrite32(u32 value, void *addr);  
I/O内存释放:
1. void release_mem_region(unsigned long start, unsigned long len);   
像I/O内存一样使用I/O端口:
1. void *ioport_map(unsigned long port, unsigned int count);  
该函数重新映射count个I/O端口,使其看起来像I/O内存。此后,驱动程序可
在该函数返回的地址上使用ioread8及其同类的函数。
当不再需要这种映射时,需要调用下面的函数来撤销:
1. void ioport_unmap(void *addr);  

0 0
原创粉丝点击