热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

双网口驱动实现流程

1网络设备驱动的结构Linux网络设备驱动程序体系结构如下图,从上到下依次划分为4层,依次为网路协议接口层、网络设备接口层,提供实际功能的设备驱动功能层以及网络设备与媒介层。

1网络设备驱动的结构

    Linux网络设备驱动程序体系结构如下图,从上到下依次划分为4层,依次为网路协议接口层、网络设备接口层,提供实际功能的设备驱动功能层以及网络设备与媒介层。

 

 

(1) 网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的设备。

(2) 网络设备接口层向网络协议接口层提供统一的、用于描述具体网络设备属性和操作的结构体net_device,该结构体也是设备驱动功能层各个函数的容器。网络设备接口层从整体上规划了具体操作硬件的设备驱动功能层的结构。

(3) 设备驱动功能层各个函数是网络设备接口层定义的net_device结构体的具体成员,是驱动网络设备硬件完成相关功能的层序,到达该层的网络数据通过hard_start_xmit()函数发送到网络设备与媒介层,并通过网络设备上的中断触发接收从网络设备与媒介发送过来的网络帧。

(4) 网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数物理上驱动。

   

在设计具体的网络设备驱动程序时,我们要完成的主要工作是编写设备驱动功能层的相关函数以填充net_device结构体的内容并将net_device结构体注册到linux内核。

 

2 设备驱动功能层的代码实现

根据3.2.1的理论基础,找到设备驱动功能层所在的路径为

“/home/seen/pdb/uClinux-dist/linux-2.4.x/drivers/net”

2.2.1网络设备驱动的注册与注销

    网络设备驱动的注册与注销使用成对出现的register_netdev()和unregister_netdev()函数完成。这两个函数的原型为:

int register_netdev(struct net_device *dev);//注册

void unregister_netdev(struct net_device *dev);//注销

这两个函数都接收一个net_device结构体指针为参数,可见net_device数据结构在网络设备驱动中的核心地位。

2.2.2网络设备驱动程序的模块加载函数

    net_device结构体的分配和网络设备驱动注册需要在网络设备驱动程序的模块加载函数中进行,而net_device结构体的释放和网络设备的注销则需要在模块卸载函数中完成,代码如下所示.

int init_module(void)  //模块加载函数

{

int ret;

 

memset((void *)xxx_netdevice[0].name ,0 ,IFNAMSIZ);

ret=register_netdev((struct net_device *)&xxx_netdevice[0]);//注册

if(ret!=0)

{

printk("Regiter EMC 0 xxx FAILED\n");

TRACE_ERROR("Regiter EMC 0 xxx FAILED\n");

return  -ENODEV;

}

 

return 0;

}

 

void cleanup_module(void)  //模块注销函数

{        

   unregister_netdev((struct net_device *)&xxx_netdevice[0]);//注销

}

 

2.2.3网络设备的初始化

网络设备的初始化主要完成如下几个方面的工作:

(1) 进行硬件上的准备工作,检查网络设备是否存在,如果存在,则检查设备所使用的硬件资源。

(2) 进行软件接口上的准备工作,分配net_device结构体并对其数据和函数指针成员赋值。

(3) 获得设备的私有信息指针并初始化其各成员的值。如果私有信息中包括自旋锁或信号量等并发或同步操作,则需对其进行初始化。

 

网络设备初始化代码如下:

int xxx_init(struct net_device *dev)

{

static int which=0 ;     //Only one mac for NUC700

struct xxx_priv *priv; //设备私有信息结构体

 

ether_setup(dev);       //初始化以太网设备的公用成员

dev->open=xxx_open;//网络设备的打开

dev->stop=xxx_close;//网络设备的关闭

dev->do_ioctl=xxx_do_ioctl;

dev->hard_start_xmit=xxx_start_xmit;//send packets which were received from up layer

dev->tx_timeout=xxx_timeout;  //发送超时函数

dev->get_stats=xxx_get_stats;  //设备状态和信息统计

dev->watchdog_timeo =TX_TIMEOUT; //HZ

dev->irq=INT_EMCTXINT0+which;

dev->set_multicast_list=xxx_set_multicast_list;

dev->set_mac_address=xxx_set_mac_address;//设置设备MAC地址

dev->priv=(void*)(((unsigned long)kmalloc(sizeof(struct xxx_priv),GFP_KERNEL))|NON_CACHE_FLAG);

if(dev->priv == NULL)

return -ENOMEM;

memset(dev->priv, 0, sizeof(struct xxx_priv));

   memcpy(xxx_mac_address0,(char*)(MAC_ADDR),ETH_ALEN);

    memcpy(dev->dev_addr,xxx_mac_address0,ETH_ALEN);

    priv=(struct xxx_priv *)dev->priv;

    priv->which=which;

    priv->cur_tx_entry=0;

    priv->cur_rx_entry=0;

    printk("%s initial ok!\n",dev->name);

return 0;

 

}

2.2.4网络设备open函数

  网络设备打开函数主要完成以下工作:

(1) 使能设备的硬件资源,申请I/O区域,激活DMA通道等。

(2) 调用linux内核提供的netif_start_queue()函数,激活设备发送队列。

int xxx_open(struct net_device *dev)

{

申请端口、IRQ

#if HUBMODEL  //hub model.

Hub_Init(dev,which);//hub相关寄存器使能。

#else //router model

Router_Init(dev,which);//router相关寄存器使能

#endif…

request_irq(INT_EMCTXINT0+which,&tx_interrupt,SA_INTERRUPT,"",dev)   //发包中断

irequest_irq(INT_EMCRXINT0+which,&rx_interrupt,SA_INTERRUPT,"",dev)  //收包中断,当有数据包到达网口时,调用该中断函数处理接   收数据包

 netif_start_queue(dev);    //激活发送队列

}

2.2.5  安全芯片HUB模式相关寄存器使能

void Hub_Init(struct net_device *dev,int num)

{

}

1.2.5 安全芯片ROUTER模式相关寄存器使能

void  Router_Init(struct net_device *dev,int num)

{

}

2.2.6发送网络包

   如4.1网络设备驱动结构的流程图所示,网络设备驱动完成数据包的发送流程如下:

   (1) 网络设备驱动层序从上层协议传递过来的sk_buff参数中获得数据包的有效数据和长度,将有效数据放入临时缓冲区。

 (2)对于以太网,如果有效数据有效长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填0.

 (3)设置硬件的寄存器,驱使网络设备进行数据发送操作。

对于本工程而言,发送数据包的函数原型为:

int xxx_start_xmit(struct sk_buff *skb, struct net_device *dev)

该函数对应于dev->hard_start_xmit

 

static int xxx_init(struct net_device *dev)

{

.......

 

//发送数据包

dev->hard_start_xmit=nuc700_start_xmit;

 

.......

}

    对于发送数据包函数xxx_start_xmit()来说,此时的数据包是最完整的ip网络数据包,通过解析数据包的目标ip地址,来决定数据包最终向wan口转发,还是向lan口转发。需要向数据包中加入2个字节的flagsflag值为0x8004表示向lan口转发数据,flag值为0x80A0表示向wan口转发数据。

 

2.2.7 接收网络包

     如4.1网络设备驱动结构的流程图所示,当设备的中断处理程序rx_interrupt()判断中断类型为数据包接收中断时,则调用函数netdev_rx()完成更深入的数据包接收工作。

      void netdev_rx(struct net_device *dev)

netdev_rx()做了三件事情:

首先,从硬件读取到接收数据包有效数据的长度;

然后,读取硬件上接收的数据并放入数据缓冲区;

最后,将数据包上交给上层协议。

接收网络包部分的代码比较易懂,这里我就不再列出。可以阅读网络驱动内核源码相关部分。

 

2.2.8网线插拔状态监测

    顾名思义,驱动层能够要知道当前2个网口的网线是否连接正常。

通过一个定时器,每经过若干个时钟周期检测一下网口的硬件连接状态。

xxx_autodetect()具体实现了这一功能。具体见源码,这里不再说明。

 

3应用层与设备驱动层通信机制

     通过http设置RouterHub模式的切换,通过ioctl来告知kernel层这一变化。Kernel层根据这一变化决定数据包如何转发。

当通过http方式“Enable”路由模式时,协议层通过html_para()获知这一信息。html_para()调用ioctlioclt通过SIOICCONFIGROUTER命令字将这一消息告诉kernel层。

对于kernel层来说是通过xxx_do_ioctl()这一函数来实现,该函数包含一个switch语句,用来解析协议层传递过来的命令字,当发现命令字为SIOICCONFIGROUTER时,会把全局变量router_model1,然后kernel层就开始以Router的方式对网络包经行转发。

 

skb_put(skb,len)skb_pushskb,len)的区别

skb_put() 增长数据区的长度来为memcpy准备空间许多的网络操作需要加入一些桢头这可以使用skb_push来将数据区向后推为头留出空间. 

 

请参见下图: 

---------------------------------------- 

| head | data | | 

---------------------------------------- 

 

skb_put 

----------------------------------------- 

| head | data | put_data | | 

----------------------------------------- 

 

skb_push 

------------------------------------------ 

| head | push_data | data | | 

------------------------------------------

 


推荐阅读
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • Linux环境下进程间通信:深入解析信号机制
    本文详细探讨了Linux系统中信号的生命周期,从信号生成到处理函数执行完毕的全过程,并介绍了信号编程中的注意事项和常见应用实例。通过分析信号在进程中的注册、注销及处理过程,帮助读者理解如何高效利用信号进行进程间通信。 ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • Linux环境下C语言实现定时向文件写入当前时间
    本文介绍如何在Linux系统中使用C语言编程,实现在每秒钟向指定文件中写入当前时间戳。通过此示例,读者可以了解基本的文件操作、时间处理以及循环控制。 ... [详细]
  • 本文介绍了如何在 C# 和 XNA 框架中实现一个自定义的 3x3 矩阵类(MMatrix33),旨在深入理解矩阵运算及其应用场景。该类参考了 AS3 Starling 和其他相关资源,以确保算法的准确性和高效性。 ... [详细]
  • CentOS 6.8 上安装 Oracle 10.2.0.1 的常见问题及解决方案
    本文记录了在 CentOS 6.8 系统上安装 Oracle 10.2.0.1 数据库时遇到的问题及解决方法,包括依赖库缺失、操作系统版本不兼容、用户权限不足等问题。 ... [详细]
  • 本文介绍了Linux系统中的文件IO操作,包括文件描述符、基本文件操作函数以及目录操作。详细解释了各个函数的参数和返回值,并提供了代码示例。 ... [详细]
  • 树链问题的优化解法:深度优先搜索与质因数分解
    本文介绍了一种通过深度优先搜索(DFS)和质因数分解来解决最长树链问题的方法。我们通过枚举树链上的最大公约数(GCD),将所有节点按其质因子分类,并计算每个类别的最长链,最终求得全局最长链。 ... [详细]
  • 在创建新的Android项目时,您可能会遇到aapt错误,提示无法打开libstdc++.so.6共享对象文件。本文将探讨该问题的原因及解决方案。 ... [详细]
  • 本题探讨了在大数据结构背景下,如何通过整体二分和CDQ分治等高级算法优化处理复杂的时间序列问题。题目设定包括节点数量、查询次数和权重限制,并详细分析了解决方案中的关键步骤。 ... [详细]
  • 本文详细介绍了如何使用 PHP 接收并处理微信支付的回调结果,确保支付通知能够被正确接收和响应。 ... [详细]
  • 本文介绍如何利用栈数据结构在C++中判断字符串中的括号是否匹配。通过顺序栈和链栈两种方式实现,并详细解释了算法的核心思想和具体实现步骤。 ... [详细]
  • HDU 2871 内存管理问题(线段树优化)
    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2871。本题涉及内存管理操作,包括重置、申请、释放和查询内存块。通过使用线段树进行高效管理和维护。 ... [详细]
author-avatar
sprout--_557
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有