LwIP简介
LwIP是轻量化的TCP/IP协议,由瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。LwIP具有高度可移植性、代码开源,提供了三种编程接口(API):RAW API、NETCONN API 和 Socket API,用于与TCP/IP代码进行通信。
通过官网可获取LwIP源码包及contrib包。源代码包主要包含LwIP内核的源码文件,contrib包中包含部分移植和应用LwIP的demo。contrib包不属于LwIP内核的一部分,但很有参考价值。
以lwip-2.1.2版本的源码包为例,如图1所示,该源码包分为三部分, src 文件为LWIP源代码文件, doc 文件包含LwIP相关文档, test 为LwIP测试文件,使用时主要关注于 src 文件下的内容。
LwIP内核是由一系列模块组合而成,包括 TCP/IP 协议栈的各种协议、内存管理、数据包管理、网卡接口、基础功能类模块、API等,构成这些模块的源文件就分布在api、apps、core、netif中,头文件则汇总在include中。
api
NETCONN API和Socket API相关的源文件,只有在操作系统的环境中,才能被编译
apps
应用程序的源文件,包括常见的应用程序,如httpd、mqtt、tftp、sntp、snmp等
core
LwIP的内核源文件
include
LwIP所有模块对应的头文件
netif
与网卡移植有关的文件
图1 LwIP-2.1.2源码包
移植接口解析
LwIP使用数据结构体netif来描述网卡,并提供统一接口,需要与以太网底层驱动接口函数结合使用,例如底层驱动负责完成网卡的初始化、网卡的数据收发等,当LwIP内核需要发送一个数据包时,就会通过LWIP提供的接口函数去调用底层网卡的发送函数,将数据由硬件接口与软件内核衔接在一起。
contrib文件中包含部分可使用的网卡移植模板文件,其中ethernetif.c文件(contrib-2.1.0examplesethernetif目录下的ethernetif.c文件)为底层接口驱动的模板,以 LibSamples 为例,若要基于 LibSample的以太网驱动移植LwIP,则需参考ethernetif.c模板,根据以太网驱动及所需配置进行修改,将底层驱动 ethernet 相关函数填充到LwIP所需的指定功能函数中。
ethernetif.c文件中的函数通常为与硬件打交道的底层函数,当有数据需要通过网卡接收或者发送数据的时候就会被调用,经过LwIP协议栈内部进行处理后,从应用层就能得到数据或者可以发送数据。该文件中包括函数:low_level_init()、low_level_output()、low_level_input()、ethernetif_input()和ethernetif_init()函数。
ethernetif_init()
LwIP中默认的网卡初始化函数,内部封装了low_level_init()函数
ethernetif_input()
该函数用于接收网卡数据,内部封装了low_level_input()函数,在接收完毕时,将数据通过pbuf递交给上层。
low_level_init()
low_level_init()函数主要是根据实际情况对网卡进行一系列的初始化工作,例如:初始化MAC地址、长度, 设置最大传输包的大小,设置网卡的属性字段等功能。
该函数中需要调用以太网底层驱动中的相关初始化函数,以 LibSamples为例,该函数需要调用以太网底层驱动 hal_enet.c/.h 的 PHY、MAC、DMA相关初始化函数并进行配置。
low_level_output()
该函数用于实现网卡发送数据,是一个底层驱动函数,需根据以太网底层驱动进行相应修改,若想通过一个网卡发送数据,则需要将该数据传入LwIP内核中,经过层层封装最后存储在pbuf数据包中,需注意pbuf以链表的形式存在,数据发送时是以一整个数据包全部发送的。
low_level_input()
low_level_input()函数用于从网卡中接收一个数据包,并将该数据包封装在pbuf中递交给上层,该函数需要调用以太网底层驱动中的接收函数。
移植LwIP协议栈
基于LibSamples的以太网驱动对LwIP进行移植,需先将LwIP源文件中的部分文件添加到LibSamples中,如: src 源文件、 include 头文件。
若想令LwIP运行,还需补充contrib文件中部分内容,如图2所示,由于部分源文件中使用头文件写法为”arch/xx”,因此,在src文件下新建arch文件,并将需要修改的模板文件及contrib中的部分接口文件放入arch文件中。
ethernetif.c网卡移植模板文件
cc.h文件主要完成协议栈内部使用的数据类型的定义
lwipopts.h文件包含了用户对协议栈内核参数进行的配置,若未在lwipopts.h文件中进行配置,则LwIP会使用opt.h中的默认参数
perf.h文件是实现与系通通计和测量相关的功能,若未使用该功能,则无需修改
bpstruct.h、epstruct.h由contrib文件下的ports文件所提供,属于堆栈的一部分,无需修改
图2 LWIP移植所需部分文件
lwipopts.h文件中需要根据是否为操作系统模拟层、堆内存大小、是否使用TCP及TCP相关配置等进行宏定义配置,例如:宏定义 NO_SYS 表示无操作系统模拟层,因为当前为无操作系统的移植,所以设置该宏定义为1。
... /** *NO_SYS==1:ProvidesVERYminimalfunctionality.Otherwise, *useLwIPfacilities. */ #defineNO_SYS1 ...
cc.h文件中包含处理器相关的变量类型、数据结构及字节对齐的相关宏,需根据处理器及编译器进行修改。
... #defineLWIP_NO_STDINT_H1 typedefunsignedcharu8_t; typedefsignedchars8_t; typedefunsignedshortu16_t; typedefsignedshorts16_t; typedefunsignedlongu32_t; typedefsignedlongs32_t; typedefu32_tmem_ptr_t; typedefintsys_prot_t; #defineU1