lwIP Wiki
Register
Advertisement

There have been a few questions about how lwIP can be used in a standalone environment (i.e., an environment without a multi-threaded operating system) lately. The purpose of this document is to describe how lwIP is designed to be used with and without a multi-threaded operating system.

The lwIP single-threaded core[]

The core of lwIP consists of the actual implementations of the IP, ICMP, UDP, and TCP protocols, as well as support functions such as buffer and memory management. The core components are the only ones that are needed when lwIP is to be run in a single-threaded (non-OS) environment. You can also add and run DHCP, DNS, but they are not needed. You can also compile for UDP or TCP only.

The core components can be viewed as a software library which has the following interface:

  • ip_input(pbuf, netif): Takes an IP packet and the incoming network interface as arguments and does the TCP/IP processing for the packet.
  • As of lwip 1.4.0, the timers functionality has taken over the TCP timer and also added support for DNS and DHCP timers. You will use sys_check_timeouts()
    • For older versions, tcp_tmr() should be called every 250 ms (=TCP_TMR_INTERVAL); as it does all TCP timer processing such as doing retransmissions. Only actually needed if you run TCP.

Because none of the core functions ever needs to block when run in a single-threaded environment, a sys_arch (operating system abstraction layer) implementation is not needed (and semaphore and mailbox functions are stub definitions only).

The main loop or equivalent will take care of calling the link-layer driver, which will in turn call ip_input(pbuf, netif), which will, in turn, process the IP datagram and call the upper layer protocol handler and finally your callback function for your application.

A simple main loop for a single-threaded system (lwIP 1.4.0+) might look like this:

while(1) {  
    /* poll the driver, get any outstanding frames, alloc memory for them, and
     * call netif->input, which is actually ip_input().
     * This may be called differently, depending on the underlying driver.
     * Eg. on STM it's ethernetif_input(&gnetif) */
    poll_driver(netif);
    /* Handle all system timeouts for all core protocols */
    sys_check_timeouts();      
}

An implementation with the above main loop can already be pinged.

A working example for a main loop for a single-threaded system can be found in contrib/ports/unix/proj/minimal/.

System timers[]

You probably have your own system timers in your bare metal system; however, you might want to take advantage of lwIP timer system for your own purposes or to handle periodic or delayed networking application tasks.

Lets's say you have this function:

static void myfunction(something *myarg);

Then you will arrange for it to be called some time later and passed an argument:

        sys_timeout(APPOPPRIATE_TIME, (sys_timeout_handler) myfunction, myarg);

And when you are tired of it, you remove the timer:

        sys_untimeout((sys_timeout_handler) myfunction, myarg);

Each and every timeout you set uses an element in a system structure, so you have to reserve some space for this in your configuration. If you are curious enough (you should be) to check opts.h, you'll see something like this:

/**
 * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts.
 * The default number of timeouts is calculated here for all enabled modules.
 * The formula expects settings to be either '0' or '1'.
 */
#ifndef MEMP_NUM_SYS_TIMEOUT
#define MEMP_NUM_SYS_TIMEOUT            (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (
2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT)
#endif

You'll need to add the right number and put it in your lwipopts.h file.

Pre 1.4.0[]

For older versions of lwIP, A simple main loop for a single-threaded system might look like this:

while(1) {  
   if(poll_driver(netif) == PACKET_READY) {  
     pbuf = get_packet(netif);  
     ip_input(pbuf, netif);  
   }  
   if (clock() - last_arp_time >= ARP_TMR_INTERVAL * CLOCKTICKS_PER_MS)  
   {  
       etharp_tmr();  
       last_arp_time = clock();  
   }  
   if(clock() - last_time >= TCP_TMR_INTERVAL * CLOCKTICKS_PER_MS) {  
     tcp_tmr();  
     last_time = clock();  
   }      
 }

ATTENTION: Note that CLOCKTICKS_PER_MS is only valid for a hardware timer with at least one tick per millisecond. If you want to use an OS-tick-counter (e.g. one that is incremented every 10ms), you have to adjust the code to:

clock() - last_time >= TCP_TMR_INTERVAL / MS_PER_TICK

In case you use more than just TCP/IP and ARP, you might need to add more calls to timers. In a multi-threaded system all these timers are called from api/tcpip.c, so this source file is a good place to check if you need to call any more timers (like IP_REASSEMBLY, DHCP, etc.). Hint: Both in api/tcpip.c and the examples above, the timers do not "catch up". That means if one timer tick is delayed (for whatever reason), all following timer ticks will be delayed, too.

You should upgrade to the latest release and save yourself a lot of work and bug hunting

Continue reading at Raw/native API and doc/rawapi.txt. Be aware that this doc might be a bit outdated

lwIP in a multi-threaded system[]

lwIP is designed to be able to be run in a multi-threaded system with applications running in concurrent threads. The model used in this case is that all TCP/IP processing is done in a single thread. The application thread communicates with the TCP/IP thread using the sequential API.

The inter-thread communication is implemented in the two files api_lib.c and api_msg.c. The former contains the functions used by the application programs and the latter implements the TCP/IP stack interface. A third file, tcpip.c, handles incoming packets and timer events as described in the previous section.

When run in a multi-threaded environment, incoming packets are handled by the function tcpip_input() (or alternatively tcpip_ethinput()), which takes the same arguments as the ip_input() function. The difference between the two functions is that the tcpip_input() function does not process the incoming packet immediately. It merely puts the packet on a queue which later is drained by the TCP/IP thread.

When being run in a multi-threaded system, timer events are taken care of internally in tcpip.c.

Here a possible start:

struct netif this_netif;

void init()
{
  memset( &my_netif, 0, sizeof(my_netif));
  tcpip_init( ethernet_init_inside_thread, "");
}

void ethernet_init_inside_thread( void *parm)
{
	struct ethernetif *ethernetif;
	struct ip_addr netmask;

        this_netif.state =NULL;
        this_netif.name[0] =65;
	this_netif.name[1] =66;
	this_netif.output = etharp_output;
	this_netif.linkoutput = low_level_output;
	this_netif.next =NULL;
	IP4_ADDR( &netmask, 255, 255, 255, 0);
	netif_add( &this_netif, NULL, &netmask, NULL, NULL, ethernetif_init_low, tcpip_input);
	netif_set_default(&this_netif);
	this_netif.hwaddr_len = ETHARP_HWADDR_LEN;
	this_netif.hwaddr[0] = 1; // or whatever u like
	this_netif.hwaddr[1] = 2;
	this_netif.hwaddr[2] = 3;
	this_netif.hwaddr[3] = 4;
	this_netif.hwaddr[4] = 5;
	this_netif.hwaddr[5] = 6;
	this_netif.mtu = 1500;
	this_netif.flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
	dhcp_start(&this_netif);
}

[]

Advertisement