上 编写Linux网络设备驱动( 二 )


注:本小节摘自Alan Cox的《Bus-》
Linux提供了一个API集(下文称为[设备操作API]),抽象所有总线和设备的I/O操作,使设备驱动程序的编写独立于总线类型 。
4.1 内存映射的I/O
最广泛支持的I/O的操作是[内存映射I/O] 。[内存映射I/O]是指,部分的CPU地址空间被解释为访问设备,而不是访问内存 。一些体系结构为[内存映射I/O]的设备定义了固定的地址,但大多数体系提供了检测设备地址的方法 。PCI总线是很好的例子 。本文不教你如何获得一个设备地址,假设你已经知道设备地址 。
物理地址是 long类型,你不能直接使用这些地址 。你应该调用,来获得一个适合(传递给下面函数)的虚拟地址 。当你使用完的设备(比如模块卸载),必须调用以返还虚拟地址给内核 。
4.2 访问设备
在Linux提供[设备操作API]中,驱动程序最常用的接口是访问的设备寄存器的读和写函数 。Linux提供了读取和写入8位,16位,32位和64位量的函数,分别为 byte, word, long, 和 quad,函数命名readb,readw,readl,readq,,,和 。
有些设备(如帧缓冲)更倾向一次内发起超过8个字节的传输 。对于这些设备,可使用,和功能 。不要使用或对I/O地址操作,因为它们不能保证[按顺序]复制数据 。
[设备操作API]中的读写函数是假设严格[按照源码字面顺序]执行的,编译器不能对它进行乱序优化 。如果希望设备读写有一定的优化,可使用原始的函数(等原始无抽象的函数) 。但是要非常小心,要在适当的地方插入内存屏障指令——rmb()/wmb() 。
4.3 独立端口IO
另外一种常用的IO操作是[独立端口IO] 。端口IO的地址是独立于内存地址空间的,端口IO的访问速度不如内存映射I/O,地址空间也小很多 。不过,不像内存映射I/O,访问端口IO的设备相对直观,不需要考虑以上提到的一些问题 。
【上编写Linux网络设备驱动】[设备操作API]中提供了访问端口IO的函数,分别操作字节(byte)、双字(word)和四字(long):inb, inw, inl, outb, outw 和 outl 。
以上函数还有提供给慢速设备的变种:后加一“_p”;还有类似功能的ins 和 outs 。
5.理解PCI配置空间
是一支PCI接口设备,PCI是一种通用的扩展总线,而非与CPU体系相关的本地总线(local bus),从而CPU不能直接对寻址访问,必须经PCI总线控制器转译 。PCI总线设计实现的核心是PCI总线控制器(有的地方译为PIC主桥,PCI Host ),它将整个系统划分两个数字通信域,两个域独立编址 。一个是原来CPU与内存和设备通信的[CPU域],一个是PCI总线控制器的(因它本身就是一CPU),这里称为[PCI总线域] 。为了跨越两域通信,系统将CPU域划出一个“”——将CPU部分寻址空间划给PCI总线用 。PCI总线控制器对这部分地址进行管理,实现即插即用等一些现代总线功能 。而所谓的[配置空间]只是配合PCI总线控制器实现地址管理提供必要的状态信息[注] 。
注:个人觉得[配置空间]用“空间”一词欠佳,容易混淆其它地址空间概念,增加理解PCI总线原理的难度 。
[配置空间]是每支PCI设备(包括PCI桥)集成一集寄存器,[配置空间]是面向PCI总线控制器而言的,此空间的基地址是PCI设备的拓扑位置(总线号/设备号/功能号) 。PCI定义每支PCI设备的[配置空间]为256字节,如下图,其中最前面的64个字节已由标准定义,余下的空间由设备自定义 。
围绕[配置空间]有两种事务和三种操作角色,事务是[配置]和[使用配置],角色有静态配置的厂商和动态配置的操作系统,还有使用配置的设备驱动 。静态配置的例子,如厂商在设备生产时配置其 ID和 ID;动态配置的例子,如操作系统初始化代码根据PCI设备的拓扑位置,配置设备的基地址(Base ~5)[注] 。