您的位置:首页 > 运维架构 > Linux

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()。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: