linux设备驱动程序(第三版)阅读笔记(九)

来源:互联网 发布:天津知味餐厅 编辑:程序博客网 时间:2024/05/01 19:50

 

说明:版权所有归作者,只供学习交流,若有其它用途请联系作者,转载请遵守IT人职业规范,请注明转载地址

 

 

第九章:与硬件通信

1,(寄存器与内存的区别)寄存器与内存的主要区别在于寄存器操作有副作用(side effect或边际效果):读取某个地址时可能导致该地址内容发生变化,比如很多设备中断状态寄存器只要一读取,便自动清零。

 

2,(I/O空间和内存)X86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言的,它们是彼此独立的地址空间,在32位的X86系统中,I/O空间大小为64K,内存空间大小为4G

 

3,(各处理器支持I/O内存空间情况)

X86支持内存空间和IO空间

ARM只支持内存空间

MIPS:只支持内存空间

PowerPC:只支持内存空间

 

4,(IO端口和IO内存)

IO端口:当一个寄存器或内存位于IO空间时,称其为IO端口。

IO内存:当一个寄存器或内存位于内存空间时称其为IO内存。

 

5,(操作I/O端口)I/O端口的操作需按如下步骤完成

1,申请    2,访问   3,释放

申请:内核提供了一套函数来充许驱动申请它需要的I/O端口,其中核心的函数是:

#include<linux/ioport.h>

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

这个函数告诉内核,你要使用从first开始的n个端口,name参数是设备的名字。如果申请成功,返回非NULL,申请失败,返回NULL

系统中端口的分配情况记录在/proc/ioports中。如果不能分配需要的端口,可以来这里查看谁在使用。

 

释放:如果不再使用某组I/O端口(可能在卸载模块时),则应该使用下面的函数将这些端口返回给系统:

void release_region(unsigned long start, unsigned long n)

下面的函数充许驱动程序检查给定的I/O端口集是否可用:

int check_region(unsigned long first, unsigned long n);

如果给定的端口不可用,则返回值是负的错误代码。我们不赞成使用这个函数,因为它的返回值并不能确保是否能够分配成功,这是因为,检查和其后的分配并不是原子的操作。我们再这里列出这个函数,是因为仍有一些驱动程序在使用它,但是我们应该始终使用request_region,因为这个函数执行了必要的锁定。

 

操作IO端口:当驱动程序请求了需要使用的I/O端口范围后,必须读取或写入这些端口。为此,大多数硬件都会把8位、16位和32位的端口区分开来。它们不能像访问系统内存那样混淆使用。

 

unsigned inb(unsignedport)(读)

void outb(unsigned charbyte, unsigned port);(写)

字节(8位宽度)读写端口。Port参数在一些平台上被定义为unsigned long,而在另一些平台上被定义为unsigned short。不同平台上inb返回值类型也不相同。

 

unsigned inw(unsigned port);

void outw(unsigned shortword, unsigned port);

这些函数用于访问16位端口(字宽度);不能用于S390平台,因为这个平台只支持字节宽度的I/O操作。

 

unsigned inl(unsigned port);

void outl(unsigned long word, unsigned port);

这些函数用于访问32位端口。longword参数根据不同平台被定义成unsigned long类型或unsigned int类型。和字宽度I/O一样,“长字”I/OS390平台上也不可使用。

 

注意:这里没有定义64位的I/O操作。即使在64位的体系架构上,端口地址空间也只使用最大32位的数据通路。

 

6,(操作IO内存):对IO内存的操作需要按一下四步完成:

(1)申请  2)映射    (3)访问   (4)释放

申请:

#include<linux/ioport.h>

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

该函数从start开始分配len字节长的内存区域。如果成功,返回非NULL指针;否则返回NULL值。所有的I/O内存分配情况均可从/proc/iomem得到。

 

释放:

void iounmap(void*addr);(解除映射)

void release_mem_region(unsigned long start, unsigned long len);(释放I/O内存)

 

下面是用来检查给定的I/O内存区域是否可用的老函数:

int check_mem_region(unsigned long start, unsigned long len);

但是,和check_region一样,这个函数不安全,应避免使用。

 

映射:在访问I/O内存之前,必须进行物理地址到虚拟地址的映射,ioremap函数具有此功能:

#include<asm/io.h>

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

 

访问:在某些平台上,我们可以将ioremap的返回值直接当作指针使用。但是,这种使用不具有可移植性,而内核开发者正在致力于减小这类使用。访问I/O内存的正确方法是通过一组专用于此目的的函数(在<asm/io.h>中定义)。

要从I/O内存中读取,可使用下面函数之一:

Unsigned int ioread8(void *addr);

Unsigned int ioread16(void *addr);

Unsigned int ioread32(void *addr);

其中,addr应该是从ioremap获得的地址(可能包含一个整数偏移量);返回值则是从给定I/O内存读取到的值。

 

写入I/O内存的函数:

Void iowrite8(u8, void*addr);

Void iowrite16(u16, void*addr);

Void iowrite32(u32, void*addr);

 

如果必须在给定的I/O内存地址处读写一系列的值,则可使用上述函数的重复版本:

Void ioread8_rep(void *addr, void  *buf, unsigned long count);

Void ioread16_rep(void *addr, void  *buf, unsigned long count);


 

Void ioread32_rep(void *addr, void *buf, unsigned long count);


 

Void iowrite8_rep(void *addr, const void  *buf, unsigned long count);


 

Void iowrite16_rep(void *addr, const void  *buf, unsigned long count);


 

Void iowrite32_rep(void *addr, const void  *buf, unsigned long count);

上述函数从给定的buf向给定的addr读取或写入count个值。注意,count以被写入的数据大小为单位表示,比如,ioread32_repaddr中读取count32位值到buf中。

 

上面给出的函数均在给定的addr处执行所有的I/O操作。如果我们要在一块I/O上执行操作,则可以使用下面的函数之一:

Void memset_io(void *addr, u8 value, unsigned int count);

Void memcpy_fromio(void *dest, void *source, unsigned int count);

Void memcpy_toio(void *dest, void  *source, unsigned int count);


 

 

 

如果读者阅读内核源代码,可能会遇到一组老的I/O内存函数。这些函数仍能工作,但不鼓励在新的代码中使用这些函数。主要原因是这些函数不执行类型检查,因此安全性较差。这些函数(宏)的原型如下:

Unsigned readb(address);

Unsigned readw(address);

Unsigned readl(address);

 

Unsigned writeb(address);

Unsigned writew(address);

Unsigned writel(address);

 

原创粉丝点击