一些 PCI 配置寄存器是要求的, 一些是可选的. 每个 PCI 设备必须包含有意 义的值在被要求的寄存器中, 而可选寄存器的内容依赖外设的实际功能. 可选的字段不被 使用, 除非被要求的字段的内容指出它们是有效的. 因此, 被要求的字段声称板的功能, 包括其他的字段是否可用.
注意 PCI 寄存器一直是小端模式. 尽管标准被设计为独立于体系, PCI 设计者有时露出 一些倾向 PC 环境. 驱动编写者应当小心处理字节序, 当存取多字节配置寄存器时; 在 PC 上使用的代码可能在其他平台上不工作. Linux 开发者已经处理了这个字节序问题(见 下一节, "存取配置空间"), 但是这个问题必须记住. 如果你曾需要转换数据从主机序到 PCI 序, 或者反之, 你可求助在 <asm/byteorder.h> 中定义的函数, 在第 11 章介绍, 知道 PCI 字节序是小端.
描述所有的配置项超出了本书的范围. 常常地, 随每个设备发布的技术文档描述被支持的 寄存器. 我们感兴趣的是一个驱动如何能知道它的设备以及它如何能存取设备的配置空间.
3 个或者 4 个 PCI 寄存器标识一个设备: verdorID, deviceID, 和 class 是 3 个常常 用到的. 每个 PCI 制造商分配正确的值给这些只读寄存器, 并且驱动可使用它们来查找 设备. 另外, 字段 subsystem verdorID 和 subsystem deviceID 有时被供应商设置来进 一步区分类似的设备.
我们看这些寄存器的细节: vendorID
这个 16-位 寄存器标识一个硬件制造商. 例如, 每个 Intel 设备都标有相同的供 应商号, 0x8086. 这样的号有一个全球的注册, 由 PCI 特别利益体所维护, 并且 供应商必须申请有一个唯一的分配给它们的号.
deviceID
这是另一个 16-位 寄存器, 由供应商选择; 对于这个设备 ID 没有要求官方的注 册. 这个 ID 常常和 供应商 ID 成对出现来组成一个唯一的 32-位 标识符给一个 硬件设备. 我们使用词语"签名"来指代供应商和设备 ID 对. 一个设备驱动常常依 靠签名来标识它的设备; 你可在硬件手册中找到对于目标设备要寻找的值.
class
每个外设都属于一个类. 类寄存器是一个 16-位 值, 它的高 8 位标识"基类"(或 者群). 例如, "ethernet"和"token ring"是 2 个类都属于"network"群, 而 "serial"和"parallel"属于"communication"群. 一些驱动可支持几个类似的设备, 每个都有一个不同的签名但是都属于同样的类; 这些驱动可依赖类寄存器标识它们 的外设, 就象后面所示.
subsystem vendorID subsystem deviceID
这些字段可用来进一步标识一个设备. 如果芯片对于本地总线是一个通用接口芯片, 它常常被用在几个完全不同的地方, 并且驱动必须标识出它在与之通话的实际设备. 子系统标志用作此目的.
使用这些不同的标识符, 一个 PCI 驱动可告知内核它支持什么类型的设备. struct pci_device_id 结构被用来定义一个驱动支持的不同类型 PCI 设备的列表. 这个结构包 含不同的成员:
u32 vendor;
u32 device;
这些指定一个设备的 PCI 供应商和设备 ID. 如果驱动可处理任何供应商或者设备 ID, 值 PCI_ANY_ID 应当用作这些成员上.
u32 subvendor;
u32 subdevice;
这些指定一个设备的 PCI 子系统供应商和子系统设备 ID. 如果驱动可处理任何类 型的子系统 ID, 值 PCI_ANY_ID 应当用作这些成员上.
u32 class;
u32 class_mask;
这 2 个值允许驱动来指定它支持一类 PCI 类设备. 不同的 PCI 设备类( 一个 VAG 控制器是一个例子 )在 PCI 规范里被描述. 如果一个驱动可处理任何子系统 ID, 值 PCI_ANY_ID 应当用作这些字段.
kernel_ulong_t driver_data;
这个值不用来匹配一个设备, 但是用来持有信息, PCI 驱动可用来区分不同的设备, 如果它想这样.
有 2 个帮助宏定义应当被用来初始化一个 struct pci_device_id 结构: PCI_DEVICE(vendor, device)
这个创建一个 struct pci_device_id , 它只匹配特定的供应商和设备 ID. 这个 宏设置这个结构的子供应商和子设备成员为 PCI_ANY_ID.
PCI_DEVICE_CLASS(device_class, device_class_mask)
这个创建一个 struct pci_device_id, 它匹配一个特定的 PCI 类. 一个使用这些宏来定义一个驱动支持的设备类型的例子, 在下面的内核文件中可找到:
drivers/usb/host/ehci-hcd.c:
static const struct pci_device_id pci_ids[] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
.driver_data = (unsigned long) &ehci_driver,
},
{ /* end: all zeroes */ }
};
drivers/i2c/busses/i2c-i810.c:
static struct pci_device_id i810_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) },
{ 0, },
};
这些例子创建一个 struct pci_device_id 结构的列表, 列表中最后一个是被设置为全零 的的空结构. 这个 ID 的数组用在 struct pci_driver (下面讲述), 并且它还用来告诉 用户空间这个特定的驱动支持哪个设备.