您的位置:首页 > 运维架构 > Linux

linux网卡驱动分析之驱动加载

2013-03-09 13:17 423 查看
本文以Intel e1000e网卡驱动为例,分析网卡驱动的基本流程,内核版本为2.6.18。

一、驱动注册

网卡是一种PCI设备,为了正确的注册到内核,所有的PCI设备驱动都必须创建一个主要的结构体

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命令加载驱动,这两个命令为应用程序,在应用程序里调用了一个系统调用:

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。

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区分不同的设备。

[b]1、MODULE_DEVICE_TABLE

[/b]

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设备都是热插拔设备,一般的网卡就不是热插拔设备。



[b]2、pci_register_driver
[/b]

一般的网卡不是热插拔设备,当驱动调用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)
在调用probe之前,内核遍历所有的PCI总线上的设备,调用match函数,在此match的实例为pci_bus_match,看是否有设备跟e1000_pci_tbl中支持的设备匹配:

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,
};
pci_bus_match最终调用pci_match_one_device函数进行匹配:

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;
}
如果系统中存在多个相同的设备,就会多次调用驱动中的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函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: