必威体育Betway必威体育官网
当前位置:首页 > IT技术

e1000网络驱动分析

时间:2019-07-15 12:15:24来源:IT技术作者:seo实验室小编阅读:82次「手机版」
 

e1000

e1000网络驱动分析

e1000是intel千兆以太网卡的驱动源码。官方关于驱动的使用可以参考如下链接。

https://www.intel.cn/content/www/cn/zh/support/articles/000006866/network-and-i-o/ethernet-products.html

本文主要结合e1000驱动的源码进行分析. 版本是#define DRV_VERSION "7.3.21-k8-NAPI",这个是ubuntu4.4.0-63版本上的内核。代码文件如下几个:

e1000_ethtool.c

e1000.h

e1000_hw.c

e1000_hw.h

e1000_main.c

e1000_osdep.h

e1000_param.c

makefile

1.2.1 加载和卸载

驱动加载时候先运行e1000_init_module函数,主要实现注册到PCI子系统中,代码如下。

static int __init e1000_init_module(void)

{

int ret;

   pr_info("%s - version %s\n", e1000_driver_string, e1000_driver_version);

      pr_info("%s\n", e1000_copyright);

   ret = pci_register_driver(&e1000_driver);

if (copybreak != COPYBREAK_DEFAULT) {

   if (copybreak == 0)

           pr_info("copybreak disabled\n");

   else

                   pr_info("copybreak enabled for "

                      "packets <= %u bytes\n", copybreak);

   }

return ret;

}

module_init(e1000_init_module);

   通过调用pci_register_driver函数注册e1000_driver(为pci_driver为驱动主结构)驱动,将其注册到PCI设备的链表中。

具体如下:

pci_register_driver会调用

__pci_register_driver(drivers/pci/pci-driver.c)函数,该函数调用driver_register函数. driver_register会检测是否已在总线上已经注册或是否需要更新,没问题就问题就调用bus_add_driver函数,bus_add_driver将驱动加到总线上,然后调用pci_driver中定义的probe函数。

还有driver_add_groups函数创建组,kobject_uevent函数发送一个uevent通知用户层。

部分原型如下:

int __pci_register_driver(struct pci_driver *drv, struct module *owner,

             const char *mod_name)

   我们知道在bus_add_driver函数中,会调用pci_driver结构体指向的probe函数。e1000的probe函数是e1000_probe,该函数执行完也就是驱动和设备绑定了, e1000_probe函数完成设备初始化工作,包括pci_dev结构体,操作系统初始化,e1000_adapter结构体配置,和硬件重启工作。设备device也初始化完了。

   卸载是e1000_exit_module函数,直接调用pci_unregister_driver,和pci_register_driver函数是对立的。

static void __exit e1000_exit_module(void)

{

   pci_unregister_driver(&e1000_driver);

}

module_exit(e1000_exit_module);

驱动支持的参数是copybreak,默认是256.

module_param(copybreak, uint, 0644);

MODULE_PARM_DESC(copybreak,

     "Maximum size of packet that is copied to a new buffer on receive");

1.2.2 网络驱动关键数据结构

设计到的主要结构体有pci_dev,e1000_adapter和net_device。

pci_dev结构是网卡的配置空间和I/O与内存区域,net_device结构则向内核提供了操作网卡的抽象接口。

e1000_adapter结构除了体现相应的硬件无关性外,还管理了发送与接收数据包的相应缓冲空间,网卡的物理地址空间映射后的虚拟地址也在此结构中保存。

e1000_adapter结构中的e1000_hw结构主要保存网卡的硬件参数,其值就是通过读取pci_dev的内容获取而来的。

pci_device_id: PCI Device ID Table

e1000_adapter:设备私有数据结构,定义在e1000.h中。

e1000_hw:硬件相关的结构体,定义在e1000_hw.h中。

pci_driver表示pci驱动。

static struct pci_driver e1000_driver = {

   .name     = e1000_driver_name,//驱动的名字,必须唯一

   .id_table = e1000_pci_tbl,//指向pci_device_id表的指针

   .probe    = e1000_probe,//驱动的probe函数指针,被PCI核心调用。

   .remove   = e1000_remove,//当有pci_dev从系统移除时候调用函数的指针,或驱动被卸载时

#ifdef CONFIG_PM                  //如果定义电源管理

/* Power Management Hooks */

   .suspend  = e1000_suspend,//pci_dev被挂起时调用的指针。

   .resume   = e1000_resume,//pci_dev恢复时调用的函数指针

#endif

   .shutdown = e1000_shutdown,//系统关闭时候调用

   .err_handler = &e1000_err_handler//错误处理句柄

};

static const struct pci_ERROR_handlers e1000_err_handler = {

   .error_detected = e1000_io_error_detected,

   .slot_reset = e1000_io_slot_reset,

   .resume = e1000_io_resume,

};

pci_error_handlers结构体是错误发现,插槽重启的函数句柄。

   其中net_device_ops的定义如下, 包含了网络设备接口操作函数:

static const struct net_device_ops e1000_netdev_ops = {

   .ndo_open               = e1000_open,

   .ndo_stop               = e1000_close,

   .ndo_start_xmit         = e1000_xmit_frame,

   .ndo_get_stats          = e1000_get_stats,

   .ndo_set_rx_mode        = e1000_set_rx_mode,

   .ndo_set_mac_address    = e1000_set_mac,

   .ndo_tx_timeout         = e1000_tx_timeout,

   .ndo_change_mtu         = e1000_change_mtu,

   .ndo_do_ioctl           = e1000_ioctl,

   .ndo_validate_addr      = eth_validate_addr,

   .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,

   .ndo_vlan_rx_kill_vid   = e1000_vlan_rx_kill_vid,

#ifdef CONFIG_NET_POLL_controller

   .ndo_poll_controller    = e1000_netpoll,

#endif

   .ndo_fix_features       = e1000_fix_features,

   .ndo_set_features       = e1000_set_features,

};

   用来填充net_device结构体。

结构体e1000_buffer是buffer_info缓冲块数组的单元,用与接收从硬件传送过来的数据(是skb的包裹函数)

struct e1000_buffer {

struct sk_buff *skb;

   dma_addr_t dma;

struct page *page;

unsigned long time_stamp;

   u16 length;

   u16 next_to_watch;

unsigned int segs;

unsigned int bytecount;

   u16 mAPPed_as_page;

};

   结构体e1000_tx_ring指向ring内存的描述符。

struct e1000_tx_ring {

/* pointer to the descriptor ring memory */

void *desc;

/* physical address of the descriptor ring */

   dma_addr_t dma;

/* length of descriptor ring in bytes */

unsigned int size;

/* number of descriptors in the ring */

unsigned int count;

/* next descriptor to associate a buffer with */

unsigned int next_to_use;

/* next descriptor to check for DD status bit */

unsigned int next_to_clean;

/* array of buffer information structs */

struct e1000_buffer *buffer_info;

   u16 tdh;

   u16 tdt;

bool last_tx_tso;

};

借用《linux-千兆网卡驱动实现机制浅析》一张图如下,比较直观的说明了e1000_adaper,e1000_rx_ring,e1000_rx_desc,e1000_buffer结构体之间的关系:

   其中缓冲块的初始化由函数e1000_tx_map实现。

1.2.3 设备打开和关闭

根据e1000_netdev_ops结构体中的定义,打开设备使用的函数为e1000_open。当网络接口激活的时候被调用。

   关闭函数为e1000_close也定义在e1000_netdev_ops结构体中,用于禁用网络接口。

1.2.4 数据接收和发送

数据传输是net_device接口的关键组件。驱动程序提供一个hard_start_xmit函数,协议层在发送时调用它来向下传递数据包,现在也是定义在net_dev_ops结构体中如下:

static const struct net_device_ops e1000_netdev_ops = {

   .ndo_open               = e1000_open,

   .ndo_stop               = e1000_close,

   .ndo_start_xmit         = e1000_xmit_frame,

   .ndo_get_stats          = e1000_get_stats,

   .ndo_set_rx_mode        = e1000_set_rx_mode,

   .ndo_set_mac_address    = e1000_set_mac,

   .ndo_tx_timeout         = e1000_tx_timeout,

   .ndo_change_mtu         = e1000_change_mtu,

   .ndo_do_ioctl           = e1000_ioctl,

   .ndo_validate_addr      = eth_validate_addr,

   .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,

   .ndo_vlan_rx_kill_vid   = e1000_vlan_rx_kill_vid,

#ifdef CONFIG_NET_POLL_CONTROLLER

   .ndo_poll_controller    = e1000_netpoll,

#endif

   .ndo_fix_features       = e1000_fix_features,

   .ndo_set_features       = e1000_set_features,

};

可以看到发送函数为e1000_xmit_frame。

 现在的中断驱动程序都采用的是NAPI方式,需要提供poll函数,本驱动中是e1000_netpoll轮询函数。

   当有新数据包要发送时候,首先上层协议调用e1000_xmit_frame函数,然后在该函数中调用e1000_tx_queue来根据相应的参数找到缓冲块存放,缓冲块中有dma成员,表示该数据包所在的总线地址,控制总线会把内容映射到总线地址,然后由网卡传送出去。

   当有新数据包达到时,首先触动中断处理函数e1000_intr,该中断函数会将数据包放在buffer_info的缓冲块中(),就是讲总线地址指向的内容复制到skb中,然后根据skb中的协议将其传给上层协议的接收函数。

1.2.5 个别函数分析说明

e1000_get_hw_dev函数通过结构体e1000_hw来得到net_device.

e1000_request_irq函数,通过request_irq函数申请一个终端号,同时关联终端处理函数e1000_intr。

static int e1000_request_irq(struct e1000_adapter *adapter)

e1000_intr函数为终端处理函数,硬中断。

static irqreturn_t e1000_intr(int irq, void *data)

e1000_free_irq函数,调用free_irq释放中断。

e1000_irq_disable函数禁用中断。

e1000_setup_all_tx_resources函数分配发送描述符。

e1000_setup_all_rx_resources函数分配接收描述符

netif_carrier_on/netif_carrier_off/netif_carrier_ok网卡驱动程序通过来和内核网络子系统传递信息。netif_carrier_on,告诉内核子系统网络链接完整。netif_carrier_off,告诉内核子系统网络断开。netif_carrier_ok,查询网络断开还是链接。以上函数主要是改变net_device dev的state状态来告知内核链路状态的变化。

e1000_power_up_phy函数给物理设备上电。

1.2.6 单独编译e1000驱动模块

路径在drivers下,找到对应驱动如net,例如:

drivers/net/ethernet/intel/e1000

可以单独复制出来,

然后在Makefile中增加如下代码:

KERN_DIR = /usr/src/linux-headers-4.4.0-63-generic

all:

   make -C $(KERN_DIR) M=`pwd` modules

clean:

make -C $(KERN_DIR) M=`pwd` modules clean

-C 参数后面加config文件所在的文件夹,也可以是

KERN_DIR = /lib/modules/`uname -r`/build

M:参数后面是要编译的模块

1.2.7 参考

Linux-千兆网卡驱动实现机制浅析 关于PCIE的粗的理解可以查看此文

相关阅读

分享到:

栏目导航

推荐阅读

热门阅读