PCI介绍
PCI总线有自己的I/O空间(32位)和内存空间(32/64位),CPU访问PCI空间需要映射,硬上电自动完成将不同的外设映射到不同的CPU地址空间上。PCI设备的使用模型是这样的,外设可以抽象成一片内存或者一排连续的端口,具体而言每个功能256字节的配置内存。它们可以被排布在PCI总线地址空间上,然后映射到host CPU的地址空间里。每个PCI插槽有4个中断引脚,每个功能可以使用其中一个。每个PCI外设由domain(16位)、bus(8位)、device(5位)以及function(3位)组成的编号来标识。每个domain可以有最多256个bus,每个bus上最多可以有32个device,每个divece上最多可以有8中function。查看当前环境PCI信息可以用下面几种命令
1 2 3
| lspci cat /proc/pci cat /proc/bus/pci
|
PCI设备暴露出的256字节的配置内存,前64字节是标准化的,PCI设备始终使用小端字节序。通常来说,在这256个配置寄存器中由几个只读寄存器,由厂商设定,用于识别设备
名称 |
描述 |
是否常用 |
vendorID |
厂商标识信息,生产PCI设备的厂商需要注册 |
是 |
deviceID |
设备标识信息,不需要注册,但是厂商需要指定 |
是 |
class |
类型标识 |
是 |
subsystem vendorID |
细分标识 |
否 |
subsystem deviceID |
细分标识 |
否 |
驱动PCI设备的标识
驱动需要用struct pci_device_id
结构体告知内核其支持的设备类型,该结构体内包含和PCI设备标识寄存器类似的字段
1 2 3 4 5 6 7 8 9 10 11 12
| struct pci_device_id { __u32 vendor, device; __u32 subvendor, subdevice; __u32 class, class_mask; kernel_ulong_t driver_data; };
PCI_DEVICE(vendor, device) PCI_DEVICE_CLASS(device_class, device_class_mask)
MODULE_DEVICE_TABLE(platform, xxx_device_ids)
|
注册设备
PCI设备的驱动注册和字符设备的驱动注册类似,参照如下接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static struct pci_driver pci_driver = { .name = "xxx_name", .id_table = ids, .probe = probe, .remove = remove, };
const char *name; const struct pci_device_id *id_table;
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); void (*remove) (struct pci_dev *dev); int (*suspend) (struct pci_dev *dev, u32 state); int (*resume) (struct pci_dev *dev);
static int __init pci_xxx_name_init(void) { return pci_register_driver(&pci_driver); }
static void __exit pci_xxx_name_exit(void) { pci_unregister_driver(&pci_driver); }
|
访问配置空间
当probe的时候(内核会根据注册时提交的设备ID信息安排probe),驱动需要完成对设备配置区域的读写,可以使用下面这组接口
1 2 3 4 5 6 7 8 9
| #include <linux/pci.h>
int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_read_config_word(struct pci_dev *dev, int where, u16 *val); int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(struct pci_dev *dev, int where, u8 val); int pci_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);
|
上面这组读写接口实际上会调用下面这组接口,当驱动不能访问pci_dev
的时候也可以直接调用下面这组接口
1 2 3 4 5 6
| int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val); int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val); int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val); int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val); int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val); int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val);
|
访问I/O和内存空间
PCI设备最多可以实现6个I/O地址区域,这6个地址区域既可以映射到PCI的I/O地址空间,也可以映射到PCI的内存地址空间,大多数还是选择后者。当I/O地址被映射到内存的时候,要注意边际作用。内核提供了辅助函数来查询某个设备6个I/O地址区域,返回的首地址可能是内存地址,也可能是I/O端口号。
1 2 3
| unsigned long pci_resource_start(struct pci_dev *dev, int bar); unsigned long pci_resource_end(struct pci_dev *dev, int bar); unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
|