PCI 总线在一般的小型手持设备中不太可能用到,但在工控和通信设备及其 PC 中却引领着潮流。在 Linux 系统中,PCI 设备驱动和 USB 设备驱动有共性,其驱动都由总线相关部分和自身设备类型驱动两部分组成。
1、 PCI 总线及其配置空间, PCI 总线在 Linux 内核中的数据结构。PCI 设备驱动的 PCI 相关部分围绕着 pci_driver 结构体的成员函数展开,
2、 pci_driver 结构体及其成员函数的含义,分析PCI设备驱动的框架结构。
21.1 PCI总线与配置空间
21.1.1 PCI总线的 Linux 描述
PCI 是 CPU 和外围设备通信的高速传输总线。PCI 规范能够实现 32 位并行数据传输,工作频率为 33MHz 或 66MHz,最大吞吐率达 266MB/s。
PCI 总线体系结构是一种层次式的体系结构,在层次式体系结构中,PCI 桥设备占据着重要的地位,它将父总线与子总线连接在一起。树的顶端是系统的 CPU,它通过一个较为特殊的 PCI 桥设备 — Host/PCI 桥设备与根 PCI 总线连接起来。
作为一种特殊的 PCI 设备,PCI 桥包括以下几种。
Host/PCI 桥:用于连接 CPU 与 PCI 根总线,第 1 个根总线的编号为 0。在 PC 中,内存控制器通常被集成到 Host/PCI 桥设备芯片中,因此,Host/PCI 桥通常也被称为“北桥芯片组(North Bridge Chipset)”。
PCI/ISA 桥:用于连接旧的 ISA 总线。通常,PCI 中的类似 i8359A 中断控制器这样的设备也会被集成到 PCI/ISA 桥设备中,PCI/ISA 桥通常也被称为“南桥芯片组(SouthBridge Chipset)”。
PCI-to-PCI 桥:用于连接 PCI 主总线(primary bus)与次总线(secondary bus)。PCI 桥所处的 PCI 总线称为“主总线”(即次总线的父总线),桥设备所连接的 PCI 总线称为“次总线”(即主总线的子总线)。
在 Linux 系统中,PCI 总线用 pci_bus 来描述,这个结构体记录了本 PCI 总线的信息以及本PCI 总线的父总线、子总线、桥设备信息,这个结构体的定义如代码清单 21.1 所示。
代码清单 21.1 pci_bus 结构体
include/linux/pci.h
struct pci_bus {
struct list_head node; /* 链表元素 node */
struct pci_bus *parent; /*指向该 PCI 总线的父总线,即 PCI 桥所在的总线 */
struct list_head children; /* 描述了这条 PCI 总线的子总线链表的表头 */
struct list_head devices; /* 描述了这条 PCI 总线的逻辑设备链表的表头 */
struct pci_dev *self; /* 指向引出这条 PCI 总线的桥设备的 pci_dev 结构 */
struct resource *resource[PCI_BUS_NUM_RESOURCES];
/* 指向应路由到这条 PCI 总线的地址空间资源 */
struct pci_ops *ops; /* 这条 PCI 总线所使用的配置空间访问函数 */
void *sysdata; /* 指向系统特定的扩展数据 */
struct proc_dir_entry *procdir;/*该 PCI 总线在/proc/bus/pci 中对应的目录项*/
unsigned char number; /* 这条 PCI 总线的总线编号 */
unsigned char primary; /* 桥设备的主总线 */
unsigned char secondary; /* PCI 总线的桥设备的次总线号 */
unsigned char subordinate;/*PCI 总线的下属 PCI 总线的总线编号最大值*/
char name[48];
unsigned short bridge_ctl;
pci_bus_flags_t bus_flags;
struct device *bridge;
struct class_device class_dev;
struct bin_attribute *legacy_io;
struct bin_attribute *legacy_mem;
unsigned int is_added:1;
};
假定一个如图 21.1 所示的 PCI 总线系统,根总线 0 上有一个 PCI 桥,它引出子总线 Bus 1,Bus 1 上又有一个 PCI 桥引出 Bus 2。
在上图中,Bus 0 总线的 pci_bus 结构体中的 number、primary、secondary 都应该为 0,因为它是通过 Host/PCI 桥引出的根总线;Bus 1 总线的 pci_bus 结构体中的 number 和 secondary 都为 1,但是它的 primary 应该为 0;Bus 2 总线的 pci_bus 结构体中的 number 和 secondary 都应该为 2,而其 primary 则应该等于 1。这 3 条总线的 subordinate 值都应该等于 2。
系统中当前存在的所有根总线都通过其 pci_bus 结构体中的 node 成员链接成一条全局的根总线链表,其表头由 list 类型的全局变量 pci_root_buses 来描述。而根总线下面的所有下级总线通过其 pci_bus 结构体中的 node 成员链接到其父总线的 children 链表中。这样,通过这两种 PCI总线链表,Linux 内核就将所有的 pci_bus 结构体以一种倒置树的方式组织起来。
假定对于如图 21.2所示的多根 PCI 总线体系结构,它所对应的总线链表结构将如图 21.3 所示。
21.1.2 PCI 设备的 Linux 描述
在 Linux 系统中,所有种类的 PCI 设备用 pci_dev 结构体来描述,由于一个 PCI 接口卡上可能包含多个功能模块,每个功能被当作一个独立的逻辑设备,因此,每一个 PCI 功能,即PCI 逻辑设备都惟一地对应一个 pci_dev 设备描述符,该结构体的定义如代码清单 21.2 所示。
代码清单 21.2 pci_dev 结构体
include/linux/pci.h
/*
* The pci_dev structure is used to describe PCI devices.
*/
struct pci_dev {
struct list_head bus_list; /* node in per-bus list */
struct pci_bus *bus; /* bus this device is on */
struct pci_bus *subordinate; /* bus this device bridges to */
void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
struct pci_slot *slot; /* Physical slot this device is in */
unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class; /* 3 bytes: (base,sub,prog-if) */
u8 revision; /* PCI revision, low byte of class word */
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
u8 pcie_cap; /* PCIe capability offset */
u8 msi_cap; /* MSI capability offset */
u8 msix_cap; /* MSI-X capability offset */
u8 pcie_mpss:3; /* PCIe Max Payload Size Supported */
u8 rom_base_reg; /* which config register controls the ROM */
u8 pin; /* which interrupt pin this device uses */
u16 pcie_flags_reg; /* cached PCIe Capabilities Register */
u8 dma_alias_devfn;/* devfn of DMA alias, if any */
struct pci_driver *driver; /* which driver has allocated this device */
u64 dma_mask; /* Mask of the bits of bus address this
device implements. Normally this is
0xffffffff. You only need to change
this if your device has broken DMA
or supports 64-bit transfers. */
struct device_dma_parameters dma_parms;
pci_power_t current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
and D3 being off. */
u8 pm_cap; /* PM capability offset */
unsigned int pme_support:5; /* Bitmask of states from which PME#
can be generated */
unsigned int pme_interrupt:1;
unsigned int pme_poll:1; /* Poll device's PME status bit */
unsigned int d1_support:1; /* Low power state D1 is supported */
unsigned int d2_support:1; /* Low power state D2 is supported */
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */
unsigned int no_d3cold:1; /* D3cold is forbidden */
unsigned int d3cold_allowed:1; /* D3cold is allowed by user */
unsigned int mmio_always_on:1; /* disallow turning off io/mem
decoding during bar sizing */
unsigned int wakeup_prepared:1;
unsigned int runtime_d3cold:1; /* whether go through runtime
D3cold, not set for devices
powered on/off by the
corresponding bridge */
unsigned int ignore_hotplug:1; /* Ignore hotplug events */
unsigned int d3_delay; /* D3->D0 transition time in ms */
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
#ifdef CONFIG_PCIEASPM
struct pcie_link_state *link_state; /* ASPM link state */
#endif
pci_channel_state_t error_state; /* current connectivity state */
struct device dev; /* Generic device interface */
int cfg_size; /* Size of configuration space */
/*
* Instead of touching interrupt line and base address registers
* directly, use the values stored here. They might be different!
*/
unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
bool match_driver; /* Skip attaching driver */
/* These fields are used by common fixups */
unsigned int tran