lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCPIP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。
LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。

移植LwIP主要涉及下面4个方面
1.LwIP中需要使用信号量进行通信,所以在sys_arch中应实现相应的信号量结构体struct sys_semt和处理函数sys_sem_new() 、sys_sem_free() 、sys_sem_signal ( ) 和sys_arch_sem_wait ( ) 。
2.LwIP 使用消息队列来缓冲、传递数据报文,因此要实现消息队列结构sys_mbox_t ,以及相应的操作函数:sys_mbox_new() 、sys_mbox_free () 、sys_mbox _post () 和sys_arch_mbox_fetch() 。
3.LwIP中每个和TCP/IP相关的任务的一系列定时事件组成一个单向链表,每个链表的起始指针存在lwip_timeouts 的对应表项中。移植时需要实现struct sys_timeouts * sys_arch_timeouts (void) 函数,该函数返回正处于运行态的线程所对应的timeout 队列指针。
4.LwIP中的thread并没有优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。

 lwip建立在多线程的操作系统之上,典型的lwip应用系统包括三个进程:上层应用程序进程,lwip协议栈进程,底层硬件数据接收发送进程。

代码分析:
lwip_init():在lwip中是通过一个叫做netif的网络结构体来描述一个硬件网络接口的,这个接口的结构如下
struct netif {
  struct netif *next;   // 指向下一个netif结构的指针
  struct ip_addr ip_addr;     // IP地址相关配置
  struct ip_addr netmask;
  struct ip_addr gw;
  err_t (* input)(struct pbuf *p, struct netif *inp);  //调用这个函数可以从网卡上取得一个 数据包
  err_t (* output)(struct netif *netif, struct pbuf *p,  // IP层调用这个函数可以向网卡发送
       struct ip_addr *ipaddr);                 // 一个数据包
  err_t (* linkoutput)(struct netif *netif, struct pbuf *p);  // ARP模块调用这个函数向网
                                              // 卡发送一个数据包
  void *state;   // 用户可以独立发挥该指针,用于指向用户关心的网卡信息
  u8_t hwaddr_len; // 硬件地址长度,对于以太网就是MAC地址长度,为6各字节
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];   //MAC地址
  u16_t mtu;   // 一次可以传送的最大字节数,对于以太网一般设为1500
  u8_t flags;   // 网卡状态信息标志位
 
  char name[2]; // 网络接口使用的设备驱动类型的种类
  u8_t num;    // 用来标示使用同种驱动类型的不同网络接口
};
lwip_init中首先做了tcpip堆栈的一个初始化,之后通过neti_add初始化与底层硬件网络接口有关的,至此可以正常接收和发送数据包了

lwipopts.h 功能和性能都可进行裁剪的配置文件。
#define LWIP_SNMP 0   //SNMP协议启用
#define SNMP_CONCURRENT_REQUESTS 1 //SNMP当前允许请求数量
#define SNMP_TRAP_DESTINATIONS 1 //SNMP trap目的地址数目
#define SNMP_PRIVATE_MIB 1     //SNMP 私有节点设置允许
#define SNMP_SAFE_REQUESTS 1 //仅回复SNMP安全请求

#define SYS_LIGHTWEIGHT_PROT 1 //临界中断保护开关(多任务模式下开启)
#define NO_SYS 0 //LWIP独立运行或者基于操作系统,为0则基于操作系统

#define MEM_LIBC_MALLOC 0 //采用LWIP自带函数分配动态内存
#define MEM_LIBC_MALLOC 0 //内存池不通过内存堆来分配
#define MEM_ALIGNMENT 4 //字节对齐(和CPU的位数有关,32位设置为4)
#define MEM_SIZE 8*1024      //堆内存大小,用于发送数据
#define MEMP_SANITY_CHECK 0 //mem_free调用后检查链表是否有循环 by zc
#define MEMP_OVERFLOW_CHECK 0 //lwip堆内存溢出检查
#define MEM_USE_POOLS 0 //内存堆不通过内存池分配
#define MEM_USE_POOLS_TRY_BIGGER_POOL 0   //申请内存失败不选择大内存池
#define MEMP_USE_CUSTOM_POOLS 0 //同上

#define MEMP_NUM_PBUF 60   //来自memp的PBUF_ROM(ROM内存池)和PBUF_REF(RAM内存池)数目最大总和
#define MEMP_NUM_RAW_PCB 4    //RAW连接的PCB数目(需要LWIP RAW有效)
#define MEMP_NUM_UDP_PCB 4 //能够同时连接的UDP的PCB数目
#define MEMP_NUM_TCP_PCB (TCP_WND + TCP_SND_BUF)/TCP_MSS //能够同时连接的TCP的PCB数目 12 #define MEMP_NUM_TCP_PCB_LISTEN 1 //(TCP_WND + TCP_SND_BUF)/TCP_MSS //能够同时监听的TCP的PCB数目
#define MEMP_NUM_TCP_SEG 40    //80 能够同时在队列里的TCP的PCB数目
#define MEMP_NUM_REASSDATA 8 //最大同时等待重装的IP包数目,是整个IP包,不是IP分片
#define MEMP_NUM_ARP_QUEUE 30 //最大等待回复ARP请求的数目(ARP_QUEUEING有效)
#define MEMP_NUM_IGMP_GROUP 8 //多播组同时接收数据的最大成员数目(LWIP_IGMP有效)
#define MEMP_NUM_SYS_TIMEOUT 20 //能够同时激活的超时连接数目(NO_SYS==0有戏)
#define MEMP_NUM_NETBUF 10 //netbufs结构的数目
#define MEMP_NUM_NETCONN 16 //netconns结构的数目
#define MEMP_NUM_TCPIP_MSG_API 40 //tcpip_msg结构的最大数目,用于callback和API的通讯 by zc
#define MEMP_NUM_TCPIP_MSG_INPKT 40 //tcpip_msg接受数据包结构的最大数目 by zc
#define PBUF_POOL_SIZE 48 //内存池数量(小内存减小该选项可大大减小内存占用)

#define LWIP_ARP 1 //ARP协议允许
#define ARP_TABLE_SIZE 10    //ARP维护的表项大小
#define ARP_QUEUEING 1    //硬件地址解析时,将发送数据包计入队列
#define ETHARP_TRUST_IP_MAC 1    //所有IP数据包都会直接引起ARP table的更新, //为0则非表项内IP-MAC关系会引起ARP请求,可以避免IP欺骗,不过会造成延时
#define ETHARP_SUPPORT_VLAN 0    //非虚拟局域网,为1则仅虚拟局域网通讯有效

#define IP_FORWARD 0 //不允许不匹配数据包转发,多接口时为1
#define IP_OPTIONS_ALLOWED 1 //带IP选项数据包允许 为0则丢弃所有IP数据包
#define IP_REASSEMBLY 1 //允许接收IP包分片包(为0不允许,不能够接收大于MTU的包)
#define IP_FRAG 1 //允许发送IP包分片包
#define IP_REASS_MAXAGE 3 //允许接收的最大分段数
#define IP_REASS_MAX_PBUFS 10 //最大允许存在的IP分片包占用的内存池个数
#define IP_FRAG_USES_STATIC_BUF 1 //IP分片使用静态缓冲区
#define IP_FRAG_MAX_MTU 1500 //IP分片最大缓冲数量
#define IP_DEFAULT_TTL 255 //IP数据包最大经历设备数目
#define IP_SOF_BROADCAST 0 //IP发送广播包过滤
#define IP_SOF_BROADCAST_RECV 0 //IP接收广播包过滤

#define LWIP_ICMP 1 //开启ping包接收/发送
#define ICMP_TTL (IP_DEFAULT_TTL) //ping包最大经历设备数目
#define LWIP_BROADCAST_PING 0 //不回复广播ping包
#define LWIP_MULTICAST_PING 0 //不回复多播ping包

#define LWIP_RAW 0 //无操作系统基于回调函数驱动
#define RAW_TTL (IP_DEFAULT_TTL) //应用层数据传输次数(基于IP层的TTL)

#define LWIP_DHCP 0 //动态主机协议配置(为1时)
#define LWIP_AUTOIP 0 //动态主机IP地址配置(为1时)
#define LWIP_DHCP_AUTOIP_COOP 0 //允许上述两种配置同时存在于1个接口(为1时)
#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 //DHCP分配允许失败次数,失败则使用AUTOUP

#define LWIP_IGMP 0 //LWIP组管理协议

#define LWIP_DNS 0 //域名服务器模块(依托UDP协议)
#define DNS_TABLE_SIZE 4 //域名服务器维护的最大入口数目
#define DNS_MAX_NAME_LENGTH 256 //域名服务器主机地址最大长度
#define DNS_MAX_SERVERS 2 //域名服务器最大服务数目
#define DNS_DOES_NAME_CHECK 1 //查询域名服务器时检测地址名
#define DNS_USES_STATIC_BUF 1 //域名服务器使用静态地址
#define DNS_MSG_SIZE 512 //域名服务器接收最大通讯数据长度
#define DNS_LOCAL_HOSTLIST 0 //在本地维护域名服务器主机-地址表(为1时)
#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 //主机-地址表实时更新(为1时)

#define LWIP_UDP 1 //启用UDP协议(snmp协议基于此)
#define LWIP_UDPLITE 1 //UDP协议启用精简版
#define UDP_TTL (IP_DEFAULT_TTL) //UDP数据包传输次数
#define LWIP_NETBUF_RECVINFO 0 //接收到的数据包除首个外其它不附加目的地址和端口

#define LWIP_TCP 1 //启用TCP协议(http协议基于此)
#define TCP_TTL (IP_DEFAULT_TTL) //TCP数据包传输次数
#define TCP_WND 4*TCP_MSS //tcp窗口大小
#define TCP_MAXRTX 12 //最大允许重传TCP数据包数目
#define TCP_SYNMAXRTX 6 //最大允许重传SYN连接包数目
#define TCP_QUEUE_OOSEQ (LWIP_TCP) //TCP接收队列外数据块排序
#define TCP_MSS 1460 //tcp报文最大分段长度
#define TCP_CALCULATE_EFF_SEND_MSS 1 //tcp预计发送的分段长度,为1则根据窗口大小分配
#define TCP_SND_BUF (8*TCP_MSS) //TCP发送缓冲区 zc 7.1
#define TCP_SND_QUEUELEN (4*(TCP_SND_BUF/TCP_MSS)) //TCP发送队列长度
#define TCP_SNDLOWAT (TCP_SND_BUF/4) //TCP可发送数据长度
#define TCP_LISTEN_BACKLOG 1 //TCP多连接允许
#define TCP_DEFAULT_LISTEN_BACKLOG 0xff //TCP连接和半连接的总数
#define LWIP_TCP_TIMESTAMPS 0 //TCP时间戳选项
#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) //TCP窗口更新阈值

#define TCP_MSL 10000UL //TCP连接存在时间 单位ms
#define TCP_FIN_WAIT_TIMEOUT 20000UL //FIN等到ACK应答时间 单位ms
#define TCP_TMR_INTERVAL 20 //TCP定时器计数间隔 20ms

#define PBUF_LINK_HLEN 14 //为处理以太网头申请的包长度(本地MAC地址+远端MAC地址+协议类型) 6+6+2 #define PBUF_POOL_BUFSIZE 256 //单个内存池长度,要考虑到4字节对齐和最佳大小
#define ETH_PAD_SIZE 0 //以太网填充长度,stm32设置为0 根据发送包判断

#define LWIP_TCP_KEEPALIVE 1 //tcp保活定时器
#define TCP_KEEPIDLE_DEFAULT 60000 //保活时间 60s
#define TCP_KEEPINTVL_DEFAULT 10000 //保活探测 10s
#define TCP_KEEPCNT_DEFAULT 9U

include/arch/cc.h          // 平台相关。类型定义,大小端设置,内存对齐等include/arch/perf.h        // 平台相关的性能测量实现(没用include/arch/sys_arch.h    // RTOS抽象层。信号量,mbox等类型定义,函数声明
port/sys_arch.c            // RTOS抽象层实现

为了屏蔽不同RTOS在信号量,互斥锁,消息,任务创建等OS原语使用上的差别,lwip构造了一个RTOS的抽象层,规定了OS原语的数据类型名称和对应方法名称。我们要做的就是根据所用RTOS的api去实现这些原语。


netif/ethernetif.c
该文件是作者提供的网卡驱动和lwip的接口框架。

static void   low_level_init(struct netif *netif);

static err_t  low_level_output(struct netif *netif, struct pbuf *p);

static struct pbuf *low_level_input(struct netif *netif);

void init_lwip()
{
    
struct netif *pnetif = NULL; int flag = 1;

 tcpip_init(tcpip_init_done, &flag); // lwip协议栈的初始化 
while(flag);
 IP4_ADDR(&e0ip, 192,168,6,188); // 设置网卡ip 
IP4_ADDR(&e0mask, 255,255,255,0); // 设置子网掩码 
IP4_ADDR(&e0gw, 192,168,6,1); // 设置网关 //给lwip添加网卡 
pnetif = netif_add(&lpc1788_netif, &e0ip, &e0mask, &e0gw,
                       NULL, ethernetif_init, tcpip_input);
    netif_set_default(pnetif); // 设置该网卡为默认网卡 
netif_set_up(&lpc1788_netif); // 启动网卡,可以唤醒DHCP等服务 // 创建一个任务。这个任务负责不停的调用ethernetif_input函数从网卡读取数据 
raw_task_create(&lwip_read_obj, (RAW_U8  *)"lwip_read", &lpc1788_netif,
                    CONFIG_RAW_PRIO_MAX - 25, 0,  lwip_read_stk,
                    LWIP_READ_STK_SIZE ,  lwip_read_task, 1);
}


12-23 21:04