linux驱动-I/O
2017-02-11 20:31
106 查看
简述:
I/O是CPU和外部设备通信一种方式。linux将I/O分成两种,一种是I/O端口,另一种是I/O内存。I/O端口:
I/O端口,是指对外部设备的访问,不能通过寻址方式访问,是一种特殊的,依赖CPU体系结构,操作I/O端口,是需要用CPU体系实现的接口。linux对I/O的端口,一般是要经过分配,然后操作端口(即读写端口):
1、I/O端口分配
(1)包含头文件
#include <linux/ioport.h>
(2)分配I/O端口的接口
分配用宏request_region:
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0) extern struct resource * __request_region(struct resource *, resource_size_t start, resource_size_t n, const char *name, int flags);
功能:分配端口,分配之后,在没有释放前,有其他地方再对I/O端口再次分配,将会失败。
第一个参数:start起始端口号
第二个参数:分配以start开始在内的,n个端口
第三个参数:此次申请分配端口,取名字name(字符串),以便查询。
返回值:返回struct resource结构体指针。
2、释放I/O端口
(1)包含头文件
#include <linux/ioport.h>
(2)释放I/O端口的接口
释放接口宏release_region:
#define release_region(start,n) __release_region(&ioport_resource, (start), (n)) extern void __release_region(struct resource *, resource_size_t, resource_size_t);
功能:释放分配的I/O端口
第一个参数:start起始端口
第二个参数:释放以start起始n个连续的端口
3、操作I/O端口(读写端口)
(1)包含头文件
#include <asm/io.h>
(2)操作I/O端口函数:
u8 inb(unsigned long addr);//读8位数据(一个字节),addr端口号,返回值是读回的值 u16 inw(unsigned long addr);//读16位数据,addr是端口号,返回值是读回的值 u32 inl(unsigned long addr);//读32位数据,addr是端口号,返回值是读回的值 void outb(u8 b, unsigned long addr);//向端口写8位数据,b是数据,addr是端口号 void outw(u16 b, unsigned long addr);//向端口写16位数据,b是数据,addr是端口号 void outl(u32 b, unsigned long addr);//向端口写32位数据,b是值数据,addr是端口号 void insb(unsigned long addr, void *buffer, int count); //向端口addr,读count个8位数据(字节),数据读出来储存在buffer里面。 void insw(unsigned long addr, void *buffer, int count); //向端口addr,读count个16位数据,数据读出来储存在buffer里面。 void insl(unsigned long addr, void *buffer, int count); //向端口addr,读count个32位数据,数据读出来储存在buffer里面。 void outsb(unsigned long addr, const void *buffer, int count); //向端口addr,写count个8位数据(字节),要写的数据储存在buffer。 void outsw(unsigned long addr, const void *buffer, int count); //向端口addr,写count个16位数据,要写的数据储存在buffer。 void outsl(unsigned long addr, const void *buffer, int count); //向端口addr,写count个32位数据,要写的数据储存在buffer。
addr就是上面request_region申请端口范围,其中一个端口。
I/O内存
I/O内存,是指对外部设备访问,是通过寄存器实现,而寄存器在CPU可寻址空间范围,即寄存器读写,就是对某个地址的读写。1、I/O内存分配
(1)包含头文件:
#include <linux/ioport.h>
(2)I/O内存映射:
I/O内存,在申请前,需要将I/O内存地址(是物理地址)映射成内核虚拟地址。
不在ioport.h里面,要包含下面的头文件:
#include <asm/io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);
phys_addr:I/O起始物理地址
size:起始物理地址开始,空间大小size,这段物理空间
返回值:就是映射后的内核虚拟起始地址。
void iounmap(void * addr);
取消映射,addr是映射的起始内核虚地址。
(3)申请分配I/O内存的接口:
接口request_mem_region
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0) extern struct resource * __request_region(struct resource *, resource_size_t start, resource_size_t n, const char *name, int flags);
功能:分配I/O内存后,在没有释放前,有其他地方再对I/O内存再次分配,将会失败。
第一个参数:start是ioremap返回的内核虚拟地址
第二个参数:分配以start开始在内的,n个端口
第三个参数:次申请分配端口,取名字name(字符串),以便查询。
返回值:返回struct resource结构体指针。
2、释放I/O内存的接口
接口release_mem_region:
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n)) extern struct resource * __request_region(struct resource *, resource_size_t start, resource_size_t n, const char *name, int flags);
功能:释放分配的I/O内存
第一个参数:start是ioremap返回的内核虚拟地址
第二个参数:释放以start起始n个连续的端口
3、操作I/O端口(读写端口)
(1)包含头文件:
#include <asm/io.h>
(2)操作I/O内存函数:
unsigned int ioread8(void *addr);//读取地址addr,8位数据,返回值是读取的值 unsigned int ioread16(void *addr);//读取地址addr,16位数据,返回值是读取的值 unsigned int ioread32(void *addr);//读取地址addr,32位数据,返回值是读取的值 void iowrite8(u8 value, void *addr);//向地址addr写,8位数据,value是要写的值 void iowrite16(u16 value, void *addr);//向地址addr写,16位数据,value是要写的值 void iowrite32(u32 value, void *addr);//向地址addr写,32位数据,value是要写的值 void ioread8_rep(void *addr, void *buf, unsigned long count); //向地址addr读取,count个8位数据,读取的值储存在buf里面 void ioread16_rep(void *addr, void *buf, unsigned long count); //向地址addr读取,count个16位数据,读取的值储存在buf里面 void ioread32_rep(void *addr, void *buf, unsigned long count); //向地址addr读取,count个32位数据,读取的值储存在buf里面 void iowrite8_rep(void *addr, const void *buf, unsigned long count); //向地址addr写,count个8位数据,要写的值储存在buf里面 void iowrite16_rep(void *addr, const void *buf, unsigned long count); //向地址addr写,count个16位数据,要写的值储存在buf里面 void iowrite32_rep(void *addr, const void *buf, unsigned long count); //向地址addr写,count个32位数据,要写的值储存在buf里面
addr是request_mem_region申请的地址范围(start到start+n-1范围里)
以上函数,其实在io.h里面定义的是宏,但是,宏的形参和返回值,类型正如上面函数参数和返回值。
现在,很多I/O口的访问,都是通过读写寄存器地址,而寄存器地址都是在CPU寻址范围内,所以用I/O内存的更多,在这种情况下I/O端口和I/O内存是通用的。但更建议用I/O内存。
内存屏障
由于编译器优化,会出现部分指令执行顺序,不是按照设计的顺序执行,这种改变如果会严重影响结果,此时就需要用内存屏障。如:
writel(dev->registers.addr, io_destination_address); writel(dev->registers.size, io_size); writel(dev->registers.operation, DEV_READ); wmb(); writel(dev->registers.control, DEV_GO);
wmb()是写内存屏障,如果不加wmb(),那么有可能writel(dev->registers.control, DEV_GO)
比writel(dev->registers.operation, DEV_READ)先运行。加上wmb(),就保证了不会出现这种情况,就能保证wmb()上面的,一定比wmb()下面的先调用。
内存屏障函数定义在include/asm-generic/barrier.h
void barrier(void);//基本内存屏障,通用的 void rmb(void);//读I/O时,用的内存屏障 void wmb(void);//写I/O时,用的内存屏障 void mb(void);//可以代替rmb(),wmb()
读写I/O,建议用对应读写内存屏障,除I/O读写的,建议用barrier()。
相关文章推荐
- Microwindow嵌入试GUI和Linux驱动间的关系(5-20)
- ATI 新动作:Linux 驱动及新催化剂驱动
- 硬件全攻略--MODEM linux内猫配置 驱动软猫
- 如何编写Linux的LCD驱动
- Linux驱动入门
- 实例解读 linux 网卡驱动
- linux设备驱动
- Linux下nvidia驱动的安装技巧
- linux 设备驱动编程
- 星网锐捷通讯5800UB猫的linux驱动,继续解决
- ATI称将推出支持Linux操作系统的驱动
- Linux驱动
- 聆听自由的声音----Linux下声卡驱动软件ALSA的安装与配置
- Linux 2.6 驱动设计快速入门!
- Linux下声卡驱动软件ALSA的安装与配置
- 在Linux下如何驱动联想D-link DFE-530TX(也叫D-link DFE-530TX Rev B)网卡
- Linux下的硬件驱动——USB设备(上)(驱动配置部分)
- linux下的ntfs驱动
- Linux下的硬件驱动——USB设备
- 基于嵌入式linux的usb摄像头的驱动及采集程序的实现