📄 (ldd) ch15-外围总线概览(转载).txt
字号:
数返回的错误代码的符号值。它在1.2和2.0之间没有改变,因此没有可移植性问题。
int pcibios_present(void)
由于PCI相关的函数在无PCI的计算机上毫无意义,pcibios_present函数就是告诉驱动程
序计算机是否支持PCI;如果BIOS懂得PCI,它返回一个为真布尔值。即使CONFIG_PCI被
定义了,PCI功能仍是一个运行时选项。因此,你在调用下面介绍的函数之前要检查一下
pcibios_present,保证计算机支持PCI。
#include <linux/pci.h>
这个头文件定义了下面函数使用的所有数值的符号名。并不是所有的设备ID都在这个文
件中列出了,但你在为你的ID,销售商,类定义宏之前,最好还是看看这个文件。注意
这个文件一直在变大,因为不断有新设备的符号定义被加入。
int pcibios_find_device(unsigned short vendor, unsigned short id, unsigned
short index,
unsigned char *bus, unsigned char *function);
如果CONFIG_PCI被定义了,并且pcibios_present也是真,这个函数被用来从BIOS请求关
于设备的信息。销售商/ID对确定设备。index用来支持具有同样的销售商/ID对的几个设
备,下面将会解释。对这个函数的调用返回设备在总线上的位置以及函数指针。返回代
码为0表示成功,非0表示失败。
int pcibios_find_class(unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *function);
这个函数和上一个类似,但它寻找属于特定类的设备。参数class_code传递的形式为:1
6位的类寄存器左移八位,这与BIOS接口使用类寄存器的方式有关。这次还是,返回代码
6位的类寄存器左移八位,这与BIOS接口使用类寄存器的方式有关。这次还是,返回代码
为0表示成功,非0表示有错。
char *pcibios_strerror(int error);
这个函数用来翻译一个PCI错误代码(象pcibios_find_device返回的)为一个字符串。
你也许在查找函数返回的即不是PCIBIOS_SUCCESSFUL(0),也不是PCIBIOS_DEVICE_NOT_F
OUND时(这是当所有的设备都被找过以后所期望返回的错误代码),希望打印一条错误
信息。
下面的代码是驱动程序在加载时检测设备所使用的典型代码。如上面所提到的,这个查
找可以基于签名或者设备类。不管是哪种情况,驱动程序不许存储bus和function值,它
们在后面确定设备时要用到。function的前五位确定设备,后三位确定函数。
下面的代码中,每个设备特定的符号加前缀jail_(另一个指令列表),大写或小写依赖
于符号的种类。
如果驱动程序可以依赖于唯一的销售商/ID对,下面的循环可以用来初始化驱动程序:
(代码347)
(代码348)
如果这个代码段只处理由JAIL_VENDOR和JAIL_ID确定的一类PCI设备,那么它是正确的。
不过,很多驱动程序非常灵活,能够同时处理PCI和ISA板子。在这种情况下,驱动程序
仅在没有检测到PCI板子或CONFIG_PCIBIOS没有定义时才探测ISA设备。
使用pcibios_find_class要求jail_init_dev完成比例子中要多的工作。只要它找到了一
个属于指定类的设备,这个函数就成功返回,但驱动程序还要确认其签名也是被支持的
。这个任务通过一系列的条件语句完成,结果是抛弃很多不期望的设备。
有些PCI外围包含通用目的的PCI接口芯片和设备特定的电路。所有使用同样接口芯片的
外围板子都有同样的签名,驱动程序必须进行额外的探测以保证它在处理正确的外围设
备。因此,有时象jail_init_dev之类的函数必须准备好做一些设备特定的额外的检测,
以抛弃那些可能有正确签名的设备。
访问配置空间
在驱动程序检测到设备后,它通常要对三个地址空间读或写:内存、端口和配置。特别
在驱动程序检测到设备后,它通常要对三个地址空间读或写:内存、端口和配置。特别
地,访问配置空间对驱动程序来说极为重要,以呢这是它发现设备被映射到内存和I/O空
间什么地方的唯一的办法。
由于微处理器无法直接访问配置空间,计算机销售商必须提供一个办法来完成它。准确
的实现因此是销售商相关的,与我们这里的讨论无关。幸运的是,这个事务的软件接口
(下面描述)是标准化的,驱动程序或Linux核心都不需要知道它的细节。
至于驱动程序,配置空间可以通过8位、16位、32位的数据传送来访问。相关函数的原型
在<linux/bios32.h>:
int pcibios_read_config_byte(unsigned char bus, unsigned char function,
unsigned char where, unsigned char *ptr);
int pcibios_read_config_word(unsigned char bus, unsigned char function,
unsigned char where, unsigned char *ptr);
int pcibios_read_config_dword(unsigned char bus, unsigned char function,
unsigned char where, unsigned char *ptr);
从由bus和function确定的设备的配置空间读取1,2,4个字节。参数where是从配置
空间开始处的字节偏移。 从配置空间取出的值通过ptr返回,这些函数的返回值是错误
代码。字和双字函数将刚从小印地安字节序读出的值转换为处理器本身的字节序,因此
你并不需要处理字节序。
int pcibios_write_config_byte(unsigned char bus, unsigned char function,
unsigned char where, unsigned char val);
int pcibios_write_config_word(unsigned char bus, unsigned char function,
unsigned char where, unsigned short val);
int pcibios_write_config_dword(unsigned char bus, unsigned char function,
unsigned char where, unsigned int val);
向配置空间里写1,2,4个字节。设备仍由bus和function确定,要写的值由val传递
。字和双字函数在向外围设备写之前将数值转换为小印地安字节序。
访问配置变量的最好办法是使用在<linux/pci.h>中定义的符号名。例如,下面的两行程
序通过给pcibios_read_config_byte的where传递符号名来获取一个设备的修正ID。
序通过给pcibios_read_config_byte的where传递符号名来获取一个设备的修正ID。
Unsigned char jail_get_revision(unsigned char bus, unsigned char fn)
{
unsigned char *revision;
pcibios_read_config_byte(bus,fn, PCI_REVISION_ID,&revision);
return revision;
}
当访问多字节值时,程序远一定要记住字节序的问题。
看看一个配置快照
如果你向浏览你系统上PCI设备的配置空间,你可以编译并加载模块pci/pcidata.c,它
如果你向浏览你系统上PCI设备的配置空间,你可以编译并加载模块pci/pcidata.c,它
在O’Reilly FTP站点上提供的源文件中。
这个模块生成一个动态的/proc/pcidata文件,包含有你的PCI设备配置空间的二进制快
照。这个快照在文件每次被读时更新。/proc/pcidata的大小被限制为PAGE_SIZE字节(这
是动态/proc文件的限制,在第四章“调试技术”中“使用/proc文件系统”一节介绍过)
。这样,它只列出前PAGESIZE/256个设备的配置内存,意味着16或32个设备(也许对你
的系统已经够了)。我选择把/proc/pcidata作成二进制文件,而不是象其它/proc文件
那样是文本的,就是因为这个大小限制。
pcidata的另一个限制是它只扫描系统的第一条PCI总线。如果你的系统有到其它PCI总线
的桥,pcidata将忽略它们。
在/proc/pcidata中设备出现的顺序与/proc/pci中相反。这是因为/proc/pci读的是一个
从头部生长的链表,而/proc/pcidata则是一个简单的查找循环,它按照取到的顺序将所
有的东西输出。
例如,我的抓图器在/proc/pcidata的第二个出现,(目前)有下面的配置寄存器:
morgana% dd bs=256 skip=1 count=1 if=/proc/pcidata | od –Ax –t x1
1+0 records in
1+0 records out
000000 86 80 23 12 06 00 00 02 00 00 00 04 00 20 00 00
000010 00 00 00 f1 00 00 00 00 00 00 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000030 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00
000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
如果你将上面的输出和图15-1比较,你就可以理解这些数字。或者,你可以使用pcidump
程序,在可以从FTP站点上找到,它将输出列表格式化并标号。
pcidump的代码并不值得在这儿列出,因为这个简单程序只是一个长表,外加十行扫描这
个表的代码。相反,让我们看看一些选择的输出行:
(代码351)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -