对于 LwIP 协议栈的移植来说,用户的主要工作是为其提供网卡驱动函数。LwIP 可以运行在多种不同的硬件平台上,配合不同型号的网络 Phy 芯片使用。LwIP 为不同的网卡芯片提供了统一的抽象驱动接口,用户根据使用的网卡芯片以及硬件平台,实现功能函数。
作者:李凡
来源: https://zhuanlan.zhihu.com/p/61912306
用户需要实现三项功能驱动:网卡的初始化函数,接收函数以及发送函数。本文将基于 Xilinx LwIP 例程讨论网卡的接收驱动编写。
网卡接收驱动的工作方式可以分为两类:以轮询或者中断方式从网络 Phy 芯片中接收数据包。在 Xilinx SDK example 中,Xilinx 提供的驱动函数使用 DMA 搬运数据,以中断方式工作。
接收驱动函数需要完成两项工作:
- 将从 Phy 芯片接收到的数据转移给 LwIP 所在的应用程序。
- 将数据封装成 LwIP 的数据结构,一般为 pbuf 数据包结构体,并调整 pbuf 链的前后指向关系。
接收驱动的工作流程
从 DMA 中断到数据包指针压栈的过程
DMA 中断产生
接收驱动的工作从 DMA 产生中断起始。在 example 使用 PS 中的 psmac 硬件作为网络 mac。mac 的工作是通过 RGMII/GMII/MII 等接口与网卡 Phy 芯片通信,获得 Phy 上从网线中接收的数据包。数据包会通过 DMA 从 mac 硬件上的接收缓冲区搬运到接收缓冲指针数组指向的存储空间。
接收缓冲指针数组
在初始化 DMA 时,会初始化多个 block descriptor ,调用 LwIP 的内存分配函数为每个 bd 开辟一个 pbuf 空间。每个 bd 在数据搬运中对应一个 pbuf,将数据搬运到为自己开辟的 pbuf 空间中,关于 bd 将在后续 DMA 的文章中具体分析。
在完成一定量的数据搬移后,DMA 接收中断触发。
DMA 中断服务函数
接收中断服务函数根据搬移的数据包个数,从接收缓冲指针数组中读取数据包 pbuf 的指针,将 pbuf 指针压入一个先进先出队列。
这里讨论下前文中的驱动功能其二,数据封装。在 DMA 中断服务函数从接收缓冲数组中取出的已经是数据包 pbuf 形式。数据封装分为两部分进行,在数据搬运的过程中,DMA 将网络数据包放置于 pbuf 对应的 payload 区域。而 pbuf 其他字段信息如 next,tot\_len 字段会在中断服务函数调用 pbuf\_realloc 函数对 pbuf 链的前后连接情况进行调整。
中断服务函数中还会完成一些中断处理的日常,释放上一次中断中使用的 bd,初始化新的 DMA bd 用于下一次中断使用。
pbuf 队列
队列的作用是作为接收驱动与 LwIP 应用程序之间通信的渠道,DMA 中断服务函数将封装完成的 pbuf 数据包指针压入队列。LwIP 的数据包输入函数 low\_level\_input 从队列中取出 pbuf 指针交付 LwIP上层的输入函数,如 ethernet\_input 函数。
队列数据结构可以保证数据包的前后顺序,理论上队列还可以保证线程安全,但当前的例程为裸板 standalone ,并不存在同时读取的可能性。
调用驱动函数实现接收
主程序中通过轮询的方式,从接收驱动函数中将最新来到的数据包移入内核。
在死循环中主程序不断调用 xemacif\_input(netif) 函数,最终从前文中提到的 pbuf 队列中取出数据包,如果有的话。
轮询的方式消耗了很多时间资源,也可以使用中断的方式将数据包从驱动移至内核,在 DMA 接收中断到来,并有数据包时触发中断,调用 xemacif\_input(netif) 函数。
Xilinx 网卡驱动的目录结构
Xilinx 提供了三种网卡驱动,分别对应为使用 emacps,axiemac 以及 emaclite 三种 mac IP。其中 emacps 为 Zynq PS 中独有的硬件 mac 模块,axiemac 为一个全功能的逻辑 IP,emaclite 是一个消耗资源较少,但只支持百兆网的轻量级 IP 。
三种网卡驱动在 xadapter.c 中进行抽象,根据使用的网卡类型,调用对应的驱动函数。
xemacpsif\_dma 中为 DMA 外设的相关函数,包括初始化,发送以及接收中断函数等。
xemacpsif\_hw 中为 emac 外设的相关函数,包括初始化,启动函数等。
xemacpsif 中为与 emac 相关的接口函数。
xemacpsif\_physpeed 为与网络 phy 芯片通信握手的相关函数。
xpqueue 为数据包指针缓冲队列相关的函数。
结语
本文初步讨论了网卡接收驱动函数的工作流程,在后续的相关文章中将进一步讨论本文中一带而过的细节,比如与 DMA 相关的部分。也会有后续文章会继续讨论网卡驱动的初始化以及发送函数,并进一步探讨 LwIP 从网卡驱动收发数据包的机制。
推荐阅读
关注此系列,请关注专栏FPGA的逻辑