Xilinx lwip DHCP mini HOWTO

Martin Týma, FEE CTU

Introduction

This paper describes how to enable address allocation throught DHCP for applications developed with Xilinx Platform Studio using Xilinx's lwip library.

Description

How to make it work

The lwip v2.00.a library provided by Xilinx (at least in EDK 9.1) does not enable to use the in the lwip library included DHCP support by default. A little 'hack' is needed to make DHCP work - the lwip Makefile (Makefile_mb for Microblaze and Makefile_ppc for PowerPC) in $EDK/sw/ThirdParty/sw_services/lwip_v2_00_a/src has to be changed. Add $(LWIPDIR)/core/dhcp.c to the file list defined under COREFILES. For the MicroBlaze lwip Makefile version you can use the Makefile in atachment 1.

There is no support for obtaining the DNS server within the DHCP session by default. If you need to get the DNS server address, you can use the in the atachment also included patched files dhcp.c, dhcp.h and netif.h. Your netif structure will then include an additional entry of the type struct ip_addr called dns which will contain the DNS server IP address. The file dhcp.c belongs to ($LWIPDIR)/lwip/src/core, the files netif.h and dhcp.h to ($LWIPDIR)/lwip/src/include/lwip.

The second step is to make your application correctly use the lwip DHCP functions. The example below shows the essential code needed in an application that uses DHCP to configure its network interface:

#include <netif/xemacliteif.h>
#include <xmk.h>
#include <xparameters.h>

#include <lwip/mem.h>
#include <lwip/tcpip.h>
#include <lwip/dhcp.h>


#define DEBUG

#ifdef DEBUG
#define DEBUG_MSG(msg) xil_printf(msg);
#else
#define DEBUG_MSG(msg)
#endif

#define MAC_ADDR            {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}
#define DHCP_TIMEOUT_MS     2000

extern XEmacLiteIf_Config XEmacLiteIf_ConfigTable[]; 
static struct netif *app_netif;


void* netif_config_thread(void *arg)
{
    unsigned char macaddr[6] = MAC_ADDR;
    struct ip_addr ipaddr, netmask, gateway;
    int mscnt = 0;
    
    XEmacLiteIf_Config *xemacif_ptr = &XEmacLiteIf_ConfigTable[0];

    // Set up the MAC address for the ethernet MAC
    xemacliteif_setmac(0, (u8_t *)macaddr);

    // Clear the IP address, Subnet Mask, and Gateway
    ipaddr.addr = 0;
    netmask.addr = 0;
    gateway.addr = 0;

    // Set up the lwIP network interface
    // Allocate and configure the app's netif
    app_netif = (struct netif *) mem_malloc(sizeof(struct netif));
    if(app_netif == NULL)
    {
        DEBUG_MSG("ERROR: Out of memory for default netif\r\n");
        return;
    } 
    app_netif = netif_add(app_netif, &ipaddr, &netmask, &gateway,
      &XEmacLiteIf_ConfigTable[0], xemacliteif_init, tcpip_input);
    netif_set_default(app_netif);

    // Register the XEMAC interrupt handler with the controller
    // and enable interrupts within XMK
    register_int_handler(XPAR_OPB_INTC_0_ETHERNET_MAC_IP2INTC_IRPT_INTR,
      (XInterruptHandler)XEmacLite_InterruptHandler, xemacif_ptr->instance_ptr);
    enable_interrupt(XPAR_OPB_INTC_0_ETHERNET_MAC_IP2INTC_IRPT_INTR);

    // Start the DHCP "Client Daemon"
    DEBUG_MSG("Starting DHCPCD...\r\n");    
    dhcp_start(app_netif);

    while (1) {
        sleep(DHCP_FINE_TIMER_MSECS);
        dhcp_fine_tmr();
        mscnt += DHCP_FINE_TIMER_MSECS;
        if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) {
            dhcp_coarse_tmr();
            mscnt = 0;
        }
    }

}

void* socket_thread(void* arg)
{
    int mscnt = 0;

    // Initialize the lwIP library
    DEBUG_MSG("Initializing the lwIP library...\r\n");
    lwip_init();
    sleep(200);
    DEBUG_MSG("lwIP initialization done\r\n");

    // Start network configuration (+DHCPCD) thread
    DEBUG_MSG("Configuring IP...\r\n");
    sys_thread_new ((void (*)(void*))netif_config_thread, 0, 0);    

    // Wait for DHCP IP configuration
    while (1) {
        sleep(DHCP_FINE_TIMER_MSECS);
        if (app_netif->ip_addr.addr) {
            DEBUG_MSG("DHCP request success\r\n");
            break;
        }
        mscnt += DHCP_FINE_TIMER_MSECS;
        if (mscnt >= DHCP_TIMEOUT_MS) {
            DEBUG_MSG("ERROR: DHCP request timed out\r\n");
            return;
        }
    }            
    DEBUG_MSG("IP configuration done\r\n");

    
    // START THE APPLICATION THREAD(S)
    // sys_thread_new ((void (*)(void*)) app_thread, 0, 1);    
    // ...
}


int main()
{
    // Launch XMK
    DEBUG_MSG("System initialization start\r\n");
    xilkernel_main();

    return 0;
}

Fig. 1.: Simple application using DHCP network interface configuration

Note: The socket_thread thread must be defined in the Xilkernel configuration to be started at Xilkernel start.

How it works

After initializing the interface, the function dhcp_start(struct netif *netif) is called to configure the interface. Then every DHCP_FINE_TIMER_MSECS miliseconds the function dhcp_fine_tmr() and every DHCP_COARSE_TIMER_SECS seconds the function dhcp_coarse_tmr() has to be called.

More info can be found in the lwip dhcp.c source file and the lwip wiki [1].

References

[1] http://lwip.scribblewiki.com/DHCP

Atachments

1. lwip_patch.zip - DHCP patch archive
2. example.c - Simple DHCP using application example