//========================================================================
//
//      lwip_ecos_init.cxx
//
//      Initialisation functions for abstraction layer between lwIP and eCos
//
//========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 2004, 2006, 2007, 2008 Free Software Foundation, Inc.      
// Copyright (C) 2004, 2006, 2007, 2008 eCosCentric Limited                 
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     Jani Monoses, majorly reworked by jlarmour
// Contributors:  
// Date:          2004-05-04
// Purpose:       Part of the abstraction layer between lwIP and eCos.
// Description:   misc lwip ecos glue functions
// Usage:
//
//####DESCRIPTIONEND####
//
//========================================================================

#include <pkgconf/system.h>
#include <pkgconf/net_lwip.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/diag.h>

#include <pkgconf/kernel.h>
#include <cyg/kernel/mutex.hxx>
#include <cyg/kernel/flag.hxx>

extern "C" {
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "lwip/memp.h"
#include "lwip/tcpip.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"

#if LWIP_STATS
#include "lwip/stats.h"
#endif

#if LWIP_DHCP
#include "lwip/dhcp.h"
#endif

#if LWIP_SLIP
#include "netif/slipif.h"

static struct netif slipif;
#endif

#if PPP_SUPPORT
#include "netif/ppp/ppp.h"
#endif

#include "netif/loopif.h"

#ifdef CYGPKG_LWIP_ETH
#include "netif/etharp.h"
#include <cyg/io/eth/eth_drv.h>    // cyg_lwip_eth_ecos_init() et al
#endif

} // extern "C"


// FIXME: We can't operate even raw API apps without the support of the
// tcpip thread at present. It is not worth attempting to resolve this
// before we incorporate lwIP v1.3.0 (when it is released) as stack
// initialisation is handled very differently there.
// So for now, we set up the tcpip thread regardless.
#ifndef CYGFUN_LWIP_SEQUENTIAL_API
# define CYGFUN_LWIP_SEQUENTIAL_API 1
#endif

// -------------------------------------------------------------------------
//
// Loopback i/f
//

#if LWIP_HAVE_LOOPIF
static struct netif loopif;
#endif

// -------------------------------------------------------------------------
//
// SLIP support
//

#if LWIP_SLIP
static struct netif slipif;
#endif

// -------------------------------------------------------------------------
//
// PPP support
//

#if PPP_SUPPORT

static void 
pppMyCallback(void *a , int e, void * arg)
{
	diag_printf("callback %d \n",e);
}

/* These temporarily here */
/* This is only used by the PPP support. But when it's used, it expects
 * jiffies to really be 10ms ticks. Ick. */
__externC u32_t
sys_jiffies(void)
{
   return cyg_current_time();
}

__externC void 
ppp_trace(int level, const char *format,...)
{
    va_list args;

    (void)level;
    va_start(args, format);
    diag_vprintf(format, args);
    va_end(args);
}	
#endif

// -------------------------------------------------------------------------
//
// DHCP support
//

#if LWIP_DHCP
static void lwip_dhcp_fine_tmr(void *arg)
{
    dhcp_fine_tmr();
    sys_timeout(500, (sys_timeout_handler) lwip_dhcp_fine_tmr, NULL);
}

static void lwip_dhcp_coarse_tmr(void *arg)
{
    dhcp_coarse_tmr();
    sys_timeout(60000, (sys_timeout_handler) lwip_dhcp_coarse_tmr, NULL);
}


__externC cyg_tick_count cyg_lwip_msec_to_tick(u32_t msecs);
__externC void cyg_lwip_dhcp_init(struct netif *netif)
{
#ifdef CYGFUN_LWIP_DHCP_MANAGEMENT
    dhcp_start(netif);
# ifdef CYGSEM_LWIP_DHCP_WAIT_DHCP_COMPLETE
    // Clock converters from sys_arch.cxx
#  ifdef CYGNUM_LWIP_DHCP_WAIT_DHCP_COMPLETE_TIMEOUT
    cyg_tick_count end_ticks;

    end_ticks = Cyg_Clock::real_time_clock->current_value() +
        cyg_lwip_msec_to_tick(CYGNUM_LWIP_DHCP_WAIT_DHCP_COMPLETE_TIMEOUT);
#  endif

    while (!netif_is_up(netif) || ip_addr_isany(&(netif->ip_addr)))
    {
#  ifdef CYGNUM_LWIP_DHCP_WAIT_DHCP_COMPLETE_TIMEOUT
        if ( Cyg_Clock::real_time_clock->current_value() >= end_ticks )
            break;
#  endif
        Cyg_Thread::yield();
        Cyg_Thread::self()->delay(10);
    }

# endif
#endif
}
#endif // LWIP_DHCP

// -------------------------------------------------------------------------
//
// Ethernet specific support
//

#ifdef CYGPKG_LWIP_ETH

static void
arp_timer(void *arg)
{
  etharp_tmr();
  sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
}

extern Cyg_Flag cyg_lwip_tcpip_thread_event_flag;

__externC void
cyg_lwip_eth_dsr(void)
{
  cyg_lwip_tcpip_thread_event_flag.setbits( CYG_LWIP_TCPIP_THREAD_EVENT_ETH_DELIVERY );
}

__externC void
cyg_lwip_eth_indicate_rx_pkt(void)
{
  cyg_lwip_tcpip_thread_event_flag.setbits( CYG_LWIP_TCPIP_THREAD_EVENT_ETH_RX );
}

#endif // ifdef CYGPKG_LWIP_ETH

// -------------------------------------------------------------------------
//
// Generic interface-independent initialisation
//
// This function is called when tcpip thread finished initialisation.
// We start several timers here - these timers are all handled in the
// tcpip thread. That means that also the DHCP stuff is handled in the
// TCPIP thread. If this causes any trouble than it may be necessaray to
// use an own DHCP thread insted.
//

#ifdef CYGFUN_LWIP_SEQUENTIAL_API
struct cyg_tcpip_init_done_indicator {
public:
    cyg_tcpip_init_done_indicator()
        : cv(mut)
    {
        complete = 0;
    }
    Cyg_Mutex              mut;
    Cyg_Condition_Variable cv;
    volatile bool          complete;
};
#endif

#ifdef CYGFUN_LWIP_SEQUENTIAL_API
static void cyg_tcpip_init_done(void * arg)
{
    struct cyg_tcpip_init_done_indicator *ctipi = (struct cyg_tcpip_init_done_indicator *)arg;
#ifdef CYGPKG_LWIP_ETH
    sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
#endif
#ifdef CYGFUN_LWIP_DHCP_MANAGEMENT
    sys_timeout(500, (sys_timeout_handler) lwip_dhcp_fine_tmr, NULL);
    sys_timeout(60000, (sys_timeout_handler) lwip_dhcp_coarse_tmr, NULL);
#endif
    ctipi->mut.lock();
    ctipi->complete = true;
    ctipi->cv.signal();
    ctipi->mut.unlock();
}
#endif

__externC err_t
cyg_lwip_dummy_netif_init(struct netif *netif)
{
    return ERR_OK; 
}

/*
 * Called by the eCos application at startup
 * wraps various init calls
 */
__externC int
cyg_lwip_init(void)
{
    static int inited = 0;

    if (inited)
        return 1;
    inited++;

#if LWIP_STATS
    stats_init();
#endif
    sys_init();	        /* eCos specific initialization from sys_arch.cxx */
    mem_init();	        /* heap based memory allocator */
    memp_init();	/* pool based memory allocator */
    pbuf_init();	/* packet buffer allocator */
#ifdef CYGPKG_LWIP_ETH
    etharp_init();
#endif
    netif_init();	/* netif layer */
	
    /* Start the stack. It will spawn a new dedicated thread.
     * Use a mutex/condition variable to indicate completion of stack
     * processing from that thread.
     * Don't use a semaphore - so we can have priority inheritance.
     */

#ifdef CYGFUN_LWIP_SEQUENTIAL_API
    struct cyg_tcpip_init_done_indicator ctipi;

    ctipi.mut.lock();
    tcpip_init( cyg_tcpip_init_done, &ctipi );
    while (!ctipi.complete)
        ctipi.cv.wait();
    ctipi.mut.unlock();
#endif
    
    // Loop i/f

#if LWIP_HAVE_LOOPIF
    struct ip_addr ipaddr, netmask, gw;

    IP4_ADDR(&gw, 127,0,0,1);
    IP4_ADDR(&ipaddr, 127,0,0,1);
    IP4_ADDR(&netmask, 255,0,0,0);
    
    if ( NULL != netif_add(&loopif, &ipaddr, &netmask, &gw, NULL, loopif_init,
                           tcpip_input) )
        netif_set_up(&loopif);
#endif

    // SLIP i/f

#if LWIP_SLIP	
    struct ip_addr ipaddr, netmask, gw;
    char ok;

    IP4_ADDR(&ipaddr, CYGDAT_LWIP_SLIP_DEV_ADDR_IP);
    IP4_ADDR(&netmask, CYGDAT_LWIP_SLIP_DEV_ADDR_NETMASK);
    IP4_ADDR(&gw, CYGDAT_LWIP_SLIP_DEV_ADDR_GW);

    ok = (NULL != netif_add(&slipif, &ipaddr, &netmask, &gw, slipif.state,
                            lwip_dummy_netif_init, tcpip_input) );
    if (ok)
    {
        netif_set_up(&slipif);
        ok = ( ERR_OK == slipif_init(&slipif) );
        if (!ok)
            netif_set_down(&slipif);
# ifdef CYGOPT_LWIP_SLIP_IS_DEFAULT
        else
            netif_set_default(&slipif);
# endif
    }
#endif

    // PPP i/f

#if PPP_SUPPORT
    pppInit();
# if PAP_SUPPORT || CHAP_SUPPORT
    enum pppAuthType authtype;
#  if PAP_SUPPORT && CHAP_SUPPORT
    authtype = PPPAUTHTYPE_ANY;
#  elif PAP_SUPPORT
    authtype = PPPAUTH_TYPE_PAP;
#  else
    authtype = PPPAUTH_TYPE_CHAP;
#  endif
    pppSetAuth(authtype, CYGDAT_LWIP_PPP_AUTH_USER, CYGDAT_LWIP_PPP_AUTH_PASS);
# endif
    pppOpen(sio_open(2), pppMyCallback, NULL);

    // Because the ppp if is only added dynamically once the link is
    // up we cannot actually act on CYGOPT_LWIP_PPP_IS_DEFAULT, except
    // by ensuring no other interface is set as the default.
    // FIXME? Perhaps change src/netif/ppp/ipcp.c:ipcp_init() to
    // set default_route in the wanted options?

#endif

    // ETH
#ifdef CYGPKG_LWIP_ETH
    cyg_lwip_eth_ecos_init();
#endif

    return 0;
}
// -------------------------------------------------------------------------
// EOF lwip_ecos_init.cxx
