热门标签 | 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 | | 

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

 


推荐阅读
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • FinOps 与 Serverless 的结合:破解云成本难题
    本文探讨了如何通过 FinOps 实践优化 Serverless 应用的成本管理,提出了首个 Serverless 函数总成本估计模型,并分享了多种有效的成本优化策略。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文详细记录了在银河麒麟操作系统和龙芯架构上使用 Qt 5.15.2 进行项目打包时遇到的问题及解决方案,特别关注于 linuxdeployqt 工具的应用。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 本文详细介绍了Grand Central Dispatch (GCD) 的核心概念和使用方法,探讨了任务队列、同步与异步执行以及常见的死锁问题。通过具体示例和代码片段,帮助开发者更好地理解和应用GCD进行多线程开发。 ... [详细]
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社区 版权所有