Lwip移植

Lwip移植:

数据流向:

MII
MII
Input
Output
RecvData
SendData
Remote
PHY
Eth MAC
LWIP
App

如图,Remote为远端主机,通过网线连接。

  • 本地机器接收,数据流上依次是 PHY芯片、以太网MAC、在到软件层的协议栈LWIP、最后到应用程序。
  • 本地机器发送,数据流上依次是软件层应用程序、协议栈LWIP、硬件以太网MAC层、通过网络PHY芯片发送以太网帧。
  • PHY与MAC之间数据接口可能是MII\RMII\GMII\RGMII。
无系统移植LWIP需要做的工作:

1、网卡驱动移植。实现上图中的 Input、Output以及一个初始化函数。
2、Lwip 时钟移植(超时处理用到的)。

带FreeRTOS移植LWIP需要做的工作:

1、网卡驱动移植。实现上图中的 Input、Output以及一个初始化函数。
2、系统接口相关移植。

一、网卡驱动移植。

1、Input 函数
接口原型参考:lwip\contrib\examples\ethernetif\ethernetif.c

/**
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
/* 当网络接口中有数据包时调用这个函数,这个函数使用low_level_input()去接收网络接口中的数据。
 * 判断数据包的不同类型,调用不同的输入接口。
 */
void ethernetif_input(struct netif *netif)
{
  struct ethernetif *ethernetif;
  struct eth_hdr *ethhdr;
  struct pbuf *p;

  ethernetif = netif->state;

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* if no packet could be read, silently ignore this */
  if (p != NULL) {
    /* pass all packets to ethernet_input, which decides what packets it supports */
    if (netif->input(p, netif) != ERR_OK) {
      LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
      pbuf_free(p);
      p = NULL;
    }
  }
}
/* 不同的 low_level_input 函数即可。
 * low_level_input 函数中把驱动的char buf数据拷贝到pbuf格式。
 * netif->input(p, netif) 把转换后的pbuf数据输入到协议栈中。
 * 接收动作就完成了。
 * /

1.1、low_level_input 函数原型

 /**
 * Should allocate a pbuf and transfer the bytes of the incoming
 * packet from the interface into the pbuf.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return a pbuf filled with the received packet (including MAC header)
 *         NULL on memory error
 */
static struct pbuf *
low_level_input(struct netif *netif)
{
  struct ethernetif *ethernetif = netif->state;
  struct pbuf *p, *q;
  u16_t len;

  /* Obtain the size of the packet and put it into the "len"
     variable. */
  len = ;

#if ETH_PAD_SIZE
  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif

  /* We allocate a pbuf chain of pbufs from the pool. */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

  if (p != NULL) {

#if ETH_PAD_SIZE
    pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    for (q = p; q != NULL; q = q->next) {
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable.
       * This does not necessarily have to be a memcpy, you can also preallocate
       * pbufs for a DMA-enabled MAC and after receiving truncate it to the
       * actually received size. In this case, ensure the tot_len member of the
       * pbuf is the sum of the chained pbuf len members.
       */
      read data into(q->payload, q->len);
    }
    acknowledge that packet has been read();

    MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
    if (((u8_t *)p->payload)[0] & 1) {
      /* broadcast or multicast packet*/
      MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
    } else {
      /* unicast packet*/
      MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
    }
#if ETH_PAD_SIZE
    pbuf_add_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.recv);
  } else {
    drop packet();
    LINK_STATS_INC(link.memerr);
    LINK_STATS_INC(link.drop);
    MIB2_STATS_NETIF_INC(netif, ifindiscards);
  }
  return p;
}
/* 申请pbuf,层参数为PBUF_RAW, 类型为PBUF_POOL。
 * 然后调用网卡驱动把char buf数据拷贝到pbuf中。
 * 怎么不同情况增加计数。
 */

2、Output函数

/**
 * This function should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become available since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */
 /* 这个函数是真正执行发送动作的,以太网包存放在pbuf中。这里的pbuf有可能是一条链。
  */
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  struct ethernetif *ethernetif = netif->state;
  struct pbuf *q;

  initiate transfer();

#if ETH_PAD_SIZE
  pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif

  for (q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    send data from(q->payload, q->len);
  }

  signal that packet should be sent();

  MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
  if (((u8_t *)p->payload)[0] & 1) {
    /* broadcast or multicast packet*/
    MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
  } else {
    /* unicast packet */
    MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
  }
  /* increase ifoutdiscards or ifouterrors on error */

#if ETH_PAD_SIZE
  pbuf_add_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

  LINK_STATS_INC(link.xmit);

  return ERR_OK;
}
/* low_level_output 函数就是把pbuf数据发送出去,注意pbuf可能是一条链。
 */

3、Init函数

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 * This function should be passed as a parameter to netif_add().
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
err_t
ethernetif_init(struct netif *netif)
{
  struct ethernetif *ethernetif;

  LWIP_ASSERT("netif != NULL", (netif != NULL));

  ethernetif = mem_malloc(sizeof(struct ethernetif));
  if (ethernetif == NULL) {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
    return ERR_MEM;
  }

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);

  netif->state = ethernetif;
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
#if LWIP_IPV4
  netif->output = etharp_output;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
  netif->linkoutput = low_level_output;

  ethernetif->ethaddr = (struct eth_addr *) & (netif->hwaddr[0]);

  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}
/* Init函数很简单,就是创建一个网卡状态结构体,注册上面写的output函数, input函数则在中断中调用或者轮询。
 * 设置MAC地址等一些软件属性。
 * 最后调用low_level_init 函数,初始化网卡。low_level_init根据不同的网络芯片实现。
 */
二、无操作系统Lwip 时钟移植 – 实现sys_now 函数。
static volatile uint32_t time_now = 0;
void time_isr(void)
{
  time_now++;
}

uint32_t sys_now(void)
{
    return (uint32_t)time_now;
}
三、带FreeRTOS,系统相关接口移植。
如果使用FreeRTOS的话需要添加一些接口文件。因为带FreeRTOS的协议栈会创建一个协议栈Task。
以太网数据通过中断接收进来,在通过邮箱发送消息到协议栈中。协议栈Task收到消息,根据消息类型调用对应的协议解析函数。
这中间的Task创建,消息的传递等都需要FreeRTOS相关的系统调用接口。
参考:
  lwip\contrib\ports\freertos\sys_arch.c
  lwip\contrib\ports\freertos\include\arch\sys_arch.h
  一般可直接拿来用。
四、应用接口。

Lwip应用编程接口有3种:
1、Raw/Callback API
2、NETCONN API
3、Socket API

  • 如果没有操作系统,则只能使用第一种API做应用编程。
  • 第二种和第三种API接口都是依赖操作系统的。
五、协议栈的初始化。

1、带FreeRTOS的初始化。

void Lwip_stack_init(void)
{
    ip4_addr_t netif_ipaddr, netif_netmask, netif_gw;
  
    tcpip_init(NULL, NULL);

    IP4_ADDR(&netif_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);
    IP4_ADDR(&netif_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);
    IP4_ADDR(&netif_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);
    
    //ethernetif
    netifapi_netif_add(&netif, &netif_ipaddr, &netif_netmask, &netif_gw, &ethernetif, ethernetif_init, tcpip_input);
    
    netifapi_netif_set_default(&netif);
    netifapi_netif_set_up(&netif);

    LWIP_PLATFORM_DIAG(("************************************************"));
    LWIP_PLATFORM_DIAG((" IPv4 Address     : %u.%u.%u.%u", ((u8_t *)&netif_ipaddr)[0], ((u8_t *)&netif_ipaddr)[1],
                        ((u8_t *)&netif_ipaddr)[2], ((u8_t *)&netif_ipaddr)[3]));
    LWIP_PLATFORM_DIAG((" IPv4 Subnet mask : %u.%u.%u.%u", ((u8_t *)&netif_netmask)[0], ((u8_t *)&netif_netmask)[1],
                        ((u8_t *)&netif_netmask)[2], ((u8_t *)&netif_netmask)[3]));
    LWIP_PLATFORM_DIAG((" IPv4 Gateway     : %u.%u.%u.%u", ((u8_t *)&netif_gw)[0], ((u8_t *)&netif_gw)[1],
                        ((u8_t *)&netif_gw)[2], ((u8_t *)&netif_gw)[3]));
    LWIP_PLATFORM_DIAG(("************************************************")); 
}
/* 第一步写的input函数,需要在网络中断中调用。不同平台的网络中断函数实现不同,需要自己调整。
 * 到这里带有FreeRTOS的Lwip协议栈移植完成,下一步就是写应用程序。
 */

有FreeRTOS的话,netifapi_netif_add函数的最后一个参数应该是tcpip_input。Lwip 协议栈会创建一个任务,接收从 ethernetif_input 输入的以太网帧并解析。

2、不带操作系统的初始化。

// 在 main 函数中添加如下:
    IP4_ADDR(&netif_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);
    IP4_ADDR(&netif_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);
    IP4_ADDR(&netif_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);
    
    //ethernetif
    netifapi_netif_add(&netif, &netif_ipaddr, &netif_netmask, &netif_gw, &ethernetif, ethernetif_init, ethernet_input);
    
    netifapi_netif_set_default(&netif);
    netifapi_netif_set_up(&netif);

    LWIP_PLATFORM_DIAG(("************************************************"));
    LWIP_PLATFORM_DIAG((" IPv4 Address     : %u.%u.%u.%u", ((u8_t *)&netif_ipaddr)[0], ((u8_t *)&netif_ipaddr)[1],
                        ((u8_t *)&netif_ipaddr)[2], ((u8_t *)&netif_ipaddr)[3]));
    LWIP_PLATFORM_DIAG((" IPv4 Subnet mask : %u.%u.%u.%u", ((u8_t *)&netif_netmask)[0], ((u8_t *)&netif_netmask)[1],
                        ((u8_t *)&netif_netmask)[2], ((u8_t *)&netif_netmask)[3]));
    LWIP_PLATFORM_DIAG((" IPv4 Gateway     : %u.%u.%u.%u", ((u8_t *)&netif_gw)[0], ((u8_t *)&netif_gw)[1],
                        ((u8_t *)&netif_gw)[2], ((u8_t *)&netif_gw)[3]));
    LWIP_PLATFORM_DIAG(("************************************************")); 
	while (1)
	{
		//调用网卡接收函数
		ethernetif_input(&gnetif);
		//处理 LwIP 中超时事件
		sys_check_timeouts();
	}

3、Lwip还需要一个配置文件,lwipopts.h

参考在 lwip\contrib\examples\example_app。

#define NO_SYS                     0
// NO_SYS 为0时使用FreeRTOS, 为1时不带操作系统
#define LWIP_SOCKET                (NO_SYS==0)
#define LWIP_NETCONN               (NO_SYS==0)
#define LWIP_NETIF_API             (NO_SYS==0)
// 如果没有操作系统,则无法使用Socket API、NETCONN API、NETIF_API。

#define MEM_SIZE               10240
// 协议栈使用的堆的大小

这里仅列出一小部分配置选项,Lwip可高度配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值