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的粗的理解可以查看此文

e1000网络驱动分析相关推荐

  1. linux网卡e1000下载,Linux E1000网卡驱动分析

    本分析主要针对e1000网卡,驱动源码为7.3.20-k2.本文的目的不是为了讲述如何编写驱动程序,主要是分析网卡驱动内部的实现机制. Linux-千兆网卡驱动实现机制浅析 作者: Minit, 出处 ...

  2. 6.S081 lab: networking e1000 网卡驱动 附 Linux 网卡驱动编写分析

    本文是 6.S081 操作系统课程学习最后一个 lab,编写一个 intel 的 e1000 网卡的驱动在 xv6 下.需要复习知识有:操作系统知识,计算机组成原理 DMA 相关,循环缓冲区的概念,e ...

  3. linux 源码 网络驱动,Linux网络驱动源码分析(一)

    功能:注册PCI驱动,参数为要注册的pci驱动的结构体. 下面来详细的分析以下这个函数,如此,才能更清楚的了解驱动和设备的匹配过程. pci_register_driver->driver_re ...

  4. linux网络驱动lookback,Linux lookback驱动分析

    Linux的网络驱动中,lookback 驱动算是最为简单的.本次分析的程序来自 Linux-2.6.32.68 源码,其中 lookback.c 驱动程序位于 /drives/net/ 目录下. 普 ...

  5. Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)

    在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层 ...

  6. linux网络-网卡驱动分析(基于imx6ul和ZYNQ分析)

    0.说明 内核网络驱动总结,从设备树到内核驱动加载初始化及网卡通信整个流程. 1.环境 1.1 硬件环境 NXP imx6ul 平台 1.2 参考资料 IMX6ULLRM.pdf   22 章 10/ ...

  7. VxWorks网络驱动配置及分析

       VxWorks支持两种形式的网络驱动,一种是BSD驱动支持通用的BSD4.4网络,API,结构等和大多数BSD网络的驱动类似.另一种是END网络驱动,是VxWorks独有的,根据VxWorks ...

  8. VxWorks网络驱动配置及分析 来源:http://www.cublog.cn/

    VxWorks支持两种形式的网络驱动,一种是BSD驱动支持通用的BSD4.4网络,API,结构等和大多数BSD网络的驱动类似.另一种是END网络驱动,是VxWorks独有的,根据VxWorks MUX ...

  9. dpdk pmd驱动分析

    缘起dpdk_devbind.py 运行dpdk程序之前我们一般都会使用dpdk_devbind.py程序对指定的驱动与设备进行绑定与解绑(如下命令).那么dpdk_devbind.py程序是如何实现 ...

最新文章

  1. VS中dll以及lib生成路径设置
  2. 剪映导出帧率选多少_剪映帧率怎么设置?剪映帧率设置方法
  3. web公选课第三节2020.5.18
  4. C++ 中explicit的使用
  5. 准确检测图像的轮廓 opencv_图像处理案例实战
  6. PMT_Stream数据结构
  7. 1.Ehcache(01)——简介、基本操作
  8. 链栈的入栈和出栈代码_代码简介:全栈开发仍然有效
  9. html巡检脚本,WEB服务器巡检脚本
  10. facebook加密货币项目_Facebook数字货币:Libra的起源—加密货币(比特币)
  11. 博弈论1(正则型博弈)
  12. 网站运营活动策划方案模板
  13. 滨江机器人餐厅_滨江机器人的视觉效果好吗?
  14. Class 学习 (Es6阮一峰)
  15. 深度遍历和广度遍历(图解)
  16. Adobe Premiere基础-炫酷文字快闪(十四)
  17. 产业驱动消费升级 百果园获品途 2018“商业变革影响力”奖
  18. 微信公众平台编辑器教程-微信公众号使用教程32
  19. 《此生,若你安好.便是晴天》 -- @ShinePug
  20. 【HarmonyOS】鸿蒙3.0使用WebView进行链接跳转,告警“hwbr_engine_AwContentsClient: Denied starting an intent without a

热门文章

  1. springboot集成artemis
  2. 【HDU】5761 Rower Bo
  3. Linux 登录MySQL出现 unknown variable...错误
  4. 访问控制模型总结(DAC MAC RBAC ABAC)
  5. python中的max(x,key=str)
  6. Python-random函数用法
  7. 树莓派3安装win10arm 系统
  8. awk从入门到入土(19)awk扩展插件,让awk如虎添翼
  9. Could not load the Qt platform plugin “xcb“ 问题解决
  10. 华为eNSP模拟器操作技巧之关闭信息提示