数据结构
-
pci_device_id
pci_device_id设备标识符,这不是Linux使用的本地ID,是根据PCI标准定义的ID -
pci_dev
每个PCI设备都会被分派一个pci_dev实例,这个结构由内核引用一个PCI设备 -
pci_driver
定义PCI层和设备驱动程序之间的接口。这个结构主要由函数指针构成。所有的PCI设备都会使用这个结构。
name驱动程序的名称
id_table这是一个pci_device_id向量,用于把一些设备关联到此驱动程序。
probe函数是当PCI层发现正在搜寻驱动程序的设备pci_device_id 和前边提到的id_table中的某个元素匹配时,就会调用此函数。这个函数应该开启硬件,分配所需的资源等。
remove函数当驱动程序从内核移除时或者当可热插拔设备删除时,PCI层调用这个函数。
struct pci_driver {
struct list_head node;
const char *name;
const struct pci_device_id *id_table; /* Must be non-NULL for probe to be called */
int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend)(struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late)(struct pci_dev *dev, pm_message_t state);
int (*resume_early)(struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* On PF */
const struct pci_error_handlers *err_handler;
const struct attribute_group **groups;
struct device_driver driver;
struct pci_dynids dynids;
};
PCI NIC设备驱动程序注册
PCI设备通过一些独一无二的参数识别一个设备。一般通过vendor和device可以识别设备。
每个设备驱动都会把一个pci_device_id 向量注册到内核中,这个向量中保存有这个驱动可以处理的设备ID。
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
PCI设备驱动程序使用pci_register_driver和pci_unregister_driver向内核注册和注销驱动程序。
sys文件系统中有输出PCI相关总线的信息。
电源管理和网络唤醒
pci_driver 中的suspend和resume函数处理PCI电源管理事件。
这两个函数除了需要负责PCI状态的保存和恢复。
当NIC使用PCI时,这两个函数还需要
- suspend主要用于停止设备出口队列,使得设备无法再传输。
- resume重启出口队列,使得设备可以继续传输报文。
PCI NIC驱动程序注册范例
通过这个例子我们可以看到首先代码会定义一个pci_device_id 的向量数组,其中的每个元素就是支持的一个PCI设备类型。
此外驱动程序由e100_init_module进行初始化,会调用pci_register_driver将驱动注册到内核。
#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich }
static const struct pci_device_id e100_id_table[] = {
INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
...
INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7),
{ 0, }
};
static struct pci_driver e100_driver = {
.name = DRV_NAME,
.id_table = e100_id_table,
.probe = e100_probe,
.remove = e100_remove,
#ifdef CONFIG_PM
/* Power Management hooks */
.suspend = e100_suspend,
.resume = e100_resume,
#endif
.shutdown = e100_shutdown,
.err_handler = &e100_err_handler,
};
static int __init e100_init_module(void)
{
if (((1 << debug) - 1) & NETIF_MSG_DRV) {
pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
pr_info("%s\n", DRV_COPYRIGHT);
}
return pci_register_driver(&e100_driver);
}
static void __exit e100_cleanup_module(void)
{
pci_unregister_driver(&e100_driver);
}
module_init(e100_init_module);
module_exit(e100_cleanup_module);