深入理解Linux网络技术内幕——PCI层和网络接口卡
概述
struct pci_driver { struct list_head node; char *name; //驱动程序名字 const struct pci_device_id *id_table; /* ID向量,内核用于把设备关联到此驱动程序 */ 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); struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; };
PCI NIC设备的注册
struct pci_device_id { __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ //通常 vendor, device就足以标识设备 __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID *///很少用到 __u32 class, class_mask; /* (class,subclass,prog-if) triplet *///设备所属的类,如network类 kernel_ulong_t driver_data; /* Data private to the driver *///不属于PCI标识部分,而是驱动私有参数 };
int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name) void pci_unregister_driver(struct pci_driver *drv) pci_module_init()//在一些驱动程序上作为__pci_register_driver别名
电源管理和网络唤醒
PCI NIC驱动程序注册范例
以Intel PRO/100 Ethernet驱动程序说明NIC设备驱动程序的注册,源文件为drivers/net/e100.c。
初始化pci_device_id内容:
#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 } /****************************************************************************************/ #define DEFINE_PCI_DEVICE_TABLE(_table) \ const struct pci_device_id _table[] __devinitconst /***************************************************************************************/ static DEFINE_PCI_DEVICE_TABLE(e100_id_table) = { INTEL_8255X_ETHERNET_DEVICE(0x1029, 0), INTEL_8255X_ETHERNET_DEVICE(0x1030, 0), INTEL_8255X_ETHERNET_DEVICE(0x1031, 3), INTEL_8255X_ETHERNET_DEVICE(0x1032, 3), INTEL_8255X_ETHERNET_DEVICE(0x1033, 3), INTEL_8255X_ETHERNET_DEVICE(0x1034, 3), INTEL_8255X_ETHERNET_DEVICE(0x1038, 3), INTEL_8255X_ETHERNET_DEVICE(0x1039, 4), INTEL_8255X_ETHERNET_DEVICE(0x103A, 4), INTEL_8255X_ETHERNET_DEVICE(0x103B, 4), INTEL_8255X_ETHERNET_DEVICE(0x103C, 4), INTEL_8255X_ETHERNET_DEVICE(0x103D, 4), INTEL_8255X_ETHERNET_DEVICE(0x103E, 4), INTEL_8255X_ETHERNET_DEVICE(0x1050, 5), INTEL_8255X_ETHERNET_DEVICE(0x1051, 5), INTEL_8255X_ETHERNET_DEVICE(0x1052, 5), INTEL_8255X_ETHERNET_DEVICE(0x1053, 5), INTEL_8255X_ETHERNET_DEVICE(0x1054, 5), INTEL_8255X_ETHERNET_DEVICE(0x1055, 5), INTEL_8255X_ETHERNET_DEVICE(0x1056, 5), INTEL_8255X_ETHERNET_DEVICE(0x1057, 5), INTEL_8255X_ETHERNET_DEVICE(0x1059, 0), INTEL_8255X_ETHERNET_DEVICE(0x1064, 6), INTEL_8255X_ETHERNET_DEVICE(0x1065, 6), INTEL_8255X_ETHERNET_DEVICE(0x1066, 6), INTEL_8255X_ETHERNET_DEVICE(0x1067, 6), INTEL_8255X_ETHERNET_DEVICE(0x1068, 6), INTEL_8255X_ETHERNET_DEVICE(0x1069, 6), INTEL_8255X_ETHERNET_DEVICE(0x106A, 6), INTEL_8255X_ETHERNET_DEVICE(0x106B, 6), INTEL_8255X_ETHERNET_DEVICE(0x1091, 7), INTEL_8255X_ETHERNET_DEVICE(0x1092, 7), INTEL_8255X_ETHERNET_DEVICE(0x1093, 7), INTEL_8255X_ETHERNET_DEVICE(0x1094, 7), INTEL_8255X_ETHERNET_DEVICE(0x1095, 7), INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7), INTEL_8255X_ETHERNET_DEVICE(0x1209, 0), INTEL_8255X_ETHERNET_DEVICE(0x1229, 0), INTEL_8255X_ETHERNET_DEVICE(0x2449, 2), INTEL_8255X_ETHERNET_DEVICE(0x2459, 2), INTEL_8255X_ETHERNET_DEVICE(0x245D, 2), INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7), { 0, } };
在模块的初始化和卸载接口中完成PCI设备驱动程序的注册和注销:
static struct pci_driver e100_driver = { .name = DRV_NAME, .id_table = e100_id_table, .probe = e100_probe, .remove = __devexit_p(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);
其中的一些函数指针原型:
#define DRV_NAME "e100" static int __devinit e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct nic *nic; int err; if (!(netdev = alloc_etherdev(sizeof(struct nic)))) { if (((1 << debug) - 1) & NETIF_MSG_PROBE) pr_err("Etherdev alloc failed, aborting\n"); return -ENOMEM; } …… …… }
PCI子系统总览
(a)在系统引导时,会建立一个数据库,把每个总线都关联到一份已侦测到而使用该总线的设备列表。PCI总线的描述符处理其他参数外,还包括一个已侦测PCI设备的列表。
(b)当驱动程序被加载,调用pci_register_driver注册pci_driver到PCI层时,PCI会使用pci_driver结构中的PCI设备ID参数id_table与已侦测到的PCI设备列表匹配,若匹配到就会建立该驱动程序的设备列表。对于每个匹配到的设备,PCI层会调用相匹配的驱动程序中的pci_driver结构中的probe函数,建立并注册相关联的网络设备。
/proc/pci文件包含了已注册的PCI设备的信息。pciutils套件中的lspci命令会输出有关本地PCI设备的信息,其中有些信息取自/sys。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。