//==========================================================================
//
//      src/lwip/eth_drv.c
//
//      Hardware independent ethernet driver for lwIP
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2007 Free Software Foundation, Inc.
// Copyright (C) 2006, 2007 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 <jani@iv.ro>, jlarmour
// Contributors: 
// Date:         2002-04-05
// Purpose:      Hardware independent ethernet driver
// Description:  Based on the standalone driver for RedBoot.
//               This is used to accompany other hardware independent aspects
//               depending on the driver model - standard stack-independent
//               drivers, or lwIP specific ones.
//               
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <pkgconf/hal.h>
#include <pkgconf/io_eth_drivers.h>
#include <pkgconf/net_lwip.h>

#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/io/eth/eth_drv.h>
#include <string.h>
#include <cyg/hal/hal_if.h>

#include "lwip/opt.h"
#include "lwip/ip.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "netif/etharp.h"


// ------------------------------------------------------------------------

/* The following function is to be supplied by the user if manual setup is requested. */
__externC char cyg_lwip_eth_init_manual( struct netif *netif, char inum, unsigned char *enaddr );

// ------------------------------------------------------------------------

#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG 
char cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY;
#endif

/* Interfaces to rest of lwIP port */
__externC err_t cyg_lwip_dummy_netif_init(struct netif *netif);

// ------------------------------------------------------------------------
#define eth_drv_string1(_x_) #_x_
#define eth_drv_string(_x_) eth_drv_string1(_x_)

#define unmacroIP4_ADDR( _x, _y ) IP4_ADDR( _x, _y )

#define INIT_ETH_IF_NAME(netif, num)                                    \
    CYG_MACRO_START                                                     \
    static const char lwip_eth_if_name##num [2] = eth_drv_string(CYGDAT_LWIP_ETH_DEV_NAME##num); \
    netif->name[0] = lwip_eth_if_name##num [0];                         \
    netif->name[1] = lwip_eth_if_name##num [1];                         \
    CYG_MACRO_END

#define INIT_ETH_IF_ADDR_STATIC(netif, num, ok)                         \
    CYG_MACRO_START                                                     \
    struct ip_addr ipaddr, netmask, gw;                                 \
    unmacroIP4_ADDR( &ipaddr, CYGDAT_LWIP_ETH_DEV_ADDR_STATIC_IP##num );       \
    unmacroIP4_ADDR( &netmask, CYGDAT_LWIP_ETH_DEV_ADDR_STATIC_NETMASK##num ); \
    unmacroIP4_ADDR( &gw, CYGDAT_LWIP_ETH_DEV_ADDR_STATIC_GW##num );           \
    ok = ( NULL != netif_add((netif), &ipaddr, &netmask, &gw,           \
                             (netif)->state, cyg_lwip_dummy_netif_init, \
                             tcpip_input) );                            \
    if (ok)                                                             \
        netif_set_up(netif);                                            \
    CYG_MACRO_END

#define INIT_ETH_IF_ADDR_DHCP(netif, num, ok)                           \
    CYG_MACRO_START                                                     \
    struct ip_addr ipaddr, netmask, gw;                                 \
    IP4_ADDR(&ipaddr, 0,0,0,0);                                         \
    IP4_ADDR(&netmask, 0,0,0,0);                                        \
    IP4_ADDR(&gw, 0,0,0,0);                                             \
    ok = ( NULL != netif_add((netif), &ipaddr, &netmask, &gw,           \
                             (netif)->state, cyg_lwip_dummy_netif_init, \
                             tcpip_input) );                            \
    if (ok) {                                                           \
        netif_set_up(netif);                                            \
        init_dhcp=1;                                                    \
    }                                                                   \
    CYG_MACRO_END


// ecosif_output():
//
// This function is called by the TCP/IP stack when an IP packet
// should be sent. It calls the function called cyg_lwip_eth_low_level_output() to
// do the actual transmission of the packet.
//
//
static err_t
ecosif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
{
  // resolve hardware address, then send (or queue) packet
  return etharp_output(netif, ipaddr, p);
}


// This function is called during system initialization to register a
// network interface with the system.
__externC err_t
cyg_lwip_eth_drv_init_netif(void *drv_handle, unsigned char *enaddr, struct netif *netif, char *do_dhcp)
{
    static char inum;
    char ok = 0;
    char init_dhcp=0;

    // While we're here, if we're using the direct driver interface, and the driver is capable of
    // using a standard interface (so RedBoot could), then check if we should reset the HAL diag console
    // channel if RedBoot has left it set to a network channel. The direct driver interface doesn't
    // support the network channel.
#if defined(CYGOPT_IO_ETH_DRIVERS_LWIP_DRIVER_DIRECT) && \
    (!CYGINT_IO_ETH_DRIVERS_LWIP_NO_STD_DRV) &&          \
    defined(CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT) &&        \
    defined(CYGSEM_HAL_USE_ROM_MONITOR)
    {
        int current_console_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); 
        int current_debug_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); 

        // We're using network debugging if the console is RedBoot's net channel, or we're using the
        // mangler, and the debug channel is RedBoot's net channel.

        if ((current_console_chan == CYG_ETH_DRV_REDBOOT_TCP_CHANNEL) ||
            ((current_console_chan == CYGNUM_CALL_IF_SET_COMM_ID_MANGLER) &&
             (current_debug_chan == CYG_ETH_DRV_REDBOOT_TCP_CHANNEL)))
        {
            // Just 0 always - no better choice to make.
            CYGACC_CALL_IF_SET_CONSOLE_COMM(0);
        }
    }
#endif
    
  
    netif->state = drv_handle;
    netif->hwaddr_len = ETHER_ADDR_LEN;
    CYG_ASSERTC( ETHER_ADDR_LEN <= NETIF_MAX_HWADDR_LEN );
    netif->output = ecosif_output;
    netif->linkoutput = cyg_lwip_eth_low_level_output;
    netif->mtu = 1500;

    /* All eCos eth drivers should be capable of broadcasts */
    /* FIXME: Set (or not) NETIF_FLAG_LINK_UP? Not used by stack itself at least. */
    netif->flags = NETIF_FLAG_BROADCAST;

#ifdef CYGPKG_LWIP_ETH_DEV0
    if ( 0 == inum )
    {
        INIT_ETH_IF_NAME( netif, 0 );
# if defined(CYGOPT_LWIP_ETH_DEV_ADDR_STATIC0)
        INIT_ETH_IF_ADDR_STATIC( netif, 0, ok );
# elif defined(CYGOPT_LWIP_ETH_DEV_ADDR_DHCP0)
        INIT_ETH_IF_ADDR_DHCP( netif, 0, ok );
# else // manual
        ok = cyg_lwip_eth_init_manual( netif, 0, enaddr );
# endif
#ifdef CYGOPT_LWIP_ETH_IS_DEFAULT0
        if (ok)
            netif_set_default(netif); 
#endif
    }
#endif
#ifdef CYGPKG_LWIP_ETH_DEV1
    if ( 1 == inum )
    {
        INIT_ETH_IF_NAME( netif, 1 );
# if defined(CYGOPT_LWIP_ETH_DEV_ADDR_STATIC1)
        INIT_ETH_IF_ADDR_STATIC( netif, 1, ok );
# elif defined(CYGOPT_LWIP_ETH_DEV_ADDR_DHCP1)
        INIT_ETH_IF_ADDR_DHCP( netif, 1, ok );
# else // manual
        ok = cyg_lwip_eth_init_manual( netif, 1, enaddr );
# endif
#ifdef CYGOPT_LWIP_ETH_IS_DEFAULT1
        if (ok)
            netif_set_default(netif); 
#endif
    }
#endif
#ifdef CYGPKG_LWIP_ETH_DEV2
    if ( 2 == inum )
    {
        INIT_ETH_IF_NAME( netif, 2 );
# if defined(CYGOPT_LWIP_ETH_DEV_ADDR_STATIC2)
        INIT_ETH_IF_ADDR_STATIC( netif, 2, ok );
# elif defined(CYGOPT_LWIP_ETH_DEV_ADDR_DHCP2)
        INIT_ETH_IF_ADDR_DHCP( netif, 2, ok );
# else // manual
        ok = cyg_lwip_eth_init_manual( netif, 2, enaddr );
# endif
#ifdef CYGOPT_LWIP_ETH_IS_DEFAULT2
        if (ok)
            netif_set_default(netif); 
#endif
    }
#endif

    inum++;

    if (!ok)
        return ERR_IF;

    if ( NULL != enaddr ) {
        // Set up hardware address
        memcpy(netif->hwaddr, enaddr, ETHER_ADDR_LEN);
    }

    *do_dhcp = init_dhcp;

    return ERR_OK;
}

//
//
// cyg_lwip_eth_drv_ecosif_input():
// This function is called when the eCos hw independent driver
// has some data to pass up to lwIP.It does it through ecosif_input.
//
__externC void
cyg_lwip_eth_drv_ecosif_input(struct netif *netif, struct pbuf *p)
{
    struct eth_hdr *ethhdr;
  
    ethhdr = p->payload;

    /* Must not accept packets for the interface until its initialised */
    if (netif_is_up(netif))
    {
        switch (htons(ethhdr->type))
        {
        case ETHTYPE_IP:
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
            if (cyg_io_eth_net_debug>1)
            {
                CYG_ETH_DRV_START_CONSOLE();
                diag_printf("ecosif_input: IP packet\n");
                CYG_ETH_DRV_END_CONSOLE();
            }
#endif
            etharp_ip_input(netif, p);
            if (!pbuf_header(p, -(s16_t)sizeof(struct eth_hdr)))
            {
                netif->input(p, netif);
                return;
            }
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
            if (cyg_io_eth_net_debug>0)
            {
                CYG_ETH_DRV_START_CONSOLE();
                diag_printf("ecosif_input: pbuf_header failed\n");
                CYG_ETH_DRV_END_CONSOLE();
            }
#endif
            /* Otherwise drop out and free the pbuf */
            break;
        case ETHTYPE_ARP:
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
            if (cyg_io_eth_net_debug>1)
            {
                CYG_ETH_DRV_START_CONSOLE();
                diag_printf("ecosif_input: ARP packet\n");
                CYG_ETH_DRV_END_CONSOLE();
            }
#endif
            etharp_arp_input(netif, (struct eth_addr *) &netif->hwaddr, p);
            return;
        default:
            break;
        }
    } // if
    pbuf_free(p);
} /* cyg_lwip_eth_drv_ecosif_input() */

/* EOF eth_drv.c */
