Developing LwIP Applications with Raw API

cancel
Showing results for 
Search instead for 
Did you mean: 

Developing LwIP Applications with Raw API

Developing LwIP Applications with Raw API

LwIP can be used in two basic modes: Mainloop mode (“NO_SYS”)(no OS/RTOS running on target system) or OS mode (TCPIP thread) (there is an OS running on the target system). In mainloop mode, only raw API can be used. In OS mode, raw API and sequential APIs can be used.

The raw API is an event-driven and non thread-safe API designed to be used without an operation system.  A particular application may register to be notified via a callback function for events such as incoming data available, outgoing data sent, error notifications, poll timer expiration, connection closed, etc. The callback functions will be called from the LwIP core layer when the corresponding event occurs.

This post is focused on how to use raw API with MCUXpresso SDK. For sequential-style and socket API, please refer to below thread.

https://community.nxp.com/docs/DOC-344955

Starting a network interface

To create a new network interface, the user allocates space for a new struct net_if  and calls netif_add,  LwIP needs an IP address. LwIP can use either DHCP or a static IP address.

IP4_ADDR(&fsl_netif0_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);

IP4_ADDR(&fsl_netif0_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);

IP4_ADDR(&fsl_netif0_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);

    netif_add(&fsl_netif0, &fsl_netif0_ipaddr, &fsl_netif0_netmask, &fsl_netif0_gw, &fsl_enet_config0, ethernetif0_init,              ethernet_input);

netif_set_default(&fsl_netif0);

netif_set_up(&fsl_netif0);

 

Pass ethernet_input API to netif_add API as input callback function that is called to pass ingress packets up in the protocol layer stack.

LwIP Initialization

Call lwip_init to init lwIP stack and all of its subsystems.

 

void

lwip_init(void)

{

#ifndef LWIP_SKIP_CONST_CHECK

  int a = 0;

#ifdef LWIP_NOASSERT /* Caused compiler warning. */

  LWIP_UNUSED_ARG(a);

#endif

  LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void *, &a) == &a);

#endif

#ifndef LWIP_SKIP_PACKING_CHECK

  LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);

#endif

 

  /* Modules initialization */

  stats_init();

#if !NO_SYS

  sys_init();

#endif /* !NO_SYS */

  mem_init();

  memp_init();

  pbuf_init();

  netif_init();

#if LWIP_IPV4

  ip_init();

#if LWIP_ARP

  etharp_init();

#endif /* LWIP_ARP */

#endif /* LWIP_IPV4 */

#if LWIP_RAW

  raw_init();

#endif /* LWIP_RAW */

#if LWIP_UDP

  udp_init();

#endif /* LWIP_UDP */

#if LWIP_TCP

  tcp_init();

#endif /* LWIP_TCP */

#if LWIP_IGMP

  igmp_init();

#endif /* LWIP_IGMP */

#if LWIP_DNS

  dns_init();

#endif /* LWIP_DNS */

#if PPP_SUPPORT

  ppp_init();

#endif

 

#if LWIP_TIMERS

  sys_timeouts_init();

#endif /* LWIP_TIMERS */

}

LwIP Raw—Main loop

 

In the while 1 loop,  we need to

  • Poll the driver to get packets.  When a packet is ready, call function ethernetif_linkinput()  to handle the actual reception of the bytes from the network interface. Then the type of the received packet is determined and the appropriate input function is called.
  • Handle all system timeouts for all core protocols.

 

Please refer to tcpecho server demo in MCUXpresso SDK.

evkbimxrt1050_lwip_tcpecho_bm\source\lwip_tcpecho_bm.c

/*!

 * @brief Main function.

 */

int main(void)

{

    struct netif fsl_netif0;

 

    time_init();

 

    lwip_init();

 

 

    tcpecho_raw_init();

 

    while (1)

    {

        /* Poll the driver, get any outstanding frames */

        ethernetif_input(&fsl_netif0);

 

        sys_check_timeouts(); /* Handle all system timeouts for all core protocols */

    }

}

 

When using the netconn and socket API, the timers are handled in the background and the user typically does not need to manually implement them.  In the raw mode, the user must manually implement any required timers.

Evkbimxrt1050_lwip_tcpecho_bm/lwip/port/sys_arch.c

void time_init(void)

{

#ifdef __CA7_REV

    /* special for i.mx6ul */

    SystemSetupSystick(1000U, (void *)time_isr, 0U);

    SystemClearSystickFlag();

#else

    /* Set SysTick period to 1 ms and enable its interrupts */

    SysTick_Config(USEC_TO_COUNT(1000U, sourceClock));

#endif

}

 

 

Using Raw API

The LwIP raw API is designed around a series of callbacks, your application registers callback functions to process specific events. Most callbacks are centered around a “protocol control block”, or PCB.

evkbimxrt1050_lwip_tcpecho_bm\lwip\contrib\apps\tcpecho_raw\tcpecho_raw.c

tcpecho_raq_init is called for initializing the TCP echo server application.

 

void

tcpecho_raw_init(void)

{

  tcpecho_raw_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);

  if (tcpecho_raw_pcb != NULL) {

    err_t err;

 

    err = tcp_bind(tcpecho_raw_pcb, IP_ANY_TYPE, 7);

    if (err == ERR_OK) {

      tcpecho_raw_pcb = tcp_listen(tcpecho_raw_pcb);

      tcp_accept(tcpecho_raw_pcb, tcpecho_raw_accept);

    } else {

      /* abort? output diagnostic? */

    }

  } else {

    /* abort? output diagnostic? */

  }

}

 

 

Tcpecho_raw_init calls tcp_new_ip_type for creating a new TCP protocol control block (PCB) tcpecho_raw_pcb.

Tcp_bind is used to bound the new created tcpecho_raw_pcb to a local IP address and port.

Next, function tcp_listen is called in order to start TCP listening process on the TCP PCB.

Finally a callback function tcpecho_raw_accept is assigned for handling incoming TCP connections on the TCP PCB (tcpecho_raw_pcb). This is done by using  function tcp_accept.

After that, the TCP echo server is ready to accept any incoming connection from remote clients.

The following example shows how incoming TCP connections are handled by the user callback function tcpecho_raw_accept.

static err_t

tcpecho_raw_accept(void *arg, struct tcp_pcb *newpcb, err_t err)

{

  err_t ret_err;

  struct tcpecho_raw_state *es;

 

  LWIP_UNUSED_ARG(arg);

  if ((err != ERR_OK) || (newpcb == NULL)) {

    return ERR_VAL;

  }

 

  /* Unless this pcb should have NORMAL priority, set its priority now.

     When running out of pcbs, low priority pcbs can be aborted to create

     new pcbs of higher priority. */

  tcp_setprio(newpcb, TCP_PRIO_MIN);

 

  es = (struct tcpecho_raw_state *)mem_malloc(sizeof(struct tcpecho_raw_state));

  if (es != NULL) {

    es->state = ES_ACCEPTED;

    es->pcb = newpcb;

    es->retries = 0;

    es->p = NULL;

    /* pass newly allocated es to our callbacks */

    tcp_arg(newpcb, es);

    tcp_recv(newpcb, tcpecho_raw_recv);

    tcp_err(newpcb, tcpecho_raw_error);

    tcp_poll(newpcb, tcpecho_raw_poll, 0);

    tcp_sent(newpcb, tcpecho_raw_sent);

    ret_err = ERR_OK;

  } else {

    ret_err = ERR_MEM;

  }

  return ret_err;

}

 

The new TCP connection is passed to callback function tcpecho_raw_accept through the parameter ‘newpcb’

Structure ‘es’ maintains application status.

Tcp_arg specifies the program state shat should be passed to all other callback functions.  ‘es’ is passed as an argument to the connection TCP PCB ‘newpcb’.

Tcp_recv sets the callback function that will be called when new data arrives. ‘tcpecho_raw_recv’ is assigned to handle all the data traffic.

tcp_err specifies the function (tcpecho_raw_error) that should be called when a fatal error has occurred on the connection.

tcp_poll specifies the polling interval and the callback function (tcpecho_raw_poll) that should be called to poll the application.

tcp_sent specifies the callback function (tcpecho_raw_sent) that should be called when data has successfully received (i.e ackknowledged) by the remote host. Finally tcpecho_raw_sent will call tcp_write

Configuration LwIP

lwipopts.h is a user file that you can use to fully configure lwIP and all of its modules. You do not need to define every option that lwIP provides; if you do not define an option, a default value will be used. Therefore, your lwipopts.h provides a way to override much of the behavior of lwIP.

Use Mainloop mode, we do not run an OS. We need to #define NO_SYS to 1.

Please refer to lwipopts.h in MCUXpresso SDK.

evkbimxrt1050_lwip_tcpecho_bm\source\lwipopts.h

/**

 * NO_SYS==1: Bare metal lwIP

 */

#define NO_SYS 1

/**

 * LWIP_NETCONN==0: Disable Netconn API (require to use api_lib.c)

 */

#define LWIP_NETCONN 0

/**

 * LWIP_SOCKET==0: Disable Socket API (require to use sockets.c)

 */

#define LWIP_SOCKET 0

 

/**

 * LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT=1: we need to free PBUF_RAM pbufs

 * from ISR context on LPC.

 */

#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)

#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT

#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1

#endif

#endif

 

 

 

 

 

 

Tags (1)
Comments

Thank you, Daniel!

Your post and clarifications are very useful. They will save the readers lots of time as it is not so straight forward to find your way through the example. I learnt it the difficult way and still battle against it. It would be very helpful if you elaborate further on how one can link his/her Data-to-Send or Receive buffers (in the main() function level) with the generated by MCUXpresso Ethernet-echo example code.

Kind regards!

Version history
Revision #:
1 of 1
Last update:
‎11-10-2019 08:26 PM
Updated by: