/**************************************************************************************************
  Filename:       mac_radio.c
  Revised:        $Date: 2007-09-11 10:58:41 -0700 (Tue, 11 Sep 2007) $
  Revision:       $Revision: 15371 $

  Description:    Describe the purpose and contents of the file.


  Copyright 2006-2009 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product.  Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/

/* ------------------------------------------------------------------------------------------------
 *                                          Includes
 * ------------------------------------------------------------------------------------------------
 */

/* hal */
#include "hal_types.h"

/* high-level */
#include "mac_pib.h"

/* exported low-level */
#include "mac_low_level.h"

/* low-level specific */
#include "mac_radio.h"
#include "mac_tx.h"
#include "mac_rx.h"
#include "mac_rx_onoff.h"
#include "mac_sleep.h"
#include "mac_backoff_timer.h"

/* target specific */
#include "mac_radio_defs.h"

/* debug */
#include "mac_assert.h"


/* ------------------------------------------------------------------------------------------------
 *                                          Includes
 * ------------------------------------------------------------------------------------------------
 */
#define ED_RF_POWER_MIN_DBM   (MAC_RADIO_RECEIVER_SENSITIVITY_DBM + MAC_SPEC_ED_MIN_DBM_ABOVE_RECEIVER_SENSITIVITY)
#define ED_RF_POWER_MAX_DBM   MAC_RADIO_RECEIVER_SATURATION_DBM


/* ------------------------------------------------------------------------------------------------
 *                                        Global Variables
 * ------------------------------------------------------------------------------------------------
 */
uint8 macPhyTxPower;
uint8 macPhyChannel;


/* ------------------------------------------------------------------------------------------------
 *                                        Local Variables
 * ------------------------------------------------------------------------------------------------
 */
static uint8 reqChannel;
static uint8 reqTxPower;


/* ------------------------------------------------------------------------------------------------
 *                                        Local Functions
 * ------------------------------------------------------------------------------------------------
 */
static uint8 radioComputeED(int8 rssiDbm);


/**************************************************************************************************
 * @fn          macRadioInit
 *
 * @brief       Initialize radio software.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioInit(void)
{
  /* variable initialization for this module */
  reqChannel    = MAC_RADIO_CHANNEL_DEFAULT;
  macPhyChannel = MAC_RADIO_CHANNEL_DEFAULT;
  reqTxPower    = MAC_RADIO_TX_POWER_DEFAULT;
  macPhyTxPower = MAC_RADIO_TX_POWER_DEFAULT;
}


/**************************************************************************************************
 * @fn          macRadioReset
 *
 * @brief       Resets the radio module.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioReset(void)
{
  macRadioStopScan();
  macRadioEnergyDetectStop();
}


/**************************************************************************************************
 * @fn          macRadioRandomByte
 *
 * @brief       Return a random byte derived from previously set random seed.
 *
 * @param       none
 *
 * @return      a random byte
 **************************************************************************************************
 */
uint8 macRadioRandomByte(void)
{
  return(MAC_RADIO_RANDOM_BYTE());
}


/**************************************************************************************************
 * @fn          macRadioSetPanCoordinator
 *
 * @brief       Configure the pan coordinator status of the radio
 *
 * @param       panCoordFlag - non-zero to configure radio to be pan coordinator
 *                             zero to configure radio as NON pan coordinator
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioSetPanCoordinator(uint8 panCoordFlag)
{
  /* abstracted radio configuration */
  MAC_RADIO_SET_PAN_COORDINATOR(panCoordFlag);
}


/**************************************************************************************************
 * @fn          macRadioSetPanID
 *
 * @brief       Set the pan ID on the radio.
 *
 * @param       panID - 16 bit PAN identifier
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioSetPanID(uint16 panID)
{
  /* abstracted radio configuration */
  MAC_RADIO_SET_PAN_ID(panID);
}


/**************************************************************************************************
 * @fn          macRadioSetShortAddr
 *
 * @brief       Set the short addrss on the radio.
 *
 * @param       shortAddr - 16 bit short address
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioSetShortAddr(uint16 shortAddr)
{
  /* abstracted radio configuration */
  MAC_RADIO_SET_SHORT_ADDR(shortAddr);
}


/**************************************************************************************************
 * @fn          macRadioSetIEEEAddr
 *
 * @brief       Set the IEEE address on the radio.
 *
 * @param       pIEEEAddr - pointer to array holding 64 bit IEEE address; array must be little
 *                          endian format (starts with lowest signficant byte)
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioSetIEEEAddr(uint8 * pIEEEAddr)
{
  /* abstracted radio configuration */
  MAC_RADIO_SET_IEEE_ADDR(pIEEEAddr);
}


/**************************************************************************************************
 * @fn          macRadioSetTxPower
 *
 * @brief       Set transmitter power of the radio.
 *
 * @param       txPower - the minus dBm for power but as a postive integer (or if configured
 *                        for it, txPower is the raw register value). If PA/LNA is installed
 *                        then txPower becomes positive dBm.
 *
 * @return      none
 **************************************************************************************************
 */
#ifndef HAL_MAC_USE_REGISTER_POWER_VALUES
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void macRadioSetTxPower(uint8 txPower)
{
  halIntState_t  s;

  /* if the selected dBm is out of range, use the closest available */
  if (txPower > MAC_RADIO_TX_POWER_MAX_DBM)
  {
    txPower = MAC_RADIO_TX_POWER_MAX_DBM;
  }

  /*
   *  Set the global variable reqTxPower.  This variable is referenced
   *  by the function macRadioUpdateTxPower() to write the radio register.
   *
   *  A lookup table is used to translate the power level to the register
   *  value.
   */
  HAL_ENTER_CRITICAL_SECTION(s);
  reqTxPower = macRadioDefsTxPowerTable[txPower];
  HAL_EXIT_CRITICAL_SECTION(s);

  /* update the radio power setting */
  macRadioUpdateTxPower();
}

#else
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void macRadioSetTxPower(uint8 txPower)
{
  halIntState_t  s;

  /* same as above but with no lookup table, use raw register value */
  HAL_ENTER_CRITICAL_SECTION(s);
  reqTxPower = txPower;
  HAL_EXIT_CRITICAL_SECTION(s);

  /* update the radio power setting */
  macRadioUpdateTxPower();
}

#endif


/**************************************************************************************************
 * @fn          macRadioUpdateTxPower
 *
 * @brief       Update the radio's transmit power if a new power level has been requested
 *
 * @param       reqTxPower - file scope variable that holds the last request power level
 *              macPhyTxPower - global variable that holds radio's set power level
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioUpdateTxPower(void)
{
  halIntState_t  s;

  /*
   *  If the requested power setting is different from the actual radio setting,
   *  attempt to udpate to the new power setting.
   */
  HAL_ENTER_CRITICAL_SECTION(s);
  if (reqTxPower != macPhyTxPower)
  {
    /*
     *  Radio power cannot be updated when the radio is physically transmitting.
     *  If there is a possibility radio is transmitting, do not change the power
     *  setting.  This function will be called again after the current transmit
     *  completes.
     */
    if (!macRxOutgoingAckFlag && !MAC_TX_IS_PHYSICALLY_ACTIVE())
    {
      /*
       *  Set new power level;  update the shadow value and write
       *  the new value to the radio hardware.
       */
      macPhyTxPower = reqTxPower;
      MAC_RADIO_SET_TX_POWER(macPhyTxPower);
    }
  }
  HAL_EXIT_CRITICAL_SECTION(s);
}


/**************************************************************************************************
 * @fn          macRadioSetChannel
 *
 * @brief       Set radio channel.
 *
 * @param       channel - channel number, valid range is 11 through 26
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioSetChannel(uint8 channel)
{
  halIntState_t  s;

  MAC_ASSERT((channel >= 11) && (channel <= 28));  /* illegal channel */

  /* critical section to make sure transmit does not start while updating channel */
  HAL_ENTER_CRITICAL_SECTION(s);

  /* set requested channel */
  reqChannel = channel;

  /*
   *  If transmit is not active, update the radio hardware immediately.  If transmit is active,
   *  the channel will be updated at the end of the current transmit.
   */
  if (!macTxActive)
  {
    macRadioUpdateChannel();
  }

  HAL_EXIT_CRITICAL_SECTION(s);
}


/**************************************************************************************************
 * @fn          macRadioUpdateChannel
 *
 * @brief       Update the radio channel if a new channel has been requested.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioUpdateChannel(void)
{
  halIntState_t  s;

  MAC_ASSERT(!macTxActive); /* cannot change channel during a transmit */

  /* if the channel has changed, set the radio to the new channel */
  HAL_ENTER_CRITICAL_SECTION(s);
  if (reqChannel != macPhyChannel)
  {
    macPhyChannel = reqChannel;
    HAL_EXIT_CRITICAL_SECTION(s);

    /* changing the channel stops any receive in progress */
    macRxOff();
    MAC_RADIO_SET_CHANNEL(macPhyChannel);

    /* If the channel is updated in the middle of receiving a frame, we must
     * clean up the Rx logic.
     */
    macRxHaltCleanup();

    macRxOnRequest();
  }
  else
  {
    HAL_EXIT_CRITICAL_SECTION(s);
  }
}


/**************************************************************************************************
 * @fn          macRadioStartScan
 *
 * @brief       Puts radio into selected scan mode.
 *
 * @param       scanMode - scan mode, see #defines in .h file
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioStartScan(uint8 scanMode)
{
  MAC_ASSERT(macSleepState == MAC_SLEEP_STATE_AWAKE); /* radio must be awake */
  MAC_ASSERT(macRxFilter == RX_FILTER_OFF); /* all filtering must be off to start scan */

  /* set the receive filter based on the selected scan mode */
  if (scanMode == MAC_SCAN_ED)
  {
    macRxFilter = RX_FILTER_ALL;
  }
  else if (scanMode == MAC_SCAN_ORPHAN)
  {
    macRxFilter = RX_FILTER_NON_COMMAND_FRAMES;
  }
  else
  {
    MAC_ASSERT((scanMode == MAC_SCAN_ACTIVE) || (scanMode == MAC_SCAN_PASSIVE)); /* invalid scan type */
    macRxFilter = RX_FILTER_NON_BEACON_FRAMES;

    /* for active and passive scans, per spec the pan ID must be 0xFFFF */
    MAC_RADIO_SET_PAN_ID(0xFFFF);
  }
}


/**************************************************************************************************
 * @fn          macRadioStopScan
 *
 * @brief       Takes radio out of scan mode.  Note can be called if
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioStopScan(void)
{
  macRxFilter = RX_FILTER_OFF;

  /* restore the pan ID (passive and active scans set pan ID to 0xFFFF) */
  MAC_RADIO_SET_PAN_ID(macPib.panId);
}


/**************************************************************************************************
 * @fn          macRadioEnergyDetectStart
 *
 * @brief       Initiates energy detect.  The highest energy detected is recorded from the time
 *              when this function is called until the energy detect is stopped.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macRadioEnergyDetectStart(void)
{
  MAC_RADIO_RECORD_MAX_RSSI_START();
}


/**************************************************************************************************
 * @fn          macRadioEnergyDetectStop
 *
 * @brief       Called at completion of an energy detect.  Note: can be called even if energy
 *              detect is already stopped (needed by reset).
 *
 * @param       none
 *
 * @return      highest energy detect measurement
 **************************************************************************************************
 */
uint8 macRadioEnergyDetectStop(void)
{
  uint8 rssiDbm;
  uint8 energyDetectMeasurement;

  rssiDbm = MAC_RADIO_RECORD_MAX_RSSI_STOP() + MAC_RADIO_RSSI_OFFSET;
#if defined (HAL_PA_LNA) || defined (HAL_PA_LNA_CC2590)
  MAC_RADIO_RSSI_LNA_OFFSET(rssiDbm);
#endif
  energyDetectMeasurement = radioComputeED(rssiDbm);

  return(energyDetectMeasurement);
}

/*=================================================================================================
 * @fn          radioComputeED
 *
 * @brief       Compute energy detect measurement.
 *
 * @param       rssi - raw RSSI value from radio hardware
 *
 * @return      energy detect measurement in the range of 0x00-0xFF
 *=================================================================================================
 */
static uint8 radioComputeED(int8 rssiDbm)
{
  uint8 ed;

  /*
   *  Keep RF power between minimum and maximum values.
   *  This min/max range is derived from datasheet and specification.
   */
  if (rssiDbm < ED_RF_POWER_MIN_DBM)
  {
    rssiDbm = ED_RF_POWER_MIN_DBM;
  }
  else if (rssiDbm > ED_RF_POWER_MAX_DBM)
  {
    rssiDbm = ED_RF_POWER_MAX_DBM;
  }

  /*
   *  Create energy detect measurement by normalizing and scaling RF power level.
   *
   *  Note : The division operation below is designed for maximum accuracy and
   *         best granularity.  This is done by grouping the math operations to
   *         compute the entire numerator before doing any division.
   */
  ed = (MAC_SPEC_ED_MAX * (rssiDbm - ED_RF_POWER_MIN_DBM)) / (ED_RF_POWER_MAX_DBM - ED_RF_POWER_MIN_DBM);

  return(ed);
}


/**************************************************************************************************
 * @fn          macRadioComputeLQI
 *
 * @brief       Compute link quality indication.
 *
 * @param       rssi - raw RSSI value from radio hardware
 *              corr - correlation value from radio hardware
 *
 * @return      link quality indicator value
 **************************************************************************************************
 */
uint8 macRadioComputeLQI(int8 rssiDbm, uint8 corr)
{
  (void) corr; /* suppress compiler warning of unused parameter */

  /*
   *  Note : Currently the LQI value is simply the energy detect measurement.
   *         A more accurate value could be derived by using the correlation
   *         value along with the RSSI value.
   */
  return(radioComputeED(rssiDbm));
}


/**************************************************************************************************
*/
