本文以Intel e1000e网卡驱动为例,分析网卡驱动的基本流程,内核版本为2.6.18。
一、驱动注册
网卡是一种PCI设备,为了正确的注册到内核,所有的PCI设备驱动都必须创建一个主要的结构体
struct pci_driver:
struct pci_driver:
- struct pci_driver {
- struct list_head node;
- 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 (*resume) (struct pci_dev *dev); /* Device woken up */
- int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
- void (*shutdown) (struct pci_dev *dev);
- struct pci_error_handlers *err_handler;
- struct device_driver driver;
- struct pci_dynids dynids;
- };
为了创建一个正确的struct pci_driver,至少要初始化四个字段:name,id_table,probe和remove。
- static struct pci_driver e1000_driver = {
- .name = e1000e_driver_name,
- .id_table = e1000_pci_tbl,
- .probe = e1000_probe,
- .remove = __devexit_p(e1000_remove),
- ...
- };
为了把struct pci_driver注册到内核,需要调用pci_register_driver函数,通常在模块初始化函数module_init()函数中调用该函数:
- static int __init e1000_init_module(void)
- {
- int ret;
- ret = pci_register_driver(&e1000_driver);
- return ret;
- }
二、驱动加载
通过insmod或者modprobe命令加载驱动,这两个命令为应用程序,在应用程序里调用了一个系统调用:
该系统调用最终调用内核函数:
该函数调用驱动程序中用module_init定义的函数e1000_init_module。
- extern long init_module(void *, unsigned long, const char *);
- asmlinkage long
- sys_init_module(void __user *umod,
- unsigned long len,
- const char __user *uargs)
- module_init(e1000_init_module);
三、驱动如何匹配设备
pci_driver结构体中有一个成员id_table,其类型为struct pci_device_id *,pci驱动里都需要初始化该成员:
- 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 */
- };
该结构中最重要的两个成员为vendor和device。vendor为厂商的ID,该值是唯一的,device为设备的ID。所有PCI设备的配置空间里都保存了这两个值。下面是PCI规范里对这两个字段的定义:
- Vendor ID This field identifies the manufacturer of the device.
- Valid vendoridentifiers are allocated by the PCI SIG
- to ensure uniqueness.0 FFFFh is an invalid value for
- Vendor ID.
- Device ID This field identifies the particular device.
- This identifier is allocatedby the vendor.
当同一个系统中的两个设备的vendor和device相同时,用subvendor和subdevice区分不同的设备。
1、MODULE_DEVICE_TABLE
1、MODULE_DEVICE_TABLE
- static DEFINE_PCI_DEVICE_TABLE(e1000e_pci_tbl) = {
- {
- PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571}, {
- PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571}, {
- PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571},
- {
- PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), board_82571},
- ...
该结构体数组列出了所有该驱动支持的设备,主要是设备的Vender ID和Device ID,在pci_register_driver中会用到该结构体数组
。
驱动中还用宏MODULE_DRVICE_TABLE定义了一个变量:
- MODULE_DEVICE_TABLE(pci, e1000e_pci_tbl);
上面的例子中,宏MODULE_DEVICE_TABLE的定义如下:
- #define MODULE_DEVICE_TABLE(type,name) \
- MODULE_GENERIC_TABLE(type##_device,name)
- #define MODULE_GENERIC_TABLE(gtype,name) \
- extern const struct gtype##_id __mod_##gtype##_table \
- __attribute__ ((unused, alias(__stringify(name))))
该宏定义替换以后就变成如下语句:
- extern const struct pci_driver_id __mod_pci_driver_id_table
- __attribute__ ((unused,alias("e1000e_pci_table")))
该宏创建一个struct pci_device_id类型的变量__mod_pci_device_table,该变量的别名是e1000e_pci_table,意味着该变量同样指向e1000e_pci_tbl这个结构体(struct pci_driver_id)数组。在稍后的内核构建过程中,depmod程序从所有的模块中搜索符号__mod_pci_device_table,如果找到该符号,就会把数组中的数据添加到/lib/module/KERNEL_VERSION/modules.pcimap中。当depmod结束之后,内核模块支持的所有PCI设备连通他们的模块名都在该文件中被列出。当内核告知热插拔系统一个新的 PCI设备已经被发现时,热插拔系统使用modules.pcimap文件来寻找要状态的恰当的驱动
。
上面结构体列出中所有的驱动支持的设备都在下面的文件里列出了:
上面结构体列出中所有的驱动支持的设备都在下面的文件里列出了:
- $cat modules.pcimap | grep e1000e
- e1000e 0x00008086 0x0000105e 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x0000105f 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010a4 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010bc 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010a5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x00001060 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010d9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010da 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010d5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- e1000e 0x00008086 0x000010b9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0
- ...
上面的方法是针对热插拔设备的,但是,不是所有的PCI设备都是热插拔设备,一般的网卡就不是热插拔设备。
2、pci_register_driver
一般的网卡不是热插拔设备,当驱动调用pci_register_driver函数后,内核搜寻系统中的存在的设备,如果有匹配e1000_pci_tbl结构体数组中所列出的设备,就会调用驱动程序中的probe函数。调用过程如下:
在调用probe之前,内核遍历所有的PCI总线上的设备,调用match函数,在此match的实例为pci_bus_match,看是否有设备跟e1000_pci_tbl中支持的设备匹配:
pci_bus_match最终调用pci_match_one_device函数进行匹配:
如果有设备匹配了e1000_pci_tbl中的设备,就会调用pci_bus_type实例中的probe函数,在此probe的实例为pci_device_probe,最终调用函数pci_call_probe函数,该函数调用驱动程序中pci_driver实例中的probe函数:
如果系统中存在多个相同的设备,就会多次调用驱动中的probe函数,但是驱动给设备分配的资源(IO内存、中断等)及设备名是不同的,比如系统中有两个网卡,驱动分配的网络设备名可能为eth0和eth1。
如下转来的内容可以说明设备驱动加载时是如何匹配设备的:
对于所有的PCI设备,在系统引导时,会建立一种 数据库,把每个总线都关联一份已侦测并且使用该总线的设备列表。对于PCI设备来说,系统中就存在着这样一个数据库,其中保存着所有使用PCI总线的设备ID,此ID即上文提到的pci_device_id。
此时,(a)图就代表着所有使用PCI总线的设备数据库。当设备驱动程序A被加载时,会调用pci_register_driver并提供 pci_driver实例与PCI层注册,同时pci_driver结构中包含一个驱动程序所能处理的设备ID表(即e1000_pci_tbl);接着,PCI子系统使用该表去查在已经保存的设备数据库中是否存在匹配,于是会建立该驱动程序的设备列表,如图(b)所示;此外,对每个匹配的设备而言,PCI层会调用相匹配的驱动程序中的 pci_driver结构中所提供的probe函数。
2、pci_register_driver
一般的网卡不是热插拔设备,当驱动调用pci_register_driver函数后,内核搜寻系统中的存在的设备,如果有匹配e1000_pci_tbl结构体数组中所列出的设备,就会调用驱动程序中的probe函数。调用过程如下:
- pci_register_driver
- |
- __pci_register_driver
- |
- bus_add_driver
- |
- driver_attach
- |
- bus_for_each_dev /*遍历所有PCI总线上的设备*/
- |
- __driver_attach
- |
- driver_probe_device
- |
- drv->bus->match(dev, drv)
- |
- dev->bus->probe(dev)
- struct bus_type pci_bus_type = {
- .name = "pci",
- .match = pci_bus_match,
- .uevent = pci_uevent,
- .probe = pci_device_probe,
- .remove = pci_device_remove,
- .suspend = pci_device_suspend,
- .shutdown = pci_device_shutdown,
- .resume = pci_device_resume,
- .dev_attrs = pci_dev_attrs,
- };
- static inline const struct pci_device_id *
- pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
- {
- if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
- (id->device == PCI_ANY_ID || id->device == dev->device) &&
- (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
- (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
- !((id->class ^ dev->class) & id->class_mask))
- return id;
- return NULL;
- }
如果有设备匹配了e1000_pci_tbl中的设备,就会调用pci_bus_type实例中的probe函数,在此probe的实例为pci_device_probe,最终调用函数pci_call_probe函数,该函数调用驱动程序中pci_driver实例中的probe函数:
- static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
- const struct pci_device_id *id)
- {
- int error;
- ...
- error = drv->probe(dev, id);
- ...
- return error;
- }
如下转来的内容可以说明设备驱动加载时是如何匹配设备的:
对于所有的PCI设备,在系统引导时,会建立一种 数据库,把每个总线都关联一份已侦测并且使用该总线的设备列表。对于PCI设备来说,系统中就存在着这样一个数据库,其中保存着所有使用PCI总线的设备ID,此ID即上文提到的pci_device_id。
此时,(a)图就代表着所有使用PCI总线的设备数据库。当设备驱动程序A被加载时,会调用pci_register_driver并提供 pci_driver实例与PCI层注册,同时pci_driver结构中包含一个驱动程序所能处理的设备ID表(即e1000_pci_tbl);接着,PCI子系统使用该表去查在已经保存的设备数据库中是否存在匹配,于是会建立该驱动程序的设备列表,如图(b)所示;此外,对每个匹配的设备而言,PCI层会调用相匹配的驱动程序中的 pci_driver结构中所提供的probe函数。