📄 (ldd) ch15-外围总线概览(转载).txt
字号:
(LDD) Ch15-外围总线概览(转载)
第十五章 外围总线概览
在第八章“硬件管理”中,我们介绍了最低级的硬件控制,本章提供一个较高级的总线
体系结构的概览。总线由电气接口和编程接口组成。在这一章,我打算介绍编程接口。
本章覆盖了几种总线体系结构。不过,基本重点是访问PCI外围的核心功能,因为近来,
PCI总线是最常用的外围总线,也是核心支持最好的总线。
PCI接口
PCI接口
尽管很多计算机用户认为PCI(外围部件互连,Peripheral Component Interconnect)
是布局电气线路的一种方法,但实际上,它是一组完全的规范,定义了计算机的不同部
分是如何交互的。
PCI规范覆盖了与计算机接口相关的绝大多数方面。我不打算在这里全部介绍,在本节中
,我主要关心一个PCI驱动程序是如何找到它的硬件,并获得对它的访问的。在第二章“
构造和运行模块”的“自动和手工配置”一节,及在第九章“中断处理”的“自动检测
中断号”一节中讨论过的探测技术同样可以应用于PCI设备,但规范还提供了探测的另外
办法。
PCI结构被设计来替代ISA标准,由三个主要目标:在计算机和其外围之间传送数据时有
更高的性能,尽可能地做到平台无关性,使在系统中增减外围设备得到简化。
PCI通过使用比ISA高的时钟频率来获得更高的性能;它的时钟运行在25或33MHZ(实际时
钟是系统时钟的几分之一的整数倍),而且马上就会游66MHZ的扩展。另外,它被装配在
32位的数据总线上,64位的扩展正在规范中。平台无关性一直是计算机总线的一个设计
目标,这是PCI的尤其重要的一个特征,因为PC世界一直以来总是被处理器特定的标准所
主宰。
不过对驱动程序作者来说,最要紧的是对接口板自动检测的支持。PCI设备是无跳线的(
与大多数ISA外围不同),并且在引导时被自动配置。因此,设备驱动程序必须能访问设
备上的配置信息来完成初始化。这些情形都不需要任何探测。
PCI寻址
每个外围由一个总线号、一个设备号、和一个功能号确定。虽然PCI规范允许一个系统最
多拥有256条总线,但PC只有一条。每条总线最多带32个设备,但每个设备可以是最多个
功能的多功能板(如一个音频设备带一个CD-ROM驱动器)。每个功能可以由一个16位的
键或两个8位的键确定。Linux核心采用后一种方法。
每个外围板子的硬件电路回答与三个地址空间相关的询问:内存位置,I/O端口,和配置
寄存器。前两个地址空间由PCI总线上的所有设备共享(也就是说,当你访问一个内存位
置,所有的设备都将同时看到这个总线周期)。而配置空间则利用“地理寻址”,每个
槽有一个配置事务的私用使能线,PCI控制器一次访问一个板子,不会有地址冲突。考虑
到驱动程序,内存和I/O是以通常的inb,memcpy等来访问。而配置事务则通过调用特定的
核心函数访问配置寄存器来完成。至于中断,每个PCI设备有4个中断管脚,它们到处理
器中断线的路由是主板的任务;PCI中断可以设计为共享的,这样即使是一个有限中断线
的处理器也能带很多PCI接口板。
PCI总线的I/O空间使用32位的地址总线(这样就是4GB的I/O端口),而内存空间则可以
用32位或64位地址访问。地址对每个设备来说应该是唯一的,但也有可能有两个设备错
误地映射到同一个地址,使得哪个都不能被访问。一个好消息是接口卡提供的每个内存
和I/O地址区段都可以通过配置事务重映射。这就是设备可以在引导时被初始化从而避免
地址冲突的机制这些区段当前映射到的地址可以从配置空间读出,因此Linux驱动程序可
以不通过探测就访问其设备。一旦配置寄存器被读出,驱动程序就可以安全的访问它的
硬件。
PCI配置空间由每个设备函数256个字节构成,配置寄存器的布局是标准化的。配置空间
有四个字节含有一个唯一的函数ID,因此驱动程序可以通过在外围查找特定的ID来B确定
它的设备*。总之,每个设备板子被地理寻址以取得它的配置寄存器;这个信息可以用来
确定这个板子或采取进一步动作。
从前面的描述,应该清楚PCI接口标准比ISA的主要创新是配置地址空间。因此,除了通
常的驱动程序代码外,PCI驱动程序还需要访问配置空间的能力。
在本章的其余部分,我将使用单词“设备”来指一个设备功能,因为多功能板上的每个
功能均是一个独立的实体。当我提到一个设备,我是指元组“总线号,设备号,功能号
”。如前所述,每个元组在Linux中由两个8位数字表示。
引导时
让我们看一下PCI是如何工作的,从系统引导开始,因为那时设备被配置。
当PCI设备被加电时,硬件关闭。或者说,设备只对配置事务响应。加电时,设备没有映
射到计算机地址空间的内存和I/O端口;所有其它的设备特定的特征,象中断线,也都被
关闭。
幸运的是,每个PCI母板都装有懂得PCI 的固件,根据平台的不同被称做BIOS、NVRAM、
或PROM。固件提供对设备配置地址空间的访问,即使处理器的指令集不提供这样的能力
。
在系统引导时,固件对每个PCI外围执行配置事务,从而为它提供的任何地址区段分配一
个安全的地方。到设备驱动程序访问设备时,它的内存和I/O区段已经被映射到处理器的
地址空间。驱动程序可以改变这个缺省的分配,但它通常并不这样做,除非有一些设备
相关的原因要求这样。
在Linux中,用户可以通过读/proc/pci来查看PCI 设备,这是个文本文件,系统中每个P
CI板子有一项。下面是/proc/pci中一项的例子:
(代码344)
/proc/pci中每一项是一个设备的设备无关特征的概述,如它的配置寄存器所描述的。例
如,上面这一项告诉我们这个设备有板上内存,已被映射到地址0xf1000000。一些古怪
的细节的含义以后在我介绍过配置寄存器后将会清楚。
检测设备
如前面提到的,配置空间的布局是设备无关的。在这一节,我们将看看用来确定外围的
配置寄存器。
PCI设备有一个256字节的地址空间。前64个字节是标准化的,而其余的则是设备相关的
。图15-1显示了设备无关配置空间的布局。
如图所示,有些PCI的配置寄存器是要求的,而有些则是可选的。每个PCI设备必须在必
要寄存器中包含有意义的值,而可选寄存器的内容则以来与实际外围的能力。可选域并
不使用,除非必要域的内容表明它们是有效的。这样,必要域断言了板子的能力,包括
其它域可用与否。
有意思的是注意到PCI寄存器总是小印地安字节顺序的。尽管标准要设计为体系结构无关
的,PCI的设计者有时还是显示出对PC环境的偏见。驱动程序的作者应该留神字节顺序,
特别是访问多字节的配置寄存器时;在PC上工作的代码可能在别的平台上就不行。Linux
特别是访问多字节的配置寄存器时;在PC上工作的代码可能在别的平台上就不行。Linux
的开发者已经注意了字节排序问题(见下一节“访问配置空间”),但这个问题还是要
牢记在心。不幸的是,标准函数ntohs和ntohl都不能用,因为网络字节顺序与PCI顺序相
反;在Linux2.0中没有标准函数将PCI字节顺序转换为主机字节顺序,每个用单个字节构
成多字节值的驱动程序都应该特别小心地正确处理印地安字节序。核心版本2.1.10引入
了几个函数来处理这些字节顺序问题,它们在第十七章“最近的发展”中“转换函数”
一节介绍。
(图15-1:标准化的PCI配置寄存器)
描述所有的配置项超出了本书的范围。通常,与设备一起发布的技术文档会描述它支持
的寄存器。我们感兴趣的是驱动程序如何找到它的设备,以及它如何访问设备的配置空
间。
三个PCI寄存器确定一个设备:销售商,设备ID,和类。每个PCI外围把它自己的值放入
这些只读寄存器,驱动程序可以用它们来查找设备。让我们更仔细地看看这些寄存器:
销售商
这个16位的寄存器确定硬件的生产商。例如,每个Intel的设备都会标上同样的销售商号
,8086 hex(是个随即值?)。这样的号码有一个全球的注册,生产商必须申请一个唯
一的号。
设备ID
这是另一个16位寄存器,由生产商选择;不需要有官方的注册。这个ID通常与销售商ID
成对出现,形成一个硬件设备的唯一的32位标志符。我将用单词“签名”来指销售商/设
备ID对。一个设备驱动程序经常以来于签名来确定它的设备;驱动程序的作者从硬件文
档中知道要寻找什么值。
类
每个外围设备都属于一个类。类寄存器是个16位的值,它的高八位确定 “基类”(或组
)。例如,“以太网”和“令牌环”是属于“网络”组的两类,而“串行”和“并行”
类属于“通信”组。有些驱动程序可以支持几种类似的设备,它们虽然有不同的签名,
却属于同一类;这些驱动程序可以依赖于类寄存器来确定它们的外围,如以后所示。
下面的头文件,宏,以及函数都将被PCI驱动程序用来寻找它的硬件设备:
#include <linux/config.h>
驱动程序需要知道是否PCI函数在核心是可用的。通过包含这个头文件,驱动程序获得了
对CONFIG_宏的访问,包括CONFIG_PCI(将在下面介绍)。从1.3.73以来,这个头文件包含
对CONFIG_宏的访问,包括CONFIG_PCI(将在下面介绍)。从1.3.73以来,这个头文件包含
在<linux/fs.h>中;如果想向后兼容,你必须把它显式地包含。
CONFIG_PCI
如果核心包括对PCI BIOS调用的支持,那么这个宏被定义。并不是每个计算机都有PCI总
线,所以核心的开发者应该把 PCI的支持做成编译时选项,从而在无PCI的计算机上运行
Linux时节省内存。如果CONFIG_PCI没有定义,那么这个列表中其它的函数都不可用,驱
动程序应使用预编译的条件语句将PCI支持全都排除在外,以避免加载时的“未定义符号
”错。
#include <linux/bios32.h>
这个头文件声明了本节介绍的所有的原型,因此一定要被包含。这个头文件还定义了函
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -