设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Linux系统中网卡驱动的加载流程

2012-3-12 15:50| 发布者: 红黑魂| 查看: 2673| 评论: 0|来自: 比特网

摘要: 在linux系统中,其网卡驱动大多通过PCI总线与系统相连,同时,内核对于所有PCI总线上的设备是通过PCI子系统来进行管理,通过PCI子系统提供各种PCI设备驱动程序共同的所有通用功能。因此,作为linux系统中的PCI网卡设 ...

linux系统中,其网卡驱动大多通过PCI总线与系统相连,同时,内核对于所有PCI总线上的设备是通过PCI子系统来进行管理,通过PCI子系统提供各种PCI设备驱动程序共同的所有通用功能。因此,作为linux系统中的PCI网卡设备,其加载的流程必然分为两个部分:作为PCI设备向PCI子系统注册;作为网卡设备向网络子系统注册。

  下面也将从两个方面,分析一下网卡驱动在内核加载的两个流程。

  PCI设备驱动程序的注册:

  PCI设备驱动程序使用pci_register_driver函数完成内核的注册,其定义在include/linux/pci.h文件内

  /*

  * pci_register_driver must be a macro so that KBUILD_MODNAME can be expanded

  */

  #define pci_register_driver(driver) \

  __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

  其主要结构是名为driver的pci_driver结构体,

  struct pci_driver {

  struct list_head node;

  const char *name;

  /* 驱动所关联的PCI设备 */

  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 (*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;

  };

  在pri_driver结构体中有两个比较关键的函数,分别是struct pci_device_id *id_table、以及probe函数。其中id_table是一个id向量表,内核通过其来把一些相关的设备关联到此驱动程序,PCI设备独一无二的识别方式是通过一些参数的组合,包含开发商以及模型等,这些参数都存储在内核的pci_device_id数据结构中:

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

  };

  对于每一个PCI设备驱动程序,在注册的时候都会把一个pci_device_id实例注册到内核中,这个实例向量就包含了此驱动程序所能处理的所有设备ID。

  另外一个probe函数,主要用于当PCI子系统发现它正在搜寻驱动程序的设备ID与前面所提到的id_table匹配,就会调用此函数。对于网络设备而言,此函数会开启硬件、分配net_device结构、初始化并注册新设备。

  总的来说,对于所有的PCI设备,在系统引导时,会建立一种数据库,把每个总线都关联一份已侦测并且使用该总线的设备列表。对于PCI设备来说,系统中就存在着这样一个数据库,其中保存着所有使用PCI总线的设备ID,此ID即上文提到的pci_device_id。以下图为例,来说明当设备驱动程序加载时会发生什么。

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

  网络设备的注册:

  作为一个PCI网络设备,再其完成PCI总线的注册后,剩下的工作就是通过probe函数来完成作为网络设备的注册流程。对于每一个网络设备在内核中都通过一个net_device结构来表示(此结构涉及内容较多,就不再细说),因此网络设备的注册流程从字面上也应该分为两个部分,net_device结构的分配、完成分配到的dev结构向网络子系统的注册。

  下面,首先说一下net_device结构的分配,在内核中通过alloc_netdev函数来完成分配

  #define alloc_netdev(sizeof_priv, name, setup) \

  alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)

  struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,

  void (*setup)(struct net_device *),

  unsigned int txqs, unsigned int rxqs)

  此函数的参数主要有三个:sizeof_priv,私有数据结构的大小,因为作为任一个网络设备都有一个net_device结构,但又由于设备的多样性,很难在dev结构中包含所有必需的字段,因此每一个设备一般都需要一个私有结构来保存一些自己的信息;name,设备名称,此名称可能只是一部分名称,由内核通过某种规则来完成,比如对于以太网设备,只知道是eth设备,但不知道具体的标号,此时可通过传递eth%d作为name参数,由内核完成具体的分配流程;setup,设置函数,此函数用于初始化net_device的部分字段,主要用于一些具有某种共同特性字段的初始化,比如对于以太网设备,其MTU等字段具有相同的值,因此可通过ether_setup来完成公共字段的初始化。

  对于Ethernet设备为例,其注册流程如上图所示,首先通过调用alloc_netdev的包裹函数,alloc_etherdev,来完成net_device函数的分配以及部分数据的初始化;接下来,通过netdev_boot_setup_check函数来检查加载内核时用户是否提供了任何引导期间参数;最终,新的设备net_device实例会利用register_netdevice插入至网络设备数据库。

  PS:对于系统中的所有net_device数据结构存在于三张表中,一个是全局的列表dev_base,另外两张hash表dev_name_head、dev_index_head。dev_base实例的全局列表能够方便内核浏览设备;dev_name_head是以设备名为索引,方面通过dev的名称搜寻到设备的dev结构;dev_index_head是以设备的ID,dev->ifindex为索引,方面通过设备的ID来搜寻设备的。

  对于一个完整的net_device结构的注册,register_netdevice实际上只完成了一部分工作,而将剩余部分的工作让netdev_run_todo予以完成。

  对于register_netdev函数的功能主要包括:

  。。。to be continued!


酷毙
1

雷人

鲜花

鸡蛋

漂亮

刚表态过的朋友 (1 人)

  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部