Linux查看网卡加载驱动,linux网卡驱动分析之驱动加载

通过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 */

};

该结构中最重要的两个成员为ve

ndor和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

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函数。调用过程如下:

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。

0818b9ca8b590ca3270a3433284dd417.png

此时,(a)图就代表着所有使用PCI总线的设备数据库。当设备驱动程序A被加载时,会调用pci_register_driver并提供 pci_driver实例与PCI层注册,同时pci_driver结构中包含一个驱动程序所能处理的设备ID表(即e1000_pci_tbl);接着,PCI子系统使用该表去查在已经保存的设备数据库中是否存在匹配,于是会建立该驱动程序的设备列表,如图(b)所示;此外,对每个匹配的设备而言,PCI层会调用相匹配的驱动程序中的 pci_driver结构中所提供的probe函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值