/*
 *            Copyright (c) 1998-2003 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 304                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 *
 *  Description
 *  ====================================================================
 *  This file contains the routines used to read and write ACE configuration
 *  parameters in NVRAM.  The implementation contained in here is for 
 *  NET+Works development boards.  You may need to customize these routines
 *  if your application uses a different structure for NVRAM, or if you
 *  have extended or altered the ACE module.
 *
 */

#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "bsp.h"
#include "boardParams.h"
#include "narmnvrm.h"
#include "appconf_api.h"
#include "Npttypes.h"
#include "tx_api.h"
#include "dhcpOptions.h"
#include "narmapi.h"

/**
 * The ACE parameters API provides a set of functions to read and write the
 * ACE configuration to NVRAM.  In general, the only changes you need to make
 * are to the data structures dhcp_desired_params and NADefaultEthInterfaceConfig.
 * 
 *
 * The array dhcp_desired_params lists the DHCP options that the DHCP client will
 * request from the server.  You must update this list to include any options that
 * your application requires.
 *
 *
 * The array NADefaultEthInterfaceConfig contains the default configuration settings
 * used by ACE if NVRAM has not been initialized.  You should update this as required
 * by your application.  Note that the default static IP settings are actually 
 * read from appconf.h, and the value of the constant APP_ENABLE_AUTO_IP in 
 * appconf.h controls whether Auto-IP will be enabled.  The settings in appconf.h
 * are written into NADefaultEthInterfaceConfig by the function 
 * customizeAceGetDefaultEthernetConfig the first time it is called.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 * @overview ACE Parameters
 */

#define DEFAULT_NUMBER_OF_INTERFACES        2

#define PRIMARY_ETHERNET_INTERFACE          0
#define AUTO_IP_INTERFACE                   1

#define CONFIG_IS_VALID     TRUE
#define CONFIG_NOT_VALID    FALSE
#define IS_ENABLED          TRUE
#define IS_DISABLED         FALSE


#define DEFAULT_RARP_RETRANSMIT_INTERVAL    2
#define DEFAULT_AUTOIP_DELAY                0
#define DEFAULT_RARP_DELAY                  60
#define DEFAULT_BOOTP_DELAY                 30
#define DEFAULT_DHCP_DELAY                  0
#define DEFAULT_PING_ARP_DELAY              60

/**
 * List of DHCP options that the DHCP client will request from
 * the server.  Update this list to include any additional DHCP 
 * options required by your application.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 */
static WORD8 const dhcp_desired_params[] = 
{
	SUBNET_MASK, ROUTERS_ON_SUBNET, DNS,
	HOST_NAME, DOMAIN_NAME, BROADCAST_ADDR,
	NTP_SERVERS, NIS_DOMAIN_NAME,TCP_KEEP_ALIVE_INTERVAL,
    DEFAULT_IP_TTL, TCP_DEFAULT_TTL,TCP_KEEP_ALIVE_INTERVAL,
	TCP_KEEP_ALIVE_GARBAGE, IP_FORWARDING,IF_MTU
};


/**
 * Suitable default values for ACE configuration values that
 * will cause ACE to use all protocols to find an available IP
 * address.  DHCP, BOOTP, RARP, and Ping-ARP are enabled on
 * eth0.  Auto-IP is enabled on eth0:0.  
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 */
static aceConfigInterfaceInfo NADefaultEthInterfaceConfig[] = 
{
    {
        CONFIG_IS_VALID,
        IS_ENABLED,
        BP_ETH_INTERFACE,                   /* config for Ethernet interface*/
        {                                   /* static config record*/
            CONFIG_NOT_VALID,
            IS_DISABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know IP address */
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know subnet mask */
            ACE_UNKNOWN_IP_ADDRESS,         /* gateway */
            FALSE,                          /* don't auto assign*/
            "",                             /* don't know domain name*/
            {                               /* don't know name server address*/
                ACE_UNKNOWN_IP_ADDRESS, 
                ACE_UNKNOWN_IP_ADDRESS, 
                ACE_UNKNOWN_IP_ADDRESS
            },                         
            {ACE_PROT_STATIC, 0, 0, ACE_ALWAYS_SHUTDOWN}
        },
        {                                   /* config for DHCP*/
            CONFIG_IS_VALID,
            IS_ENABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest an IP address */
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest a server*/
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest a gateway*/
            ACE_INFINITE,                   /* get as long a lease as possible */
            4,
            ACE_DONT_KNOW_START_TIME,
            ACE_RESTART_DHCP_DISCOVER,
            TRUE,                           /* need broadcast response */
            TRUE,                           /* do initial random delay */
            0,                              /* use default ARP deply timeout */
            { 0},                           /* will copy dhcp_desired_params here*/
            asizeof(dhcp_desired_params),   /* number of DHCP parameters*/
            {ACE_PROT_DHCP, 0, DEFAULT_DHCP_DELAY, ACE_CONT_IF_GOT_ADDRESS}
        },
        {                                   /* BOOTP config*/
            CONFIG_IS_VALID,
            IS_ENABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know client IP address*/
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know server IP address*/
            ACE_INFINITE,                   /* retry BOOTP forever*/
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know gateway IP address*/
            "",                             /* don't know bootfile name*/
            "",                             /* don't know server host name*/
            {ACE_PROT_BOOTP, 0, DEFAULT_BOOTP_DELAY, ACE_ALWAYS_SHUTDOWN}
        },
        {                                   /* AutoIpInfo*/
            CONFIG_IS_VALID,                /* disable Auto IP by default*/
            IS_DISABLED,
            BP_AUTOIP_START_WITH_RANDOM_IP_ADDRESS,
            {ACE_PROT_AUTOIP, 0, DEFAULT_AUTOIP_DELAY, ACE_CONT_IF_GOT_ADDRESS}
        },
        {
            CONFIG_IS_VALID,                /* use RARP*/
            IS_ENABLED,
            DEFAULT_RARP_RETRANSMIT_INTERVAL,
            {ACE_PROT_RARP, 0, DEFAULT_RARP_DELAY, ACE_ALWAYS_SHUTDOWN}
        },
        {
            CONFIG_IS_VALID,                /* use Ping ARP*/
            IS_ENABLED,
            {ACE_PROT_PING_ARP, 0, DEFAULT_PING_ARP_DELAY, ACE_ALWAYS_SHUTDOWN}
        }
    }
#if (DEFAULT_NUMBER_OF_INTERFACES > 1)
/*
 * This second Ethernet configuration structure exists to support Auto-IP.
 * Auto-IP has to be implemented on a secondary interface (in this case eth0:0)
 * since its addresses are not routable.  The default setup below has Auto-IP
 * disabled.  However, the structure will be updated to enable Auto-IP if
 * APP_ENABLE_AUTO_IP is set to TRUE.
 */
    ,{
        CONFIG_IS_VALID,
        IS_DISABLED,
        BP_ETH_0INTERFACE,                   /* config for Ethernet interface*/
        {                                   /* static config record*/
            CONFIG_NOT_VALID,
            IS_DISABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know IP address */
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know subnet mask */
            ACE_UNKNOWN_IP_ADDRESS,         /* gateway */
            FALSE,                           /* don't auto assign*/
            "",                             /* don't know domain name*/
            {                               /* don't know name server address*/
                ACE_UNKNOWN_IP_ADDRESS, 
                ACE_UNKNOWN_IP_ADDRESS, 
                ACE_UNKNOWN_IP_ADDRESS
            },                         
            {ACE_PROT_STATIC, 0, 0, ACE_ALWAYS_SHUTDOWN}
        },
        {                                   /* config for DHCP*/
            CONFIG_IS_VALID,
            IS_DISABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest an IP address */
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest a server*/
            ACE_UNKNOWN_IP_ADDRESS,         /* can't suggest a gateway*/
            ACE_INFINITE,                   /* get as long a lease as possible */
            4,
            ACE_DONT_KNOW_START_TIME,
            ACE_RESTART_DHCP_DISCOVER,
            TRUE,                          /* need broadcast response */
            TRUE,                          /* do initial random delay */
            0,                              /* use default ARP deply timeout */
            { 0},                           /* will copy dhcp_desired_params here*/
            asizeof(dhcp_desired_params),   /* number of DHCP parameters*/
            {ACE_PROT_DHCP, 0, DEFAULT_DHCP_DELAY, ACE_CONT_IF_GOT_ADDRESS}
        },
        {                                   /* BOOTP config*/
            CONFIG_IS_VALID,
            IS_DISABLED,
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know client IP address*/
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know server IP address*/
            ACE_INFINITE,                   /* retry BOOTP forever*/
            ACE_UNKNOWN_IP_ADDRESS,         /* don't know gateway IP address*/
            "",                             /* don't know bootfile name*/
            "",                             /* don't know server host name*/
            {ACE_PROT_BOOTP, 0, DEFAULT_BOOTP_DELAY, ACE_ALWAYS_SHUTDOWN}
        },
        {                                   /* AutoIpInfo*/
            CONFIG_IS_VALID,                /* disable Auto IP by default*/
            IS_DISABLED,
            BP_AUTOIP_START_WITH_RANDOM_IP_ADDRESS,
            {ACE_PROT_AUTOIP, 0, DEFAULT_AUTOIP_DELAY, ACE_CONT_IF_GOT_ADDRESS}
        },
        {
            CONFIG_IS_VALID,                /* use RARP*/
            IS_DISABLED,
            DEFAULT_RARP_RETRANSMIT_INTERVAL,
            {ACE_PROT_RARP, 0, DEFAULT_RARP_DELAY, ACE_ALWAYS_SHUTDOWN}
        },
        {
            CONFIG_IS_VALID,                /* use Ping ARP*/
            IS_DISABLED,
            {ACE_PROT_PING_ARP, 0, DEFAULT_PING_ARP_DELAY, ACE_ALWAYS_SHUTDOWN}
        }
    }
#endif
};

static const aceConfigInterfaceInfo defaultUnusedInterfaceConfig = 
{
    CONFIG_NOT_VALID,
    IS_DISABLED,
    "",                                 /* Need to fill in with interface name*/
    {                                   /* static config record*/
        CONFIG_NOT_VALID,
        IS_DISABLED
    },
    {                                   /* config for DHCP*/
        CONFIG_NOT_VALID,
        IS_DISABLED
    },
    {                                   /* BOOTP config*/
        CONFIG_NOT_VALID,
        IS_DISABLED
    },
    {                                   /* AutoIpInfo*/
        CONFIG_NOT_VALID,               /* disable Auto IP by default*/
        IS_DISABLED
    },
    {
        CONFIG_NOT_VALID,               /* RARP */
        IS_DISABLED
    },
    {
        CONFIG_NOT_VALID,          /* Ping ARP */
        IS_DISABLED
    }
};

static void doesNothing(void);

static aceSemaphoreFn GET_SEMAPHORE = doesNothing;
static aceSemaphoreFn SET_SEMAPHORE = doesNothing;

/**
 * Shared parameter buffer protected with semaphores.
 *
 * @category ACE:ace_params
 * @since 6.0
 */
static devBoardParamsType boardParamsBuffer;


/**
 * Used internally to wait for the semaphore that protects access to the shared parameter
 * buffer to become available.
 *
 * @category ACE:ace_params
 * @since 6.0
 */
static void doesNothing(void)
{
}




/**
 * Used to install semaphore functions when operating in multitasking environment.
 * This semaphore is used for ACE configuration settings, stored in NVRAM.
 *
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   getFn       pointer to get semaphore function
 * @param   setFn       pointer to set semaphore function
 */
void aceInstallSemaphoreFunction(aceSemaphoreFn getFn, aceSemaphoreFn setFn)
{
    GET_SEMAPHORE = getFn;
    SET_SEMAPHORE = setFn;
}



/**
 * Read the development board configuration setings from NVRAM and store it into
 * a shared buffer.  This function assumes that the 
 * semaphore that protects the shared configuration buffer from reentrant
 * access has already been acquired.  The values are read into the shared
 * buffer.
 *
 * @category ACE:ace_params
 * @since 6.0
 *
 * @return BP_SUCCESS           Success
 * @return BP_FAILURE           Unable to read NVRAM
 * @return BP_WRONG_VERSION     config version field is incorrect
 */
static int readConfig(void)
{
    int result;
    
    result = customizeReadDevBoardParams (&boardParamsBuffer);
    
    if (result == BP_SUCCESS)
    {
        if (boardParamsBuffer.aceConfig.version != ACE_CONFIG_VERSION)
        {
            result = BP_WRONG_VERSION;
        }
    }
    
    return result;
}

            



/**
 * Read the entire ACE configuration structure from NVRAM and store it into
 * the buffer passed into this routine.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   config  pointer to buffer to hold ACE configuration record read from
 *                  NVRAM.
 *
 * @return BP_SUCCESS           Success
 * @return BP_FAILURE           Unable to read NVRAM
 * @return BP_WRONG_VERSION     config version field is incorrect
 */
int customizeAceGetConfig(aceConfigInfo * config)
{
    int result;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    result = readConfig ();
    
    if ((result == BP_SUCCESS) || (result == BP_WRONG_VERSION))
    {
        memcpy(config, &boardParamsBuffer.aceConfig, sizeof *config);
    }
    
    SET_SEMAPHORE();
    
    return result;
}



/**
 * Write the entire ACE configuration structure to NVRAM from a buffer
 * passed into this routine.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   config  pointer to buffer that holds ACE configuration record to be
 *                  written to NVRAM.
 *
 * @return BP_SUCCESS           Success
 * @return BP_FAILURE           Unable to write NVRAM
 * @return BP_WRONG_VERSION     config version field is incorrect
 */
int customizeAceSetConfig(aceConfigInfo const * config)
{
    int result = BP_WRONG_VERSION;
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    if (config->version == ACE_CONFIG_VERSION)
    {
        result = readConfig ();
        
        if ((result == BP_SUCCESS) || (result == BP_WRONG_VERSION))
        {
            memcpy(&boardParamsBuffer.aceConfig, config, sizeof boardParamsBuffer.aceConfig);
            result = customizeWriteDevBoardParams(&boardParamsBuffer);
        }
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Get the default configuration for the Ethernet interface.  The default config
 * is to get IP parameters by any means.
 *
 * @param   config  pointer to buffer that to be loaded with default configuration
 *                  values for the Ethernet interface
 * @param   index   indicates which entry in NADefaultEthInterfaceConfig should be used
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @return BP_SUCCESS       Success
 */
int customizeAceGetDefaultEthernetConfig(aceConfigInterfaceInfo * config, unsigned index)
{
    static int haveInitialized = FALSE;
    unsigned long app_ip_address_val, app_ip_subnet_mask_val, app_ip_gateway_val;
    char	*valueStr;

    
    NA_ASSERT(config != NULL);
    NA_ASSERT(index < DEFAULT_NUMBER_OF_INTERFACES);

    /* calculate app_ip_address_val value */
    valueStr = NAGetAppIpAddress ();
    app_ip_address_val = NAInet_addr (valueStr);

    /* calculate app_ip_subnet_mask_val value */
    valueStr = NAGetAppIpSubnetMask ();
    app_ip_subnet_mask_val = NAInet_addr(valueStr);

    /* calculate app_ip_subnet_mask_val value */
    valueStr = NAGetAppIpGateway ();
    app_ip_gateway_val = NAInet_addr (valueStr);

    if (!haveInitialized)
    {
        memcpy(&NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].dhcp_config.desired_params, dhcp_desired_params, sizeof dhcp_desired_params);
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.isConfigValid = TRUE;
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.isEnabled = NAGetAppUseStaticIP();
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.ip_address = app_ip_address_val;
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.subnet_mask = app_ip_subnet_mask_val;
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.gateway = app_ip_gateway_val;
        NADefaultEthInterfaceConfig[PRIMARY_ETHERNET_INTERFACE].static_config.auto_assign = NAGetAppUseStaticIP();
        NADefaultEthInterfaceConfig[AUTO_IP_INTERFACE].isEnabled = NAGetEnableAutoIp();
        NADefaultEthInterfaceConfig[AUTO_IP_INTERFACE].autoip_config.isEnabled = NAGetEnableAutoIp();
        haveInitialized = TRUE;
    }
    
    *config = NADefaultEthInterfaceConfig[index];
    
    return BP_SUCCESS;
}


/**
 * Get default configuration values for ACE.  The default configuration is to use
 * all protocols to get an IP address for the eth0 interface.
 *
 * @param   config  pointer to buffer that to be loaded with default configuration
 *                  values for ACE.
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @return BP_SUCCESS       Success
 */
int customizeAceGetDefaultConfig(aceConfigInfo * config)
{
    int index;
    int first_unused = 0;
    
    NA_ASSERT(config != NULL);
    
    config->version = ACE_CONFIG_VERSION;
    
#if	BSP_WANT_ETHERNET
    customizeAceGetDefaultEthernetConfig(&config->ace_interface_info[0], 0);
    first_unused++;

#if (DEFAULT_NUMBER_OF_INTERFACES > 1)    
    customizeAceGetDefaultEthernetConfig(&config->ace_interface_info[1], 1);
    first_unused++;
#endif    
#endif    

#if	BSP_WANT_WIRELESS

#if	BSP_WANT_ETHERNET && (CONFIG_ACE_MAX_INTERFACES < 2*DEFAULT_NUMBER_OF_INTERFACES)
#error CONFIG_ACE_MAX_INTERFACES must be >= 2*DEFAULT_NUMBER_OF_INTERFACES
#endif

	customizeAceGetDefaultInterfaceConfig(BP_WLN_INTERFACE, &config->ace_interface_info[first_unused]);
    first_unused++;

#if (DEFAULT_NUMBER_OF_INTERFACES > 1)    
	customizeAceGetDefaultInterfaceConfig(BP_WLN_0INTERFACE, &config->ace_interface_info[first_unused]);
    first_unused++;
#endif    
#endif /* BSP_WANT_WIRELESS */

    for (index = first_unused; index < asizeof(config->ace_interface_info); index++)
    {
        config->ace_interface_info[index] = defaultUnusedInterfaceConfig;
    }
    
    return BP_SUCCESS;
}


/**
 * Read the ACE board parameters into the shared buffer and then find the
 * interface record for the indicated interface.  This function assumes
 * that the semaphore has already been gotten.
 *
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 *
 * @return NULL     no config for indicated interface
 * @return otherwise pointer to record in shared buffer with interface config
 */
static aceConfigInterfaceInfo * getInterfaceConfig(char const * ifname)
{
    aceConfigInterfaceInfo *result = NULL;
    int index;
    
    if (readConfig() == BP_SUCCESS)
    {
        for (index = 0; index < CONFIG_ACE_MAX_INTERFACES; index++)
        {
            if (boardParamsBuffer.aceConfig.ace_interface_info[index].isConfigValid)
            {
                if (strcmp(boardParamsBuffer.aceConfig.ace_interface_info[index].ifname, ifname) == 0)
                {
                    result = &boardParamsBuffer.aceConfig.ace_interface_info[index];
                    break;
                }
            }
        }
    }
    
    return result;
}




/**
 * This function is used when we are about to write to an interface's 
 * configuration record.
 * Read the ACE board parameters into the shared buffer and find the 
 * record for the indicated interface.  If the record cannot be found,
 * then find an unused record and zero it in preparation to write new
 * values to all or part of it.
 *
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 *
 * @return NULL     no room for configuration
 * @return otherwise pointer to record in shared buffer with interface config
 */
static aceConfigInterfaceInfo * getInterfaceConfigToWrite(char const * ifname)
{
    aceConfigInterfaceInfo *result = NULL;
    int index;
    
    if (readConfig() == BP_SUCCESS)
    {
        result = getInterfaceConfig(ifname);
        if (result == NULL)
        {
            /*
             * If we couldn't find an existing record, then look for an empty one.
             */
            for (index = 0; index < CONFIG_ACE_MAX_INTERFACES; index++)
            {
                if (!boardParamsBuffer.aceConfig.ace_interface_info[index].isConfigValid)
                {
                    result = &boardParamsBuffer.aceConfig.ace_interface_info[index];
                    *result = defaultUnusedInterfaceConfig;
                    result->isConfigValid = TRUE;
                    result->isEnabled = TRUE;
                }
            }
        }
    }
    
    return result;
}




/**
 * Read the ACE configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     config version field is incorrect
 */
int customizeAceGetInterfaceConfig(char const * ifname, aceConfigInterfaceInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, interfaceConfig, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Get a configuration record that contains appropriate default values for the
 * indicated interface.  This routine does NOT check to see if a configuration
 * for the interface already exists.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer to write configuration values into
 *
 * @return BP_SUCCESS       Success
 */
int customizeAceGetDefaultInterfaceConfig(char const * ifname, aceConfigInterfaceInfo * config)
{
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    
#if	BSP_WANT_ETHERNET
    if (strcmp(ifname, BP_ETH_INTERFACE) == 0)
    {
        customizeAceGetDefaultEthernetConfig(config, 0);
    }
    else
    if (strcmp(ifname, BP_ETH_0INTERFACE) == 0)
    {
        customizeAceGetDefaultEthernetConfig(config, 1);
    }
    else
#endif
#if	BSP_WANT_WIRELESS
	/* For wireless, use the same default config as ethernet. */
    if (strcmp(ifname, BP_WLN_INTERFACE) == 0)
    {
        customizeAceGetDefaultEthernetConfig(config, 0);
		/* Update the interface name. */
		strcpy(config->ifname, BP_WLN_INTERFACE);
#if	BSP_WANT_ETHERNET
		/* Use the next higher static IP address. */
		config->static_config.ip_address++;
#endif
    }
    else
    if (strcmp(ifname, BP_WLN_0INTERFACE) == 0)
    {
        customizeAceGetDefaultEthernetConfig(config, 1);
		strcpy(config->ifname, BP_WLN_0INTERFACE);
    }
    else
#endif
    {
        *config = defaultUnusedInterfaceConfig;
    }
    
    return BP_SUCCESS;
}


/**
 * Write the ACE configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 */
int customizeAceSetInterfaceConfig(char const * ifname, aceConfigInterfaceInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (interfaceConfig, config, sizeof *interfaceConfig);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Read the static IP configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     config version field is incorrect
 */
int customizeAceGetStaticConfig(char const * ifname, configAceStaticInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, &interfaceConfig->static_config, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Write the static IP configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * The routine will fail if no configuration for the interface exists at all.  In
 * this case you can use customizeAceGetDefaultInterfaceConfig() and 
 * customizeAceSetInterfaceConfig() to create a configuration record for the
 * interface, and then call this routine again to update it.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceSetStaticConfig(char const * ifname, configAceStaticInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (&interfaceConfig->static_config, config, sizeof interfaceConfig->static_config);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Read the DHCP configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceGetDhcpConfig(char const * ifname, configAceDhcpInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, &interfaceConfig->dhcp_config, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Write the DHCP configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * The routine will fail if no configuration for the interface exists at all.  In
 * this case you can use customizeAceGetDefaultInterfaceConfig() and 
 * customizeAceSetInterfaceConfig() to create a configuration record for the
 * interface, and then call this routine again to update it.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceSetDhcpConfig(char const * ifname, configAceDhcpInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (&interfaceConfig->dhcp_config, config, sizeof interfaceConfig->static_config);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Read the BOOTP configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceGetBootpConfig(char const * ifname, configAceBootpInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, &interfaceConfig->bootp_config, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Write the BOOTP configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * The routine will fail if no configuration for the interface exists at all.  In
 * this case you can use customizeAceGetDefaultInterfaceConfig() and 
 * customizeAceSetInterfaceConfig() to create a configuration record for the
 * interface, and then call this routine again to update it.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceSetBootpConfig(char const * ifname, configAceBootpInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (&interfaceConfig->bootp_config, config, sizeof interfaceConfig->static_config);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Read the Auto IP configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceGetAutoipConfig(char const * ifname, configAceAutoipInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, &interfaceConfig->autoip_config, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Write the Auto IP configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * The routine will fail if no configuration for the interface exists at all.  In
 * this case you can use customizeAceGetDefaultInterfaceConfig() and 
 * customizeAceSetInterfaceConfig() to create a configuration record for the
 * interface, and then call this routine again to update it.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceSetAutoipConfig(char const * ifname, configAceAutoipInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (&interfaceConfig->autoip_config, config, sizeof interfaceConfig->static_config);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Read the Ping ARP configuration values for a single interface into a buffer
 * supplied by the caller.  The interface is identified by name.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to read NVRAM
 * @return BP_NO_RECORD     There is no record for the indicated interface
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceGetRarpConfig(char const * ifname, configAceRarpInfo * config)
{
    int result = BP_NO_RECORD;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfig(ifname);
    
    if (interfaceConfig)
    {
        memcpy (config, &interfaceConfig->rarp_config, sizeof *config);
        result = BP_SUCCESS;
    }
    
    SET_SEMAPHORE();
    
    return result;
}


/**
 * Write the Ping ARP configuration values for a single interface into NVRAM.
 * The configuration data is supplied in a buffer provided by the caller.
 * If the indicated interface already has a configuration, then the old
 * configuration record is overwritten.  Otherwise, a new interface configuration
 * record is added to the NVRAM structure if space is available.
 *
 * The routine will fail if no configuration for the interface exists at all.  In
 * this case you can use customizeAceGetDefaultInterfaceConfig() and 
 * customizeAceSetInterfaceConfig() to create a configuration record for the
 * interface, and then call this routine again to update it.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to write the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer that to read configuration values into
 *
 * @return BP_SUCCESS       Success
 * @return BP_FAILURE       Unable to write NVRAM
 * @return BP_FULL          There is no record for a new configuration record
 * @return BP_WRONG_VERSION     version field in current record is incorrect
 */
int customizeAceSetRarpConfig(char const * ifname, configAceRarpInfo const * config)
{
    int result = BP_FULL;
    aceConfigInterfaceInfo *interfaceConfig = NULL;
    
    GET_SEMAPHORE();
    
    NA_ASSERT(config != NULL);
    NA_ASSERT(ifname != NULL);
    NA_ASSERT(strlen(ifname));
    
    interfaceConfig = getInterfaceConfigToWrite(ifname);
    
    if (interfaceConfig)
    {
        memcpy (&interfaceConfig->rarp_config, config, sizeof interfaceConfig->static_config);
        result = customizeWriteDevBoardParams(&boardParamsBuffer);
    }
    
    SET_SEMAPHORE();
    
    return result;
}



/**
 * Find the ACE configuration record for the indicated interface in the 
 * ACE config passed to us.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   ifname  a nul terminated string that contains the name of the interface
 *                  to read the configuration record for.  For example "eth0" or
 *                  "eth0:0".  The string is case sensitive.
 * @param   config  pointer to buffer with ACE config info
 *
 * @return NULL             no config for interface was found
 * @return pointer to interface configuration
 */
aceConfigInterfaceInfo * customizeAceFindInterfaceConfig(char const * ifname, aceConfigInfo * config)
{
    int index;
    aceConfigInterfaceInfo *result = NULL;
    
    if (config->version == ACE_CONFIG_VERSION)
    {
        for (index = 0; index < CONFIG_ACE_MAX_INTERFACES; index++)
        {
            if (config->ace_interface_info[index].isConfigValid)
            {
                if (strcmp(config->ace_interface_info[index].ifname, ifname) == 0)
                {
                    result = &config->ace_interface_info[index];
                    break;
                }
            }
        }
    }
    
    return result;
}


/**
 * Find an unused configuration record in the ACE config info structure.  If the
 * forceIt parameter is set, then if no records are available, the first one will
 * be zeroed and returned.
 *
 * @external
 * @category ACE:ace_params
 * @since 6.0
 *
 * @param   config      pointer to buffer with ACE config info
 * @param   forceIt     grab record zero if nothing else is available
 *
 * @return NULL             no unused record was found
 * @return pointer to unused interface configuration
 */
aceConfigInterfaceInfo * customizeAceFindUnusedInterfaceConfig(aceConfigInfo * config, BOOLEAN forceIt)
{
    int index;
    aceConfigInterfaceInfo *result = NULL;
    static aceConfigInterfaceInfo const zero = {0};
    
    if (config->version == ACE_CONFIG_VERSION)
    {
        for (index = 0; index < CONFIG_ACE_MAX_INTERFACES; index++)
        {
            if (!config->ace_interface_info[index].isConfigValid)
            {
                config->ace_interface_info[index] = zero;
                result = &config->ace_interface_info[index];
                break;
            }
        }
    }
    else
    {
        config->version = ACE_CONFIG_VERSION;
        for (index = 0; index < CONFIG_ACE_MAX_INTERFACES; index++)
        {
            config->ace_interface_info[index] = zero;
        }
        result = &config->ace_interface_info[index];
    }

    if ((result == NULL) && (forceIt))
    {
        config->ace_interface_info[0] = zero;
        result = &config->ace_interface_info[0];
    }
    
    return result;
}


