//-----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//-----------------------------------------------------------------------------
//
// Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved.
// THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
// AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  File:  LoLoParameter.c
//
//  LogicLoader Parameter process code.
//
//-----------------------------------------------------------------------------
#include <bsp.h>
#include "LoLoParameter.h"

//-----------------------------------------------------------------------------
// Types

//-----------------------------------------------------------------------------
// Definition


//-----------------------------------------------------------------------------
// Local Functions
void DumpLoLoParameter(void);

// External Functions

//-----------------------------------------------------------------------------
// External Variables
extern PBSP_ARGS g_pBSPARGS;        // From debug.c


//-----------------------------------------------------------------------------
// Global Variables
TLoLoOption gLoLoOption;

// RTC
extern const TRTCFunc gPMIC_RTCFunc;
extern const TRTCFunc gMX31_RTCFunc;
static const TRTCDefault gpLoLoRTCDefault[] = {
   {HAL_STR_RTC_IMX31,      &gMX31_RTCFunc},
   {HAL_STR_RTC_IMX31_PMIC, &gPMIC_RTCFunc},
   {NULL,                   NULL}
};

static const TBooleanDefault gpLoLoShareEthDefault[] = {
   {"1",  TRUE},
   {"0",  FALSE},
   {NULL, FALSE}
};

static const TBooleanDefault gpLoLoKITLDefault[] = {
   {"true",  TRUE},
   {"false", FALSE},
   {NULL,    FALSE}
};

static const TBooleanDefault gpLoLoCleanSysHiveDefault[] = {
   {"1",  TRUE},
   {NULL, TRUE}
};

static const TBooleanDefault gpLoLoCleanUsrHiveDefault[] = {
   {"1",  TRUE},
   {NULL, TRUE}
};

static const TBooleanDefault gpLoLoDbgSerialDefault[] = {
   {HAL_STR_DBG_SERIAL_IMX31, TRUE},
   {HAL_STR_DBG_SERIAL_NULL,  FALSE},
   {NULL,                     TRUE}
};

static const TNumerialDefault gpLoLoDbgLEDsDefault[] = {
   {"0",  0},
   {"1",  RUNNING_INDICATOR_DLED_INDEX},
   {"2",  RUNNING_INDICATOR_DLED_INDEX},
   {"3",  RUNNING_INDICATOR_DLED_INDEX},
   {NULL, 0}
};

/*-------------------------------------------------------------------------*/
/*!
 * \brief   Initialize all global HAL Parameters.
 *
 * \b Purpose:
 *
 * This function is called at startup to assign the initial values to the standard
 * HAL-level driver interfaces. These interfaces include; debug-serial,
 * debug ethernet, and rtc. The purpose of this function is to simplify
 * the rest of the OAL code and remove duplicate code.
 */
void OALInitLoLoParameter(void)
{
   gLoLoOption.fShareEthEnable  = FALSE;            // share_eth:0:
   gLoLoOption.fKITLEnable      = FALSE;            // kitl:false:
   gLoLoOption.fIsIPAssigned    = FALSE;            // ip_addr::
   gLoLoOption.dwIP             = 0;
   gLoLoOption.fCleanSysHive    = FALSE;            // clean_syshive::
   gLoLoOption.fCleanUsrHive    = FALSE;            // clean_usrhive::
   gLoLoOption.fDbgSerialEnable = FALSE;            // dbg_serial:null:
   gLoLoOption.pRTCFunc         = &gMX31_RTCFunc;   // rtc:rtc_imx31:
#ifdef dwDefaultThreadQuantum
   gLoLoOption.dwQuantum        = dwDefaultThreadQuantum; // quantum::
#else
   gLoLoOption.dwQuantum        = g_pOemGlobal->dwDefaultThreadQuantum;
#endif
   gLoLoOption.dwDbgLEDs        = 0;                // dbg_leds:0:

   return;
}

/*!--------------------------------------------------------------------------
 *
 * \brief   This routine will take a dotted decimal IP address as represent here and return a binary version of it.
 *
 * \b Purpose
 *
 *
 * \param   pszDottedD
 *       Pointer to the ip address string.
 *
 * \param   pdwIP
 *       a pointer to the 32-bit value of the ip address.
 *
 * \return  TRUE if successful, FALSE otherwise (ie, the string is not a valid
 *       hexadecimal number).
 *
 *-------------------------------------------------------------------------*/
static BOOL My_inet_addr(char *pszDottedD, DWORD *pdwIP)
{
   DWORD dwIP=0;
   DWORD dwIPSub;
   DWORD cBytes, ii;
   char *pszLastNum;

   // Replace the dots with NULL terminators
   pszLastNum = pszDottedD;
   for (cBytes=0; cBytes<4; cBytes++) {
      while ((*pszDottedD != '.') && (*pszDottedD != '\0')) {
         pszDottedD++;
      }
      if ((pszDottedD == '\0') && (cBytes != 3)) {
         return 0;
      }
      *pszDottedD = '\0';

      dwIPSub = 0;
      for (ii=0; pszLastNum[ii]!='\0'; ii++) {
         if ((pszLastNum[ii] < '0') || (pszLastNum[ii] > '9')) {
            // Non-numerial ascii, return Failed
            return FALSE;
         }
         dwIPSub *= 10;
         dwIPSub += pszLastNum[ii] - '0';
      }
      dwIP |= (dwIPSub & 0xFF) << (8 * cBytes);
      pszLastNum = ++pszDottedD;
   }

   *pdwIP = dwIP;

   return TRUE;
} // inet_ntoa()

/*!--------------------------------------------------------------------------
 *
 * \brief   Converts a string into its 32-bit value.
 *
 * \b Purpose
 *
 *
 * \param   buf
 *       Pointer to the 32bit formatted hexadecimal string.
 *
 * \param   len
 *       Character length of the string in buf (not including the NULL
 *       terminator)
 *
 * \param   value
 *       32-bit value of the hex-string.
 *
 * \return  1 if successful, 0 otherwise (ie, the string is not a valid
 *       hexadecimal number).
 *
 *-------------------------------------------------------------------------*/
int atox(const unsigned char *buf, unsigned int len, unsigned int *value)
{
   unsigned int c;
   unsigned int result;
   int          ii;

   result = 0;
   *value = 0;
   ii = len;

   /* Look for a leading '0x' or '0X' in the number. */
   if ((ii > 2) && ('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
      buf += 2;
      ii -= 2;
   }

   if (ii > (2 * sizeof(unsigned int))) { // limit 8 (32bit)
      return 0;
   }

   while (ii-- && *buf) {
      /* Get the leftmost character. */
      c = *buf++;

      /* Normalize the character to zero. */
      c -= '0';

      /* Account for the discontinuity between '0-9' and 'A-Z'. */
      if (c > 9) {
         c -= 7;

         /* Account for the lower case letters. */
         if (c > 15) {
            c -= 32;
         }
      }

      if (c > 15) {
         return (0);
      }

      result = (result << sizeof(unsigned int)) | c;
   }

   *value = result;

   return (1);
} /* end atox() */


/*-------------------------------------------------------------------------*/
/*!
 *
 * \brief   Returns the value of a key in the HAL parameter string.
 *
 * \b Purpose:
 *
 * This function is used to parse the HAL parameter string. It is called
 * with the key the caller would like to determine the value of. It
 * returns a pointer to that key's value or NULL if the key is not found
 * in the HAL parameter string. It also returns the length of the key's
 * value in the parameter len. Using these two returned values, the caller
 * can make a local copy of the key's value for its own use. This function
 * has been tweaked to parse the string in time O(n) with zero copies.
 *
 * For example:   hal_param_get_value(HAL_STR_DBG_SERIAL);
 *
 * Will search the HAL parameter string for the token HAL_STR_DBG_SERIAL.
 * If that token is found, this function will return the next token.
 *
 * For example, if the HAL parameter string looked like:
 *
 *    :....:HAL_STR_DBG_SERIAL:dbg_serial_null:....:
 *
 * This function would return a pointer to the start of the sub-string
 * "dbg_serial_null." The parameter "len" would be set to 15, or
 * strlen("dbg_serial_null").
 *
 * Note, HAL_STR_DBG_SERIAL is defined as a macro in the header file
 * hal_parameters.h. Using macros instead of actual strings makes code
 * maintenance and porting easier.
 *
 * \param   key
 *       The key within the HAL parameter string the caller would
 *       like the value of.
 *
 * \param   len
 *       Pointer to an integer where the length of the string which
 *       is the key's value may be returned.
 *
 * \return  A pointer to the key's value string if found, NULL if the
 *       key does not exist within the HAL parameter string.
 *
 */
/*-------------------------------------------------------------------------*/
unsigned char *hal_param_get_value(unsigned char *key, unsigned int *len)
{
   unsigned char *t_crnt;
   unsigned char *t_next;
   unsigned char *ret_value;
   BOOL fFound;
   int key_len, ii;
   BSP_ARGS *pBSPArgs;

   /* Grab a local copy of the key's length. This is used to simplify
    * processing later on. */
   key_len = strlen(key);

   pBSPArgs = (PBSP_ARGS)OALPAtoVA(IMAGE_SHARE_ARGS_RAM_PA_START, FALSE);

   /* This first loop searches the list for a token equal to key. */
   t_crnt = pBSPArgs->LoLoBootArgs;
   // t_crnt = tmp_hal_param_string;
   fFound = FALSE;
   while (!fFound) {
      /* Set t_crnt to the beginning of the next token. */
      while ((*t_crnt != '\0') && (HAL_STR_DELIMITER_ == *t_crnt)) {
         t_crnt++;
      }

      if (*t_crnt == '\0') {   /* We have exhausted the list. */
         break;
      }

      /* Set t_next to the delimiter marking the end of this token. */
      t_next = t_crnt;
      while ((*t_next != '\0') && (HAL_STR_DELIMITER_ != *t_next) ) {
         t_next++;
      }

      /* Subtracting t_crnt from t_next gives us the length of the current
       * token. If this doesn't match key_len, then we don't bother looking
       * at this token further. */
      if (key_len != (t_next - t_crnt)) {
         t_crnt = t_next;
      }
      else {
         /* We have found a token which at least has the same size as key.
          * Let's look at each character to see if it is a match. */
         for (ii=0; ii<key_len; ++ii) {
            if (key[ii] != t_crnt[ii]) {
               break;
            }
         }

         /* If we got all the way through the loop, we found a match. */
         if (key_len == ii) {
            fFound = TRUE;
         }
         else {
            t_crnt = t_next; /* Look at the next token. */
         }
      }
   }

   /* At this point, we have found the key we were looking for. t_next
    * should be pointing to the delimiter after the key, or NULL if we
    * have an ill-formatted list. */
   if (fFound) {
      t_crnt = t_next;

      /* Set t_crnt to the beginning of the key's value. */
      while ((*t_crnt != '\0') && (HAL_STR_DELIMITER_ == *t_crnt)) {
         t_crnt++;
      }

      t_next = t_crnt;

      /* Move t_next to the end of the key's value. */
      while ((*t_next != '\0') && (HAL_STR_DELIMITER_ != *t_next)) {
         t_next++;
      }

      /* If t_next is not equal to t_crnt, then the key's value actually
       * contains some characters. Take note of the key's length and
       * return t_crnt to the caller. */
      if (t_next == t_crnt) {
         *len = 0;
         ret_value = NULL;
      }
      else {
         *len = (t_next - t_crnt);
         ret_value = t_crnt;
      }
   }
   else {
      /* We never found a token which matched key. */
      *len = 0;
      ret_value = NULL;
   }

   return (ret_value);

} /* end hal_param_get_value() */


/*-------------------------------------------------------------------------*/
/*!
 * \brief   Populates all global HAL Parameters based on the BOOT String
 *
 * \b Purpose:
 *
 * This function is called at startup to assign values to the standard
 * HAL-level driver interfaces. These interfaces include; debug-serial,
 * debug ethernet, and rtc. The purpose of this function is to simplify
 * the rest of the OAL code and remove duplicate code.
 */
/*-------------------------------------------------------------------------*/
void hal_param_resolve_globals(void)
{
   unsigned char *value;
   unsigned int len;
   unsigned int ii;

   OALInitLoLoParameter();

   /*-----------------------------------------------------------------
    * HAL_STR_SHARE_ETH:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_SHARE_ETH, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoShareEthDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoShareEthDefault[ii].pchDriverName, len)) {
            gLoLoOption.fShareEthEnable = gpLoLoShareEthDefault[ii].fEnable;
            break;
         }
      }
   }

   /*-----------------------------------------------------------------
    * HAL_STR_KITL:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_KITL, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoKITLDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoKITLDefault[ii].pchDriverName, len)) {
            gLoLoOption.fKITLEnable = gpLoLoKITLDefault[ii].fEnable;
            break;
         }
      }
   }

   /*-----------------------------------------------------------------
    * HAL_STR_KITL_IP_ADDR:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_KITL_IP_ADDR, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      gLoLoOption.fIsIPAssigned = My_inet_addr(value, &gLoLoOption.dwIP);
   }

   /*-----------------------------------------------------------------
    * HAL_STR_CLEAN_SYS_HIVE:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_CLEAN_SYS_HIVE, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoCleanSysHiveDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoCleanSysHiveDefault[ii].pchDriverName, len)) {
            gLoLoOption.fCleanSysHive = gpLoLoCleanSysHiveDefault[ii].fEnable;
            break;
         }
      }
   }

   /*-----------------------------------------------------------------
    * HAL_STR_CLEAN_USR_HIVE:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_CLEAN_USR_HIVE, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoCleanUsrHiveDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoCleanUsrHiveDefault[ii].pchDriverName, len)) {
            gLoLoOption.fCleanUsrHive = gpLoLoCleanUsrHiveDefault[ii].fEnable;
            break;
         }
      }
   }

   /*-----------------------------------------------------------------
    * HAL_STR_DBG_SERIAL:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_DBG_SERIAL, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoDbgSerialDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoDbgSerialDefault[ii].pchDriverName, len)) {
            gLoLoOption.fDbgSerialEnable = gpLoLoDbgSerialDefault[ii].fEnable;
            break;
         }
      }
   }

#if 0
   /*-----------------------------------------------------------------
    * HAL_STR_DBG_ENET:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_DBG_ENET, &len);

   if ((NULL != value) && (0 != len)) {
      ii = 0;

      /* Don't run off the end of the available drivers list. */
      while (NULL != dbg_enet_drvrs[ii]) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, dbg_enet_drvrs[ii]->driver_name, len)) {
            hal_drvr_dbg_enet = dbg_enet_drvrs[ii];
            break;
         }

         ii++;
      }
   }
#endif

   /*-----------------------------------------------------------------
    * HAL_STR_RTC:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_RTC, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoRTCDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoRTCDefault[ii].pchDriverName, len)) {
            gLoLoOption.pRTCFunc = gpLoLoRTCDefault[ii].pRTCFunc;
            break;
         }
      }
   }

   /*-----------------------------------------------------------------
    * HAL_STR_THREAD_QUANTUM:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_THREAD_QUANTUM, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      atox(value, len, &gLoLoOption.dwQuantum);
   }

   /*-----------------------------------------------------------------
    * HAL_STR_DEBUG_LEDS:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_DEBUG_LEDS, &len);

   if ((NULL != value) && (0 != len)) {
      /* Don't run off the end of the available drivers list. */
      for (ii=0; gpLoLoDbgLEDsDefault[ii].pchDriverName!=NULL; ii++) {
         /* Compare the HAL-level driver's name with the key's value. */
         if (!strncmp(value, gpLoLoDbgLEDsDefault[ii].pchDriverName, len)) {
            gLoLoOption.dwDbgLEDs = gpLoLoDbgLEDsDefault[ii].dwNumerial;
            break;
         }
      }
   }

#ifdef _ARM_
#if CARD_ENGINE_IMX31_10
   /*-----------------------------------------------------------------
    * HAL_STR_COPROCESSORS:
    *---------------------------------------------------------------*/
   value = hal_param_get_value(HAL_STR_COPROCESSORS, &len);

   if ((NULL != value) && (0 != len)) {
      atox(value, len, &coprocessors_access_control);
   }
#endif // _ARM_
#endif // 0

   return;

} /* end hal_param_resolve_globals() */


