/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * =======================================================================
 * This module contains the APIs to set and get board paramaters. Some of 
 * the APIs from boardParams.c are moved here to reduce bootloader size. 
 *
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 */

#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <bsp_sys.h>
#include <bsp_net.h>
#include <wln_api.h>
#include <narmnvrm.h>
#include <bootldr.h>
#include <boardParams.h>
#include <appconf_api.h>

#if 0
#define NAMI_SUPPORTED	(PLATFORM != connectcore9p9360_a && PLATFORM != connectcore7u_a)

#if NAMI_SUPPORTED
#include "nami.h"    
#endif

#define STORAGE_DEBUG(x, ...)
//#define STORAGE_DEBUG(x, ...) printf(__VA_ARGS__)
//#define STORAGE_DEBUG(x, ...) trace(EDP_TRACE, x, __VA_ARGS__)

#if NAMI_SUPPORTED
/* we must not go beyond the area that NAMI uses */
#define STORAGE_SIZE_IS_VALID(x, y)  (NANVSizeValid((x), (y)) && (((x) + (y)) < NAMI_LOCATION_IN_FLASH))
//#define STORAGE_SIZE_IS_VALID(x, y)  (((x) + (y)) < NAMI_LOCATION_IN_FLASH)

#else
/* always valid. NANVRead and NANVWrite will return error if not valid */
#define STORAGE_SIZE_IS_VALID(x, y)  (NANVSizeValid((x), (y)))
//#define STORAGE_SIZE_IS_VALID(x, y)  (1)
#endif
#endif

/*ad*
 * Reads the static IPv6 gateway configuration for the interface from NVRAM. 
 *
 * One static IPv6 gateway record is used per interface, it is stored 
 * in NVRAM with the interface name "eth0" for Ethernet and "wln0" for wireless.
 *
 * The static IPv6 configuration record can contained unspecified IPv6 address,
 * if there is no need to manually configure an IPv6 gateway.
 * If NVRAM is not used, or there is no record for ifname in NVRAM, the default record is returned:
 *  <list type="ordered">
 *      <item> For "eth0" or "wln0" APP_IPV6_GATEWAY or APP_2ND_INTERFACE_IPV6_ADDRESS is used, if defined </item>
 *      <item> Otherwise returned IPv6 address will have AF_INET6 family and unspecified IPv6 address </item>
 *  </list>
 * BP_SUCCESS is returned if no IP address is defined for static IPv6 gateway, the gatewayPtr 
 * structure would have unspecified (all-zero) IPv6 address on return.
 *
 * @external
 * @category Networking:IAM:iam_params
 * @include <boardParams.h>
 * @since 7.6
 *
 * @param  ifname               Interface name: "eth0" or "wln0"
 * @param  gatewayPtr           Pointer to a structure to return the static IPv6 gateway
 *
 * @return BP_SUCCESS           Successful
 * @return BP_INVALID_ARGUMENT  Either ifname or gatewayPtr is NULL
 * @return BP_NO_RESOURCES      Not enough memory load the board paramaters
 *
 * @see @link customizeIamSetIpv6GatewayConfig
 * @see @link customizeIamFindIpv6GatewayConfig
 * @see @link customizeIamGetDefaultIpv6GatewayConfig
 * @see @link customizeReadDevBoardParams
 *
*ad*/
int customizeIamGetIpv6GatewayConfig(const char * ifname, struct sockaddr_storage * gatewayPtr)
{
    devBoardParamsType * params = NULL;
    int     result = BP_SUCCESS;
    BOOLEAN found = FALSE;

    if (ifname == NULL || gatewayPtr == NULL)
    {
        result = BP_INVALID_ARGUMENT;
        goto done;
    }
	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params == NULL)
	{
        result = BP_NO_RESOURCES;
		goto done;
	}

    if (customizeReadDevBoardParams(params) == BP_SUCCESS)             
    {
        *gatewayPtr = *customizeIamFindIpv6GatewayConfig(ifname, params);
        found = TRUE;
    }

    if (!found)
    {
        *gatewayPtr = customizeIamGetDefaultIpv6GatewayConfig(ifname);
    }
    free(params);

done:
    return result;
}

/*ad*
 * Saves the static IPv6 gateway configuration for the interface in NVRAM. 
 *
 * One static IPv6 gateway record is used per interface, it is stored 
 * in NVRAM with the interface name "eth0" for Ethernet and "wln0" for wireless.
 *
 * The gateway can have unspecified (all-zero) IPv6 address with address family AF_UNSPEC 
 * or AF_INET6, if there is no need to manually configure an IPv6 gateway.
 *
 * @external
 * @category Networking:IAM:iam_params
 * @include <boardParams.h>
 * @since 7.6
 *
 * @param  ifname               Interface name: "eth0" or "wln0"
 * @param  gatewayPtr           Pointer to the static IPv6 gateway to save in NVRAM
 *
 * @return BP_SUCCESS           Successful
 * @return BP_NO_RECORD         NVRAM does not have a static IPv6 gateway record for 
 *                              for this specified ifname
 * @return BP_INVALID_ARGUMENT  Either ifname or gatewayPtr is NULL
 * @return BP_NO_RESOURCES      Not enough memory load the board paramaters
 *
 * @see @link customizeIamGetIpv6GatewayConfig
 * @see @link customizeIamFindIpv6GatewayConfig
 * @see @link customizeIamGetDefaultIpv6GatewayConfig
 * @see @link customizeReadDevBoardParams
 *
*ad*/
int customizeIamSetIpv6GatewayConfig(const char * ifname, const struct sockaddr_storage * gatewayPtr)
{
    devBoardParamsType *params = NULL;
    int i, result = BP_NO_RECORD;

    if (ifname == NULL || gatewayPtr == NULL)
    {
        result = BP_INVALID_ARGUMENT;
        goto done;
    }

	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params == NULL)
	{
		result = BP_NO_RESOURCES;
        goto done;
	}
    result = customizeReadDevBoardParams (params);             /* read params in NVRAM*/

    for (i = 0; i < NA_IAM_EX_ENTRIES; i++)
    {
        if (strcmp(ifname, params->iamExParams.ifname[i]) == 0)
        {
            params->iamExParams.ipv6gateway[i] = *gatewayPtr;
            result = BP_SUCCESS;
            break;
        }
    }
    if (result == BP_SUCCESS)
       customizeWriteDevBoardParams(params);

done:
    if (params)
	    free(params);

    return result;
}
     
/*ad*
 * Find the static IPv6 gateway record for the indicated interface in the NVRAM configuration 
 * structure passed in to this function.
 *
 * One static IPv6 gateway record is used per interface, it is stored 
 * in NVRAM with the interface name "eth0" for Ethernet and "wln0" for wireless.
 *
 * @external
 * @category Networking:IAM:iam_params
 * @include <boardParams.h>
 * @since 7.6
 *
 * @param  ifname               Interface name: "eth0" or "wln0"
 * @param  nvParamsPtr          Pointer to device board parameters buffer.
 *
 * @return  Pointer to a static IPv6 gateway within device board parameters buffer. 
 *          NULL is returned if the ifname if not found. 
 *
 * @see @link customizeIamGetIpv6GatewayConfig
 * @see @link customizeIamSetIpv6GatewayConfig
 * @see @link customizeIamGetDefaultIpv6GatewayConfig
 * @see @link customizeReadDevBoardParams
 *
*ad*/
struct sockaddr_storage * customizeIamFindIpv6GatewayConfig(const char * ifname, devBoardParamsType * nvParamsPtr)
{
    struct sockaddr_storage * gatewayPtr = NULL;
    int i;

    if (ifname == NULL || nvParamsPtr == NULL)
    {
        goto done;
    }

    for (i = 0; i < NA_IAM_EX_ENTRIES; i++)
    {
        if (strcmp(ifname, nvParamsPtr->iamExParams.ifname[i]) == 0)
        {
            gatewayPtr = &nvParamsPtr->iamExParams.ipv6gateway[i];
            break;
        }
    }
    
done:
    return gatewayPtr;
}

/*ad*
 * Return system default value for the  static IPv6 gateway for the interface. 
 *
 * One static IPv6 gateway record is used per interface, it is stored 
 * in NVRAM with the interface name "eth0" for Ethernet and "wln0" for wireless. 
 *
 * If the name is "eth0" or "wln0", APP_IPV6_GATEWAY or APP_2ND_INTERFACE_IPV6_GATEWAY 
 * is used, if defined. Otherwise the function returns  a sockaddr_storage structure 
 * with AF_INET6 family and unspecified IPv6 address.
 *
 *
 * @external
 * @category Networking:IAM:iam_params
 * @include <boardParams.h>
 * @since 7.6
 *
 * @param  ifname   Interface name
 *
 * @return  Structure, containing system default value for the  static IPv6 gateway.
 *
 * @see @link customizeIamFindIpv6GatewayConfig
 * @see @link customizeIamGetIpv6GatewayConfig
 * @see @link customizeIamSetIpv6GatewayConfig
 * @see @link customizeReadDevBoardParams
 *
*ad*/
struct sockaddr_storage customizeIamGetDefaultIpv6GatewayConfig(const char * ifname)
{
    char *ipv6AddrStr = NULL;
    int result = BP_SUCCESS;
    struct sockaddr_storage gateway;

    if (ifname != NULL)
    {
#if BSP_WANT_ETHERNET
        if (strcmp(ifname, BP_ETH_INTERFACE) == 0)
        {
            ipv6AddrStr = NAGetAppIpv6Gateway();
        }
#endif

#if BSP_WANT_WIRELESS
        if (ipv6AddrStr == NULL && strcmp(ifname, BP_WLN_INTERFACE) == 0)
        {
        #if !BSP_WANT_ETHERNET
            ipv6AddrStr = NAGetAppIpv6Gateway();
        #else
            ipv6AddrStr = NAGetApp2ndInterfaceIpv6Gateway();
        #endif
        }
#endif
    }
	memset(&gateway, 0, sizeof gateway);
    if (ipv6AddrStr != NULL)
    {
    	int error_code = inet_pton (AF_INET6, ipv6AddrStr, &gateway.addr.ipv6.sin6_addr);
        if (error_code != 1)
        {
            memset(&gateway, 0, sizeof gateway);
            result = BP_FAILURE;
        }
    }
	gateway.ss_len = sizeof(struct sockaddr_in6);
	gateway.ss_family = AF_INET6;

	return gateway;
}


#if BSP_WANT_WIRELESS
/*
 * customizeGetWirelessParameters
 *
 *  Description:
 *
 *      This function retrieves the wireless network parameters 
 *      from NVRAM and returns it in the buffer passed to us.  
 *      If we are unable to read NVRAM, then a default value is used.
 * 
 *  Notes:
 *
 *      THIS CODE MUST BE CUSTOMIZED FOR YOUR BOARD.  The implementation
 *      below is for NET+OS development boards only.
 *
 *  Parameters:
 *
 *      wln_params  Pointer to destination of wireless parameters.
 *
 */
int customizeGetWirelessParameters(wln_params *wlnParams)
{
    devBoardParamsType *params;
    int result;

    NA_ASSERT(wlnParams != NULL);

	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params == NULL)
	{
		return -1;
	}
    result = customizeReadDevBoardParams (params);             /* read params in NVRAM*/
    
    /* not same anymore, one is wln_params and other one is NaWlnParams_t,
       so field by field mapping is future safe */
    wlnParams->bss_type     = params->wlnParams.bss_type;
    wlnParams->ssid_len     = params->wlnParams.ssid_len;
    wlnParams->channel      = params->wlnParams.channel;
    wlnParams->authen       = params->wlnParams.authen;
    wlnParams->encrypt      = params->wlnParams.encrypt;
    wlnParams->wep_key_id   = params->wlnParams.wep_key_id;
    wlnParams->wep_key_len  = params->wlnParams.wep_key_len;
    wlnParams->eap_methods  = params->wlnParams.eap_methods;
    wlnParams->encrypt      = params->wlnParams.encrypt;
    wlnParams->options      = params->wlnParams.options;
    wlnParams->chan_mask[0] = params->wlnParams.chan_mask;
    wlnParams->chan_mask[1] = params->wlnExtParams.ext_chan_mask;
    wlnParams->tx_rate      = params->wlnParams.tx_rate;
    wlnParams->rts_thresh   = params->wlnParams.rts_thresh;
    wlnParams->frag_thresh  = params->wlnParams.frag_thresh;
    wlnParams->power_mode   = params->wlnParams.power_mode;
    wlnParams->band         = params->wlnParams.band;    
    wlnParams->tx_power     = params->wlnParams.tx_power;
    memcpy(wlnParams->ssid, params->wlnParams.ssid, sizeof wlnParams->ssid);
    memcpy(wlnParams->sta_addr, params->wlnParams.sta_addr, sizeof wlnParams->sta_addr);
    memcpy(wlnParams->wep_key, params->wlnParams.wep_key, sizeof wlnParams->wep_key);
    memcpy(wlnParams->wpa_psk, params->wlnParams.wpa_psk, sizeof wlnParams->wpa_psk);
    memcpy(wlnParams->identity, params->wlnParams.identity, sizeof wlnParams->identity);
    memcpy(wlnParams->password, params->wlnParams.password, sizeof wlnParams->password);     
    
	free(params);

    return result;
}

int customizeSetWirelessParameters(wln_params *wlnParams)
{
    devBoardParamsType *params;
    int result;

    NA_ASSERT(wlnParams != NULL);

	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params == NULL)
	{
		return -1;
	}
    result = customizeReadDevBoardParams (params);             /* read params in NVRAM*/
    
    /* not same anymore, one is wln_params and other one is NaWlnParams_t,
       so field by field mapping is future safe */
    params->wlnParams.bss_type     = wlnParams->bss_type;
    params->wlnParams.ssid_len     = wlnParams->ssid_len;
    params->wlnParams.channel      = wlnParams->channel;
    params->wlnParams.authen       = wlnParams->authen;
    params->wlnParams.encrypt      = wlnParams->encrypt;
    params->wlnParams.wep_key_id   = wlnParams->wep_key_id;
    params->wlnParams.wep_key_len  = wlnParams->wep_key_len;
    params->wlnParams.eap_methods  = wlnParams->eap_methods;
    params->wlnParams.encrypt      = wlnParams->encrypt;
    params->wlnParams.options      = wlnParams->options;
    params->wlnParams.chan_mask    = wlnParams->chan_mask[0];
    params->wlnExtParams.ext_chan_mask  = wlnParams->chan_mask[1];
    params->wlnParams.tx_rate      = wlnParams->tx_rate;
    params->wlnParams.rts_thresh   = wlnParams->rts_thresh;
    params->wlnParams.frag_thresh  = wlnParams->frag_thresh;
    params->wlnParams.power_mode   = wlnParams->power_mode;
    params->wlnParams.band         = wlnParams->band;    
    params->wlnParams.tx_power     = wlnParams->tx_power;
    memcpy(params->wlnParams.ssid, wlnParams->ssid, sizeof params->wlnParams.ssid);
    memcpy(params->wlnParams.sta_addr, wlnParams->sta_addr, sizeof params->wlnParams.sta_addr);
    memcpy(params->wlnParams.wep_key, wlnParams->wep_key, sizeof params->wlnParams.wep_key);
    memcpy(params->wlnParams.wpa_psk, wlnParams->wpa_psk, sizeof params->wlnParams.wpa_psk);
    memcpy(params->wlnParams.identity, wlnParams->identity, sizeof params->wlnParams.identity);
    memcpy(params->wlnParams.password, wlnParams->password, sizeof params->wlnParams.password);     
    
    customizeWriteDevBoardParams(params);

	free(params);

    return result;
}

#ifdef MAC_PS_ENABLED

/**
 * Gets the current dutycycle values set in NVRAM if 
 * @link BSP_WIFI_DUTY_CYCLE_CONFIG_ENABLE is set. Otherwise this API returns the
 * ON and OFF time configured in 
 * @link BSP_WIFI_DUTY_CYCLE_ON_TIME and
 * @link BSP_WIFI_DUTY_CYCLE_OFF_TIME. See 
 * @link wln_duty_cycle for the units
 * and allowed limits of the ON and OFF periods.  
 *
 * @category BSP:Device_Drivers:dutycycle
 * @include <boardParams.h>
 * @external
 * @since 7.4
 * @platform connectcorewi9p9215_a, connectwiem9210 
 *
 * @see @link wln_duty_cycle
 * @see @link naWlnSetDutyCycle
 * @see @link naWlnGetDutyCycle
 * @see @link customizeSetWirelessDutyCycle
 *
 * @param dutycycle  Pointer to store the duty cycle values set in NVRAM.
 *
 * @return 0        successful
 * @return negative null pointer is passed as dutycycle
 *
 * @note Use @link naWlnGetDutyCycle to get the current value set in wireless
 * driver.
**/
int customizeGetWirelessDutyCycle(wln_duty_cycle * dutycycle)
{
    int ret = -1;
#if (BSP_WIFI_DUTY_CYCLE_CONFIG_ENABLE == TRUE)
    devBoardParamsType *params;
#endif

    NA_ASSERT(dutycycle != NULL);
    if (dutycycle != NULL)
        memset (dutycycle, 0, sizeof(wln_duty_cycle));
    else
        return ret;

#if (BSP_WIFI_DUTY_CYCLE_CONFIG_ENABLE == TRUE)
	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params != NULL)
	{
        if (customizeReadDevBoardParams (params) == BP_SUCCESS)  /* read params in NVRAM */
        {
            if (params->wlnParams.options & WLN_OPT_PS_ENABLE)
            {   // update only if the flag is enabled
                memcpy (dutycycle, &params->wlnExtParams.dutyCycle, sizeof params->wlnExtParams.dutyCycle);
            }
            ret = 0;
        }        
        
	    free(params);
    }
#else
    dutycycle->ontime = BSP_WIFI_DUTY_CYCLE_ON_TIME;
    dutycycle->offtime = BSP_WIFI_DUTY_CYCLE_OFF_TIME;
    ret = 0;
#endif

    return ret;    
}

/**
 * Sets the specified dutycycle values in the NVRAM or board parameter area. 
 * See @link wln_duty_cycle for the units and allowed limits of the ON and 
 * OFF periods. Function returns negative value if one of the periods is out
 * of range. Calling this function with OFF time of 0 msec will disable the
 * duty cycling. 
 *
 * @category BSP:Device_Drivers:dutycycle
 * @include <boardParams.h>
 * @external
 * @since 7.4
 * @platform connectcorewi9p9215_a, connectwiem9210 
 *
 * @see @link wln_duty_cycle
 * @see @link naWlnGetDutyCycle
 * @see @link naWlnSetDutyCycle
 * @see @link customizeGetWirelessDutyCycle
 *
 * @param dutycycle     ON and OFF periods in msec to set in NVRAM.
 *
 * @return 0        successful
 * @return negative error
 *
 * @note Use @link naWlnSetDutyCycle to set the value in the wireless driver. 
 * Otherwise changes will take effect only after reset. 
 **/ 
int customizeSetWirelessDutyCycle(wln_duty_cycle * dutycycle)
{
    int ret = -1;
    devBoardParamsType *params;

    NA_ASSERT(dutycycle != NULL);

    if ((dutycycle->ontime < WLN_PS_MIN_ON_TIME) || (dutycycle->offtime > WLN_PS_MAX_OFF_TIME))
        return -1;
    
	params = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
	if (params != NULL)
	{
        if (customizeReadDevBoardParams (params) == BP_SUCCESS)  /* read params in NVRAM */
        {
            if (dutycycle->offtime == 0)
                params->wlnParams.options &= ~WLN_OPT_PS_ENABLE;
            else
                params->wlnParams.options |= WLN_OPT_PS_ENABLE;
                    
            memcpy (&params->wlnExtParams.dutyCycle, dutycycle, sizeof params->wlnExtParams.dutyCycle);
            if (customizeWriteDevBoardParams (params) == BP_SUCCESS)
            {
                ret = 0;
            }
        }        
        
	    free(params);
    }

    return ret;
}

#endif //  MAC_PS_ENABLED
#endif //  BSP_WANT_WIRELESS  

/*ad*
 * Gets the user data from NVRAM. The maximum allowed length is 292 bytes.
 * It reads the total number of bytes asked in size field if the offset + size
 * is less than 292.
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  data     Buffer pointer to hold the user data
 * @param  offset   Byte ofset from where data is to be read
 * @param  size     Size of the buffer (maximum allowed offset+size is 292
 *                  bytes)
 *
 * @return BP_SUCCESS           successful
 * @return BP_INVALID_ARGUMENT  Either data is NULL or offset+size is more than 292
 * @return BP_NO_RESOURCES      not enough memory to extract the board paramater
 * @return BP_FAILURE           failed to read board paramater
 *
 * @see @link customizeSetUserParams
 * @see @link customizeReadDevBoardParams
 *
*ad*/
int customizeGetUserParams(void * data, int offset, int size)
{
    devBoardParamsType *params = NULL;
    int result = BP_SUCCESS;

    if ((data == NULL) || ((offset + size) > BP_USER_DATA_LEN))
    {
        result = BP_INVALID_ARGUMENT;
        goto getParamsDone;
    }

	if ((params = (devBoardParamsType *)malloc(sizeof *params)) == NULL)
    {
		result = BP_NO_RESOURCES;
        goto getParamsDone;        
    }
    
    result = customizeReadDevBoardParams (params);
    if (result == BP_SUCCESS)
        memcpy(data, params->userData + offset, size);

    getParamsDone:
    if (params != NULL)
	    free(params);

    return result;
}

/*ad*
 * Sets the user data in NVRAM. The maximum allowed length is 292 bytes.
 * It writes the total number of bytes asked in size field if the offset 
 * + size is less than 292.
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  data     Buffer pointer which holds the user data
 * @param  offset   Byte ofset from where data is to be written
 * @param  size     Data size (maximum allowed offset+size is 292 bytes)
 *
 * @return BP_SUCCESS           successful
 * @return BP_INVALID_ARGUMENT  Either data is NULL or offset+size is more than 292
 * @return BP_NO_RESOURCES      not enough memory to extract the board paramater
 * @return BP_FAILURE           failed to read/write board paramater
 *
 * @see @link customizeGetUserParams
 * @see @link customizeReadDevBoardParams
 *
*ad*/
int customizeSetUserParams(void * data, int offset, int size)
{
    devBoardParamsType *params = NULL;
    int result = BP_SUCCESS;

    if ((data == NULL) || ((offset + size) > BP_USER_DATA_LEN))
    {
        result = BP_INVALID_ARGUMENT;
        goto setParamsDone;
    }

	if ((params = (devBoardParamsType *)malloc(sizeof *params)) == NULL)
    {
		result = BP_NO_RESOURCES;
        goto setParamsDone;        
    }
    
    result = customizeReadDevBoardParams(params);
    if (result == BP_SUCCESS)
    {
        memcpy(params->userData + offset, data, size);
        result = customizeWriteDevBoardParams(params);    
    }

    setParamsDone:
    if (params != NULL)
	    free(params);

    return result;
}

#if 0
typedef struct storageParamsInfo {
    unsigned short  id;
    unsigned short  size;
    unsigned long   crc32;
} storageParamsInfo_t;

/**
 * Writes data in NVRAM after development board's startup parameters.
 * 
 * This allows user to store and retrieve any data in volatile area (NVRAM) after
 * development board's startup parameters. See @link customizeReadDevBoardParams
 * and @link customizeWriteDevBoardParams for development board's startup parameters.
 *
 * It will write number of bytes of data plus additional 8 bytes overhead for internal use.
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  id       An unique identifier value of the data written to NVRAM.
 *                  This is used to write and read the data in NVRAM.
 *                  It must be between 0 and 65535. It cannot be 0 or 65535.
 * @param  data     Pointer to data to be written into NVRAM.
 * @param  size     Number of bytes of data to be wrriten into NVRAM.
 *
 * @return NASTATUS_SUCCESS             Data has been successfully written into NVRAM.
 * @return NASTATUS_INVALID_HANDLE      Invalid identifier or size.
 * @return NASTATUS_MALLOC_ERROR        Fails to allocate memory.
 * @return NASTATUS_RESOURCE_ERROR      Fails on reading or writing NVRAM
 * @return NASTATUS_INVALID_PARAMETER   The size exceeds the maximum size of NVRAM
 *
 * @see @link customizeReadStorageParams
 * @see @link customizeRemoveStorageParams
 *
 * @note If the size of the development board's startup parameters has changed,
 * the data written previously will be lost.
 *
**/
NaStatus customizeWriteStorageParams(NaCustomizeStorageId_t id, void * data, unsigned short size)
{
 
    NaStatus    rc = NASTATUS_SUCCESS;
    unsigned    offset;
    char        * thePtr = NULL;
    unsigned    totalSize = 0;
    unsigned    storageSize = 0;
    WORD32      storageOffset; 
    BOOLEAN     isFound = FALSE;
    int         ccode;

    storageParamsInfo_t storageInfo;

    NA_ASSERT(data != NULL);
    NA_ASSERT(size > 0);
    NA_ASSERT(id > 0 && id < 0xFFFF);
    NA_ASSERT(id != (NaCustomizeStorageId_t)-1);

    if (id == 0 || id == (NaCustomizeStorageId_t)-1)
    {
        rc = NASTATUS_INVALID_HANDLE;
        goto _ret;
    }


    if (data != NULL && size > 0)
    {
        /* skip board parameter */
        offset = BSP_START_OF_DEV_BOARD_PARAMETERS + sizeof(devBoardParamsType);
        storageOffset = offset;

        do {
            /* let read and find id and size in nvram */
            if (!isFound) storageOffset = offset;

            ccode = NANVRead(offset, (char *)&storageInfo, sizeof(storageInfo));
            if (ccode != 0)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: NANVRead[%d] fails %d\n", id, ccode);
                rc = NASTATUS_RESOURCE_ERROR;
                goto _ret;   
            }
            if (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0)
            {
                if (isFound)
                {
                    /* we find this id previous with
                     * different size so let's get the rest of
                     * data after this id.
                     *
                     * Get the total size of data after this id
                     * and move/copy the data into this id area.
                     * This id of data will then append at the end.
                     */
                     totalSize += storageInfo.size + sizeof(storageInfo);
                }
                else if (storageInfo.id == id)
                {
                    /* found it */
                    isFound = TRUE;
                    storageSize = storageInfo.size + sizeof(storageInfo);
                    if (storageInfo.size == size)
                    {
                        /* same size */
                        break;
                    }
                }
            }
            offset += sizeof(storageInfo);
            offset += (unsigned)storageInfo.size;

        } while (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0);

        if (isFound && totalSize > 0)
        {
            /* size must be incorrect */

            /* let's move and copy the storage area after this id 
             * area into this id area 
             */
            isFound = FALSE;

            thePtr = malloc(totalSize);
            if (thePtr == NULL)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: MALLOC[%d] fails %d size %d\n", 
                                             id, ccode, (int)(storageInfo.size + sizeof(storageInfo)));
                rc = NASTATUS_MALLOC_ERROR;
                goto _ret;
            }

           /* read the data below this id area and write to this id area */
            offset = storageOffset + storageSize;
            ccode = NANVRead(offset, (char *)thePtr, totalSize);
            if (ccode != 0)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: NANVRead[%d] fails %d\n", id, ccode);
                rc = NASTATUS_RESOURCE_ERROR;
                goto _ret;   
            }
            ccode = NANVWrite(storageOffset, (char *) thePtr, totalSize);
            if (ccode != 0)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: NANVWrite[%d] fails %d\n", id, ccode);
                rc = NASTATUS_RESOURCE_ERROR;
                goto _ret;   
            }
            storageOffset += totalSize;
            free(thePtr);
            thePtr = NULL;

        }

        /* start writing the data for this id */
        totalSize = size + sizeof(storageInfo);
        if (!STORAGE_SIZE_IS_VALID(storageOffset, totalSize))
        {   /* invalid size */
            rc = NASTATUS_INVALID_PARAMETER;
            goto _ret;
        }

        thePtr = (char *)malloc(totalSize);
        if (thePtr == NULL)
        {
           STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: MALLOC[%d] fails %d size %d\n", id, ccode, totalSize);
            rc = NASTATUS_MALLOC_ERROR;
            goto _ret;
        }

        storageInfo.id = (unsigned short) id;
        storageInfo.size = (unsigned short) size;
        storageInfo.crc32 = blCrc32(0, data, size);

        if (isFound)
        {
            /* read the data and see whether it's different before actual writing */
            ccode = NANVRead(storageOffset, (char *)thePtr, totalSize);
            if (ccode != 0)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: NANVRead[%d] fails %d\n", id, ccode);
                rc = NASTATUS_RESOURCE_ERROR;
                goto _ret;   
            }
            if (memcmp(thePtr, &storageInfo, sizeof(storageInfo)) == 0)
            {
                /* same id, size, and crc32; so let's compare the data */
                if (memcmp(thePtr+sizeof(storageInfo), data, size) == 0)
                {
                    /* same data. just return success without writing */
                    goto _ret;
                }
            }

        }

        /* write nvram */
        memcpy(thePtr, &storageInfo, sizeof(storageInfo));
        memcpy(thePtr+sizeof(storageInfo), data, size);

        ccode = NANVWrite(storageOffset, (char *)thePtr, totalSize);
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeWriteStorageParams: NANVWrite[%d] fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;
        }
        STORAGE_DEBUG(TRACE_INFO, "customizeWriteStorageParams: id = %d, size = %d\n", id, size);
    }

_ret:
    if (thePtr != NULL) free(thePtr);
    return rc;
}

/**
 * Reads data from NVRAM after development board's startup parameters.
 * 
 * This is used to retrieve any data in volatile area (NVRAM) after
 * development board's startup parameters. See @link customizeReadDevBoardParams
 * and @link customizeWriteDevBoardParams for development board's startup parameters.
 * 
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  id       An unique identifier value of the data read from NVRAM.
 *                  This is used to read the data in NVRAM.
 * @param  data     Pointer to memory where data will be written to.
 * @param  size     Number of bytes will be retrieved and written to the data.
 *
 * @return NASTATUS_SUCCESS             Data has been successfully retrieved and written to the data.
 * @return NASTATUS_INVALID_HANDLE      Invalid identifier.
 * @return NASTATUS_ENTRY_NOT_FOUND     The identifier is not found.
 * @return NASTATUS_RESOURCE_ERROR      Fails on reading NVRAM.
 * @return NASTATUS_INVALID_PARAMETER   Incorrect size. 
 *                                      The size either exceeds the maximum allowed size in NVRAM or
 *                                      is not the same size written in NVRAM.
 * @return NASTATUS_INVALID_CHANNEL     Invalid CRC32. This may indicate corrupted data.
 *
 * @see @link customizeReadStorageParams
 * @see @link customizeRemoveStorageParams
 *
 * @note If the size of the development board's startup parameters has changed,
 * the data written previously will be lost.
 *
**/
NaStatus customizeReadStorageParams(NaCustomizeStorageId_t id, void * data, unsigned short size)
{
    int rc = NASTATUS_SUCCESS;
    int ccode;
    unsigned offset;
    unsigned storageOffset;
    BOOLEAN isFound = FALSE;
    unsigned long crc32;

    storageParamsInfo_t storageInfo;

    NA_ASSERT(data != NULL);
    NA_ASSERT(size > 0);

    
    NA_ASSERT(id > 0);
    NA_ASSERT(id != (NaCustomizeStorageId_t)-1);

    if (id == 0 || id == (NaCustomizeStorageId_t)-1)
    {
        rc = NASTATUS_INVALID_HANDLE;
        goto _ret;
    }

    if (data == NULL || size == 0)
    {
        goto _ret;
    }

    offset = BSP_START_OF_DEV_BOARD_PARAMETERS + sizeof(devBoardParamsType);

    do {
        /* let read and find id and size in nvram */

        ccode = NANVRead(offset, (char *)&storageInfo, sizeof(storageInfo));
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeReadStorageParams: NANVRead[%d] fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;   
        }
        if (storageInfo.id == id)
        {
            /* found it */
            storageOffset = offset;
            isFound = TRUE;
            break;
        }
        offset += sizeof(storageInfo);
        offset += (unsigned)storageInfo.size;

    } while (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0);


    if (isFound)
    {

        if (storageInfo.size != size)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeReadStorageParams[%d]: incorrect size read size = %d size in nvram = %d\n", 
                                           id, size, (int)storageInfo.size);
            rc = NASTATUS_INVALID_PARAMETER;
            goto _ret;
        }

#if 0
        if (!STORAGE_SIZE_IS_VALID(storageOffset+sizeof(storageInfo), size))
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeReadStorageParams[%d]: incorrect size read size = %d exceeds maximum size\n", 
                                           id, size);
            rc = NASTATUS_INVALID_PARAMETER;
            goto _ret;
        }
#endif

        ccode = NANVRead(storageOffset+sizeof(storageInfo), (char *)data, size);
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeReadStorageParams: NANVRead[%d] fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;   
        }
        crc32 = blCrc32(0, data, size);
        if (storageInfo.crc32 != crc32)
        {
            rc = NASTATUS_INVALID_CHANNEL;
        }
    }
    else
    {
        rc = NASTATUS_ENTRY_NOT_FOUND;
    }

_ret:
    return rc;
}

/**
 * Removes data from NVRAM after development board's startup parameters.
 * 
 * This is used to erase any data in volatile area (NVRAM) after
 * development board's startup parameters. See @link customizeReadDevBoardParams
 * and @link customizeWriteDevBoardParams for development board's startup parameters.
 * 
 * The data of the given identifier will be completely removed. The unique identifier will
 * no longer exist.
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  id       An unique identifier value of the data to be earsed from NVRAM.
 *                  If id is (unsigned)-1, all data after development board's startup parameters
 *                  will be earsed and deleted.
 *
 * @return NASTATUS_SUCCESS             Data has been successfully erased and deleted/
 * @return NASTATUS_MALLOC_ERROR        Fails to allocate memory.
 * @return NASTATUS_RESOURCE_ERROR      Fails on reading or writing NVRAM.
 *
 * @see @link customizeReadStorageParams
 * @see @link customizeWriteStorageParams
 *
**/
NaStatus customizeRemoveStorageParams(NaCustomizeStorageId_t id)
{
    NaStatus    rc = NASTATUS_SUCCESS;
    unsigned    offset;
    char        * thePtr = NULL;
    unsigned    totalSize = 0;
    WORD32      storageOffset; 
    WORD32      storageSize = 0; 
    BOOLEAN     isFound = FALSE;
    int         ccode;

    storageParamsInfo_t storageInfo;

    /* skip board parameter */
    offset = BSP_START_OF_DEV_BOARD_PARAMETERS + sizeof(devBoardParamsType);
    storageOffset = offset;

    do {

        /* let read and find id and size in nvram */
        if (!isFound) storageOffset = offset;

        ccode = NANVRead(offset, (char *)&storageInfo, sizeof(storageInfo));
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeRemoveStorageParams[%d]: NANVRead fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;   
        }

        if (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0)
        {
            if (isFound)
            {
                /* we find this id previous with
                 * different size so let's get the rest of
                 * data after this id.
                 *
                 * Get the total size of data after this id
                 * and move/copy the data into this id area.
                 */
                 totalSize += (unsigned)storageInfo.size + sizeof(storageInfo);
            }
            else if (id == (unsigned) -1 || storageInfo.id == id)
            {
                /* found it */
                isFound = TRUE;
                storageSize = storageInfo.size + sizeof(storageInfo);;
            }
        }

        offset += sizeof(storageInfo);
        offset += (unsigned)storageInfo.size;

    } while (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0);

    if (isFound)
    {
        /* found the id so let erase it */
        thePtr = malloc(totalSize + storageSize);
        if (thePtr == NULL)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeRemoveStorageParams[%d]: MALLOC fails %d size %d\n", 
                                         id, ccode, (int)(storageInfo.size + sizeof(storageInfo)));
            rc = NASTATUS_MALLOC_ERROR;
            goto _ret;
        }

        if (id != (unsigned)-1)
        {
            /* if not erase entire area, we need to read
             * the data below this id area and move it up.
             */
            offset = storageOffset + storageSize;
            ccode = NANVRead(offset, (char *)thePtr, totalSize);
            if (ccode != 0)
            {
                STORAGE_DEBUG(TRACE_WARNING, "customizeRemoveStorageParams[%d]: NANVRead fails %d\n", id, ccode);
                rc = NASTATUS_RESOURCE_ERROR;
                goto _ret;   
            }

            memset(thePtr+totalSize, -1, storageSize);
        }
        else
        {
            memset(thePtr, -1, totalSize + storageSize);
        }


        ccode = NANVWrite(storageOffset, (char *) thePtr, totalSize + storageSize);
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeRemoveStorageParams[%d]: NANVWrite fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;   
        }

    }

_ret:
    if (thePtr != NULL) free(thePtr);
    return rc;
}


/**
 * Gets number of bytes of the specified storage identified in NVRAM.
 * 
 * This is used to get the size of data that has been wriiten into NVRAM via
 * @link customizeWriteStorageParams.
 *
 * @external
 * @category BSP:Board_Params
 * @include <boardParams.h>
 * @since 7.5
 *
 * @param  id       An identifier value of the data that was written to NVRAM
 *                  using @link customizeWriteStorageParams
 * @param  size     Pointer to memory where number of bytes of the data will be written to.
 *
 * @return NASTATUS_SUCCESS             Successfully get the size of the data.
 * @return NASTATUS_INVALID_HANDLE      Invalid identifier.
 * @return NASTATUS_ENTRY_NOT_FOUND     The identifier is not found.
 * @return NASTATUS_RESOURCE_ERROR      Fails on reading NVRAM.
 * @return NASTATUS_INVALID_PARAMETER   Invalid size. Size is NULL.
 *
 * @see @link customizeWriteStorageParams
 * @see @link customizeReadStorageParams
 * @see @link customizeRemoveStorageParams
 *
**/
NaStatus customizeGetStorageParamsInfo(unsigned id, unsigned * size)
{
    int rc = NASTATUS_ENTRY_NOT_FOUND;
    int ccode;
    unsigned offset;
    storageParamsInfo_t storageInfo;

    NA_ASSERT(size != NULL);

    NA_ASSERT(id > 0);
    NA_ASSERT(id != (unsigned)-1);

    if (id == 0 || id == (unsigned)-1)
    {
        rc = NASTATUS_INVALID_HANDLE;
        goto _ret;
    }

    if (size == NULL)
    {
        rc = NASTATUS_INVALID_PARAMETER;
        goto _ret;
    }

    offset = BSP_START_OF_DEV_BOARD_PARAMETERS + sizeof(devBoardParamsType);

    do {
        /* let read and find id and size in nvram */

        ccode = NANVRead(offset, (char *)&storageInfo, sizeof(storageInfo));
        if (ccode != 0)
        {
            STORAGE_DEBUG(TRACE_WARNING, "customizeReadStorageParams: NANVRead[%d] fails %d\n", id, ccode);
            rc = NASTATUS_RESOURCE_ERROR;
            goto _ret;   
        }
        if (storageInfo.id == id)
        {
            /* found it */
            *size = storageInfo.size;
            rc = NASTATUS_SUCCESS;
            break;
        }
        offset += sizeof(storageInfo);
        offset += (unsigned)storageInfo.size;

    } while (storageInfo.id != (unsigned short)-1 && storageInfo.id != 0);

_ret:
    return rc;
}
#endif
