view src/cs/layer1/tm_cust0/l1tm_cust.c @ 600:8f50b202e81f

board preprocessor conditionals: prep for more FC hw in the future This change eliminates the CONFIG_TARGET_FCDEV3B preprocessor symbol and all preprocessor conditionals throughout the code base that tested for it, replacing them with CONFIG_TARGET_FCFAM or CONFIG_TARGET_FCMODEM. These new symbols are specified as follows: CONFIG_TARGET_FCFAM is intended to cover all hardware designs created by Mother Mychaela under the FreeCalypso trademark. This family will include modem products (repackagings of the FCDEV3B, possibly with RFFE or even RF transceiver changes), and also my desired FreeCalypso handset product. CONFIG_TARGET_FCMODEM is intended to cover all FreeCalypso modem products (which will be firmware-compatible with the FCDEV3B if they use TI Rita transceiver, or will require a different fw build if we switch to one of Silabs Aero transceivers), but not the handset product. Right now this CONFIG_TARGET_FCMODEM preprocessor symbol is used to conditionalize everything dealing with MCSI. At the present moment the future of FC hardware evolution is still unknown: it is not known whether we will ever have any beyond-FCDEV3B hardware at all (contingent on uncertain funding), and if we do produce further FC hardware designs, it is not known whether they will retain the same FIC modem core (triband), if we are going to have a quadband design that still retains the classic Rita transceiver, or if we are going to switch to Silabs Aero II or some other transceiver. If we produce a quadband modem that still uses Rita, it will run exactly the same fw as the FCDEV3B thanks to the way we define TSPACT signals for the RF_FAM=12 && CONFIG_TARGET_FCFAM combination, and the current fcdev3b build target will be renamed to fcmodem. OTOH, if that putative quadband modem will be Aero-based, then it will require a different fw build target, the fcdev3b target will stay as it is, and the two targets will both define CONFIG_TARGET_FCFAM and CONFIG_TARGET_FCMODEM, but will have different RF_FAM numbers. But no matter which way we are going to evolve, it is not right to have conditionals on CONFIG_TARGET_FCDEV3B in places like ACI, and the present change clears the way for future evolution.
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 01 Apr 2019 01:05:24 +0000
parents b870b6a44d31
children
line wrap: on
line source

/************* Revision Controle System Header *************
 *                  GSM Layer 1 software
 * L1TM_CUST.C
 *
 *        Filename %M%
 *        Version  %I%
 *        Date     %G%
 * 
 ************* Revision Controle System Header *************/

#include "l1_confg.h"
#if TESTMODE

#include "tm_defs.h"
#include "l1_const.h"
#include "l1_types.h"

#include "l1tm_defty.h"
#include "l1tm_cust.h"

#if (AUDIO_TASK == 1)
  #include "l1audio_const.h"
  #include "l1audio_cust.h"
  #include "l1audio_defty.h"
#endif
  
#if (L1_GTT == 1)
  #include "l1gtt_const.h"
  #include "l1gtt_defty.h"
#endif
#include "l1_defty.h"
#include "l1_msgty.h"
#include "l1_tabs.h"

#include "l1tm_msgty.h"
#include "l1tm_varex.h"

#include "abb.h"

#if (RF==35)
  #include "tpudrv35.h"
  #include "l1_rf35.h"
#endif

#if (RF==12)
  #include "tpudrv12.h"
  #include "l1_rf12.h"
#endif

#if (RF==10)
  #include "tpudrv10.h"
  #include "l1_rf10.h"
#endif

#if (RF==8)
  #include "tpudrv8.h"
  #include "l1_rf8.h"
#endif

#if (RF==2)
  #include "l1_rf2.h"
#endif

#include <string.h>

// Import band configuration from Flash module (need to replace by an access function)
//extern UWORD8       std;
extern T_L1_CONFIG  l1_config;
extern T_RF rf;
extern T_RF_BAND rf_band[GSM_BANDS];
extern UWORD16 AGC_TABLE[AGC_TABLE_SIZE];
extern T_ADC adc;
extern T_ADCCAL adc_cal;
extern UWORD16 TM_ul_data[16]; //Uplink data to be stored into ABB Uplink buffer
extern T_STD_CONFIG std_config[];
static UWORD8 tm_band = 0;

// External function prototypes
void get_cal_from_nvmem (UWORD8 *ptr, UWORD16 len, UWORD8 id);
UWORD8 save_cal_in_nvmem (UWORD8 *ptr, UWORD16 len, UWORD8 id);
void Cust_init_std(void);
void l1_tpu_init_light(void);

enum {
  TM_RF_ID        = 0,
  TM_ADC_ID       = 1
};

typedef signed char effs_t;
// external FFS function prototypes
effs_t ffs_mkdir(const char *pathname);
void config_ffs_write(char type);

/***********************************************************************/
/*                           TESTMODE 4.X                              */
/***********************************************************************/


/*----------------------------------------------------------*/
/* Cust_tm_init()                                           */
/*----------------------------------------------------------*/
/* Parameters :                                             */
/* Return     :                                             */
/* Functionality : Init default configuration for TM params */
/*----------------------------------------------------------*/

void Cust_tm_init(void)
{
  UWORD32 i;

  l1_config.adc_enable                             = ADC_ENABLE; // ADC readings enabled
  l1_config.agc_enable                             = AGC_ENABLE; // AGC algo enabled
  l1_config.afc_enable                             = AFC_ENABLE; // AFC algo enabled
  l1_config.tmode.rf_params.bcch_arfcn             = TM_BCCH_ARFCN;
  l1_config.tmode.rf_params.tch_arfcn              = TM_TCH_ARFCN;
  l1_config.tmode.rf_params.mon_arfcn              = TM_MON_ARFCN;
  l1_config.tmode.rf_params.channel_type           = TM_CHAN_TYPE; // TCH_F
  l1_config.tmode.rf_params.subchannel             = TM_SUB_CHAN;
  l1_config.tmode.rf_params.reload_ramps_flag      = 0;
  l1_config.tmode.rf_params.tmode_continuous       = TM_NO_CONTINUOUS;
  l1_config.tmode.rx_params.slot_num               = TM_SLOT_NUM; // Time Slot
  l1_config.tmode.rx_params.agc                    = TM_AGC_VALUE; //This may be outside the range of the RF chip used
  l1_config.tmode.rx_params.pm_enable              = TM_PM_ENABLE;
  l1_config.tmode.rx_params.lna_off                = TM_LNA_OFF;
  l1_config.tmode.rx_params.number_of_measurements = TM_NUM_MEAS;  
  l1_config.tmode.rx_params.place_of_measurement   = TM_WIN_MEAS;
  l1_config.tmode.tx_params.txpwr                  = TM_TXPWR; // Min power level for GSM900
  l1_config.tmode.tx_params.txpwr_skip             = TM_TXPWR_SKIP;
  l1_config.tmode.tx_params.timing_advance         = TM_TA;
  l1_config.tmode.tx_params.burst_type             = TM_BURST_TYPE; // default is normal up-link burst
  l1_config.tmode.tx_params.burst_data             = TM_BURST_DATA; // default is all zeros
  l1_config.tmode.tx_params.tsc                    = TM_TSC; // Training Sequence ("BCC" on BSS)
  #if (CODE_VERSION != SIMULATION)
    l1_config.tmode.stats_config.num_loops         = TM_NUM_LOOPS; // 0 actually means infinite
  #else
    l1_config.tmode.stats_config.num_loops         = 4; // 0 actually means infinite
  #endif
  l1_config.tmode.stats_config.auto_result_loops   = TM_AUTO_RESULT_LOOPS; // 0 actually means infinite
  l1_config.tmode.stats_config.auto_reset_loops    = TM_AUTO_RESET_LOOPS; // 0 actually means infinite
  l1_config.tmode.stats_config.stat_type           = TM_STAT_TYPE;
  l1_config.tmode.stats_config.stat_bitmask        = TM_STAT_BITMASK;

  #if (CODE_VERSION != SIMULATION)
    // Initialize APCDEL1 register of Omega
    ABB_Write_Register_on_page(PAGE0, APCDEL1, (C_APCDEL1 - 0x0004) >> 6);
  #endif

  l1tm.tm_msg_received                       = FALSE;

  for (i=0;i<16;i++)
    TM_ul_data[i]=0;

  #if L1_GPRS
    l1_config.tmode.rf_params.pdtch_arfcn          = TM_PDTCH_ARFCN;
    l1_config.tmode.rf_params.multislot_class      = TM_MULTISLOT_CLASS;
    l1_config.tmode.stats_config.stat_gprs_slots   = TM_STAT_GPRS_SLOTS;
    l1_config.tmode.rx_params.timeslot_alloc       = TM_RX_ALLOCATION;
    l1_config.tmode.rx_params.coding_scheme        = TM_RX_CODING_SCHEME;
    l1_config.tmode.tx_params.timeslot_alloc       = TM_TX_ALLOCATION;
    l1_config.tmode.tx_params.coding_scheme        = TM_TX_CODING_SCHEME;
    for (i=0; i<8; i++)
      l1_config.tmode.tx_params.txpwr_gprs[i]      = TM_TXPWR_GPRS;

    for (i=0; i<27; i++)
      l1_config.tmode.tx_params.rlc_buffer[i] = 0;
  #endif
}


/**********************************************************************/
/* Test mode functions used for RF calibration                        */
/**********************************************************************/

void Cust_tm_rf_param_write(T_TM_RETURN *tm_return, WORD16 index, UWORD16 value)
{
  switch (index)
  {
    case STD_BAND_FLAG:
    {
      UWORD8 std_temp, band_temp;

      std_temp  = value & 0xff;  // tm_band = b7..0 of value
      band_temp = value >> 8;    // band    = b15..8 of value
          // get define 
      //if (sizeof(std_config)/sizeof(T_STD_CONFIG) <= std_temp)
      if (9 <= std_temp)  // std max
      {
        tm_return->status = E_BADINDEX;
        break;
      }
      else if ( GSM_BANDS <= band_temp)
      {
        tm_return->status = E_BADINDEX;
        break;
      }
      else if ( BAND_NONE == std_config[std_temp].band[band_temp])
      {
        tm_return->status = E_BADINDEX;
        break;
      } 
      else
      {
        l1_config.std.id = std_temp;  
        tm_band = band_temp; 
        // update RAM struct with either default or ffs
        Cust_init_std();
        l1_tpu_init_light();
        tm_return->status = E_OK;
        break;
      }
    }
    
    case INITIAL_AFC_DAC:
    {
      rf.afc.eeprom_afc = (WORD16) value << 3; // shift to put into F13.3 format
      l1_config.params.eeprom_afc = rf.afc.eeprom_afc;

      tm_return->status = E_OK;
      break;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      break;
    }
  } // end switch
}

void Cust_tm_rf_param_read(T_TM_RETURN *tm_return, WORD16 index)
{
  volatile UWORD16 value;

  switch (index)
  {
    case STD_BAND_FLAG:
    {
       value = ((tm_band << 8) | (l1_config.std.id) ); // return global std, tm_band (intel format)
      break;
    }
    case INITIAL_AFC_DAC:
    {
      value = rf.afc.eeprom_afc >> 3; // returned as F13.3
      break;
    }
    default:
    {
      tm_return->size = 0;
      tm_return->status = E_BADINDEX;
      return;
    }
  } // end switch

  memcpy(tm_return->result, (UWORD8 *)&value, 2);
  tm_return->size = 2;
  tm_return->status = E_OK;
}

void Cust_tm_rf_table_write(T_TM_RETURN *tm_return, WORD8 index, UWORD8 size, UWORD8 table[])
{
  UWORD8 band=0;

  tm_return->index = index;  // store index before it gets modified
  tm_return->size = 0;

  switch (index)
  {
    case RX_AGC_TABLE:
    {
      if (size != sizeof(AGC_TABLE))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&AGC_TABLE[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case AFC_PARAMS:
    {

      #if (VCXO_ALGO == 1)
        if (size != 24)  //  4 UWORD32 + 4 WORD16 values
      #else
        if (size != 16)  // 4 UWORD32 values
      #endif
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf.afc.psi_sta_inv, table, size);
      l1_config.params.psi_sta_inv  = rf.afc.psi_sta_inv;
      l1_config.params.psi_st       = rf.afc.psi_st;
      l1_config.params.psi_st_32    = rf.afc.psi_st_32;
      l1_config.params.psi_st_inv   = rf.afc.psi_st_inv;

    #if (CODE_VERSION == NOT_SIMULATION)
      #if (VCXO_ALGO == 1)
        l1_config.params.afc_dac_center = rf.afc.dac_center;
        l1_config.params.afc_dac_min    = rf.afc.dac_min;   
        l1_config.params.afc_dac_max    = rf.afc.dac_max;   
        l1_config.params.afc_snr_thr    = rf.afc.snr_thr;   
      #endif
    #endif

      tm_return->status = E_OK;
      break;
    }
    case RX_AGC_GLOBAL_PARAMS:
    {
      if (size != 8)  // 4 UWORD16 values
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf.rx.agc.low_agc_noise_thr, table, size);
      l1_config.params.low_agc_noise_thr  = rf.rx.agc.low_agc_noise_thr;
      l1_config.params.high_agc_sat_thr   = rf.rx.agc.high_agc_sat_thr;
      l1_config.params.low_agc            = rf.rx.agc.low_agc;
      l1_config.params.high_agc           = rf.rx.agc.high_agc;

      tm_return->status = E_OK;
      break;
    }
    case RX_IL_2_AGC_MAX:
    {
      if (size != sizeof(rf.rx.agc.il2agc_max))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf.rx.agc.il2agc_max[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case RX_IL_2_AGC_PWR:
    {
      if (size != sizeof(rf.rx.agc.il2agc_pwr))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf.rx.agc.il2agc_pwr[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case RX_IL_2_AGC_AV:
    {
      if (size != sizeof(rf.rx.agc.il2agc_av))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf.rx.agc.il2agc_av[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case TX_LEVELS:
    {
      if (size != sizeof(rf_band[tm_band].tx.levels))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf_band[tm_band].tx.levels[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case TX_CAL_CHAN: // generic for all bands
    {
      if (size != sizeof(rf_band[tm_band].tx.chan_cal_table))
      {
        tm_return->status = E_BADSIZE;
        break;
      }
      
      memcpy(&rf_band[tm_band].tx.chan_cal_table[0][0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case TX_CAL_TEMP: // generic for all bands
    {
      if (size != sizeof(rf_band[tm_band].tx.temp))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf_band[tm_band].tx.temp[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case RX_CAL_CHAN:  // generic for all bands
    {
      if (size != sizeof(rf_band[tm_band].rx.agc_bands))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf_band[tm_band].rx.agc_bands[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case RX_CAL_TEMP:  // generic for all bands
    {
      if (size != sizeof(rf_band[tm_band].rx.temp))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf_band[tm_band].rx.temp[0], table, size);
      tm_return->status = E_OK;
      break;
    }
    case RX_AGC_PARAMS:
    {
      if (size != sizeof(rf_band[tm_band].rx.rx_cal_params))
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&rf_band[tm_band].rx.rx_cal_params, table, size);
      if (tm_band == 0)
      {
          l1_config.std.g_magic_band1             = rf_band[tm_band].rx.rx_cal_params.g_magic;
          l1_config.std.lna_att_band1             = rf_band[tm_band].rx.rx_cal_params.lna_att;
          l1_config.std.lna_switch_thr_low_band1  = rf_band[tm_band].rx.rx_cal_params.lna_switch_thr_low;
          l1_config.std.lna_switch_thr_high_band1 = rf_band[tm_band].rx.rx_cal_params.lna_switch_thr_high;
        }
      else if (tm_band == 1)
        {
          l1_config.std.g_magic_band2             = rf_band[tm_band].rx.rx_cal_params.g_magic;
          l1_config.std.lna_att_band2             = rf_band[tm_band].rx.rx_cal_params.lna_att;
          l1_config.std.lna_switch_thr_low_band2  = rf_band[tm_band].rx.rx_cal_params.lna_switch_thr_low;
          l1_config.std.lna_switch_thr_high_band2 = rf_band[tm_band].rx.rx_cal_params.lna_switch_thr_high;
        }
        else
        {
          tm_return->status = E_INVAL;
          break;
        }

      tm_return->status = E_OK;
      break;
    }
    case TX_CAL_EXTREME:
    case RX_CAL_LEVEL:
    {
      tm_return->status = E_NOSUBSYS;
      break;
    }
    #if L1_GPRS
      case RLC_TX_BUFFER_CS1:
      case RLC_TX_BUFFER_CS2:
      case RLC_TX_BUFFER_CS3:
      case RLC_TX_BUFFER_CS4:
      {
        UWORD8 i, buffer_size;

        tm_return->index = index;  // store index before it gets modified
        tm_return->size  = 0;

        buffer_size = size/2 + size%2;  // bytes will be concatenated into UWORD16

        if (buffer_size > 27)  //max. number of data bytes
        {
          tm_return->status = E_BADSIZE;
          break;
        }

        // make sure that last byte is zero in case of odd number of bytes
        table[size] = 0; 
 
        // init the whole buffer before downloading new data
        for (i=0; i<27; i++)
          l1_config.tmode.tx_params.rlc_buffer[i] = 0;

        for (i=0; i<buffer_size; i++)
        {
          l1_config.tmode.tx_params.rlc_buffer[i] = (table[2*i+1] << 8) | table[2*i];
        }
        l1_config.tmode.tx_params.rlc_buffer_size = buffer_size;

        tm_return->status = E_OK;
        break;
      }
    #endif
    case TX_DATA_BUFFER:
    {
      UWORD8 i;

      tm_return->index = index;  // store index before it gets modified
      tm_return->size  = 0;

      if (size != 32)  // 16 UWORD16 (containing 10 data bits each)
      {
        tm_return->status = E_BADSIZE;
        break;
      }

      memcpy(&TM_ul_data, table, size);

      for (i=0; i<16; i++)
      {
        TM_ul_data[i] = TM_ul_data[i] << 6;
      }        	    

      tm_return->status = E_OK;
      break;
    }  
    default:
    {
      tm_return->status = E_BADINDEX;
      break;
    }
  } // end switch
}

void Cust_tm_rf_table_read(T_TM_RETURN *tm_return, WORD8 index)
{
  switch (index)
  {
    case RX_AGC_TABLE:
    {
      tm_return->size = sizeof(AGC_TABLE);
      memcpy(tm_return->result, &AGC_TABLE[0], tm_return->size);
      break;
    }
    case AFC_PARAMS:
    {
      #if (VCXO_ALGO == 1)
      tm_return->size = 24;  // 4 UWORD32's + 4 WORD16
      #else
      tm_return->size = 16;  // 4 UWORD32's
      #endif
      memcpy(tm_return->result, &rf.afc.psi_sta_inv, tm_return->size);
      break;
    }
    case RX_AGC_GLOBAL_PARAMS:
    {
      tm_return->size = 8;  // 4 UWORD16's
      memcpy(tm_return->result, &rf.rx.agc.low_agc_noise_thr, tm_return->size);

      break;
    }
    case RX_IL_2_AGC_MAX:
    {
      tm_return->size = sizeof(rf.rx.agc.il2agc_max);
      memcpy(tm_return->result, &rf.rx.agc.il2agc_max[0], tm_return->size);
      break;
    }
    case RX_IL_2_AGC_PWR:
    {
      tm_return->size = sizeof(rf.rx.agc.il2agc_pwr);
      memcpy(tm_return->result, &rf.rx.agc.il2agc_pwr[0], tm_return->size);
      break;
    }
    case RX_IL_2_AGC_AV:
    {
      tm_return->size = sizeof(rf.rx.agc.il2agc_av);
      memcpy(tm_return->result, &rf.rx.agc.il2agc_av[0], tm_return->size);
      break;
    }
    case TX_LEVELS:
    {
      tm_return->size = sizeof(rf_band[tm_band].tx.levels);
      memcpy(tm_return->result, &rf_band[tm_band].tx.levels[0], tm_return->size);
      break;
    }
    case TX_CAL_CHAN: // generic for all bands
    {
      tm_return->size = sizeof(rf_band[tm_band].tx.chan_cal_table);
      memcpy(tm_return->result, &rf_band[tm_band].tx.chan_cal_table[0][0], tm_return->size);
      break;
    }
    case TX_CAL_TEMP:  // generic for all bands
    {
      tm_return->size = sizeof(rf_band[tm_band].tx.temp);
      memcpy(tm_return->result, &rf_band[tm_band].tx.temp[0], tm_return->size);
      break;
    }
    case RX_CAL_CHAN:  // generic for all bands
    {
      tm_return->size = sizeof(rf_band[tm_band].rx.agc_bands);
      memcpy(tm_return->result, &rf_band[tm_band].rx.agc_bands[0], tm_return->size);
      break;
    }
    case RX_CAL_TEMP:  // generic for all bands
    {
      tm_return->size = sizeof(rf_band[tm_band].rx.temp);
      memcpy(tm_return->result, &rf_band[tm_band].rx.temp[0], tm_return->size);
      break;
    }
    case RX_AGC_PARAMS:
    {
      // WARNING: sizeof(rf.rx.rx_cal_params[band]) returns 12 because of alignment
      tm_return->size = 10;  // five UWORD16's
      memcpy(tm_return->result, &rf_band[tm_band].rx.rx_cal_params, tm_return->size);
      break;
    }
    case TX_CAL_EXTREME:
    case RX_CAL_LEVEL:
    {
      tm_return->size = 0;
      tm_return->status = E_NOSUBSYS;
      return;
    }

    #if L1_GPRS
      case RLC_TX_BUFFER_CS1:
      case RLC_TX_BUFFER_CS2:
      case RLC_TX_BUFFER_CS3:
      case RLC_TX_BUFFER_CS4:
      {
        tm_return->size = l1_config.tmode.tx_params.rlc_buffer_size * 2; // UWORD16's
        memcpy(tm_return->result, &l1_config.tmode.tx_params.rlc_buffer[0], tm_return->size);
        break;
      }
    #endif

    case TX_DATA_BUFFER:
    {
      UWORD8 i;
      for (i=0; i<16; i++)
      {
        tm_return->result[2*i]=(TM_ul_data[i] >> 6) & 0x00FF;
        tm_return->result[2*i+1]=(TM_ul_data[i] >> 14) & 0x0003;
      }

      tm_return->size = 32; //16*UWORD16
      break;
    }

    #if (RF==35)
    case RX_PLL_TUNING_TABLE:
    {
      tm_return->size = sizeof(pll_tuning); //6*UWORD16
      memcpy(tm_return->result, &pll_tuning, tm_return->size);
      pll_tuning.enable    = 0;
      break;
    }
    #endif

    default:
    {
      tm_return->size = 0;
      tm_return->status = E_BADINDEX;
      return;
    }
  }  // end switch

  tm_return->index  = index;
  tm_return->status = E_OK;
}

void Cust_tm_rx_param_write(T_TM_RETURN *tm_return, WORD16 index, UWORD16 value)
{
  switch (index)
  {
    case RX_FRONT_DELAY:
    {
      //delay for dual band not implemented yet
      rf.tx.prg_tx = value;
      l1_config.params.prg_tx_gsm = rf.tx.prg_tx;
      l1_config.params.prg_tx_dcs = rf.tx.prg_tx;

      tm_return->status = E_OK;
      break;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      break;
    }
  } // end switch
}

void Cust_tm_rx_param_read(T_TM_RETURN *tm_return, WORD16 index)
{
  volatile UWORD16 value;

  switch (index)
  {
    case RX_FRONT_DELAY:
    {
      value = rf.tx.prg_tx;
      break;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      tm_return->size = 0;
      return;
    }
  } // end switch

  memcpy(tm_return->result, (UWORD8 *)&value, 2);
  tm_return->size = 2;
  tm_return->status = E_OK;
}

void Cust_tm_tx_param_write(T_TM_RETURN *tm_return, WORD16 index, UWORD16 value, UWORD8 band)
{
  switch (index)
  {
    case TX_APC_DAC:
    {
      // generic for all bands
      rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].apc = value;

      tm_return->status = E_OK;
      break;
    }
    case TX_RAMP_TEMPLATE:
    {
      if (value >= sizeof(rf_band[tm_band].tx.ramp_tables)/sizeof(rf_band[tm_band].tx.ramp_tables[0]))  // [0..15]
      {
        tm_return->status = E_INVAL;
        break;
      }

      // generic for all bands
        rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].ramp_index = value;

      tm_return->status = E_OK;
      l1_config.tmode.rf_params.reload_ramps_flag = 1;
      break;
    }
    case TX_CHAN_CAL_TABLE:
    {
      if (value >= sizeof(rf_band[tm_band].tx.chan_cal_table)/sizeof(rf_band[tm_band].tx.chan_cal_table[0]))
      {
        tm_return->status = E_INVAL;
        break;
      }

      // generic for all bands
      rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].chan_cal_index = value;

        tm_return->status = E_OK;
      
      break;
      }
    case TX_BURST_TYPE:
      {
      if (value > 1)  // [0..1]
      {
        tm_return->status = E_INVAL;
        break;
      }
      l1_config.tmode.tx_params.burst_type = value;
      tm_return->status = E_OK;
      break;
    }
    case TX_BURST_DATA:
    {
      // range is [0..10], currently we support [0..13] at the moment
      if (value > 13)
      {
        tm_return->status = E_INVAL;
        break;
      }
      l1_config.tmode.tx_params.burst_data = value;
      tm_return->status = E_OK;
      break;
    }
    case TX_TRAINING_SEQ:
    {
      if (value > 7)  // [0..7]
      {
        tm_return->status = E_INVAL;
        break;
      }
      l1_config.tmode.tx_params.tsc = value;
      tm_return->status = E_OK;
      break;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      break;
    }
  } // end switch
}

void Cust_tm_tx_param_read(T_TM_RETURN *tm_return, WORD16 index, UWORD8 band)
{
  volatile UWORD16 value;

  switch (index)
  {
    case TX_PWR_LEVEL:
    {
      value = l1_config.tmode.tx_params.txpwr;
      break;
    }
    case TX_APC_DAC:
    {
      value = rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].apc;
      break;
    }
    case TX_RAMP_TEMPLATE:
    {
      value = rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].ramp_index;
      break;
    }
    case TX_CHAN_CAL_TABLE:
    {
      value = rf_band[tm_band].tx.levels[l1_config.tmode.tx_params.txpwr].chan_cal_index;
      break;
    }
    case TX_BURST_TYPE:
    {
      value = l1_config.tmode.tx_params.burst_type;
      break;
    }
    case TX_BURST_DATA:
    {
      value = l1_config.tmode.tx_params.burst_data;
      break;
    }
    case TX_TIMING_ADVANCE:
    {
      value = l1_config.tmode.tx_params.timing_advance;
      break;
    }
    case TX_TRAINING_SEQ:
    {
      value = l1_config.tmode.tx_params.tsc;
      break;
    }
    case TX_PWR_SKIP:
    {
     value = l1_config.tmode.tx_params.txpwr_skip;
     break;
    }
    #if L1_GPRS
      case TX_GPRS_POWER0:
      case TX_GPRS_POWER1:
      case TX_GPRS_POWER2:
      case TX_GPRS_POWER3:
      case TX_GPRS_POWER4:
      case TX_GPRS_POWER5:
      case TX_GPRS_POWER6:
      case TX_GPRS_POWER7:
      {
        value = l1_config.tmode.tx_params.txpwr_gprs[index - TX_GPRS_POWER0];
        break;
      }
      case TX_GPRS_SLOTS:
      {
        value = l1_config.tmode.tx_params.timeslot_alloc;
        break;
      }
      case TX_GPRS_CODING:
      {
        value = l1_config.tmode.tx_params.coding_scheme;
        break;
      }
    #endif
    default:
    {
      tm_return->status = E_BADINDEX;
      tm_return->size = 0;
      return;
    }
  } // end switch

  memcpy(tm_return->result, (UWORD8 *)&value, 2);
  tm_return->size = 2;
  tm_return->status = E_OK;
}

void Cust_tm_tx_template_write(T_TM_RETURN *tm_return, WORD8 index, UWORD8 size, UWORD8 table[])
{
  if (index >= sizeof(rf_band[tm_band].tx.ramp_tables)/sizeof(T_TX_RAMP))
  {
    tm_return->status = E_BADINDEX;
  }
  else if (size != sizeof(T_TX_RAMP))
  {
    // We are writing both the up and down ramps; size must be exact.
    tm_return->status = E_BADSIZE;
  }
  else
  {
    memcpy(rf_band[tm_band].tx.ramp_tables[index].ramp_up, &table[0], size/2);
    memcpy(rf_band[tm_band].tx.ramp_tables[index].ramp_down, &table[size/2], size/2);
    tm_return->status = E_OK;
    l1_config.tmode.rf_params.reload_ramps_flag = 1;
  }

  tm_return->index = index;
  tm_return->size = 0;
}

void Cust_tm_tx_template_read(T_TM_RETURN *tm_return, WORD8 index)
{
  tm_return->index = index;

  if (index >= sizeof(rf_band[tm_band].tx.ramp_tables)/sizeof(T_TX_RAMP))
  {
    tm_return->status = E_BADINDEX;
    tm_return->size = 0;
    return;
  }
   
  memcpy(&tm_return->result[0], rf_band[tm_band].tx.ramp_tables[index].ramp_up, sizeof(rf_band[tm_band].tx.ramp_tables[index].ramp_up));
  memcpy(&tm_return->result[sizeof(rf_band[tm_band].tx.ramp_tables[index].ramp_up)], rf_band[tm_band].tx.ramp_tables[index].ramp_down, sizeof(rf_band[tm_band].tx.ramp_tables[index].ramp_down));
  tm_return->size = sizeof(rf_band[tm_band].tx.ramp_tables[index]);
  tm_return->status = E_OK;
}

void Cust_tm_misc_param_write(T_TM_RETURN *tm_return, WORD16 index, UWORD16 value)
{
  switch (index)
  {
    case GPIOSTATE0:
    case GPIODIR0:
    case GPIOSTATE1:
    case GPIODIR1:
    case GPIOSTATE0P:
    case GPIODIR0P:
    case GPIOSTATE1P:
    case GPIODIR1P:
    {
      tm_return->status = E_NOSUBSYS;
      break;
    }
    case CONVERTED_ADC0:
    case CONVERTED_ADC1:
    case CONVERTED_ADC2:
    case CONVERTED_ADC3:
    case CONVERTED_ADC4:
    case CONVERTED_ADC5:
    case CONVERTED_ADC6:
    case CONVERTED_ADC7:
    case CONVERTED_ADC8:
    {
      adc.converted[index - CONVERTED_ADC0] = value;
      tm_return->status = E_OK;
      break;
    }
    
    case RAW_ADC0:
    case RAW_ADC1:
    case RAW_ADC2:
    case RAW_ADC3:
    case RAW_ADC4:
    case RAW_ADC5:
    case RAW_ADC6:
    case RAW_ADC7:
    case RAW_ADC8:
    {
      adc.raw[index - RAW_ADC0]  = value;
      tm_return->status = E_OK;
      break;
    }
    
    case ADC0_COEFF_A:
    case ADC1_COEFF_A:
    case ADC2_COEFF_A:
    case ADC3_COEFF_A:
    case ADC4_COEFF_A:
    case ADC5_COEFF_A:
    case ADC6_COEFF_A:
    case ADC7_COEFF_A:
    case ADC8_COEFF_A:
    {
      adc_cal.a[index - ADC0_COEFF_A]  = value;
      tm_return->status = E_OK;
      break;
    }
    
    case ADC0_COEFF_B:
    case ADC1_COEFF_B:
    case ADC2_COEFF_B:
    case ADC3_COEFF_B:
    case ADC4_COEFF_B:
    case ADC5_COEFF_B:
    case ADC6_COEFF_B:
    case ADC7_COEFF_B:
    case ADC8_COEFF_B:
    {
      adc_cal.b[index - ADC0_COEFF_B]  = value;
      tm_return->status = E_OK;
      break;
    }
    case SLEEP_MODE:
    {
      tm_return->status = E_NOSUBSYS;
      break;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      break;
    }
  } // end switch
}

void Cust_tm_misc_param_read(T_TM_RETURN *tm_return, WORD16 index)
{
  volatile UWORD16 value;

  switch (index)
  {
    case GPIOSTATE0:
    case GPIODIR0:
    case GPIOSTATE1:
    case GPIODIR1:
    case GPIOSTATE0P:
    case GPIODIR0P:
    case GPIOSTATE1P:
    case GPIODIR1P:
    {
      tm_return->status = E_NOSUBSYS;
      tm_return->size = 0;
      return;
    }
    case CONVERTED_ADC0:
    case CONVERTED_ADC1:
    case CONVERTED_ADC2:
    case CONVERTED_ADC3:
    case CONVERTED_ADC4:
    case CONVERTED_ADC5:
    case CONVERTED_ADC6:
    case CONVERTED_ADC7:
    case CONVERTED_ADC8:
    {
      value = adc.converted[index - CONVERTED_ADC0];
      break;
    }
    case RAW_ADC0:
    case RAW_ADC1:
    case RAW_ADC2:
    case RAW_ADC3:
    case RAW_ADC4:
    case RAW_ADC5:
    case RAW_ADC6:
    case RAW_ADC7:
    case RAW_ADC8:
    {
      value = adc.raw[index - RAW_ADC0];
      break;
    }
    case ADC0_COEFF_A:
    case ADC1_COEFF_A:
    case ADC2_COEFF_A:
    case ADC3_COEFF_A:
    case ADC4_COEFF_A:
    case ADC5_COEFF_A:
    case ADC6_COEFF_A:
    case ADC7_COEFF_A:
    case ADC8_COEFF_A:
    {
      value = adc_cal.a[index - ADC0_COEFF_A];
      break;
    }
    case ADC0_COEFF_B:
    case ADC1_COEFF_B:
    case ADC2_COEFF_B:
    case ADC3_COEFF_B:
    case ADC4_COEFF_B:
    case ADC5_COEFF_B:
    case ADC6_COEFF_B:
    case ADC7_COEFF_B:
    case ADC8_COEFF_B:
    {
      value = adc_cal.b[index - ADC0_COEFF_B];
      break;
    }
    case SLEEP_MODE:
    {
      tm_return->status = E_NOSUBSYS;
      tm_return->size = 0;
      return;
    }
    default:
    {
      tm_return->status = E_BADINDEX;
      tm_return->size = 0;
      return;
    }
  } // end switch

  memcpy(tm_return->result, (UWORD8 *)&value, 2);
  tm_return->size = 2;
  tm_return->status = E_OK;
}

void Cust_tm_misc_enable(T_TM_RETURN *tm_return, WORD16 action)
{
    UWORD8 status;
  
    // FIXME: This enum really should go into testmode header file.
    enum ME_CFG_WRITE_E {
        CFG_WRITE_MKDIRS   = 100,
        CFG_WRITE_RF_CAL   = 102,
        CFG_WRITE_RF_CFG   = 103,
        CFG_WRITE_TX_CAL   = 104,
        CFG_WRITE_TX_CFG   = 105,
        CFG_WRITE_RX_CAL   = 106,
        CFG_WRITE_RX_CFG   = 107,
        CFG_WRITE_SYS_CAL  = 108,
        CFG_WRITE_SYS_CFG  = 109
  };

  tm_return->size = 0;
  tm_return->index = action;
  tm_return->status = E_OK;

    // FIXME: This code should actually be in misc_enable()
  switch(action)
  {
    case CFG_WRITE_MKDIRS:
        ffs_mkdir("/gsm");
        ffs_mkdir("/pcm");
        ffs_mkdir("/sys");
        ffs_mkdir("/mmi");
        ffs_mkdir("/vos");
        ffs_mkdir("/var");
        ffs_mkdir("/gsm/rf");
        ffs_mkdir("/gsm/com");
        ffs_mkdir("/vos/vm");
        ffs_mkdir("/vos/vrm");
        ffs_mkdir("/vos/vrp");
        ffs_mkdir("/var/log");
        ffs_mkdir("/var/tst");
        ffs_mkdir("/gsm/rf/tx");
        ffs_mkdir("/gsm/rf/rx");
      break;
    case CFG_WRITE_RF_CAL:   config_ffs_write('f'); break;
    case CFG_WRITE_RF_CFG:   config_ffs_write('F'); break;
    case CFG_WRITE_TX_CAL:   config_ffs_write('t'); break;
    case CFG_WRITE_TX_CFG:   config_ffs_write('T'); break;
    case CFG_WRITE_RX_CAL:   config_ffs_write('r'); break;
    case CFG_WRITE_RX_CFG:   config_ffs_write('R'); break;
    case CFG_WRITE_SYS_CAL:  config_ffs_write('s'); break;
    case CFG_WRITE_SYS_CFG:  config_ffs_write('S'); break;
    default:
      tm_return->status = E_BADINDEX;
    }
}

void Cust_tm_special_param_write(T_TM_RETURN *tm_return, WORD16 index, UWORD16 value)
{
  tm_return->size = 0;
  tm_return->index = index;
  tm_return->status = E_NOSYS;
}

void Cust_tm_special_param_read(T_TM_RETURN *tm_return, WORD16 index)
{
  tm_return->size = 0;
  tm_return->index = index;
  tm_return->status = E_NOSYS;
}

void Cust_tm_special_table_write(T_TM_RETURN *tm_return, WORD8 index, UWORD8 size, UWORD8 table[])
{
  tm_return->size = 0;
  tm_return->index = index;
  tm_return->status = E_NOSYS;
}

void Cust_tm_special_table_read(T_TM_RETURN *tm_return, WORD8 index)
{
  tm_return->size = 0;
  tm_return->index = index;
  tm_return->status = E_NOSYS;
}

void Cust_tm_special_enable(T_TM_RETURN *tm_return, WORD16 action)
{
  tm_return->size = 0;
  tm_return->index = action;
  tm_return->status = E_NOSYS;
}

#endif // TESTMODE