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

本文以Intel e1000e网卡驱动为例,分析网卡驱动的基本流程,内核版本为2.6.18。

一、驱动注册

网卡是一种PCI设备,为了正确的注册到内核,所有的PCI设备驱动都必须创建一个主要的结构体
struct pci_driver:
[cpp]  view plain copy
  1. struct pci_driver {  
  2.     struct list_head node;  
  3.     char *name;  
  4.     const struct pci_device_id *id_table;   /* must be non-NULL for probe to be called */  
  5.     int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);   /* New device inserted */  
  6.     void (*remove) (struct pci_dev *dev);   /* Device removed (NULL if not a hot-plug capable driver) */  
  7.     int  (*suspend) (struct pci_dev *dev, pm_message_t state);  /* Device suspended */  
  8.     int  (*resume) (struct pci_dev *dev);                   /* Device woken up */  
  9.     int  (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);   /* Enable wake event */  
  10.     void (*shutdown) (struct pci_dev *dev);  
  11.   
  12.     struct pci_error_handlers *err_handler;  
  13.     struct device_driver    driver;  
  14.     struct pci_dynids dynids;  
  15. };  

为了创建一个正确的struct pci_driver,至少要初始化四个字段:name,id_table,probe和remove。
[cpp]  view plain copy
  1. static struct pci_driver e1000_driver = {  
  2.     .name     = e1000e_driver_name,  
  3.     .id_table = e1000_pci_tbl,  
  4.     .probe    = e1000_probe,  
  5.     .remove   = __devexit_p(e1000_remove),  
  6.          ...  
  7. };  

为了把struct pci_driver注册到内核,需要调用pci_register_driver函数,通常在模块初始化函数module_init()函数中调用该函数:
[cpp]  view plain copy
  1. static int __init e1000_init_module(void)  
  2. {  
  3.     int ret;  
  4.     ret = pci_register_driver(&e1000_driver);  
  5.   
  6.     return ret;  
  7. }  

二、驱动加载

通过insmod或者modprobe命令加载驱动,这两个命令为应用程序,在应用程序里调用了一个系统调用:
[cpp]  view plain copy
  1. extern long init_module(void *, unsigned longconst char *);  
该系统调用最终调用内核函数:
[cpp]  view plain copy
  1. asmlinkage long  
  2. sys_init_module(void __user *umod,  
  3.         unsigned long len,  
  4.         const char __user *uargs)  
该函数调用驱动程序中用module_init定义的函数e1000_init_module。
[cpp]  view plain copy
  1. module_init(e1000_init_module);  

三、驱动如何匹配设备

pci_driver结构体中有一个成员id_table,其类型为struct pci_device_id *,pci驱动里都需要初始化该成员:
[cpp]  view plain copy
  1. struct pci_device_id {  
  2.     __u32 vendor, device;       /* Vendor and device ID or PCI_ANY_ID*/  
  3.     __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */  
  4.     __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */  
  5.     kernel_ulong_t driver_data; /* Data private to the driver */  
  6. };  
该结构中最重要的两个成员为vendor和device。vendor为厂商的ID,该值是唯一的,device为设备的ID。所有PCI设备的配置空间里都保存了这两个值。下面是PCI规范里对这两个字段的定义:
[cpp]  view plain copy
  1. Vendor ID      This field identifies the manufacturer of the device.   
  2.                Valid vendoridentifiers are allocated by the PCI SIG   
  3.                to ensure uniqueness.0 FFFFh is an invalid value for  
  4.                Vendor ID.  
  5. Device ID      This field identifies the particular device.  
  6.                This identifier is allocatedby the vendor.  
当同一个系统中的两个设备的vendor和device相同时,用subvendor和subdevice区分不同的设备。


1、MODULE_DEVICE_TABLE

[cpp]  view plain copy
  1. static DEFINE_PCI_DEVICE_TABLE(e1000e_pci_tbl) = {  
  2.     {  
  3.     PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571}, {  
  4.     PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571}, {  
  5.     PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571},  
  6.     {  
  7.     PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), board_82571},  
  8.  ...  
该结构体数组列出了所有该驱动支持的设备,主要是设备的Vender ID和Device ID,在pci_register_driver中会用到该结构体数组
 
驱动中还用宏MODULE_DRVICE_TABLE定义了一个变量:
[cpp]  view plain copy
  1. MODULE_DEVICE_TABLE(pci, e1000e_pci_tbl);  
上面的例子中,宏MODULE_DEVICE_TABLE的定义如下:
[cpp]  view plain copy
  1. #define MODULE_DEVICE_TABLE(type,name)      \  
  2.   MODULE_GENERIC_TABLE(type##_device,name)  
  3.   
  4. #define MODULE_GENERIC_TABLE(gtype,name)            \  
  5. extern const struct gtype##_id __mod_##gtype##_table        \  
  6.   __attribute__ ((unused, alias(__stringify(name))))  
该宏定义替换以后就变成如下语句:
[html]  view plain copy
  1. extern const struct pci_driver_id __mod_pci_driver_id_table  
  2.  __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文件来寻找要状态的恰当的驱动
上面结构体列出中所有的驱动支持的设备都在下面的文件里列出了:
[html]  view plain copy
  1. $cat modules.pcimap | grep e1000e  
  2. e1000e               0x00008086 0x0000105e 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  3. e1000e               0x00008086 0x0000105f 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  4. e1000e               0x00008086 0x000010a4 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  5. e1000e               0x00008086 0x000010bc 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  6. e1000e               0x00008086 0x000010a5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  7. e1000e               0x00008086 0x00001060 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  8. e1000e               0x00008086 0x000010d9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  9. e1000e               0x00008086 0x000010da 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  10. e1000e               0x00008086 0x000010d5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  11. e1000e               0x00008086 0x000010b9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0  
  12. ...  

上面的方法是针对热插拔设备的,但是,不是所有的PCI设备都是热插拔设备,一般的网卡就不是热插拔设备。

2、pci_register_driver


一般的网卡不是热插拔设备,当驱动调用pci_register_driver函数后,内核搜寻系统中的存在的设备,如果有匹配e1000_pci_tbl结构体数组中所列出的设备,就会调用驱动程序中的probe函数。调用过程如下:
[cpp]  view plain copy
  1. pci_register_driver  
  2. |  
  3. __pci_register_driver  
  4. |  
  5. bus_add_driver  
  6. |  
  7. driver_attach  
  8. |  
  9. bus_for_each_dev  /*遍历所有PCI总线上的设备*/  
  10. |  
  11.  __driver_attach  
  12. |  
  13. driver_probe_device  
  14. |  
  15. drv->bus->match(dev, drv)  
  16. |  
  17. dev->bus->probe(dev)  
在调用probe之前,内核遍历所有的PCI总线上的设备,调用match函数,在此match的实例为pci_bus_match,看是否有设备跟e1000_pci_tbl中支持的设备匹配:
[cpp]  view plain copy
  1. struct bus_type pci_bus_type = {  
  2.     .name       = "pci",  
  3.     .match      = pci_bus_match,  
  4.     .uevent     = pci_uevent,  
  5.     .probe      = pci_device_probe,  
  6.     .remove     = pci_device_remove,  
  7.     .suspend    = pci_device_suspend,  
  8.     .shutdown   = pci_device_shutdown,  
  9.     .resume     = pci_device_resume,  
  10.     .dev_attrs  = pci_dev_attrs,  
  11. };  
pci_bus_match最终调用pci_match_one_device函数进行匹配:
[cpp]  view plain copy
  1. static inline const struct pci_device_id *  
  2. pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)  
  3. {  
  4.     if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&  
  5.         (id->device == PCI_ANY_ID || id->device == dev->device) &&  
  6.         (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&  
  7.         (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&  
  8.         !((id->class ^ dev->class) & id->class_mask))  
  9.         return id;  
  10.     return NULL;  
  11. }  

如果有设备匹配了e1000_pci_tbl中的设备,就会调用pci_bus_type实例中的probe函数,在此probe的实例为pci_device_probe,最终调用函数pci_call_probe函数,该函数调用驱动程序中pci_driver实例中的probe函数:
[cpp]  view plain copy
  1. static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,  
  2.               const struct pci_device_id *id)  
  3. {  
  4.     int error;  
  5. ...  
  6.     error = drv->probe(dev, id);  
  7. ...  
  8.     return error;  
  9. }  
如果系统中存在多个相同的设备,就会多次调用驱动中的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函数。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Linux系统中重新加载网卡驱动的方法如下: 1. 打开终端,使用root权限登录系统。 2. 输入以下命令,查看当前系统中已加载网卡驱动: `lsmod | grep <网卡驱动名>` 其中,<网卡驱动名>是你要重新加载网卡驱动的名称。 3. 如果该网卡驱动已经加载,则需要先卸载该驱动,输入以下命令: `rmmod <网卡驱动名>` 其中,<网卡驱动名>是你要重新加载网卡驱动的名称。 4. 输入以下命令,重新加载网卡驱动: `modprobe <网卡驱动名>` 其中,<网卡驱动名>是你要重新加载网卡驱动的名称。 5. 输入以下命令,查看是否成功加载网卡驱动: `lsmod | grep <网卡驱动名>` 如果输出结果中包含该网卡驱动的名称,则说明加载成功。 6. 如果加载失败,可以尝试重启系统后再次尝试重新加载网卡驱动。 ### 回答2: Linux可以通过重新加载网卡驱动来解决网卡无法工作的问题。下面是具体的步骤: 1. 查看网卡驱动的状态。可以使用ifconfig命令来查看当前计算机的网络接口状态,如是否启动、IP地址等。也可以使用lspci命令来查看网卡的硬件信息,包括厂商、型号等。 2. 卸载网卡驱动。可以使用rmmod命令来卸载当前正在使用的网卡驱动,例如: sudo rmmod e1000e 其中,e1000e是一个网卡驱动的名称,具体名称需要根据当前系统的情况进行调整。 3. 加载新的网卡驱动。在卸载了原有的网卡驱动后,可以使用modprobe命令来加载新的网卡驱动,例如: sudo modprobe igb 其中,igb是一种常见的网卡驱动名称,也需要根据当前系统的情况进行调整。 4. 检查网络接口状态。重新加载网卡驱动后,需要再次使用ifconfig命令来检查网络接口的状态,确保网卡已经重新启动并且可以正常工作。 总体而言,重新加载网卡驱动是一种解决网卡问题的有效方法,但是需要注意的是,不同的系统和硬件环境下,网卡驱动的名称可能会有所不同,所以在具体操作时需要进行调整。同时,如果问题仍然无法解决,可能需要进一步检查硬件连接、网络配置等方面的问题。 ### 回答3: 为了重新加载 Linux 网卡驱动,我们需要执行以下步骤: 第一步,查询当前系统中可用的网卡设备: 首先使用以下命令查询系统中的网卡设备: # ifconfig, 此时会列出系统中已经配置好的网卡设备名称和配置信息,比如 IP 地址、子网掩码、广播地址等。 第二步,查看网卡驱动状态: 接下来使用以下命令查看网卡驱动的状态: # lsmod | grep eth 此命令用于查询当前系统中的网卡驱动。如果所有的网卡都没有被加载,则没有任何输出结果。 第三步,卸载网卡驱动: 如果需要重新加载网卡驱动,则需要先卸载已经加载网卡驱动。 # modprobe -r eth0 需要根据实际情况选择需要卸载的网卡驱动。 第四步,重新加载网卡驱动: 最后,我们可以使用以下命令重新加载网卡驱动: # modprobe eth0 需要根据实际情况选择需要重新加载网卡驱动。 完成以上步骤后,我们就成功地重新加载Linux网卡驱动。这种方法是解决 Linux 系统中网络连接问题的一种有效途径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值