学习交流加
- 个人qq:
1126137994- 个人微信:
liu1126137994- 学习交流资源分享qq群:
962535112
改造iMX6(fec)网卡驱动程序前期工作之:阅读ethercat-1.5.2.pdf文档的第四章内容。
ethercat-1.5.2.pdf文档链接:
前期我已经将iMX6网卡驱动程序整体分析了一遍(点击链接查看之前分析的网卡驱动程序),今天来阅读ethercat官方文档,看应该如何修改iMX6网卡驱动程序,从而保证数据传输的实时性。
分析网络设备驱动程序的基本功能阅读ethercat官方文档:点击下载文档
第四章:
Tasks of a Network Driver:
网络设备驱动程序通常处理OSI模型(下面有搜索的介绍什么是OSI模型)的较低两层,即物理层和数据链路层。一个网络设备(网口,phy芯片)就是处理物理层的数据的。它代表连接到介质的硬件,以物理层协议描述的方式发送和接收数据。而网络驱动程序是从内核的网络堆栈中获取数据,将数据传送到硬件(物理传输)。然后如果硬件接收到数据,它就会反馈给驱动程序(通过中断的方式),然后驱动程序通过读取硬件的存储器获取到反馈回来的数据,并将数据传送给内核的网络堆栈空间。还有就是一个网络设备驱动程序必须处理包括队列控制,统计和设备相关功能的功能。
4.1网络设备驱动程序的基础:
1.Interrupt Operation:
网络设备通常提供一个硬件中断,用于通知接收数据的驱动程序,是否数据传输成功或者失败。驱动程序必须注册一个中断服务例程(ISR),每次执行时,硬件都会发出这样的事件。如果中断是由自己的设备抛出(多个设备可以共享一个硬件中断),那么中断的原因必须通过读取设备的中断寄存器来确定。
2.The net_device Structure:
驱动程序为每个设备注册一个net_device结构,以与网络堆栈通信,并创建一个网络接口,在以太网驱动程序中,这个接口显示为ethX,其中X是在注册时由内核分配的数字。驱动程序为每个设备注册一个net_device结构,以与网络堆栈通信,并创建一个\网络接口。在以太网驱动程序中,这个接口显示为ethX,其中X是在注册时由内核分配的数字。net_device结构体通过几个回调接收事件(来自用户空间或来自网络堆栈),必须在注册之前设置。然而并非每个回调都是强制性的,但对于合理的操作,以下的回调是必要的:
open():当需要启动网络通信时,就会调用此函数,例如在用户空间设置了ethX之后,数据帧接收
必须被驱动程序开启。
stop():这个函数是关闭关闭“设备”,即是数据帧的接收停止
hard_start_xmit():这个函数将每一个要传送的数据帧的指针给一个sk_buffer结构体,传送完数据之后再释放
get_stats():这个函数返回一个指向设备的net_device_stats的结构体指针,每当收到一个帧,发
送或发生错误时,该结构中的相应计数器就必须增加。
重要内容相关解释:
The netif Interface:
网络堆栈中的所有其他通信都是通过netif_*()函数调用完成的。例如:当成功打开一个设备,必须通知网络堆栈,现在可以将数据帧传递给接口Interface,这是通过netif_start_queue()这个函数的调用完成的。接着调用hard_start_xmit()函数。 此外,网络驱动程序通常管理帧传输队列,如果这个传输队列被议案满了(fill up),调用:netif_stop_queue()告诉网络堆栈必须停止一段时间的数据帧的传输,当一些数据帧被发送了之后,通过调用netif_wake_queue()函数告知更多的数据过来排队等待被传送。还有一个重要的函数netif_receive_skb(),它将设备传来的数据传送到网络堆栈。其中数据帧是放在一个叫做套接字的缓冲区中(socket buffer)。
Socket Buffers:
套接字缓冲区是整个网络堆栈的基本数据类型。它们充当网络数据的容器,并且能够快速添加数据页眉和页脚,或者再次剥离它们。因此,套接字缓冲区由一个分配的缓冲区和几个标记缓冲区(头)以及数据开始,以数据尾和缓冲区结束的指针组成。此外,Socket Buffer由一个网络头和(在接收到数据的前提下)一个指向net_device的指针组成。存在创建套接字缓冲区(dev_alloc_skb())的函数,front(skb_push())或back(skb_put())用于添加数据
front(skb_pull()) 或(skb_trim())用于移除数据,
或者 删除缓冲区(kfree_skb())。
一个套接字缓冲区从一个层传递到另一个层,并被上一次使用它的层释放。 在发送的情况下,释放必须由网络驱动程序完成。
4.2Native EtherCAT Device Drivers(本地EtherCAT 设备驱动程序):
这种网卡驱动程序的话,是需要经过修改的非标准以太网驱动程序。那么它有什么特点呢?下面我们来讲解:
1)Dedicated Hardware(专用硬件)
为了实现高性能和实时性,EtherCAT主站需要直接访问以太网硬件。这意味着网络设备一定不能像往常一样连接到内核的网络堆栈,因为内核会尝试把它用作普通的以太网设备。
2)Interrupt-less Operation(无中断操作)
EtherCAT数据帧是通过逻辑EtherCAT环进行传送与接收的。通信是高度确定性的:一个帧被发送,并在一段时间后再次被接收,不需要告诉驱动程序帧接收。用mster(主机)代替硬件驱动程序去查询接收的帧。
下图显示了有和没有中断的循环帧传送和接收的两个工作流程:
在左边的工作流程“中断操作”中,首先处理来自上一个周期的数据,并且用新的数据报组装一个新的帧,这个数据帧是一会要被发送的。当循环工作完成后,又从硬件传来了一个数据帧,此时硬件中断被触发,并执行中断服务程序(ISR)。ISR将从硬件中取出帧数据并启动帧解析:数据报将被处理,以便数据在下一个周期中准备好处理。
在右边的工作流程“无中断操作”中,没有硬件中断使能,而是由主站通过执行ISR轮询硬件,如果在此期间收到数据帧,则该数据帧将被解析。然后剩下的就跟左边的操作一样:接收到的数据被处理,一个新的帧被包装和发送,剩余的周期没有任何事情要做。
无中断操作是可取的,因为硬件中断不利于驱动程序的实时性。因为硬件中断不确定的发生率,会导致增加抖动。此外,如果使用实时扩展(如RTAI),则需要做一些额外的工作来优先化中断。
3)Ethernet and EtherCAT Devices(以太网和Ethercat设备)
另一个问题在于Linux处理相同类型的设备的方式。例如:PCI驱动程序扫描PCI总线以查找可处理的设备。然后它将自己注册成为所有找到的设备的驱动程序。问题是:一个没有被修改的驱动程序不能忽略一个有可能即将被使用的设备(即我们的Ethercat设备),这样驱动程序就得等待Ethercat设备,那么我们的普通的网络设备就无法被驱动了。必须有一种方法来处理同一类型的多个设备,其中一个为EtherCAT保留,而另一个则视为普通的以太网设备。
出于所有这些原因,作者认为唯一可接受的解决方案是修改标准以太网驱动程序,使其保持正常的功能,但能够将一个或多个设备视为支持EtherCAT的设备。
下面是这个解决方案的有点:
•无需告诉标准驱动程序忽略某些设备。
•一个用于EtherCAT和非EtherCAT设备的网络驱动程序。
•无需从头开始实施网络驱动程序,前开发者已经解决了。
该解决方案的缺点:
•修改后的驱动程序变得更加复杂,因为它必须处理EtherCAT和非EtherCAT设备。
•驱动程序代码中还有许多额外的个案区分。
•标准驱动程序的更改和 须不时地移植到支持EtherCAT的版本
4.3Generic EtherCAT Device Driver(通用的Ethercat设备驱动程序)
该方案的话,使用的是标准的以太网网卡驱动程序,不要修改。但是我们还是过来分析一下,为什么这样做性能会差一些。
下图是通用以太网驱动模块:
这张图非常的经典。它通过网络堆栈连接到本地以太网设备,内核模块被命名为ec_generic,它在主模块之后加载,例如具有本地EtherCAT功能的以太网驱动程序。通用设备驱动程序扫描网络堆栈接口,一旦有设备驱动程序被注册了,它就将其提给Ethercat主站。如果主站接受了一个设备,那么通用驱动程序就会创建一个 packet socket (包套接字),socket_type设置为SOCK_RAW绑定到该设备上,设备接口的所有功能(见4.6节)将在该套接字上运行。
该方案的优点:
•可以使用Linux以太网驱动程序覆盖的任何以太网硬件
用于EtherCAT。
•不必对实际的以太网驱动程序进行修改。通用
该方案的缺点:
•性能比本地方法稍差,因为该框架数据必须遍历网络堆栈的较低层。
•不能像RTAI一样使用内核实时扩展驱动程序,因为网络堆栈代码使用动态内存分配和其他的事情,这可能会导致系统在实时的情况下冻结。
设备激活:
为了通过套接字发送和接收帧,链接到该套接字的以太网设备必须被激活,否则所有的帧都将被拒绝。激活必须在主模块被加载之前发生并且可以有多种方式操作:
•Ad-hoc,使用命令ip link set dev ethX up(或使用常用的命令:ifconfig ethX up)
•根据分布情况进行配置,例如使用ifcfg文件(/ etc/ sysconfig / network / ifcfg-ethX)在openSUSE和其他。这是更好的方式如果EtherCAT主站在系统启动时启动。但是以太网设备只能被激活,不能分配IP地址,使用STARTMODE = auto作为配置就足够了。
(我之前直接使用:ifconfig eth0 up)
4.4Providing Ethernet Devices(提供以太网设备)
加载主模块后,必须加载其他模块设备连接到主设备(参见4.6节),主模块要可以从模块参数判断要接收的设备。 如果使用init脚本来启动master,则可以在sysconfig文件中指定要使用的驱动程序和设备(请参阅第7.4.2小节)。
提供以太网设备的模块可以是:
•具有本地EtherCAT功能的网络驱动程序模块(请参阅第4.2节)或
•通用EtherCAT设备驱动程序模块(请参阅第4.3节)。
4.5Redundancy(冗余)
Redundancy的意思是主站与从站之间有读个网络链接。并且数据的传输是在每一个主链路上发出去。这样的话,即使某一个主站与从站链接断开了,仍然可以保证数据的完整传送。
完全冗余总线操作的先决条件是每个从设备至少可以通过一个主设备连接。在这种情况下,单个连接失败(即电缆中断)将永远不会导致不完整的过程数据。两个以太网设备无法处理双重故障。
在配置时使用–with-devices开关配置冗余(参见第9章),并使用ec_master内核模块的backup_devices参数(参见2.1节)或(sys-)配置文件中的相应变量MASTERx_BACKUP(参见第7.4.2)。
总线扫描是在任何以太网链路上进行拓扑更改后完成的。应用程序接口(请参阅第3章)和命令行工具(请参阅第7.1节)都有查询冗余操作状态的方法。
4.6 EtherCAT Device Interface(EtherCAT )
为了理解网络设备驱动程序模块可以将设备连接到特定的EtherCAT主站的方式,需要对主站模块(2.1节)进行预测。
主模块为网络设备驱动程序提供了一个“设备接口”,要使用该接口,网络设备驱动程序模块必须包含带有EtherCAT主代码的头文件devices / ecdev.h,该头文件为EtherCAT设备提供了一个功能接口 。设备接口的所有功能都以前缀ecdev命名。
设备接口的文档可以在头文件或接口文档的相应模块中找到(请参见第9.3节的生成说明)
4.7 Patching Native Network Drivers(修改本地网卡驱动程序)
好了,上面分析了那么多,这里终于给出了如何修改网卡驱动程序让它支持Ethercat协议了。
参4.2节的内容,下面给出需要修改的地方:
1.第一条简单的规则是,所有EtherCAT都必须避免netif _ *()调用设备。如前所述,EtherCAT设备没有连接到
网络协议栈,因此不能调用其接口函数。
2.禁用中断:EtherCAT设备应该在没有运行的情况下运行中断。所以任何调用中断处理程序和启用中断的调用
在硬件层面也必须避免。
3.标准网卡驱动程序中,将数据包发送后将释放数据包所占用的内存,或者放回预先分配的内存池中。而EtherCAT通信中主站不会为每个发送操作使用新的套接字缓冲区,而是在主站初始化时分配一个缓冲区重复使用。这个套接字缓冲区在每次发送操作时都填入一个EtherCAT数据帧并传递给EtherCAT的hard_start_xmit()函数回调。为此,套接字缓冲区是必要的不像平常那样被网络驱动程序释放。
注:一个以太网驱动程序通常处理几个以太网设备,每个设备被一个带有私有数据priv_data字段的net_device结构体描述,将依赖于该驱动程序的数据附加到该net_device结构体,为了区分正常的以太网设备和EtherCAT主设备使用的设备,驱动程序使用的私有数据结构可以通过一个指针扩展,该指针指向一个由ecdev_offer()返回的ec_device_t对象(参见4.6节)。 RealTek RTL-8139快速以太网驱动程序是一个“简单的”以太网驱动程序,可以作为修补新驱动程序的一个例子,后面我就会参考这个例子,来修改我的网卡驱动程序。
补充:什么是OSI模型:点击查看OSI七层模型详解