/*
   Embedded 802.11 wireless network interface
   MAC layer hardware interface

   Copyright (c) 2007-2008 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 library contains tx power control
   for the Airoha RF chip.
*/
#if AIROHA_PWR_CALIBRATION

#include "mac.h"
#include "reg_def.h"
#include "npttypes.h"
#include "i2c_api.h"
#include "boardParams.h"
#include "airoha_pwr_cal.h"
#include <math.h>

#if MANUFACTURING_MODE
int needTobeSuspend = 0;
#endif

extern BOOLEAN isRF_AIROHA_7230(void);
extern int subBand(int channel);

static void _wifi_initPwrCntrl(int new_cycle);
// Array for RF power calibration data
static WiFiPwrCal   _wcal[WLN_POWER_CALIBRATION_LENGTH/4];  //enough space for 8 sets of data
static int getCurve(void);

static int lowestPwrBG_CCK, highestPwrBG_CCK, lowestPwrBG_OFDM, highestPwrBG_OFDM, lowestPwrA, highestPwrA, version;

// APC state machine structure
WifiPwrCntrl _wapc;

// Airoha power settings, initial setting are intentionally set low to
// assure we stay within Airoha chip-to-chip process variance. These
// setting are also the fail safe values if the calibration data
// gets corrupted. 0dbm - 16dbm
int const _wPwr[] =
{
    0, 
    3,  6, 11, 13,
   16, 18, 20, 22,
   24, 26, 28, 30,
   32, 34, 36, 38,
   39, 40, 41, 42,  /* extended for tst_wifi */
   41, 42, 43, 44,  /* extended for tst_wifi */
   45, 46, 47, 48,  /* extended for tst_wifi */
   
};

/* This table can translate dbm into mW */
#if 0
static dbmToMwTranslationTable_T dbmToMwTranslationTable[] = {
	{-1, 0.794328},
	{ 0, 1},
	{ 1, 1.258925},
	{ 2, 1.584893},
	{ 3, 1.995262},
	{ 4, 2.511886},
	{ 5, 3.162278},
	{ 6, 3.981072},
	{ 7, 5.011872},
	{ 8, 6.309573},
	{ 9, 7.943282},
	{10, 10},
	{11, 12.58925},
	{12, 15.84893},
	{13, 19.95262},
	{14, 25.11886},
	{15, 31.62278},
	{16, 39.81072},
	{17, 50.11872},
	{18, 63.09573},
	{19, 79.43282},
	{20, 100},
	};
#endif //#if 0

int tryToTakeAverageVoltage = 0;

static char writeBufferRaw[3];
static  short readBufferRaw;
static  unsigned int writeBufferLengthRaw;
static  unsigned int readBufferLengthRaw;
        
static void pwrCalTimerFunc (unsigned long unused);
static void inactivityTimerFunc (unsigned long unused);
static void debounceTimerFunc (unsigned long unused);
static TX_TIMER pwrCalTimer;
static TX_TIMER inactivityTimer;
static TX_TIMER debounceTimer;

static TX_THREAD I2C_ADC_Read;
static char I2C_ADC_Read_thread_stack[1024*4];
static void I2C_ADC_readmax(unsigned long unused);
int readAdcPeak(void);
int clearAdcPeak(void);


#ifdef AIROHA_PWR_CALIBRATION_DEBUG
static TX_THREAD airoha_pwr_cal_thread;
static char airoha_pwr_cal_thread_stack[1024*4];
static void readmax (unsigned long unused);
#endif
void initPwrCalTimer(void)
{
        /* Create an event flag group. */
        tx_event_flags_create(&airoha_pwr_cal_group,
                "airoha pwrcal event group");
        tx_timer_create(&pwrCalTimer,"Airoha Power Calibration Timer",
                        pwrCalTimerFunc, 0, 3000, 0,
                        TX_NO_ACTIVATE);
        tx_timer_create(&inactivityTimer,"Airoha Inactivity Timer",
                        inactivityTimerFunc, 0, 3000, 0,
                        TX_NO_ACTIVATE);
        tx_timer_create(&debounceTimer,"Airoha Debounce Timer",
                        debounceTimerFunc, 0, 3000, 0,
                        TX_NO_ACTIVATE);
                        
        tx_thread_create(&I2C_ADC_Read, "I2C ADC peak read thread",
                I2C_ADC_readmax, 0,
                (void *) I2C_ADC_Read_thread_stack, 1024*4,
                16, 16, 10,
                TX_AUTO_START);
                
                
                
#ifdef AIROHA_PWR_CALIBRATION_DEBUG
        tx_thread_create(&airoha_pwr_cal_thread, "airoha pwr cal thread",
                readmax, 0,
                (void *) airoha_pwr_cal_thread_stack, 1024*4,
                16, 16, 10,
                TX_AUTO_START);
#endif  
}

static void pwrCalTimerFunc (unsigned long unused)
{
        (void)unused;
        
        tx_timer_deactivate(&pwrCalTimer);
        tx_event_flags_set(&airoha_pwr_cal_group, CAL_TIMEOUT_EVENT, TX_OR);    
}

static void inactivityTimerFunc (unsigned long unused)
{
        (void)unused;
        
        tx_timer_deactivate(&inactivityTimer);
        tx_event_flags_set(&airoha_pwr_cal_group, INACTIVE_TIMEOUT_EVENT, TX_OR);       
}

static void debounceTimerFunc (unsigned long unused)
{
        (void)unused;
        
        
        tx_timer_deactivate(&debounceTimer);
        tx_event_flags_set(&airoha_pwr_cal_group, DEBOUNCE_TIMEOUT_EVENT, TX_OR);       
}



#ifdef AIROHA_PWR_CALIBRATION_DEBUG
extern int lowRate, highRate;
static void readmax (unsigned long unused)
{
        (void)unused;
        while(1)
        {
        tx_thread_sleep(15*NABspTicksPerSecond); 
        /*enableTxStartInt (1);
        _wapc.ForceADCRead= 1;*/
                
        //_wifi_read_adc();
        printf("ofdmCount=%d, pskCount=%d, avgDone1=%d, avgTry=%d, avgDone2=%d, avgTry2=%d\n", _wapc.ofdmCount, _wapc.pskCount, 
        	_wapc.avgDone1, _wapc.avgTry, _wapc.avgDone2, _wapc.avgTry2);
        printf("avgTryTotal=%d, readingPeakTxStart=%d, readingPeakTxDone=%d, goodReading=%d\n",_wapc.avgTryTotal, _wapc.readingPeakTxStart, _wapc.readingPeakTxDone, _wapc.goodReading);
        printf("lowRate=%d, highRate=%d\n", lowRate, highRate);
        printf("txStartCount = %d, txStillOn = %d, _wapc.Rawdata = %d, AvgHistory=%d\n",_wapc.txStartCount, _wapc.txStillOn, _wapc.Rawdata, _wapc.AvgHistory);
        printf("history = %d %d %d %d %d %d %d %d %d %d\n", _wapc.history[0],_wapc.history[1],_wapc.history[2],_wapc.history[3],_wapc.history[4], 
        	_wapc.history[5],_wapc.history[6],_wapc.history[7],_wapc.history[8],_wapc.history[9]);
       // memset(&_wapc.history, 0, 4*10);
        printf("CustIndex = %8.3f, TargetIndex = %d, AirohaIndex =%d, CalIndex = %d, NewPwrLevel=%d\n",_wapc.CustIndex, _wapc.TargetIndex, _wapc.AirohaIndex, _wapc.CalIndex,_wapc.NewPwrLevel);
        printf("\n");
        //enableTxStartInt (0);
        
	}
        
}
#endif

static void I2C_ADC_readmax(unsigned long unused)
{
        
        unsigned long actual_events;
        (void)unused;
        
        while (1)
        {
                if(tx_event_flags_get(&airoha_pwr_cal_group, MAC_SEND_FRAME_EVENT,
                TX_OR_CLEAR, &actual_events, TX_WAIT_FOREVER) == TX_SUCCESS  )
                {
                        _wifi_pwrCntrl();
                        //tx_thread_sleep(100);
                }
                
#if MANUFACTURING_MODE
            if(needTobeSuspend)
                tx_thread_suspend(tx_thread_identify());
#endif
        }
                        
}

    
// ADC121C021/ADC121C027 registers
#define ADC_CONV_RESULT         0x0
#define ADC_ALERT_STATUS        0x1
#define ADC_CONF                0x2
#define ADC_LOW_LIMIT           0x3
#define ADC_HIGH_LIMIT          0x4
#define ADC_HYST                0x5
#define ADC_CONV_LOWEST         0x6
#define ADC_CONV_HIGHEST        0x7

#define ADC_SLAVE_ADDR          0x51     /* ADC121C027 && ADDR==GND */


/* initialize I2C interface for ADC */
int _wifi_init_adc(void)
{
     	/* disable txStart interrupt*/
      	enableTxStartInt (0);
      	tryToTakeAverageVoltage=0;
         
        if(NAI2CInit(I2C_MASTER, I2C_FAST_SPEED) != I2C_SUCCESS)
                return -1;
          
        /* let's turn on the automatic conversion, set to the highest sampling rate, which is 27 kSPS */    
        writeBufferLengthRaw=2;
        writeBufferRaw[0]= 0x2;
        writeBufferRaw[1]= 0x20;
        readBufferRaw = 0;
        readBufferLengthRaw=0;
        if (NAI2COperation((ADC_SLAVE_ADDR << 1), 
                writeBufferRaw, &writeBufferLengthRaw, 
                (char *)&readBufferRaw, &readBufferLengthRaw, 
                100, I2C_HOST_WRITE_ONCE) != I2C_SUCCESS)
                {
                        return -1;
                }
 
       return 0;
}

/* This function read the ADC register 7 to get the highest conversion -- peak value */
int readAdcPeak(void)
{       
        int wait = NABspTicksPerSecond/10;
        
        readBufferRaw = 0;
        readBufferLengthRaw=2; 
        writeBufferRaw[0] = 0x7; 
        writeBufferLengthRaw = 1;
        
        if (NAI2COperation((ADC_SLAVE_ADDR << 1), 
                writeBufferRaw, &writeBufferLengthRaw, 
                (char *)&readBufferRaw, &readBufferLengthRaw, 
                wait, I2C_HOST_WRITE_THEN_READ) == I2C_SUCCESS)
                { 
                        /*
                         * To keep backward compatible with the mfg calibration data
                         * (peak reading  - PEAK_ADJUSTMENT)
                         */
                                return (readBufferRaw >> 4);
                }
        
        return 0;
}

/* This function write 0 the ADC register 7 to clear the highest conversion -- peak value */
int clearAdcPeak(void)
{       
	int wait = NABspTicksPerSecond/10;
	
        /* let's clear the peak register */    
        writeBufferLengthRaw=3;
        writeBufferRaw[0]= 0x7;
        writeBufferRaw[1]= 0x00;
        writeBufferRaw[2]= 0x00;
        readBufferRaw = 0;
        readBufferLengthRaw=0;
        if (NAI2COperation(ADC_SLAVE_ADDR << 1, 
                writeBufferRaw, &writeBufferLengthRaw, 
                (char *)&readBufferRaw, &readBufferLengthRaw, 
                wait, I2C_HOST_WRITE_ONCE) != I2C_SUCCESS)
                {
                        return -1;
                }
       return 0;
}

/* just read the convertion results at register 0, this is called from mfg calibration routine */ 
int _wifi_readadc(void)
{       
        int wait = NABspTicksPerSecond/10;
             
        readBufferRaw = 0;
        readBufferLengthRaw=2; 
        writeBufferRaw[0] = 0x0; 
        writeBufferLengthRaw = 1;
        
        if (NAI2COperation(ADC_SLAVE_ADDR << 1, 
                writeBufferRaw, &writeBufferLengthRaw, 
                (char *)&readBufferRaw, &readBufferLengthRaw, 
                wait, I2C_HOST_WRITE_THEN_READ) == I2C_SUCCESS)
                { 
                                return (readBufferRaw >> 4);
                }
        
        return 0;
}

/* get the line/curve index by channel number */
static int getCurve(void)
{
	//epStatus *pSta = rootEndPoint[0];
	int calibrationIndex = 0;
	
	   if (!CHAN_5G (global_chan/*pSta->macStatus.channel*/))
	   {
	        calibrationIndex = 0;
	        
	   }
	   else if(subBand(global_chan) == A_SUBBAND1)
	   {
	        calibrationIndex = 2;
	   }
	   else if(subBand(global_chan) == A_SUBBAND2)
	   {
	        calibrationIndex = 3;
	   }
	   else if(subBand(global_chan) == A_SUBBAND3)
	   {
	        calibrationIndex = 4;
	   }
	   else if(subBand(global_chan) == A_SUBBAND4)
	   {
	        calibrationIndex = 5;
	   }
	   else if(subBand(global_chan) == A_SUBBAND5)
	   {
	        calibrationIndex = 6;
	   }
	   else
	        calibrationIndex = 0;  //default to be 0
	        
	return calibrationIndex;
	
}

int _wifi_setAirohaPwr(void)
{
 //  auto float powerLevel, powerCalc, pwrDiff;
 //  auto float targetPower, SATarget, SAPower;
//   auto float tempADC;
        float powerLevel, targetPower, pwrDiff;
        int  indexOffset;
        int calibrationIndex;
        
#ifdef WIFI_AIROHA_PWR_VERBOSE
        float SATarget, SAPower;
#endif


   #ifdef WIFI_AIROHA_PWR_VERBOSE
   printf("v4...Start of APC Routine, RF_Index=%d\n", _wapc.AirohaIndex);
   #endif
   
    calibrationIndex = getCurve();
    if (version >= CALIBRATION_VERSION_3 && calibrationIndex == 0 && _wapc.Modulation == _WIFI_OFDM)
    {
        calibrationIndex = CAL_BG_BAND_OFDM_ONLY;
        targetPower = (float)(_wapc.CustIndex > highestPwrBG_OFDM)? highestPwrBG_OFDM : _wapc.CustIndex ;
    }
    else
    {
        //targetPower = (float)_wapc.CustIndex  * _WIFI_PWR_GAIN;
       // fix input dBm power level to make it the target power level (which should always be the case),
       // since target should equal calibrated output power (equals the FCC/Reg power level)
       targetPower = (float)_wapc.CustIndex;    /* this is Jim's change*/
    }

   if(isRF_AIROHA_7230())
   {
   	//P = A*V^2 + BV=(A*V + B)*V
   	powerLevel = (_wcal[calibrationIndex].slope * _wapc.AvgData +
                _wcal[calibrationIndex].intercept) * _wapc.AvgData;
                
        // above produces milliwatts
	// now convert milliwatts to dBm; use table if you wish, or use float=10 *log10f(float);
	powerLevel = 10*log10f(powerLevel);
    }
    else
    {
    	powerLevel = (_wcal[calibrationIndex].intercept +
                (_wapc.AvgData/_wcal[calibrationIndex].slope));
    	
    }
    
    _wapc.CalIndex = calibrationIndex;
    
   

   // Set to default value
   indexOffset = 1;

   #ifdef WIFI_AIROHA_PWR_VERBOSE
   if(_wapc.Modulation == _WIFI_OFDM)
   {
        SATarget = targetPower - _WIFI_CABLE_LOSS - 3.0;
      SAPower  = powerLevel  - _WIFI_CABLE_LOSS - 3.0;
   }
   else
   {
        SATarget = targetPower - _WIFI_CABLE_LOSS;
      SAPower  = powerLevel  - _WIFI_CABLE_LOSS;
   }
   #endif
   // Only allow closed-loop power control for power levels of > 9dBm for AL2236
   if(targetPower  < _WIFI_PWR_THRESHOLD )
   {
      return WIFI_APC_COMPLETE;
   }
   
   if((_wapc.TrackUpDown & 0x03) == 0x03)
   {
      #ifdef WIFI_AIROHA_PWR_VERBOSE
      printf("'#'  ADC=%02x RFIndex=%d %d SAPwr=%.2f SATarget=%.2f \n",
             _wapc.AvgData, _wapc.AirohaIndex, _wapc.Modulation,
             SAPower, SATarget);
      #endif
      return WIFI_APC_COMPLETE;
   }

   if(!_wapc.NewPwrLevel)
   {
      if(powerLevel > (targetPower - _WIFI_PWR_ACCEPT2_LOW) &&
         powerLevel < (targetPower - _WIFI_PWR_ACCEPT2_HIGH))
      {
         #ifdef WIFI_AIROHA_PWR_VERBOSE
         printf("'=' ADC=%02x RFIndex=%d %d SAPwr=%.2f SATarget=%.2f \n",
                _wapc.AvgData, _wapc.AirohaIndex, _wapc.Modulation,
                SAPower, SATarget);
         #endif
         return WIFI_APC_COMPLETE;
      }
      _wifi_initPwrCntrl(TRUE);
      return WIFI_APC_START;
   }
   else
   {
      if(powerLevel > (targetPower - _WIFI_PWR_ACCEPT1_LOW) &&
         powerLevel < (targetPower - _WIFI_PWR_ACCEPT1_HIGH))
      {
        #ifdef WIFI_AIROHA_PWR_VERBOSE
         printf("'=' ADC=%02x RFIndex=%d %d SAPwr=%.2f SATarget=%.2f \n",
                _wapc.AvgData, _wapc.AirohaIndex, _wapc.Modulation,
                SAPower, SATarget);
         #endif
         return WIFI_APC_COMPLETE;
      }
      if((pwrDiff = fabs(powerLevel - targetPower)) > _WIFI_PWR_RESOLUTION )
      {
        if(pwrDiff > 6.0)
        {
                pwrDiff = 6.0;
        }
        indexOffset = (int) (pwrDiff/_WIFI_PWR_RESOLUTION);
      }
   }

   if(powerLevel > targetPower)
   {    
        // tracking down
        
        #ifdef WIFI_AIROHA_PWR_VERBOSE
      printf("'-' ADC=%02x RFIndex=%d %d SAPwr=%.2f SATarget=%.2f \n",
             _wapc.AvgData, _wapc.AirohaIndex, _wapc.Modulation,
             SAPower, SATarget);
      #endif
      _wapc.TrackUpDown |= 0x01;
      _wapc.AirohaIndex -= indexOffset;
      if(_wapc.AirohaIndex  < (int)((_wcal[calibrationIndex].maxIndex & 0xffff0000) >> 16))
      {
         _wapc.AirohaIndex  = (int)((_wcal[calibrationIndex].maxIndex & 0xffff0000) >> 16);
         _wifi_Airoha(AIROHA_PWR_SETTING | ((long)_wapc.AirohaIndex  << 4) );
         return WIFI_APC_DEBOUNCE;
      }
      _wifi_Airoha(AIROHA_PWR_SETTING | ((long)_wapc.AirohaIndex  << 4) );
   }
   else
   {
        // tracking up
        
        // try not to go over target power limit
                // typically we'll be over by one index setting when tracking up
                // if room in the offset, backoff by one index point here
                if (indexOffset > 2)
                        indexOffset --;
                        
        #ifdef WIFI_AIROHA_PWR_VERBOSE
      printf("'+' ADC=%02x RFIndex=%d %d SAPwr=%.2f SATarget=%.2f \n",
             _wapc.AvgData, _wapc.AirohaIndex, _wapc.Modulation,
             SAPower, SATarget);
      #endif
      _wapc.TrackUpDown |= 0x02;
      _wapc.AirohaIndex += indexOffset;
      if(_wapc.AirohaIndex > (int)(_wcal[calibrationIndex].maxIndex & 0x0000ffff))
      {
         if (calibrationIndex == 0 && version >= CALIBRATION_VERSION_3 && 
            (_wapc.AvgData < (int)(_wcal[CAL_BG_BAND_CCK_ONLY_EXT].maxIndex & 0x000000ff)))
         {
            //due to b_tx_gain adaptive control, the max index could go over for lower power
            _wapc.AirohaIndex++;
            if(_wapc.AirohaIndex > (int)(_wcal[calibrationIndex].maxIndex & 0x0000ffff) + 1)
                _wapc.AirohaIndex = (int)(_wcal[calibrationIndex].maxIndex & 0x0000ffff) + 1;
         }
         else       
            _wapc.AirohaIndex = (int)(_wcal[calibrationIndex].maxIndex & 0x0000ffff);
            
         if(_wapc.AirohaIndex > 63)
         {
                _wapc.AirohaIndex = 63;
         }
         _wifi_Airoha(AIROHA_PWR_SETTING | ((long)_wapc.AirohaIndex << 4));
         return WIFI_APC_COMPLETE;
        }   
      _wifi_Airoha(AIROHA_PWR_SETTING | ((long)_wapc.AirohaIndex << 4));
   }
   return WIFI_APC_DEBOUNCE;
}

/**
 * apc_wifi_discountAvg() is a function that accumulates a raw ADC reading
 * into an average reading.
 * 
 * The variable wapc->AvgHistory contains the previous average, and the
 * variable wapc->AvgCycles contains the number of readings, already incremented.
 * 
 * The discounted value is saved and returned.
 * 
 * param  adc_rawdata   raw ADC value to be discounted
 * 
 * @retval dis_value   discounted ADC value
 * 
 * @note Called from airoha_pwr_cal.c::_wifi_PwrAdjust() \n
 * which called from airoha_pwr_cal.c::_wifi_pwrCntrl() \n
 * which is called from mac_hw_piper.c::SendFrame() immediately prior to frame transmission
 */
int _wifi_discountAvg(int adc_rawdata)
{
        //int adc_avgdata=0;
        //float alpha = _WIFI_DISCOUNT_ALPHA;
        float AvgHistory;
        
        // in case average goes too far, restart it
	if (_wapc.AvgCycles > 0x7FFFFFF0) {
		_wapc.AvgCycles = 1;
		_wapc.AvgHistory = adc_rawdata;
	}
	
        if (_wapc.AvgHistory == 0)
                AvgHistory = adc_rawdata;
        else
                AvgHistory = _wapc.AvgHistory;

        if (_wapc.AvgCycles) {
		AvgHistory =
			( AvgHistory / (float)_wapc.AvgCycles ) * ( _wapc.AvgCycles - 1 )
				+ ( (float)adc_rawdata / (float)_wapc.AvgCycles );
	}
	
        _wapc.AvgHistory = (int) AvgHistory;
        
        return _wapc.AvgHistory;
}
/**
 * _wifi_initPwrCntrl() is a function that starts an APC cycle
 * as either a NEW or OLD cycle.
 * 
 * @param  new_cycle   1=start NEW cycle, 0=start OLD cycle
 * 
 * @return none
 */
static void _wifi_initPwrCntrl(int new_cycle)
{
   if(new_cycle)
   {
      _wapc.NewPwrLevel  = TRUE;
   }
   else
   {
      _wapc.NewPwrLevel  = FALSE;
   }
   _wapc.Enable =    TRUE;
   _wapc.Interrupt = FALSE;
   _wapc.TrackUpDown = 0;
   _wapc.State = WIFI_APC_START;
   //_wapc.Inactivity = set_timeout(_WIFI_INACTIVITY_PERIOD);
   tx_timer_deactivate(&inactivityTimer);
   tx_timer_change(&inactivityTimer,_WIFI_INACTIVITY_PERIOD*NABspTicksPerSecond, 0);
   tx_timer_activate(&inactivityTimer);
   
}

/**
 * _wifi_PwrAdjust() is a function that instantiates the APC FSM.
 * 
 * This function is called in response to events outside of the power controller.
 * 
 * On entry, _wapc->State contains the current state.
 *
 * On exit, _wapc->State may or may not be changed.
 * 
 * @param  none
 * 
 * @return none
 * 
 * @note Called from airoha_pwr_cal.c::_wifi_pwrCntrl() \n
 * which is called from mac_hw_piper.c::SendFrame() immediately prior to frame transmission
 */
void _wifi_PwrAdjust(void)
{
        unsigned long actual_events;
//        epStatus *pSta = rootEndPoint[0];
        
   switch(_wapc.State)
   {
      case WIFI_APC_START:
         _wapc.State = WIFI_APC_ADC_LOOP;
         //_wapc.CalTimeout = set_timeout(_WIFI_PWR_CAL_TIMEOUT);
         tx_timer_deactivate(&pwrCalTimer);
         tx_timer_change(&pwrCalTimer,_WIFI_PWR_CAL_TIMEOUT*NABspTicksPerSecond, 0);
         tx_timer_activate(&pwrCalTimer);
         break;

      case WIFI_APC_ADC_LOOP:
         _wapc.State = WIFI_APC_ADC_AVG;
         _wapc.AvgData = 0;
         _wapc.AvgCycles = 0;
         
         //_wc_write8(NCTXINTR, _TX_START_ENABLE);
         enableTxStartInt (1);
         tryToTakeAverageVoltage = 1;
         break;

      case WIFI_APC_ADC_AVG:
         //_wc_write8(NCTXINTR, _TX_START_ENABLE);
         enableTxStartInt (1);
         tryToTakeAverageVoltage = 1;
         #ifdef AIROHA_PWR_CALIBRATION_DEBUG
         _wapc.avgTryTotal++;
         #endif
         
         if( !isBeaconTestOn() && tx_event_flags_get(&airoha_pwr_cal_group, TX_START_EVENT, TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS)
         {
         	#ifdef AIROHA_PWR_CALIBRATION_DEBUG
         	_wapc.readingPeakTxStart++;
         	#endif
        		//if(tx_event_flags_get(&airoha_pwr_cal_group, TX_DONE_EVENT, TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS)
        		//{
        			#ifdef AIROHA_PWR_CALIBRATION_DEBUG
        			_wapc.readingPeakTxDone++;
        			#endif
	        		 _wapc.RawTemp = readAdcPeak();
	            if (_wapc.RawTemp > PEAK_ADJUSTMENT_CCK)
	            {
	                    if (CHAN_5G (global_chan/*pSta->macStatus.channel*/))
	                    {
	        		 		_wapc.Modulation = _WIFI_OFDM;
	        		 		_wapc.Rawdata = _wapc.RawTemp - PEAK_ADJUSTMENT_OFDM;
	        		 	}
	        		 	else
	        		 	{
	        		 		_wapc.Modulation = _WIFI_PSK_CCK;
	        		 		_wapc.Rawdata = _wapc.RawTemp - PEAK_ADJUSTMENT_CCK;
	        		 	}
	        		 		
	        		 	#ifdef AIROHA_PWR_CALIBRATION_DEBUG
	        		 	_wapc.history[_wapc.ii%10]= _wapc.RawTemp;
	                    _wapc.ii++;
	                    _wapc.goodReading++;
	                    #endif
	                         	 	                         	
	                    if (clearAdcPeak() != 0)
	                        clearAdcPeak();
	                        
	                    tx_thread_sleep(2);
	                    _wapc.Interrupt = TRUE;
	        			tryToTakeAverageVoltage = 0;
    	                         	
    	        }
    	        else
    	            _wapc.Rawdata = 0;
	                         
	                        
                	//}
                         
        }
        	
         
         if(_wapc.Interrupt || isContTXOn() || isBeaconTestOn() /*(_wc_read32(0x8) & 0x00100000)*/)
         {
                //_wc_write8(NCTXINTR, 0);
                enableTxStartInt (0);
                tryToTakeAverageVoltage = 0;
                _wapc.Interrupt = FALSE;
            
                if(isContTXOn() || isBeaconTestOn()/* _wc_read32(0x8) & 0x00100000*/)
                {
                        _wapc.RawTemp = readAdcPeak(); //_wifi_readadc();
                        if (_wapc.RawTemp > PEAK_ADJUSTMENT_CCK)
                        {
                            _wapc.Modulation = isContOFDM();
                            if (_wapc.Modulation == _WIFI_OFDM)
                                _wapc.Rawdata = _wapc.RawTemp - PEAK_ADJUSTMENT_OFDM;
                            else
                                _wapc.Rawdata = _wapc.RawTemp - PEAK_ADJUSTMENT_CCK;
                            
                            if (clearAdcPeak() != 0)
	                            clearAdcPeak();
	                        
	                        tx_thread_sleep(2);
                        }
                        else
                            _wapc.Rawdata = 0;
                }
                
            if(_wapc.Rawdata != 0)
            {
                if((_wapc.Modulation == _WIFI_OFDM) &&  !CHAN_5G (global_chan/*pSta->macStatus.channel*/))
                {
                    if (version < CALIBRATION_VERSION_3)
                    {
                        _wapc.Rawdata +=  (_wcal[1].intercept +
                                 ((float)_wapc.AirohaIndex/_wcal[1].slope));
                    }
                }
                // ADC 3 point average with range checking
                                 
                if(_wapc.NewPwrLevel)
                {
                        #ifdef AIROHA_PWR_CALIBRATION_DEBUG
                        _wapc.avgTry++;
                        #endif
                        _wapc.AvgData += (int) _wapc.Rawdata;
                        _wapc.AvgCycles++;
                        
                        if(_wapc.AvgCycles == _WIFI_ADC_AVG_COUNT)
                        {
                                _wapc.AvgData =_wapc.AvgData/(_WIFI_ADC_AVG_COUNT);
                                if((int)_wapc.Rawdata >= (_wapc.AvgData - _WIFI_ADC_RANGE_LIMIT) &&
                                (int)_wapc.Rawdata <= (_wapc.AvgData + _WIFI_ADC_RANGE_LIMIT))
                                {
                                        _wapc.State = WIFI_APC_ADJUST_PWR;
                                        _wapc.AvgHistory = _wapc.AvgData;
                                        #ifdef AIROHA_PWR_CALIBRATION_DEBUG
                                        _wapc.avgDone1++;
                                        #endif
                                }
                                else
                                {
                                        _wapc.State = WIFI_APC_ADC_LOOP;
                                }
                                break;
                        }
                        
                }
                else
                {
                        #ifdef AIROHA_PWR_CALIBRATION_DEBUG
                        _wapc.avgTry2++;
                        #endif
                        // ADC discount average with range checking
                        _wapc.AvgCycles++;
                        _wapc.AvgData = _wifi_discountAvg(_wapc.Rawdata);
                        
                        
                        if(_wapc.AvgData >= (_wapc.Rawdata - _WIFI_ADC_RANGE_LIMIT) &&
                                _wapc.AvgData <= (_wapc.Rawdata + _WIFI_ADC_RANGE_LIMIT))
                        {
                                if(_wapc.AvgCycles == _WIFI_ADC_AVG_COUNT)
                                {
                                        _wapc.State = WIFI_APC_ADJUST_PWR;
                                        #ifdef AIROHA_PWR_CALIBRATION_DEBUG
                                        _wapc.avgDone2++;
                                        #endif
                                        
                                        break;
                                }
                                
                        }
                        else
                        {
                                _wapc.State = WIFI_APC_ADC_LOOP;
                        }
                }
            //_wc_write8(NCTXINTR, _TX_START_ENABLE);
                }
                enableTxStartInt (1);
                tryToTakeAverageVoltage=1;
         }
         if(tx_event_flags_get(&airoha_pwr_cal_group, CAL_TIMEOUT_EVENT,
                TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS /*chk_timeout(_wapc.CalTimeout)*/)
         {
                //_wapc.CalTimeout = set_timeout(_WIFI_PWR_CAL_PERIOD);
                tx_timer_deactivate(&pwrCalTimer);
                tx_timer_change(&pwrCalTimer,_WIFI_PWR_CAL_PERIOD*NABspTicksPerSecond, 0);
                tx_timer_activate(&pwrCalTimer);
                _wapc.State = WIFI_APC_INTERVAL_TRIGGER;
                
                // fix APC FSM, state WIFI_APC_ADC_AVG, leaves tx rate at 1 Mbps after timeout
                enableTxStartInt( 0 );
		tryToTakeAverageVoltage = 0;
         }
         break;

      case WIFI_APC_ADJUST_PWR:
         // Note: TX_START interrupt is automatically disabled by the ISR
         _wapc.State = _wifi_setAirohaPwr();
         //_wapc.Debounce = _SET_SHORT_TIMEOUT(_WIFI_PWR_CAL_DEBOUNCE);
         tx_timer_deactivate(&debounceTimer);
         tx_timer_change(&debounceTimer,2/*_WIFI_PWR_CAL_DEBOUNCE*NABspTicksPerSecond*/, 0);
         tx_timer_activate(&debounceTimer);
         break;

      case WIFI_APC_DEBOUNCE:
         if(tx_event_flags_get(&airoha_pwr_cal_group, DEBOUNCE_TIMEOUT_EVENT,
                TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS /*_CHK_SHORT_TIMEOUT(_wapc.Debounce)*/)
         {
            _wapc.State = WIFI_APC_START;
         }
         break;

      case WIFI_APC_COMPLETE:
         // Setup for next pwr control cycle (seconds)
         //_wapc.CalTimeout = set_timeout(_WIFI_PWR_CAL_PERIOD);
         tx_timer_deactivate(&pwrCalTimer);
         tx_timer_change(&pwrCalTimer,_WIFI_PWR_CAL_PERIOD*NABspTicksPerSecond, 0);
         tx_timer_activate(&pwrCalTimer);
         
         _wapc.State = WIFI_APC_INTERVAL_TRIGGER;
         _wapc.NewPwrLevel  = FALSE;
         
        // fix APC FSM, state WIFI_APC_COMPLETE, exists to TRIGGER with avg reading mode enabled
	//  which causes 1/6 Mbps (low rate) to be used for all transmissions even when 
	//  power control not actively measuring power
         enableTxStartInt( 0 );
	 tryToTakeAverageVoltage = 0;
			
         //_wapc.Inactivity = set_timeout(_WIFI_INACTIVITY_PERIOD);
         tx_timer_deactivate(&inactivityTimer);
         tx_timer_change(&inactivityTimer,_WIFI_INACTIVITY_PERIOD*NABspTicksPerSecond, 0);
         tx_timer_activate(&inactivityTimer);
         break;

      case WIFI_APC_INTERVAL_TRIGGER:
         // Check for timeout to do another pwr control cycle
         if(tx_event_flags_get(&airoha_pwr_cal_group, CAL_TIMEOUT_EVENT,
                TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS /*chk_timeout(_wapc.CalTimeout)*/)
         {
            _wifi_initPwrCntrl(FALSE);
            _wapc.State = WIFI_APC_START;
         }
         break;

       default:
         _wifi_initPwrCntrl(TRUE);
         _wapc.State = WIFI_APC_START;
         break;
   }
}

/**
 * _wifi_pwrCntrl() is a function that generates an activity event
 * that is an input to the APC FSM.
 * 
 * It handles the case where an ADC read operation has timed out, and
 * it handles the case where the device is uncalibrated.
 * 
 * This function is called at the start of transmission of each wireless frame
 * (SendFrame() function).
 * 
 * @param  none
 * 
 * @return none
 */
void _wifi_pwrCntrl(void)
{
   // Check if CPU is operating at full speed
        unsigned long actual_events;
   if(_wapc.Enable && _wapc.CpuFullSpeed)
   {
        // Check CPU speed, if CLK reduced, set power level to a safe limit
      // and disable the APC loop until it's back to full operating speed.
     // if((RdPortI(GCDR) == 0) || ((RdPortI(GCSR) & 0x1C) != 0x08))
     // {
     //    __wifi_SetPower(_wapc.CustIndex);
     //    _wapc.CpuFullSpeed = FALSE;
     //    return;
     // }
      if(tx_event_flags_get(&airoha_pwr_cal_group, INACTIVE_TIMEOUT_EVENT,
                TX_OR_CLEAR, &actual_events, TX_NO_WAIT) == TX_SUCCESS /*chk_timeout(_wapc.Inactivity)*/)
      {
         __wifi_SetPower(_wapc.CustIndex);
         _wifi_initPwrCntrl(TRUE);
      }
      if(_wifi_RFCalCRC())
      {
         __wifi_SetPower(_wapc.CustIndex);
         _wapc.Enable = FALSE;
         return;
      }
      // Check and/or adjust Airoha power
      _wifi_PwrAdjust();
   }
}

/* Set the power in dbm to verify the calibration. RF testing purposes ONLY */
void MacSetTxPowerInDbm(int value)
{
	//MacSetTxPower(value);
#if 1 
    if (value < 0)
        value = 0;
    else if (value > 20)
        value = 20;

    __wifi_SetPower(value);
#endif
}

/*
    b_tx_gain_table = {
    {0x80,      0 },
    {0x90,       1.02 },
    {0xA0,      1.94 },
    {0xB0,      2.77 },
    {0xC0,      3.52 },
    {0xD0,      4.22 },
    {0xE0,       4.86 },
    {0xF0,       5.46 },
    };
*/

int checkPowerInput (int value)
{
    int calibrationIndex;
    unsigned char b_tx_gain;
   
   calibrationIndex = getCurve();
    
    if ((calibrationIndex == 0) && (value < lowestPwrBG_CCK))
            value = lowestPwrBG_CCK;
    else if ((calibrationIndex > 0) && (value < lowestPwrA))
            value = lowestPwrA;
    else if ((calibrationIndex == 0) && (value > highestPwrBG_CCK))
    {
            value = highestPwrBG_CCK;
            if (isRF_AIROHA_7230())
            {
                value = 16;
            }
    }
    else if ((calibrationIndex > 0) && (value > highestPwrA))
            value = highestPwrA;
            
    if (version >= CALIBRATION_VERSION_3)
    {
        if (calibrationIndex == 0) 
        {
            if (value > highestPwrBG_OFDM)
            {
                //search for the best B_TX_GAIN
                if ((value - highestPwrBG_OFDM) >= 6)
                    b_tx_gain = 0xF0;
                else if ((value - highestPwrBG_OFDM) == 5)
                    b_tx_gain = 0xE0;
                else if ((value - highestPwrBG_OFDM) == 4)
                    b_tx_gain = 0xC0;
                else if ((value - highestPwrBG_OFDM) == 3)
                    b_tx_gain = 0xB0;
                else if ((value - highestPwrBG_OFDM) == 2)
                    b_tx_gain = 0xA0;
                else if ((value - highestPwrBG_OFDM) == 1)
                    b_tx_gain = 0x90;
                else
                    b_tx_gain = 0x80;
                    
            }
            else
            {
                //keep OFDM and CCK at the same power level
                b_tx_gain = 0x80;
            }
            
           setAttenuation(b_tx_gain);
        }      
    }
        
   return value;
}
/**
 * _wifi_SetPower() is a function that sets the power level to the
 * specified dBm value and initiates APC power adjustment for this setting.
 * 
 * It handles closed and open loop modes.
 * 
 * @param  value       poweer to be set (dBm, 0-16)
 * 
 * @return none
 * 
 * @note Called from wireless_mac_hw_piper.c::MacSetTxPower() \n
 * Called from wireless_airoha_pwr_cal.c::_wifi_pwrCntrl()
 */

void __wifi_SetPower (int value)
{
   //#GLOBAL_INIT{_wapc.AvgHistory = 0;}
   //int i;
   int calibrationIndex, maxIndex, minIndex;
   
   value = checkPowerInput (value);
                
      // Disable TX_START Interrupt
     // _wc_write8(NCTXINTR, _TX_START_DISABLE);
     enableTxStartInt (0);
     tryToTakeAverageVoltage=0;
 
     _wapc.CustIndex = value;
            
      if(_wifi_RFCalCRC())
      {
         if (isRF_AIROHA_7230())
         {
                _wapc.TargetIndex = (int) (53/17.0 * (float)(value)); /* 53 is from lab testing */
                _wapc.Enable = FALSE;
         }
         else
         {
                 _wapc.TargetIndex = (int) (_WIFI_MAX_AIROHA_INDEX/17.0 * (float)(value));
                 _wapc.Enable = FALSE;
         }
      }
      else
      {
        calibrationIndex = getCurve();
        maxIndex = _wcal[calibrationIndex].maxIndex & 0x0000ffff;
        minIndex = (_wcal[calibrationIndex].maxIndex & 0xffff0000) >> 16;
        
        if (maxIndex != minIndex)
        {
                if (calibrationIndex == 0) /* bg band */
                {
                    if ((version >= CALIBRATION_VERSION_3) && (isContTXOn() || isBeaconTestOn()) && isContOFDM())
                    {
                        //this is to speed up the OFDM test
                        maxIndex = _wcal[CAL_BG_BAND_OFDM_ONLY].maxIndex & 0x0000ffff;
                        minIndex = (_wcal[CAL_BG_BAND_OFDM_ONLY].maxIndex & 0xffff0000) >> 16;
                        if (value  > highestPwrBG_OFDM)
                            value = highestPwrBG_OFDM;
                        _wapc.TargetIndex = minIndex + ((maxIndex - minIndex) * (value - lowestPwrBG_OFDM)/(highestPwrBG_OFDM - lowestPwrBG_OFDM));
                    }
                    else
                    {
                        _wapc.TargetIndex = minIndex + ((maxIndex - minIndex) * (value - lowestPwrBG_CCK)/(highestPwrBG_CCK - lowestPwrBG_CCK));
                    }
                }
                else
                {
                        /* on A bands, here we just get a starting point with straight initial value and start simple*/
                       _wapc.TargetIndex = minIndex + 2 + ((maxIndex - minIndex) * (value - lowestPwrA)/(highestPwrA - lowestPwrA));
                }
        }
        else
        {
             //some thing is really wrong 
             _wapc.TargetIndex = _wPwr[value];
        }
                
        if (_wapc.TargetIndex < minIndex )
        {
                _wapc.TargetIndex = minIndex;
        }               
        else if (_wapc.TargetIndex > maxIndex )
        {
                _wapc.TargetIndex = maxIndex;
        }
        
        _wapc.Enable = TRUE;
   		_wapc.NewPwrLevel = TRUE;
   		_wapc.State = WIFI_APC_START;
   		_wapc.CpuFullSpeed = TRUE;
   		_wapc.AvgHistory = 0;
   		_wapc.Rawdata = 0;
         _wifi_initPwrCntrl(TRUE);
      }

      _wapc.AirohaIndex = _wapc.TargetIndex;

      // Set RF pwr level
      _wifi_Airoha(AIROHA_PWR_SETTING | ((long)_wapc.AirohaIndex << 4));
      #ifdef WIFI_AIROHA_PWR_VERBOSE
      printf("Executed RF power command, index= %x\n", _wapc.AirohaIndex);
      #endif
      #ifdef WIFI_REGION_VERBOSE
      printf("TxPower Index=%d\n",value);
      #endif
   
}

/**
 * _wifi_RFCalCRC() is a function that returns the verification
 * status of the current curves array.
 * 
 * If previously called, this function simply returns the saved status,
 * which will be refreshed whenever the curves array is changed.
 *
 * If not previously called, this function will perform a full verification, update the saved status, and return the status.
 * 
 * @param  none
 * 
 * @retval 0           successful, curve data is valid
 * @retval <0          unsuccessful, curve data is invalid
 * 
 * Called from airoha_pwr_cal.c::_wifi_RFCalCRC()
 */
static int isValid = FALSE;
int _wifi_RFCalCRC(void)
{  
    int i;
        
    if (isValid == TRUE)
        return 0;
    else
    {
        if (!isRF_AIROHA_7230())
        {
            for (i=0; i<=1; i++)
            {
                if (_wcal[i].slope == 0 || _wcal[i].maxIndex == 0)
                    return -1;
                        
                if (_wcal[i].crc != CalcCRC32 ((unsigned char *)&_wcal[i], sizeof(_wcal[i]) -  sizeof(long)))
                    return -1; 
            }
            
            if (_wcal[CAL_VERSION].slope >= CALIBRATION_VERSION_3 )
            {
                for (i=7; i<=9; i++)
                {
                    if (_wcal[i].slope == 0 || _wcal[i].maxIndex == 0)
                        return -1;
                            
                    if (_wcal[i].crc != CalcCRC32 ((unsigned char *)&_wcal[i], sizeof(_wcal[i]) -  sizeof(long)))
                        return -1; 
                }
            }    
            isValid = TRUE;
            return 0;
            
        }
        else
        {
            for (i=0; i<=6; i++)
            {
                if (_wcal[i].slope == 0 || _wcal[i].maxIndex == 0)
                    return -1;
                        
                if (_wcal[i].crc != CalcCRC32 ((unsigned char *)&_wcal[i], sizeof(_wcal[i]) -  sizeof(long)))
                    return -1; 
            }
            
            if (_wcal[CAL_VERSION].slope >= CALIBRATION_VERSION_3 )
            {
                for (i=7; i<=14; i++)
                {
                    if (_wcal[i].slope == 0 || _wcal[i].maxIndex == 0)
                        return -1;
                            
                    if (_wcal[i].crc != CalcCRC32 ((unsigned char *)&_wcal[i], sizeof(_wcal[i]) -  sizeof(long)))
                        return -1; 
                }
            }    
            isValid = TRUE;
            return 0;
        }
    }
}

/**
 * getPowerCalibrationVersion() is a function that returns the power calibration
 * version.
 * 
 * 
 * @param none        
 * 
 * @retval CALIBRATION_VERSION_1           Old method of calibration which uses the
 *                                         OFDM attenuation of 3.5 dBm
 * @retval CALIBRATION_VERSION_2           New method of calibration which uses the 
 *                                         OFDM attenuation of 4.86 dBm
 */
int getPowerCalibrationVersion (void)
{
    if(_wifi_RFCalCRC() == 0)
    {
        if (_wcal[CAL_VERSION].slope == CALIBRATION_VERSION_1)
        {
            // This is the old method of calibration which uses the OFDM attenuation of 3.5 dBm
            highestPwrBG_CCK = 16;    //we have to limit the power with old version 
            return CALIBRATION_VERSION_1;
        }
        else if (_wcal[CAL_VERSION].slope == CALIBRATION_VERSION_2)
        {
            // This is the new method of calibration which uses the OFDM attenuation of 4.86 dBm
            return CALIBRATION_VERSION_2;
        }
        else
        {
            // for now the latest version is CALIBRATION_VERSION_2
            return CALIBRATION_VERSION_3;
        }           
    }
    else
    {
            //if it's never be calibrated, let's use the latest version
            return CALIBRATION_VERSION_3;
    }
    
}
/**
 * getPowerCalibrationData() is a function that returns the power calibration
 * curve array in the specified buffer and relates parameters used during calibration.
 * 
 * This function protects against curve array not being in memory, and also
 * protects against the specified buffer being the current curve array buffer in memory.
 * 
 * @param  wcal        pointer to destination curve array buffer, size 7*23 bytes (__POWER_CALIBRATION_T *)
 * 
 * @retval 0           successful
 * @retval <0          unsuccessful
 */
int getPowerCalibrationData(WiFiPwrCal*  wcal)
{  
    devBoardParamsType *nvParams;
    int ret=-1;
   
    if (wcal == NULL)
        return ret;

    nvParams = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
    if (nvParams == NULL)
        return ret;

    if (customizeReadDevBoardParams(nvParams) == 0)
    {
        memcpy(wcal, &nvParams->wlnExtParams.wlnPowerCalibration, WLN_POWER_CALIBRATION_LENGTH * sizeof (unsigned long));
        ret = 0;
    }

    free(nvParams);
    return ret;
}

/**
 * setPowerCalibrationData() is a function that writes the specified
 * power calibration curve array to flash.
 * 
 * The current memory copy of the power calibration table including
 * the curve array is unaffected.
 * 
 * @param  wcal            pointer to array of calibration curves, size 7*28 bytes (__POWER_CALIBRATION_T *)
 * @retval 0               successful
 * @retval <0              unsuccessful
 */
int setPowerCalibrationData(WiFiPwrCal*  wcal)
{
    int ret = -1;
    devBoardParamsType *nvParams;

    if (wcal == NULL)
        return ret;

    nvParams = (devBoardParamsType *)malloc(sizeof(devBoardParamsType));
    if (nvParams == NULL)
        return ret;

    if (customizeReadDevBoardParams(nvParams) == 0)
    {
        memcpy(&nvParams->wlnExtParams.wlnPowerCalibration, wcal, WLN_POWER_CALIBRATION_LENGTH * sizeof (unsigned long));
        if (customizeWriteDevBoardParams(nvParams) == 0)
            ret = 0;
    }

    free(nvParams);
    
    return ret;
}

/**
 *  initPwrCal() is a function that initializes the power control module.
 * 
 * @param  none
 * 
 * @retval none
 */
void initPwrCal(void)
{
   // Initialize A/D I2C port
   _wifi_init_adc();
   memset (_wcal, 0, sizeof(_wcal));
   getPowerCalibrationData(_wcal);
   version = getPowerCalibrationVersion();
   initPwrCalTimer();
   _wapc.Enable = TRUE;
   _wapc.NewPwrLevel = TRUE;
   _wapc.State = WIFI_APC_START;
   _wapc.CpuFullSpeed = TRUE;
   _wapc.AvgHistory = 0;
   _wapc.Rawdata = 0;
   
    if (isRF_AIROHA_7230())
    {
        lowestPwrBG_CCK = LOW_POINT_AL7230_BG_CCK;
        highestPwrBG_CCK = HIGH_POINT_AL7230_BG_CCK;
        lowestPwrBG_OFDM = LOW_POINT_AL7230_BG_OFDM;
        highestPwrBG_OFDM = HIGH_POINT_AL7230_BG_OFDM;
        lowestPwrA = LOW_POINT_AL7230_A;
        highestPwrA = HIGH_POINT_AL7230_A;
    }
    else
    {
        lowestPwrBG_CCK = LOW_POINT_AL2236_CCK;
        highestPwrBG_CCK = HIGH_POINT_AL2236_CCK;
        lowestPwrBG_OFDM = LOW_POINT_AL2236_OFDM;
        highestPwrBG_OFDM = HIGH_POINT_AL2236_OFDM;
        lowestPwrA = LOW_POINT_AL7230_A;    //Just in case something is wrong
        highestPwrA = HIGH_POINT_AL7230_A;  //Just in case something is wrong
    }    
}  

/* The following code is added to enable/disable closed-loop power control for tst_wifi only */
static TX_THREAD powerControlForTstWifi_thread;
static char powerControlForTstWifi_thread_stack[1024*4];
static void power_control_for_tst_wifi(unsigned long unused)
{
        (void)unused;

	while (1)
	{
		_wifi_pwrCntrl();
		tx_thread_sleep(2); //call it every 2ms
	}
}
void powerControlForTstWifi(BOOLEAN enable)
{
	//epStatus *pSta = rootEndPoint[0];
	
	if(enable)
	{
		/* disable the mac driver */
	       // pSta->macStatus.state = WLN_ST_STOPPED;	
	       // pSta->staState = SS_WAIT;
	        //bss_save = macParams.bss_type;
	      //  macParams.bss_type = WLN_BSS_NONE;
	      	
	      	tx_thread_suspend(&I2C_ADC_Read);
	      	
	      	
	        enableTxStartInt (0);
	        tx_thread_terminate(&powerControlForTstWifi_thread);
		tx_thread_delete(&powerControlForTstWifi_thread);
		memset(&powerControlForTstWifi_thread, 0, sizeof (TX_THREAD));
		
		//reload the calibration data
		getPowerCalibrationData(_wcal);
		isValid = FALSE;
		
		//update verion in case the DTU is recalibrated
		version = getPowerCalibrationVersion();
		if (version != CALIBRATION_VERSION_1 && isRF_AIROHA_7230())
        {
            highestPwrBG_CCK = HIGH_POINT_AL7230_BG_CCK;
        }
        else
        {
            highestPwrBG_CCK = HIGH_POINT_AL2236_CCK;
        }    
		
		//memset(&_wapc, 0, sizeof(_wapc));
		_wapc.Enable = TRUE;
   		_wapc.NewPwrLevel = TRUE;
   		_wapc.State = WIFI_APC_START;
   		_wapc.CpuFullSpeed = TRUE;
   		_wapc.AvgHistory = 0;
   		_wapc.Rawdata = 0;
		 
   		tx_thread_sleep(NABspTicksPerSecond/10);
   		
		tx_thread_create(&powerControlForTstWifi_thread, 
				"powerControlForTstWifi_thread",
                		power_control_for_tst_wifi, 
                		0,
                		(void *) powerControlForTstWifi_thread_stack, 
                		1024*4,
                		16, 
                		16, 
                		10,
                		TX_AUTO_START);
        }
        else
        {        	 
        	tx_thread_terminate(&powerControlForTstWifi_thread);
        	//memset (&_wapc, 0, sizeof _wapc);
        	
        	//reload the calibration data
		getPowerCalibrationData(_wcal);
		isValid = FALSE;
		//update verion in case the DTU is recalibrated
		version = getPowerCalibrationVersion();
		if (version != CALIBRATION_VERSION_1 && isRF_AIROHA_7230())
        {
            highestPwrBG_CCK = HIGH_POINT_AL7230_BG_CCK;
        }
        else
        {
            highestPwrBG_CCK = HIGH_POINT_AL2236_CCK;
        }    
		
		//memset(&_wapc, 0, sizeof(_wapc));
		_wapc.Enable = TRUE;
   		_wapc.NewPwrLevel = TRUE;
   		_wapc.State = WIFI_APC_START;
   		_wapc.CpuFullSpeed = TRUE;
   		_wapc.AvgHistory = 0;
   		_wapc.Rawdata = 0;
   		
        	tx_thread_resume(&I2C_ADC_Read);
        }
}



/*
 * This API extracts calibration data and format it to display.
 * Make sure the passed buffer is atleast 1024 bytes.
 *
 */
int getCalibrationFormattedData(char *buf, int *bufLen)
{
    int len, ndx = 0, ret = -1;
    char *ptr;
    WiFiPwrCal *calData = NULL;
    uint32 crc;
    unsigned int *dataPtr;
    BYTE mac_addrp[8];

    if ((buf == NULL) || (*bufLen < 1024))
    {
        goto calDone;
    }   

    calData = (WiFiPwrCal *)malloc(WLN_POWER_CALIBRATION_LENGTH/4 * sizeof(WiFiPwrCal));
    if (calData == NULL)
    {
        ret = -2;
        goto calDone;
    }

    if (getPowerCalibrationData(calData))
    {
        ret = -3;
        goto calDone;
    }

    if (customizeGetInterfaceMACAddress(BP_WLN_INTERFACE, mac_addrp) != BP_SUCCESS)
    {
        ret = -4;
        goto calDone;
    }
    
    dataPtr = (unsigned int *)calData;
    ptr = buf;
    memset(buf, 0, *bufLen);
    
    len = sprintf(ptr, "%08X%04X\r\n", *(unsigned int *)mac_addrp, *(unsigned short *)&mac_addrp[4]);
    if (len < 0)
        goto calDone;

    ptr += len; 
    
    while (ndx < WLN_POWER_CALIBRATION_LENGTH)
    {
        if ((len = sprintf(ptr, "%08X ", dataPtr[ndx])) < 0)
            goto calDone;
        
        ptr += len;
        ndx++;
        if ((ndx%4) == 0)
        {
            if ((len = sprintf(ptr, "\r\n")) < 0)
                goto calDone;
               
            ptr += len;
        }
    }

    // calculate CRC
    crc = CalcCRC32((uint8 *)buf, (ptr - buf));
    if ((len = sprintf(ptr, "%08X\r\n", (unsigned int)crc)) < 0)
         goto calDone;

    *bufLen = (ptr - buf) + len;
    ret = 0;    
    
    calDone:
    if (calData != NULL)    
        free(calData);
        
    return ret;
}

int setCalibrationFormattedData(char *buf, int bufLen)
{
    int len, ndx = 0, ret = -1;
    char *ptr;
    WiFiPwrCal *calData = NULL;
    uint32 crc;
    unsigned int *dataPtr;
    BYTE mac_addrp[8], actual_mac[8];

    if ((buf == NULL) && (bufLen < WLN_POWER_CALIBRATION_LENGTH*4))
        goto calDone;

    calData = (WiFiPwrCal *)malloc(WLN_POWER_CALIBRATION_LENGTH/4 * sizeof(WiFiPwrCal));
    if (calData == NULL)
    {
        ret = -2;
        goto calDone;
    }

    dataPtr = (unsigned int *)calData;
    ptr = buf;

    // need to swap this 8x and 4x to avoid compilation warnings...    
    len = sscanf(ptr, "%04X%08X\r\n", (unsigned int *)mac_addrp, (unsigned int *)&mac_addrp[4]);
    if (len < 0)
        goto calDone;

    ptr += 14; // we cannot use len as we did after sprintf
    
    while (ndx < WLN_POWER_CALIBRATION_LENGTH)
    {
        if ((len = sscanf(ptr, "%08X ", &dataPtr[ndx])) < 0)
            goto calDone;
        
        ptr += 9;
        ndx++;
        if ((ndx%4) == 0)
        {
            if ((len = sscanf(ptr, "\r\n")) < 0)
                goto calDone;
               
            ptr += 2;
        }
    }

    // calculate CRC
    if ((len = sscanf(ptr, "%08X", (unsigned int *)&crc)) < 0)
         goto calDone;

    // check CRC
    if (CalcCRC32((uint8 *)buf, (ptr - buf)) != crc)
    {
        ret = -5; // crc mismatch
        goto calDone;
    }

    // check MAC address
    if (customizeGetInterfaceMACAddress(BP_WLN_INTERFACE, actual_mac) != BP_SUCCESS)
    {
        ret = -4;
        goto calDone;
    }
        
    if (memcmp(actual_mac, &mac_addrp[2], 6))
    {
        ret = -6; // invalid mac
        goto calDone;
    }

    // lookd good, so write the data to NVRAM        
    if (setPowerCalibrationData(calData))
    {
        ret = -3;
        goto calDone;
    }

    ret = 0;    
    
    calDone:
    if (calData != NULL)    
        free(calData);
        
    return ret;
}

#if MANUFACTURING_MODE
/* The two APIs are provided for MFG ONLY to test I2C ADC 
 *
 * suspendPowerControl() is to let the power control thread suspend itself so I2C ADC is 
 * free for testing.
 *
 * resumePowerControl() is called after the I2C ADC testing and to resume the power control 
 * thread.
 */
void suspendPowerControl(void)
{
    needTobeSuspend = 1;
}
void resumePowerControl(void)
{
    needTobeSuspend = 0;
    tx_thread_resume(&I2C_ADC_Read);
}
#endif

#endif /* AIROHA_PWR_CALIBRATION */


