📄 (ldd) ch15-外围总线概览(转载).txt
字号:
pcidata和pcidump,与grep配合使用,对调试驱动程序的初始化代码非常有用。不过注
意,pcidata.c模块是GPL的,因为我是从核心源码中取的PCI扫描循环。这不应该对你作
意,pcidata.c模块是GPL的,因为我是从核心源码中取的PCI扫描循环。这不应该对你作
为一个驱动程序的作者有什么影响,因为我只是以一个支持工具的形式将这个模块包含
在源文件中,而不是新驱动程序的可重用模版。
访问I/O和内存空间
一个PCI外围实现六个地址区段。每个区段由内存或I/O位置组成,或者压根不存在。大
多数设备用一个内存区段代替它们的I/O端口,因为有些处理器(象Alpha)没有本身的I
/O空间,还因为PC上的I/O空间都相当拥挤。内存和I/O空间的结构化的不同通过实现一
个“内存可预取”位*来表达。将其控制寄存器映射到内存地址范围的外围将这个范围声
明为不可预取的,而PCI板子上的有些东西如视频内存是可预取的。在本节中,只要讨论
适用于内存或I/O,我就用单词“区段”来指一个PCI地址范围。
一个接口板子用配置寄存器(在图15-1中所示的6个32位寄存器,它们的符号名从PCI_BA
SE_ADDRESS_0到PCI_BASE_ADDRESS_5)报告它的区段的大小和当前位置。由于PCI定义的
I/O空间是一个32位的地址空间,因此用对内存和I/O适用同样的配置接口是可行的。如
果设备使用64位的地址总线,它可以为每个区段用两个连续的PCI_BASE_ADDRESS寄存器
在64位的内存空间来声明区段。因此有可能一个设备同时提供32位和64位的区段。
我不想在这儿讨论太多的细节,因为如果你打算写一个PCI驱动程序,你总会这个设备的
硬件手册的。特别地,我不打算使用寄存器的预取位或两个“类型”位,并且我将讨论
硬件手册的。特别地,我不打算使用寄存器的预取位或两个“类型”位,并且我将讨论
限制在32位外围上。不过,了解一下一般情况下是如何实现的,以及Linux驱动程序是如
何处理PCI内存是很有趣的。
PCI规范要求每个被实现的区段百升微被映射到一个可配置地址上。这意味着设备必须位
它实现的每个区段装备一个可编程32位解码器,并且利用64位PCI扩展的板子必须有一个
4位可编程解码器。尽管在PC上没有64位PCI总线,一些Alpha工作站则有。
由于通常一个区段的字节数是2的幂,如32、64、4KB或2MB,所以可编程解码器的实际实
现和使用都被简化了。而且,将一个区段映射到一个未对齐的地址上意义也不大;1MB的
区段自然在1M整数倍的地址处对齐,32字节的区段则在32的整数倍处。PCI规范利用了这
个对齐;它要求地址解码器需要且只需查看地址总线的高位,并且只有高位是可编程的
。这个约定也意味着任何区段的大小都必须是2 的幂。
这样,重映射一个PCI区段可以通过在配置寄存器的高位设置一个合适的值来完成。例如
,一个1M的区段,有20位的地址空间,可以通过设置寄存器的高12位进行重映射;向寄
存器写0x008xxxxx告诉板子对8MB-9MB的地址区间响应。实际上,只有非常高的地址被用
来映射PCI区段。
这种“部分解码”有几个额外的好处就是软件可以通过检查配置寄存器中非可编程位的
数目来确定PCI区段的大小。为了这个目的,PCI标准要求未使用的位必须总是读作0。通
过强制I/O区段的最小大小为8字节,内存区段为16字节,标准可以把一些额外的信息放
入同一个PCI寄存器中:“空间”位,表明区段是内存的还是I/O的;两个“类型”位;
入同一个PCI寄存器中:“空间”位,表明区段是内存的还是I/O的;两个“类型”位;
一个“预取”位,只是位内存定义的。类型位在32位区段、64位区段、以及“必须映射
在1M一下的32位区段”进行选择。最后这个值用于那些仍然运行于一些PC上的过时软件
。
检测一个PCI区段的大小可以通过使用几个定义在<linux/pci.h>中的位掩码来简化:是
个内存区段时PCI_BASE_ADDRESS_SPACE被置位;PCI_BASE_ADDRESS_MEM_MASK为内存区段
掩去配置位;PCI_BASE_ADDRESS_TO_MASK位I/O区段掩去这些位。规范还要求地址区段必
须按序分配,从PCI_BASE_ADDRESS_0到PCI_BASE_ADDRESS_5;这样一旦一个基地址未用
(也就是被置未0),你就可以知道所有的后续地址都未用。
报告PCI区段当前位置和大小的典型代码如下:
(代码353)
(代码354 #1)
这个代码是pciregion模块的一部分,与pcidata在同一个目录下发布;这个模块生成一
个/pci/pciregions文件,用上面给出的代码产生数据。当配置寄存器被修改时,中断报
告被关闭,以防止驱动程序访问被映射到错误位置的区段。使用cli而不是save_flags是
因为这个函数只在read系统调用时被执行,我们知道在系统调用的时候中断是打开的。
例如,这里是我的抓图器的/proc/pciregion的报告:
例如,这里是我的抓图器的/proc/pciregion的报告:
(代码 #2)
计算机的固件在引导时用一个类似于前面给出的循环来正确地映射区段。由于固件防止
了任何地址赋值时的冲突,Linux驱动程序通常并不改变PCI区间的映射。
有趣的是注意到上面的程序报告的内存大小有可能被夸大。例如,/proc/pciregion报告
说我的视频板子是一个16MB的设备。但这并不真实(尽管我有可能扩展我的视频RAM)。
但由于这个大小信息只是被固件用来分配地址区间,夸大区段大小对驱动程序的作者来
说并不是一个问题,他设备的内部并能正确地处理由固件分配的地址区间。
PCI中断
至于中断,PCI很容易处理。计算机的固件已经给设备分配了一个唯一的中断号,驱动程
序只需要去用它即可。中断号存在配置寄存器60中(PCI_INTERRUPT_LINE),它是一个
字节宽。这允许最多256条中断线,但实际限制依赖于使用的CPU。驱动程序不必麻烦去
检查中断号,因为在PCI_INTERRUPT_LINE中找到的一定是正确的。
如果设备不支持中断,寄存器61(PCI_INTERRUPT_PIN)为0;不然为非0。不过由于驱动
程序知道它的设备是否是中断驱动的,因此并不常需要去读PCI_INTERRUPT_PIN。
程序知道它的设备是否是中断驱动的,因此并不常需要去读PCI_INTERRUPT_PIN。
这样,处理中断的PCI特定的代码只需要这个配置字节以取得中断号,如下面所示的代码
。不然,应用第九章的信息。
result = pcibios_read_config_byte(bus,fnct,PCI_INTERRUPT_LINE,
&my_irq);
if(result){/*deal with result*/}
本节的其余部分为感兴趣的读者提供一些额外的信息,但对写驱动程序并不需要。
一个PCI连接器有四个中断脚,外围板子可以任意使用。每个管脚都是独立地路由到主板
的中断控制器,因此中断可以共享,而没有任何电气问题。中断控制器负责将中断线(
脚)映射到处理器的硬件;将这个平台相关的操作留给控制器是为了获得总线本身的平
台无关性。
位于PCI_INTERRUPUT_PIN的只读配置寄存器用来告诉计算机哪一个管脚被使用了。值得
记住的是每个设备板子最多可带8个设备;每个设备使用一个中断脚并在它自己的配置寄
存器中报告它。同一个设备板子的不同设备可以使用不同的中断脚,也可以共享同一个
。
另一方面,PCI_INTERRUPT_LINE寄存器是读/写的。在计算机引导时,固件扫描它的PCI
另一方面,PCI_INTERRUPT_LINE寄存器是读/写的。在计算机引导时,固件扫描它的PCI
设备,并按照中断脚是如何路由到它的PCI槽的为每个设备设置这个寄存器。这个值由固
件来赋,因为只有固件知道母板是如何将不同的中断脚路由到处理器的。然而,对设备
驱动程序来说,PCI_INTERRUPT_LINE寄存器是只读的。
回顾:ISA
ISA总线在设计上相当老了,而且在性能方面也名声扫地,但它依然占据着扩展设备的很
大一块市场。如果速度不是很重要,并且你想支持旧的主板,那么ISA实现要比PCI更令
人喜欢。这个旧标准的一个额外的优势是,如果你是个电子爱好者,你可以很容易地构
造你自己的设备。
令一方面,ISA的一个巨大的缺点是它紧密地绑定在PC体系结构上;接口总线具有80286
处理器的所有限制,导致系统程序员无穷的痛苦。ISA设计的令一个巨大的问题(从原先
的IBM PC继承下来的)是缺乏地理寻址,这导致了无穷的问题和为加一个新设备时漫长
的“拔下--重跳线—插上—测试”循环。有趣的是注意到即使是最老的Apple II计算机
都已经利用了地理寻址,它们的特征是无跳线的扩展板。
硬件资源
一个ISA设备可以装配I/O端口,内存区域,和中断线。
即使x86处理器支持64KB的I/O端口内存(也就是说,处理器申明16根地址线),有些老
的PC硬件也只能对最低的10根地址线解码。这将可用的地址空间限制为1024个端口,因
为在1KB-64KB区间的任何地址会被任何只能解码低地址线的设备错误地看成低地址。一
些外围通过只映射一个端口到低KB,并使用高地址线在不同的设备寄存器中选择的办法
绕过了这个限制。例如,一个映射到0x340的设备可以安全地使用端口0x740,0xB40,等
等。
如果说I/O端口的可用性受到了限制,那么内存访问就更糟了。一个ISA设备只能使用640
KB-1MB和15MB-16MB之间的内存区间。640KB-1MB区间被PC BIOS、VGA兼容的视频板、以
及各种其它设备使用,留给新设备很少的可用空间。另一方面,15M处的内存,Linux并
不直接支持;这个问题在第八章的“访问设备板子上的内存”讨论过。
ISA设备板子上第三个可用的资源是中断线。有限的中断线被路由到ISA总线,它们被所
有的接口板共享。造成的结果是,如果设备没有被正确地配置,它们可以用同样的中断
线找到它们自己。
尽管原先的ISA规范不允许跨设备的中断共享,多数设备板子还是允许的*。软件级的中
尽管原先的ISA规范不允许跨设备的中断共享,多数设备板子还是允许的*。软件级的中
断共享在第九章的“中断共享”中描述过。
ISA程序设计
至于程序设计,除了Linux核心通过维护I/O和IRQ寄存器提供有限的帮助外(在第二章中
的“使用资源”和第九章的“安装中断处理程序”中描述过),核心和BIOS中没有任何
东西使得使用ISA设备更容易一些。
本书整个第一部分给出的编程技巧同样适用于ISA设备;驱动程序可以探测I/O端口,中
断线必须用在第九章“自动检测IRQ号”介绍过的技术之一来自动检测。
“即插即用”规范
有些ISA设备板子遵循特殊的设计准则,要求特别的初始化序列,以简化增加接口板的安
装和配置。这类板子的设计规范被称做“即插即用(PnP)”,它由一组构造和配置无跳
线ISA设备的繁杂的规则集组成。PnP设备实现了可重定位的I/O区段;PC的BIOS负责这个
重定位—--PCI的风格。
简单地说,PnP的目的就是PCI设备具有的同样的灵活性,而不改变底层的电气接口(ISA
总线)。为了这个目的,规范定义了一组设备无关的配置寄存器和地理寻址接口板的方
法,即使物理总线并不携带每个板子(地理的)的走线----每个ISA信号线于每个可用槽
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -