Chapter 165. Raw API

Table of Contents

165.1. Overview
165.2. Usage
165.3. Callbacks
tcp_arg() — Set the application connection state
165.4. TCP connection setup
tcp_new() — Create a new TCP PCB
tcp_bind() — Bind PCB to local IP address and port
tcp_listen() — Make PCB listen for incoming connections
tcp_accept() — Set callback used for new incoming connections
tcp_connect() — Open connection to remote host
165.5. Sending TCP data
tcp_write() — Enqueue data for transmission
tcp_sent() — Set callback for successful transmission
165.6. Receiving TCP data
tcp_recv() — Set callback for incoming data
tcp_recved() — Indicate receipt of data
165.7. Application polling
tcp_poll() — Set application poll callback
165.8. Closing connections, aborting connections and errors
tcp_close() — Close the connection
tcp_abort() — Abort the connection
tcp_err() — Set callback for errors
165.9. Lower layer TCP interface
165.10. UDP interface
udp_new() — Create a new UDP pcb
udp_remove() — Remove a UDP pcb
udp_bind() — Bind PCB to local IP address and port
udp_connect() — Set remote UDP peer
udp_disconnect() — Set remote UDP peer
udp_send() — Send UDP packet
udp_recv() — Set callback for incoming UDP data
165.11. System initialization
165.11.1. Initialization detail

Much of the information in this chapter has been derived from lwIP's own raw API documentation, although additions, modifications and adaptations for eCos have been made.

165.1. Overview

While the high level lwIP sequential API is good for programs that are themselves sequential and can benefit from the blocking open-read-write-close paradigm, lwIP itself is event based by nature. If an application can be written with an event-based approach, then it becomes possible to integrate directly with the event-based design of the core lwIP code.

The raw TCP/IP API allows the application program to integrate better with the TCP/IP code. Program execution is event based by having callback functions being called from within the TCP/IP code. The TCP/IP code and the application program both run in the same thread. The sequential API has a much higher overhead and is not very well suited for small systems since it forces a multithreaded paradigm on the application.

The raw TCP/IP interface is not only faster in terms of code execution time but is also less memory intensive. The drawback is that program development is somewhat harder and application programs written for the raw TCP/IP interface are more difficult to understand. Still, this is the preferred way of writing applications that should be small in code size and memory usage.

Both APIs can be used simultaneously by different application programs. In fact, the sequential API is implemented as an application program using the raw TCP/IP interface.

An example of an application using the raw API can be found in the tests/ subdirectory of the lwIP eCos package. This httpd2 test is built when the CDL configuration option CYGBLD_NET_LWIP_BUILD_MANUAL_TESTS is enabled. This raw API application acts as a simple HTTP server. For more information see Section 166.2.11, “httpd2”.

165.2. Usage

The raw API is a very direct interface, and is close to the metal. If the CYGFUN_LWIP_NO_SYS option is enabled then there still needs to be a single lwIP owner thread but an application can be constructed where the main processing loop of that thread performs lwIP support as well as other application event processing as required so that only a single stack footprint is required. The trueraw application is built when CYGFUN_LWIP_NO_SYS is configured, and the CYGBLD_NET_LWIP_BUILD_MANUAL_TESTS option is enabled. This provides a simple example of an application using the raw API without the overhead of the TCP/IP helper thread.

For true raw API applications the cyg_lwip_init() function can be used to initialise the lwIP stack (as for sequential or BSD API applications), but there is no support for waiting for the network to be brought up within that function call, since when using a true raw world the caller of the cyg_lwip_init() is also responsible for processing network packets that may be needed to bring up the network interface up. If required an application can perform its own lwIP stack initialization, and does not need to use the eCos default support.

Note that if you do decide to use cyg_lwip_init() with the configuration option CYGFUN_LWIP_SEQUENTIAL_API disabled, so that solely the raw API is available, bbut with the configuration option CYGFUN_LWIP_NO_SYS also disabled, then the application will need to provide its own alternative to the tcpip_input() function which had previously been used to inject received packets into the stack. This function must be declared as follows:

err_t tcpip_input (struct pbuf *, struct netif *);

See Section 165.11, “System initialization” for further details on initialization.

Declarations for the API functions are found in header files within the lwIP include tree. The TCP functions are found in <lwip/tcp.h>, and UDP in <lwip/udp.h>.

The raw API uses many of the same types and definitions used in the sequential API. In particular the raw API functions use struct ip_addr and err_t error codes.

165.3. Callbacks

tcp_arg() — Set the application connection state

The configuration option CYGFUN_LWIP_EVENT_CALLBACK defaults to enabled. If enabled then program execution is driven by callbacks. Each callback is an ordinary C function that is called from within the TCP/IP code. Every callback function is passed the current TCP or UDP connection state as an argument. Also, in order to be able to keep program specific state, the callback functions are called with a program specified argument that is independent of the TCP/IP state.

If the CYGFUN_LWIP_EVENT_CALLBACK option is disabled then a common user-supplied function is called from within the TCP/IP code instead of the respective callback routine:

err_t lwip_tcp_event (void *arg , struct tcp_pcb *pcb , enumlwip_event , struct pbuf *p , u16_tsize , err_terr );

For the individual callbacks or the shared lwip_tcp_event() the tcp_arg() function is used for setting the private arg application connection state.

165.4. TCP connection setup

tcp_new() — Create a new TCP PCB
tcp_bind() — Bind PCB to local IP address and port
tcp_listen() — Make PCB listen for incoming connections
tcp_accept() — Set callback used for new incoming connections
tcp_connect() — Open connection to remote host

The functions used for setting up connections are similar to those of the sequential API and of the BSD socket API. A new TCP connection identifier (i.e., a protocol control block - PCB) is created with the tcp_new() function. This PCB can then be either set to listen for new incoming connections or be explicitly connected to another host.

165.5. Sending TCP data

tcp_write() — Enqueue data for transmission
tcp_sent() — Set callback for successful transmission

TCP data is sent by enqueueing the data with a call to tcp_write(). When the data is successfully transmitted to the remote host, the application will be notified with a call to a specified callback function.

165.6. Receiving TCP data

tcp_recv() — Set callback for incoming data
tcp_recved() — Indicate receipt of data

TCP data reception is callback based - an application specified callback function is called when new data arrives. When the application has taken the data, it has to call the tcp_recved() function to indicate that TCP can advertise an increase in the receive window.

165.7. Application polling

tcp_poll() — Set application poll callback

When a connection is idle (i.e., no data is either transmitted or received), lwIP will repeatedly poll the application by calling a specified callback function. This can be used either as a watchdog timer for killing connections that have stayed idle for too long, or as a method of waiting for memory to become available. For instance, if a call to tcp_write() has failed because memory wasn't available, the application may use the polling functionality to call tcp_write() again when the connection has been idle for a while.

165.8. Closing connections, aborting connections and errors

tcp_close() — Close the connection
tcp_abort() — Abort the connection
tcp_err() — Set callback for errors

165.9. Lower layer TCP interface

TCP provides a simple interface to the lower layers of the system. During system initialization, the function tcp_init() has to be called before any other TCP function is called. When the system is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() must be called at regular intervals. The tcp_fasttmr() should be called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h, and currently 250ms) and tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds, currently 500ms.

165.10. UDP interface

udp_new() — Create a new UDP pcb
udp_remove() — Remove a UDP pcb
udp_bind() — Bind PCB to local IP address and port
udp_connect() — Set remote UDP peer
udp_disconnect() — Set remote UDP peer
udp_send() — Send UDP packet
udp_recv() — Set callback for incoming UDP data

The UDP interface is similar to that of TCP, but due to the lower level of complexity of UDP, the interface is significantly simpler.

165.11. System initialization

When performing manual initialization of lwIP for use with the raw API, the function lwip_init() can be called to perform the core setup. Depending on the actual lwipopts.h configuration lwip_init() will call the necessary routines to initialize the required lwIP sub-systems.

In this example, these functions must be called in the order of appearance:

lwip_init()

Calls the individual, as configured, low-level lwIP module initialization routines.

If LWIP_ARP is defined then etharp_tmr() must be called at the regular ARP_TMR_INTERVAL interval (default 5 seconds) after the system has been initialized by this call.

Similarly if LWIP_TCP is defined then you must ensure that tcp_fasttmr() and tcp_slowtmr() are called at the predefined regular intervals.

struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw, void *state, err_t (* init)(struct netif *netif), err_t (* input)(struct pbuf *p, struct netif *netif))

Adds your network interface to the netif_list. Allocate a struct netif and pass a pointer to this structure as the first argument. Give pointers to cleared struct ip_addr structures when using DHCP, or fill them with sane numbers otherwise. The state pointer may be NULL.

The init function pointer must point to an initialization function for your ethernet netif interface. The following code illustrates an example use:

err_t netif_if_init(struct netif *netif)
{
    u8_t i;

    for(i = 0; i < 6; i++)
        netif->hwaddr[i] = some_eth_addr[i];
    init_my_eth_device();
    return ERR_OK;
}

Normally for ethernet devices the input function must point to the lwIP function ethernet_input().

netif_set_default(struct netif *netif)
Registers netif as the default network interface.
netif_set_up(struct netif *netif)
When netif is fully configured, this function must be called to allow it to be used.
dhcp_start(struct netif *netif)

If LWIP_DHCP is configured then this function creates a new DHCP client for this interface the first time the routine is called. Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at the predefined regular intervals after starting the client.

You can peek in the netif->dhcp struct for the actual DHCP status.

165.11.1. Initialization detail

If required the manual raw API initialization could directly call the required lwIP sub-system module initialization functions (rather then calling the lwip_init() function).

The calls should be performed in the following order:

stats_init()

Clears the structure where runtime statistics are gathered.

Note: The statistics support is only included if LWIP_STATS is configured, and then some of the statistics code is only present if LWIP_DEBUG is also defined.

sys_init()
Not generally used with raw API, but can be called for ease of compatibility if using sequential API in addition, initialised manually. The lwip_init() implementation only calls this function if NO_SYS is NOT defined.
mem_init()
Initializes the dynamic memory heap defined by the CDL configuration option CYGNUM_LWIP_MEM_SIZE.
memp_init()
Initializes the memory pools defined by the CDL configuration options CYGNUM_LWIP_MEMP_NUM_*.
pbuf_init()
Initializes the pbuf (packet buffer) memory pool defined by the CDL configuration option CYGNUM_LWIP_PBUF_POOL_SIZE.
netif_init()
This function will call netif_add() as appropriate to create the LWIP_HAVE_LOOPIF configured loopback network interface.
lwip_socket_init()
If LWIP_SOCKET is configured then this function is called to initialise the BSD-alike API module. It does not do much at present, but it should be called to handle future changes.
ip_init()
This function does not do much at present, but it should be called to handle future changes.
etharp_init()

Called if LWIP_ARP is configured to initialize the ARP table and queue.

Note: you must regularly call the etharp_tmr function at the ARP_TMR_INTERVAL (default 5 seconds) interval after this initialization.

raw_init()
If LWIP_RAW is configured then this function is called. It does not do much at present, but it should be called to handle future changes.
udp_init()
If LWIP_UDP is configured then this function is called to initialize the required UDP support state.
tcp_init()

If LWIP_TCP is configured then this function is called to initialise the required TCP support state.

Note: you must call tcp_fasttmr() and tcp_slowtmr() at the predefined regular intervals after this initialization.

snmp_init()
If LWIP_SNMP is configured then this function is called to start the SNMP agent support. It allocates a UDP pcb and binds it to IP_ADDR_ANY for the SNMP_IN_PORT (default 161) configured port, listening for SNMP The routine will also generate a SNMP coldstart trap if configured appropriately.
autoip_init()
If LWIP_AUTOIP is configured then this function is called for the IPv4 AutoIP support. It does not do much at present, but it should be called to handle future changes.
igmp_init()
If LWIP_IGMP is configured then this function is called for the IPv4 IGMP support. It configures the allsystems and allroutes multicast addresses.
dns_init()
If LWIP_DNS is configured then this function is called to allocate the UDP pcb for the client and initialise the default DNS server address.
ip6_init()
If LWIP_IPV6 is configured then this function is called. It does not do much at present, but it should be called to handle future changes.
nd6_init()
If LWIP_IPV6 is configured then this function is called. It does not do much at present, but it should be called to handle future changes.
mld6_init()
If LWIP_IPV6 and LWIP_IPV6_MLD are configured then this function is called. It does not do much at present, but it should be called to handle future changes.
sys_timeouts_init()
If LWIP_TIMERS is configured then this function is called. It uses the sys_timeout() to register timeout callbacks for the configured lwIP features. Normally the raw API will not be providing the sys_timeout functionality, and will, as mentioned above, have to manually ensure the relevant timeout functions are called. e.g. ARP, TCP, etc.