/*
 * Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com
 * SPDX-License-Identifier: Apache-2.0
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "NanostackRfPhyEfr32.h"

#include <string.h>

#include "mbed.h"
#include "ns_types.h"
#include "platform/arm_hal_interrupt.h"
#include "nanostack/platform/arm_hal_phy.h"
#include "mbed_toolchain.h"

#include "mbed-trace/mbed_trace.h"
#define  TRACE_GROUP  "SLRF"

/* Enable debug printing with SL_RADIO_DEBUG, override debug printer with SL_DEBUG_PRINT. */
#ifdef SL_RADIO_DEBUG
#ifndef SL_DEBUG_PRINT
#define SL_DEBUG_PRINT(...) tr_debug(__VA_ARGS__)
#endif
#else
#define SL_DEBUG_PRINT(...) 
#endif

/* RF_THREAD_STACK_SIZE defines tack size for the RF adaptor thread */
#ifndef RF_THREAD_STACK_SIZE
#define RF_THREAD_STACK_SIZE 1024
#endif

/* RF_QUEUE_SIZE defines queue size for incoming messages */
#ifndef RF_QUEUE_SIZE
#define RF_QUEUE_SIZE  8
#endif


/* RFThreadSignal used to signal from interrupts to the adaptor thread */
enum RFThreadSignal {
    SL_RX_DONE          = (1 << 1),
    SL_TX_DONE          = (1 << 2),
    SL_TX_ERR           = (1 << 3),
    SL_TX_TIMEOUT       = (1 << 4),
    SL_ACK_RECV         = (1 << 5),    
    SL_ACK_TIMEOUT      = (1 << 6),
    SL_TXFIFO_ERR       = (1 << 7),
    SL_RXFIFO_ERR       = (1 << 8),
    SL_CAL_REQ          = (1 << 9),
    SL_RSSI_DONE        = (1 << 10),
    SL_QUEUE_FULL       = (1 << 11),

    // ACK pend flag can be signalled in addition to RX_DONE
    SL_ACK_PEND         = (1 << 31),
};

/*  Adaptor thread definitions */
static void rf_thread_loop(const void *arg);
static osThreadDef(rf_thread_loop, osPriorityRealtime, RF_THREAD_STACK_SIZE);
static osThreadId rf_thread_id;

/* Queue for passing messages from interrupt to adaptor thread */
static volatile void* rx_queue[8];
static volatile size_t rx_queue_head;
static volatile size_t rx_queue_tail;

/* Silicon Labs headers */
extern "C" {
    #include "rail/rail.h"
    #include "rail/pa.h"
    #include "rail/pti.h"
    #include "rail/ieee802154/rail_ieee802154.h"
    #include "buffer-pool-memory-manager/buffer_pool_allocator.h"
}

/* RF driver data */
static phy_device_driver_s device_driver;
static int8_t  rf_radio_driver_id = -1;
static uint8_t MAC_address[8];
static uint16_t PAN_address;
static uint16_t short_address;

/* Driver instance handle */
static NanostackRfPhyEfr32 *rf = NULL;

/* Channel configurations */
static const phy_rf_channel_configuration_s phy_24ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK};
static const phy_rf_channel_configuration_s phy_subghz = {868300000U, 2000000U, 250000U, 11U, M_OQPSK};

static const phy_device_channel_page_s phy_channel_pages[] = {
        { CHANNEL_PAGE_0, &phy_24ghz},
        { CHANNEL_PAGE_2, &phy_subghz},
        { CHANNEL_PAGE_0, NULL}
};

/* Driver structures */
typedef enum {
    RADIO_UNINIT,
    RADIO_INITING,
    RADIO_IDLE,
    RADIO_TX,
    RADIO_RX,
    RADIO_CALIBRATION
} siliconlabs_modem_state_t;

static const RAIL_CsmaConfig_t csma_config = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;

#if defined(TARGET_EFR32MG1) || defined(TARGET_EFR32FG1)
#include "ieee802154_subg_efr32xg1_configurator_out.h"
#include "ieee802154_efr32xg1_configurator_out.h"
#elif defined(TARGET_EFR32MG12) || defined(TARGET_EFR32FG12)
#include "ieee802154_efr32xg12_configurator_out.h"
#include "ieee802154_efr32xg12_configurator_out.h"
#else
#error "Not a valid target."
#endif

static const RAIL_ChannelConfigEntry_t entry[] = {
  {0U,  0U,  600000U,  868300000U},
  {1U,  10U, 2000000U, 906000000U},
  {11U, 26U, 5000000U, 2405000000U}
};

#if MBED_CONF_SL_RAIL_BAND == 868
#ifndef MBED_CONF_SL_RAIL_HAS_SUBGIG
#error "Sub-Gigahertz band is not supported on this target."
#endif
static const RAIL_ChannelConfig_t channels = {
  (RAIL_ChannelConfigEntry_t *) &entry[0],
  1
};
#elif MBED_CONF_SL_RAIL_BAND == 915
#ifndef MBED_CONF_SL_RAIL_HAS_SUBGIG
#error "Sub-Gigahertz band is not supported on this target."
#endif
static const RAIL_ChannelConfig_t channels = {
  (RAIL_ChannelConfigEntry_t *) &entry[1],
  1
};
#elif MBED_CONF_SL_RAIL_BAND == 2400
#ifndef MBED_CONF_SL_RAIL_HAS_2P4
#error "2.4GHz band is not supported on this target."
#endif
static const RAIL_ChannelConfig_t channels = {
  (RAIL_ChannelConfigEntry_t *) &entry[2],
  1
};
#else
#error "sl-rail.band is not correctly defined"
#endif

static const RAIL_IEEE802154_Config_t config = { false, false,
                                    RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES,
                                    RAIL_RF_STATE_RX, 100, 192, 894, NULL };

static const RAIL_Init_t railInitParams = { 140, 38400000, RAIL_CAL_ALL_PENDING };

#if defined (MBED_CONF_SL_RAIL_HAS_2P4)
    // Set up the PA for 2.4 GHz operation
static const RADIO_PAInit_t paInit2p4 = {
        PA_SEL_2P4_HP,    /* Power Amplifier mode */
        PA_VOLTMODE_DCDC, /* Power Amplifier vPA Voltage mode */
        100,              /* Desired output power in dBm * 10 */
        0,                /* Output power offset in dBm * 10 */
        10                /* Desired ramp time in us */
    };
#endif

#if defined (MBED_CONF_SL_RAIL_HAS_SUBGIG)
    // Set up the PA for sub-GHz operation
static const RADIO_PAInit_t paInitSubGhz = {
        PA_SEL_SUBGIG,    /* Power Amplifier mode */
        PA_VOLTMODE_DCDC, /* Power Amplifier vPA Voltage mode */
        100,              /* Desired output power in dBm * 10 */
        0,                /* Output power offset in dBm * 10 */
        10                /* Desired ramp time in us */
    };
#endif

static volatile siliconlabs_modem_state_t radio_state = RADIO_UNINIT;
static volatile int8_t channel = -1;
static volatile uint8_t current_tx_handle = 0;
static volatile uint8_t current_tx_sequence = 0;
static volatile bool waiting_for_ack = false;
static volatile bool data_pending = false, last_ack_pending_bit = false;
static volatile uint32_t last_tx = 0;

/* ARM_NWK_HAL prototypes */
static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr);
static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel);
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr);
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol );

/* Local function prototypes */
static bool rail_checkAndSwitchChannel(uint8_t channel);

static void rf_thread_loop(const void *arg)
{
    SL_DEBUG_PRINT("rf_thread_loop: starting (id: %d)\n", rf_thread_id);
    for (;;) {
        osEvent event = osSignalWait(0, osWaitForever);

        if (event.status != osEventSignal) {
            continue;
        }

        platform_enter_critical();

        if (event.value.signals & SL_RX_DONE) {
            while(rx_queue_tail != rx_queue_head) {
                void* handle = (void*) rx_queue[rx_queue_tail];
                RAIL_RxPacketInfo_t* info = (RAIL_RxPacketInfo_t*) memoryPtrFromHandle(handle);
                device_driver.phy_rx_cb(
                        info->dataPtr + 1,
                        info->dataLength - 1, 
                        info->appendedInfo.lqi, 
                        info->appendedInfo.rssiLatch, 
                        rf_radio_driver_id);

                memoryFree(handle);
                rx_queue[rx_queue_tail] = NULL;
                rx_queue_tail = (rx_queue_tail + 1) % RF_QUEUE_SIZE;
            }

        } else if (event.value.signals & SL_TX_DONE) {
            device_driver.phy_tx_done_cb(rf_radio_driver_id,
                    current_tx_handle,
                    PHY_LINK_TX_SUCCESS,
                    1,
                    1);
        } else if (event.value.signals & SL_ACK_RECV) {
            device_driver.phy_tx_done_cb( rf_radio_driver_id,
                    current_tx_handle,
                    (event.value.signals & SL_ACK_PEND) ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE,
                    1,
                    1);
        } else if (event.value.signals & SL_ACK_TIMEOUT) {
            waiting_for_ack = false;
            device_driver.phy_tx_done_cb(rf_radio_driver_id,
                    current_tx_handle,
                    PHY_LINK_TX_FAIL,
                    1,
                    1);
        } else if(event.value.signals & SL_TX_ERR) {
            device_driver.phy_tx_done_cb( rf_radio_driver_id,
                    current_tx_handle,
                    PHY_LINK_CCA_FAIL,
                    8,
                    1);
        } else if(event.value.signals & SL_CAL_REQ) {
            SL_DEBUG_PRINT("rf_thread_loop: SL_CAL_REQ signal received (unhandled)\n");
        } else if(event.value.signals & SL_RXFIFO_ERR) {
            SL_DEBUG_PRINT("rf_thread_loop: SL_RXFIFO_ERR signal received (unhandled)\n");
        } else if(event.value.signals & SL_TXFIFO_ERR) {
            SL_DEBUG_PRINT("rf_thread_loop: SL_TXFIFO_ERR signal received (unhandled)\n");
        } else if(event.value.signals & SL_QUEUE_FULL) {
            SL_DEBUG_PRINT("rf_thread_loop: SL_QUEUE_FULL signal received (packet dropped)\n");
        } else {
            SL_DEBUG_PRINT("rf_thread_loop unhandled event status: %d value: %d\n", event.status, event.value.signals);
        }

        platform_exit_critical();
    }
}

/*============ CODE =========*/

/*
 * \brief Function initialises and registers the RF driver.
 *
 * \param none
 *
 * \return rf_radio_driver_id Driver ID given by NET library
 */
static int8_t rf_device_register(void)
{
    // If we already exist, bail.
    if(radio_state != RADIO_UNINIT) {
        return -1;
    }

    SL_DEBUG_PRINT("rf_device_register: entry\n");

#if MBED_CONF_SL_RAIL_BAND == 2400
    RADIO_PA_Init((RADIO_PAInit_t*)&paInit2p4);
#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868)
    RADIO_PA_Init((RADIO_PAInit_t*)&paInitSubGhz);
#endif

    // Set up PTI since it makes life so much easier
#if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1)
    RADIO_PTIInit_t ptiInit = {
        MBED_CONF_SL_RAIL_PTI_MODE,
        MBED_CONF_SL_RAIL_PTI_BAUDRATE,
        MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION,
        MBED_CONF_SL_RAIL_PTI_DOUT_PORT,
        MBED_CONF_SL_RAIL_PTI_DOUT_PIN,
        MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION,
        MBED_CONF_SL_RAIL_PTI_DCLK_PORT,
        MBED_CONF_SL_RAIL_PTI_DCLK_PIN,
        MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION,
        MBED_CONF_SL_RAIL_PTI_DFRAME_PORT,
        MBED_CONF_SL_RAIL_PTI_DFRAME_PIN
    };

    RADIO_PTI_Init(&ptiInit);
#endif

    // Set up RAIL
    RAIL_RfInit(&railInitParams);
    RAIL_ChannelConfig(&channels);
#if (MBED_CONF_SL_RAIL_BAND == 2400)
    RAIL_RadioConfig((void*) ieee802154_config_base);
    channel = 11;
#elif (MBED_CONF_SL_RAIL_BAND == 915)
    RAIL_RadioConfig((void*) ieee802154_config_915);
    channel = 1;
#elif (MBED_CONF_SL_RAIL_BAND == 868)
    RAIL_RadioConfig((void*) ieee802154_config_863);
    channel = 0;
#endif
    RAIL_IEEE802154_Init((RAIL_IEEE802154_Config_t*)&config);

    /* Get real MAC address */
    /* MAC is stored MSB first */
    memcpy(MAC_address, (const void*)&DEVINFO->UNIQUEH, 4);
    memcpy(&MAC_address[4], (const void*)&DEVINFO->UNIQUEL, 4);

    /*Set pointer to MAC address*/
    device_driver.PHY_MAC = MAC_address;
    device_driver.driver_description = (char*)"EFR32_154";

    /*Type of RF PHY*/
#if MBED_CONF_SL_RAIL_BAND == 2400
    device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE;
#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868)
    device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE;
#endif

    device_driver.phy_channel_pages = phy_channel_pages;
    /*Maximum size of payload is 127*/
    device_driver.phy_MTU = 127;
    /*1 byte header in PHY layer (length)*/
    device_driver.phy_header_length = 1;
    /*No tail in PHY layer*/
    device_driver.phy_tail_length = 0;
    /*Set address write function*/
    device_driver.address_write = &rf_address_write;
    /*Set RF extension function*/
    device_driver.extension = &rf_extension;
    /*Set RF state control function*/
    device_driver.state_control = &rf_interface_state_control;
    /*Set transmit function*/
    device_driver.tx = &rf_start_cca;
    /*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/
    device_driver.phy_rx_cb = NULL;
    device_driver.phy_tx_done_cb = NULL;
    /*Virtual upper data callback init to NULL*/
    device_driver.arm_net_virtual_rx_cb = NULL;
    device_driver.arm_net_virtual_tx_cb = NULL;

    /*Register device driver*/
    rf_radio_driver_id = arm_net_phy_register(&device_driver);

    // If the radio hasn't called the ready callback by now, place it in the initing state
    if(radio_state == RADIO_UNINIT) {
        radio_state = RADIO_INITING;
    }

#ifdef MBED_CONF_RTOS_PRESENT
    rx_queue_head = 0;
    rx_queue_tail = 0;
    rf_thread_id = osThreadCreate(osThread(rf_thread_loop), NULL);
#endif

    return rf_radio_driver_id;
}

/*
 * \brief Function unregisters the RF driver.
 *
 * \param none
 *
 * \return none
 */
static void rf_device_unregister(void)
{
    arm_net_phy_unregister(rf_radio_driver_id);
}

/*
 * \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO.
 *
 * \param data_ptr Pointer to TX data
 * \param data_length Length of the TX data
 * \param tx_handle Handle to transmission
 * \return 0 Success
 * \return -1 Busy
 */
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol )
{

    RAIL_TxData_t txData = {
        data_ptr,
        data_length + 3
    };

    switch(radio_state) {
    case RADIO_UNINIT:
        SL_DEBUG_PRINT("rf_start_cca: Radio uninit\n");
        return -1;
    case RADIO_INITING:
        SL_DEBUG_PRINT("rf_start_cca: Radio initing\n");
        return -1;
    case RADIO_CALIBRATION:
        SL_DEBUG_PRINT("rf_start_cca: Radio calibrating\n");
        return -1;
    case RADIO_TX:
        SL_DEBUG_PRINT("rf_start_cca: Radio in TX mode\n");
        return -1;
    case RADIO_IDLE:
    case RADIO_RX:
        // If we're still waiting for an ACK, don't mess up the internal state
        if(waiting_for_ack || RAIL_RfStateGet() == RAIL_RF_STATE_TX) {
            if((RAIL_GetTime() - last_tx) < 30000) {
                SL_DEBUG_PRINT("rf_start_cca: Still waiting on previous ACK\n");
                return -1;
            } else {
                SL_DEBUG_PRINT("rf_start_cca: TXerr\n");
            }
        }

        platform_enter_critical();

        data_ptr[0] = data_length + 2;
        RAIL_RfIdleExt(RAIL_IDLE_ABORT , true);
        RAIL_TxDataLoad(&txData);
        radio_state = RADIO_TX;

        RAIL_TxOptions_t txOpt;
        //Check to see whether we'll be waiting for an ACK
        if(data_ptr[1] & (1 << 5)) {
            txOpt.waitForAck = true;
            waiting_for_ack = true;
        } else {
            txOpt.waitForAck = false;
        }

        SL_DEBUG_PRINT("rf_start_cca: Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0);

        if(RAIL_TxStartWithOptions(channel, &txOpt, &RAIL_CcaCsma, (RAIL_CsmaConfig_t*) &csma_config) == 0) {
          //Save packet number and sequence
          current_tx_handle = tx_handle;
          current_tx_sequence = data_ptr[3];
          platform_exit_critical();
          return 0;
        } else {
          RAIL_RfIdle();
          RAIL_RxStart(channel);
          radio_state = RADIO_RX;
          platform_exit_critical();
          return -1;
        }
    }
    //Should never get here...
    platform_exit_critical();
    return -1;
}

/*
 * \brief Function gives the control of RF states to MAC.
 *
 * \param new_state RF state
 * \param rf_channel RF channel
 *
 * \return 0 Success
 */
static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel)
{
    int8_t ret_val = 0;
    switch (new_state)
    {
        /* Reset PHY driver and set to idle */
        case PHY_INTERFACE_RESET:
            RAIL_RfIdle();
            radio_state = RADIO_IDLE;
            break;
        /* Disable PHY Interface driver */
        case PHY_INTERFACE_DOWN:
            RAIL_RfIdle();
            radio_state = RADIO_IDLE;
            break;
        /* Enable RX */
        case PHY_INTERFACE_UP:
            if(rail_checkAndSwitchChannel(rf_channel)) {
                RAIL_IEEE802154_SetPromiscuousMode(false);
                RAIL_RxStart(channel);
                radio_state = RADIO_RX;
            } else {
                ret_val = -1;
            }
            break;
        /* Enable wireless interface ED scan mode */
        case PHY_INTERFACE_RX_ENERGY_STATE:
            SL_DEBUG_PRINT("rf_interface_state_control: Energy det req\n");
            // TODO: implement energy detection
            break;
        /* Enable RX in promiscuous mode (aka no address filtering) */
        case PHY_INTERFACE_SNIFFER_STATE:
            if(rail_checkAndSwitchChannel(rf_channel)) {
                RAIL_IEEE802154_SetPromiscuousMode(true);
                RAIL_RxStart(channel);
                radio_state = RADIO_RX;
            } else {
                ret_val = -1;
            }
            break;
    }
    return ret_val;
}

/*
 * \brief Function controls the ACK pending, channel setting and energy detection.
 *
 * \param extension_type Type of control
 * \param data_ptr Data from NET library
 *
 * \return 0 Success
 */
static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr)
{
    switch (extension_type)
    {
        /* Control MAC pending bit for Indirect data transmission */
        case PHY_EXTENSION_CTRL_PENDING_BIT:
            if(*data_ptr) {
                data_pending = true;
            } else {
                data_pending = false;
            }
            break;
        /* Return frame pending bit from last received ACK */
        case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS:
            if(last_ack_pending_bit) {
                *data_ptr = 0xFF;
            } else {
                *data_ptr = 0;
            }
            break;
        /* Set channel */
        case PHY_EXTENSION_SET_CHANNEL:
            channel = *data_ptr;
            break;
        /* Read energy on the channel */
        case PHY_EXTENSION_READ_CHANNEL_ENERGY:
            // TODO: implement energy detection
            *data_ptr = 0;
            break;
        /* Read status of the link */
        case PHY_EXTENSION_READ_LINK_STATUS:
            // TODO: return accurate value here
            SL_DEBUG_PRINT("rf_extension: Trying to read link status\n");
            break;
        /* Convert between LQI and RSSI */
        case PHY_EXTENSION_CONVERT_SIGNAL_INFO:
            // TODO: return accurate value here
            SL_DEBUG_PRINT("rf_extension: Trying to read signal info\n");
            break;
        case PHY_EXTENSION_ACCEPT_ANY_BEACON:
        SL_DEBUG_PRINT("rf_extension: Trying to accept any beacon\n");
            break;
    }
    return 0;
}

/*
 * \brief Function sets the addresses to RF address filters.
 *
 * \param address_type Type of address
 * \param address_ptr Pointer to given address
 *
 * \return 0 Success
 */
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr)
{
    int8_t ret_val = 0;
    switch (address_type)
    {
        /*Set 48-bit address*/
        case PHY_MAC_48BIT:
            // 15.4 does not support 48-bit addressing
            ret_val = -1;
            break;
        /*Set 64-bit MAC address*/
        case PHY_MAC_64BIT:
            /* Store MAC in MSB order */
            memcpy(MAC_address, address_ptr, 8);
            SL_DEBUG_PRINT("rf_address_write: MACw ");
            for(unsigned int i = 0; i < sizeof(MAC_address); i ++) {
                SL_DEBUG_PRINT("%02x:", MAC_address[i]);
            }
            SL_DEBUG_PRINT("\n");
            /* Pass MAC to the RF driver in LSB order */
            uint8_t MAC_reversed[8];
            for(unsigned int i = 0; i < sizeof(MAC_address); i ++) {
                MAC_reversed[i] = MAC_address[sizeof(MAC_address) - 1 - i];
            }
            RAIL_IEEE802154_SetLongAddress(MAC_reversed);
            break;
        /*Set 16-bit address*/
        case PHY_MAC_16BIT:
            short_address = address_ptr[0] << 8 | address_ptr[1];
            RAIL_IEEE802154_SetShortAddress(short_address);
            break;
        /*Set PAN Id*/
        case PHY_MAC_PANID:
            PAN_address = address_ptr[0] << 8 | address_ptr[1];
            RAIL_IEEE802154_SetPanId(PAN_address);
            break;
    }
    return ret_val;
}

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

static void rf_if_lock(void)
{
    platform_enter_critical();
}

static void rf_if_unlock(void)
{
    platform_exit_critical();
}

NanostackRfPhyEfr32::NanostackRfPhyEfr32() : NanostackRfPhy()
{
    // Do nothing
}

NanostackRfPhyEfr32::~NanostackRfPhyEfr32()
{
    rf_unregister();
}

int8_t NanostackRfPhyEfr32::rf_register()
{

    rf_if_lock();

    if (rf != NULL) {
        rf_if_unlock();
        error("Multiple registrations of NanostackRfPhyEfr32 not supported");
        return -1;
    }

    int8_t radio_id = rf_device_register();
    if (radio_id < 0) {
        rf = NULL;
    } else {
        rf = this;
    }

    rf_if_unlock();
    return radio_id;
}

void NanostackRfPhyEfr32::rf_unregister()
{
    rf_if_lock();

    if (rf != this) {
        rf_if_unlock();
        return;
    }

    rf_device_unregister();
    rf = NULL;

    rf_if_unlock();
}

void NanostackRfPhyEfr32::get_mac_address(uint8_t *mac)
{
    rf_if_lock();

    memcpy(mac, MAC_address, sizeof(MAC_address));

    rf_if_unlock();
}

void NanostackRfPhyEfr32::set_mac_address(uint8_t *mac)
{
    rf_if_lock();

    if (NULL != rf) {
        error("NanostackRfPhyEfr32 cannot change mac address when running");
        rf_if_unlock();
        return;
    }
    
    memcpy(MAC_address, mac, sizeof(MAC_address));

    rf_if_unlock();
}

uint32_t NanostackRfPhyEfr32::get_driver_version()
{
    RAIL_Version_t railversion;
    RAIL_VersionGet(&railversion, true);

    return (railversion.major << 24) |
           (railversion.minor << 16) |
           (railversion.rev   << 8)  |
           (railversion.build);
}


//====================== RAIL-defined callbacks =========================
/**
 * Callback that lets the app know when the radio has finished init
 * and is ready.
 */
void RAILCb_RfReady(void) {
    radio_state = RADIO_IDLE;
}

/**
 * Interrupt level callback
 * Allows the user finer granularity in tx radio events.
 *
 * Radio Statuses:
 * RAIL_TX_CONFIG_BUFFER_UNDERFLOW
 * RAIL_TX_CONFIG_CHANNEL_BUSY
 *
 * @param[in] status A bit field that defines what event caused the callback
 */
void RAILCb_TxRadioStatus(uint8_t status) {
   if(device_driver.phy_tx_done_cb != NULL) {
    if(status == RAIL_TX_CONFIG_BUFFER_UNDERFLOW ||
       status == RAIL_TX_CONFIG_CHANNEL_BUSY ||
       status == RAIL_TX_CONFIG_TX_ABORTED ||
       status == RAIL_TX_CONFIG_TX_BLOCKED) {
        waiting_for_ack = false;
#ifdef MBED_CONF_RTOS_PRESENT
        osSignalSet(rf_thread_id, SL_TX_ERR);
    }
#else
        device_driver.phy_tx_done_cb(rf_radio_driver_id,
                                      current_tx_handle,
                                      PHY_LINK_CCA_FAIL,
                                      8,
                                      1);
    } else {
        SL_DEBUG_PRINT("Packet TX error %d\n", status);
    }
#endif
  }
  radio_state = RADIO_RX;
}

/**
 * Called whenever an enabled radio status event occurs
 *
 * Triggers:
 *  RAIL_RX_CONFIG_PREAMBLE_DETECT
 *  RAIL_RX_CONFIG_SYNC1_DETECT
 *  RAIL_RX_CONFIG_SYNC2_DETECT
 *  RAIL_RX_CONFIG_INVALID_CRC
 *  RAIL_RX_CONFIG_BUFFER_OVERFLOW
 *  RAIL_RX_CONFIG_ADDRESS_FILTERED
 *
 * @param[in] status The event that triggered this callback
 */
void RAILCb_RxRadioStatus(uint8_t status) {
    switch(status) {
        case RAIL_RX_CONFIG_ADDRESS_FILTERED:
            break;
        default:
            break;
    }
}

/**
 * Callback that notifies the application that a calibration is needed.
 *
 * This callback function is called whenever the RAIL library detects that a
 * calibration is needed. It is up to the application to determine a valid
 * window to call RAIL_CalStart().
 *
 */
void RAILCb_CalNeeded(void) {
  // TODO: Implement on-the-fly recalibration
#ifdef MBED_CONF_RTOS_PRESENT
  osSignalSet(rf_thread_id, SL_CAL_REQ);
#else
  SL_DEBUG_PRINT("!!!! Calling for calibration\n");
#endif
}

/**
 * Interrupt level callback to signify when the radio changes state.
 *
 * @param[in] state Current state of the radio, as defined by EFR32 data sheet
 */
void RAILCb_RadioStateChanged(uint8_t state) {
    return;
}

/**
 * This function is called when the RAIL timer expires
 *
 * You must implement a stub for this in your RAIL application even if you
 * don't use the timer.
 */
void RAILCb_TimerExpired(void) {
}

/**
 * Interrupt level callback to signify when the packet was sent
 * @param txPacketInfo Information about the packet that was transmitted.
 * @note that this structure is only valid during the timeframe of the
 * callback.
 */
void RAILCb_TxPacketSent(RAIL_TxPacketInfo_t *txPacketInfo) {
#ifdef MBED_CONF_RTOS_PRESENT
    osSignalSet(rf_thread_id, SL_TX_DONE);
#else
  if(device_driver.phy_tx_done_cb != NULL) {
    device_driver.phy_tx_done_cb( rf_radio_driver_id,
                                  current_tx_handle,
                                  // Normally we'd switch on ACK requested here, but Nanostack does that for us.
                                  PHY_LINK_TX_SUCCESS,
                                  // Succeeded, so how many times we tried is really not relevant.
                                  1,
                                  1);
  }
#endif
  last_tx = RAIL_GetTime();
  radio_state = RADIO_RX;
}

/**
 * Receive packet callback.
 *
 * @param[in] rxPacketHandle Contains a handle that points to the memory that
 *   the packet was stored in. This handle will be the same as something
 *   returned by the RAILCb_AllocateMemory() API. To convert this into a receive
 *   packet info struct use the *** function.
 *
 * This function is called whenever a packet is received and returns to you the
 * memory handle for where this received packet and its appended information was
 * stored. After this callback is done we will release the memory handle so you
 * must somehow increment a reference count or copy the data out within this
 * function.
 */
void RAILCb_RxPacketReceived(void *rxPacketHandle) {
    RAIL_RxPacketInfo_t* rxPacketInfo = (RAIL_RxPacketInfo_t*) memoryPtrFromHandle(rxPacketHandle);
    if(rxPacketInfo->appendedInfo.crcStatus) {
        /* If this is an ACK, deal with it */
        if( rxPacketInfo->dataLength == 4                         && 
            rxPacketInfo->dataPtr[3] == (current_tx_sequence)     &&
            waiting_for_ack) {
            /* Tell the radio to not ACK an ACK */
            RAIL_AutoAckCancelAck();
            waiting_for_ack = false;
            /* Save the pending bit */
            last_ack_pending_bit = (rxPacketInfo->dataPtr[1] & (1 << 4)) != 0;
            /* Tell the stack we got an ACK */
#ifdef MBED_CONF_RTOS_PRESENT
            osSignalSet(rf_thread_id, SL_ACK_RECV | SL_ACK_PEND);
#else
            SL_DEBUG_PRINT("rACK\n");
            device_driver.phy_tx_done_cb( rf_radio_driver_id,
                                          current_tx_handle,
                                          last_ack_pending_bit ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE,
                                          1,
                                          1);
#endif
        } else {
            /* Figure out whether we want to not ACK this packet */

            /*
            * dataPtr[0] = length
            * dataLength = length w/o length byte
            * dataptr[1:2] = 0x61C9 -> 0b01100001 0b1100 1001 (version 1, dest 3, src 2, ACKreq, type = 1)
            *   [1] => b[0:2] frame type, b[3] = security enabled, b[4] = frame pending, b[5] = ACKreq, b[6] = intrapan
            *   [2] => b[2:3] destmode, b[4:5] version, b[6:7] srcmode
            */
            if( (rxPacketInfo->dataPtr[1] & (1 << 5)) == 0 ) {
                /* Cancel the ACK if the sender did not request one */
                RAIL_AutoAckCancelAck();
            }

            /* Feed the received packet into the stack */
#ifdef MBED_CONF_RTOS_PRESENT
            if (((rx_queue_head + 1) % RF_QUEUE_SIZE) != rx_queue_tail) {
                memoryTakeReference(rxPacketHandle);
                rx_queue[rx_queue_head] = rxPacketHandle;
                rx_queue_head = (rx_queue_head + 1) % RF_QUEUE_SIZE;
                osSignalSet(rf_thread_id, SL_RX_DONE);
            } else {
                osSignalSet(rf_thread_id, SL_QUEUE_FULL);
            }
#else
            SL_DEBUG_PRINT("rPKT %d\n", rxPacketInfo->dataLength);
            device_driver.phy_rx_cb(rxPacketInfo->dataPtr + 1, 
                                    rxPacketInfo->dataLength - 1, 
                                    rxPacketInfo->appendedInfo.lqi, 
                                    rxPacketInfo->appendedInfo.rssiLatch, 
                                    rf_radio_driver_id);
#endif
        }
    }
}


/**
 * Callback for when a Data Request is being received
 *
 * @param address The source address of the data request command
 *
 * This function is called when the command byte of an incoming frame is for a
 * data request, which requests an ACK. This callback will be called before the
 * packet is fully received, to allow the node to have more time to decide
 * whether to set frame pending in the outgoing ACK.
 */
void RAILCb_IEEE802154_DataRequestCommand(RAIL_IEEE802154_Address_t *address) {
    if(data_pending) {
        RAIL_IEEE802154_SetFramePending();
    }
}

/**
 * Callback that notifies the application when searching for an ACK has timed
 * out.
 *
 * @return void
 *
 * This callback function is called whenever the timeout for searching for an
 * ack is exceeded.
 */
void RAILCb_RxAckTimeout(void) {
    if(waiting_for_ack) {
        waiting_for_ack = false;
#ifdef MBED_CONF_RTOS_PRESENT
        osSignalSet(rf_thread_id, SL_ACK_TIMEOUT);
#else
        device_driver.phy_tx_done_cb( rf_radio_driver_id,
                                      current_tx_handle,
                                      PHY_LINK_TX_FAIL,
                                      1,
                                      1);
#endif
    }
}

/**
 * Function to check the requested channel against the current channel,
 * and change the radio configuration if necessary.
 *
 * @param channel The new channel number requested
 * @return bool True if able to switch to the requested channel
 *
 */
static bool rail_checkAndSwitchChannel(uint8_t newChannel) {
    if(channel == newChannel) {
        return true;
    }

    if(newChannel > 0 && newChannel < 11) {
        if(MBED_CONF_SL_RAIL_BAND == 915) {
            channel = newChannel;
            return true;
        } else {
            return false;
        }
    } else if(newChannel >= 11 && newChannel <= 26) {
        if(MBED_CONF_SL_RAIL_BAND == 2400) {
            channel = newChannel;
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

/**
 * Callback that fires when the receive fifo exceeds the configured threshold
 * value
 *
 * @param[in] bytesAvailable Number of bytes available in the receive fifo at
 * the time of the callback dispatch
 *
 * @return void
 * @warning You must implement a stub for this in your RAIL application.
 *
 * Callback that fires when the receive fifo exceeds the configured threshold
 * value.  Provides the number of bytes available in the receive fifo at the
 * time of the callback dispatch.
 */
void RAILCb_RxFifoAlmostFull(uint16_t bytesAvailable) {
#ifdef MBED_CONF_RTOS_PRESENT
    osSignalSet(rf_thread_id, SL_RXFIFO_ERR);
#else
    SL_DEBUG_PRINT("RX near full (%d)\n", bytesAvailable);
#endif
}

/**
 * Callback that fires when the transmit fifo falls under the configured
 * threshold value
 *
 * @param[in] spaceAvailable Number of bytes open in the transmit fifo at the
 * time of the callback dispatch
 *
 * @return void
 * @warning You must implement a stub for this in your RAIL application.
 *
 * Callback that fires when the transmit fifo falls under the configured
 * threshold value. It only fires if a rising edge occurs across this
 * threshold. This callback will not fire on initailization nor after resetting
 * the transmit fifo with RAIL_ResetFifo().
 *
 * Provides the number of bytes open in the transmit fifo at the time of the
 * callback dispatch.
 */
void RAILCb_TxFifoAlmostEmpty(uint16_t spaceAvailable) {
#ifdef MBED_CONF_RTOS_PRESENT
    osSignalSet(rf_thread_id, SL_TXFIFO_ERR);
#else
    SL_DEBUG_PRINT("TX near empty (%d)\n", spaceAvailable);
#endif
}

/**
 * Callback for when AGC averaged RSSI is done
 *
 * @param avgRssi Contains the the RSSI in quarter dBm (dbm*4) on success and
 * returns \ref RAIL_RSSI_INVALID if there was a problem computing the result.
 *
 * Called in response to RAIL_StartAverageRSSI() to indicate that the hardware
 * has completed averaging. If you would like you can instead use the
 * RAIL_AverageRSSIReady() to wait for completion and RAIL_GetAverageRSSI() to
 * get the result.
 */
void RAILCb_RssiAverageDone(int16_t avgRssi) {
#ifdef MBED_CONF_RTOS_PRESENT
    osSignalSet(rf_thread_id, SL_RSSI_DONE);
#else
    SL_DEBUG_PRINT("RSSI done (%d)\n", avgRssi);
#endif
}