/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (C)2013 Semtech

Description: SX1272 driver specific target board functions implementation

License: Revised BSD License, see LICENSE.TXT file include in the project

Maintainer: Miguel Luis and Gregory Cristian
*/

#include <assert.h>
#include "os/mynewt.h"
#include "hal/hal_spi.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "radio/radio.h"
#include "sx1272.h"
#include "sx1272-board.h"

#if (MYNEWT_VAL(SX1272_HAS_ANT_SW) && MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW))
#error "Cannot have both SX1272_HAS_ANT_SW and SX1272_HAS_COMP_ANT_SW set true"
#endif

/*!
 * Flag used to set the RF switch control pins in low power mode when the radio is not active.
 */
#if (MYNEWT_VAL(SX1272_HAS_ANT_SW) || MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW))
static bool g_radio_is_active = false;
#endif

extern DioIrqHandler *DioIrq[];

/*!
 * Radio driver structure initialization
 */
const struct Radio_s Radio =
{
    SX1272Init,
    SX1272GetStatus,
    SX1272SetModem,
    SX1272SetChannel,
    SX1272IsChannelFree,
    SX1272Random,
    SX1272SetRxConfig,
    SX1272SetTxConfig,
    SX1272CheckRfFrequency,
    SX1272GetTimeOnAir,
    SX1272Send,
    SX1272SetSleep,
    SX1272SetStby,
    SX1272SetRx,
    SX1272StartCad,
    SX1272SetTxContinuousWave,
    SX1272ReadRssi,
    SX1272Write,
    SX1272Read,
    SX1272WriteBuffer,
    SX1272ReadBuffer,
    SX1272SetMaxPayloadLength,
    SX1272SetPublicNetwork,
    SX1272GetWakeupTime,
    SX1272RxDisable
};

void
SX1272IoInit(void)
{
    struct hal_spi_settings spi_settings;
    int rc;

#if MYNEWT_VAL(SX1272_HAS_ANT_SW)
    rc = hal_gpio_init_out(SX1272_RXTX, 0);
    assert(rc == 0);
#endif

    /*
     * XXX: This should really be in the BSP! On the schematic the pins are
     * labeled LORA_FEM_CTX and nLORA_FEM_CTX but here they are RXTX and N_RXTX
     *
     * RXTX high, N_RXTX low = RX
     * RXTX low, N_RXTX high = TX
     *
     * By default, put in RX
     */
#if MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW)
    rc = hal_gpio_init_out(SX1272_RXTX, 1);
    assert(rc == 0);
    rc = hal_gpio_init_out(SX1272_N_RXTX, 0);
    assert(rc == 0);
#endif

    rc = hal_gpio_init_out(RADIO_NSS, 1);
    assert(rc == 0);

    hal_spi_disable(RADIO_SPI_IDX);

    spi_settings.data_order = HAL_SPI_MSB_FIRST;
    spi_settings.data_mode = HAL_SPI_MODE0;
    spi_settings.baudrate = MYNEWT_VAL(SX1272_SPI_BAUDRATE);
    spi_settings.word_size = HAL_SPI_WORD_SIZE_8BIT;

    rc = hal_spi_config(RADIO_SPI_IDX, &spi_settings);
    assert(rc == 0);

    rc = hal_spi_enable(RADIO_SPI_IDX);
    assert(rc == 0);
}

void
SX1272IoIrqInit(DioIrqHandler **irqHandlers)
{
    int rc;

    if (irqHandlers[0] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO0, irqHandlers[0], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO0);
    }

    if (irqHandlers[1] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO1, irqHandlers[1], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO1);
    }

    if (irqHandlers[2] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO2, irqHandlers[2], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO2);
    }

    if (irqHandlers[3] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO3, irqHandlers[3], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO3);
    }

    if (irqHandlers[4] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO4, irqHandlers[4], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO4);
    }

    if (irqHandlers[5] != NULL) {
        rc = hal_gpio_irq_init(SX1272_DIO5, irqHandlers[5], NULL,
                               HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_DOWN);
        assert(rc == 0);
        hal_gpio_irq_enable(SX1272_DIO5);
    }
}

void
SX1272IoDeInit(void)
{
    if (DioIrq[0] != NULL) {
        hal_gpio_irq_release(SX1272_DIO0);
    }
    if (DioIrq[1] != NULL) {
        hal_gpio_irq_release(SX1272_DIO1);
    }
    if (DioIrq[2] != NULL) {
        hal_gpio_irq_release(SX1272_DIO2);
    }
    if (DioIrq[3] != NULL) {
        hal_gpio_irq_release(SX1272_DIO3);
    }
    if (DioIrq[4] != NULL) {
        hal_gpio_irq_release(SX1272_DIO4);
    }
    if (DioIrq[5] != NULL) {
        hal_gpio_irq_release(SX1272_DIO5);
    }
}

void
SX1272SetRfTxPower(int8_t power)
{
    uint8_t paconfig;
    uint8_t padac;

    paconfig = SX1272Read(REG_PACONFIG);
    padac = SX1272Read(REG_PADAC);

    paconfig = (paconfig & RF_PACONFIG_PASELECT_MASK) | SX1272GetPaSelect(SX1272.Settings.Channel);

    if ((paconfig & RF_PACONFIG_PASELECT_PABOOST) == RF_PACONFIG_PASELECT_PABOOST) {
        if (power > 17) {
            padac = (padac & RF_PADAC_20DBM_MASK) | RF_PADAC_20DBM_ON;
        } else {
            padac = (padac & RF_PADAC_20DBM_MASK) | RF_PADAC_20DBM_OFF;
        }
        if ((padac & RF_PADAC_20DBM_ON) == RF_PADAC_20DBM_ON) {
            if (power < 5) {
                power = 5;
            }

            if (power > 20) {
                power = 20;
            }
            paconfig = (paconfig & RFLR_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power - 5) & 0x0F);
        } else {
            if (power < 2) {
                power = 2;
            }

            if (power > 17) {
                power = 17;
            }
            paconfig = (paconfig & RFLR_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power - 2) & 0x0F);
        }
    } else {
        if (power < -1) {
            power = -1;
        }

        if (power > 14) {
            power = 14;
        }
        paconfig = (paconfig & RFLR_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power + 1) & 0x0F);
    }
    SX1272Write(REG_PACONFIG, paconfig);
    SX1272Write(REG_PADAC, padac);
}

uint8_t
SX1272GetPaSelect(uint32_t channel)
{
#if (MYNEWT_VAL(SX1272_USE_PA_BOOST) == 1)
    return RF_PACONFIG_PASELECT_PABOOST;
#else
    return RF_PACONFIG_PASELECT_RFO;
#endif
}

#if (MYNEWT_VAL(SX1272_HAS_ANT_SW) || MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW))
void
SX1272SetAntSwLowPower(bool status)
{
    if (g_radio_is_active != status) {
        g_radio_is_active = status;
        if (status == false) {
            SX1272AntSwInit();
        } else {
            SX1272AntSwDeInit();
        }
    }
}

void
SX1272AntSwInit(void)
{
    /*
     * XXX: consider doing this to save power. Currently the gpio are
     * set to rx mode automatically in the IO init function.
     */
}

void
SX1272AntSwDeInit(void)
{
    /*
     * XXX: consider doing this to save power. Currently the gpio are
     * set to rx mode automatically in the IO init function.
     */
}

void
SX1272SetAntSw(uint8_t opMode)
{
    os_sr_t sr;

    OS_ENTER_CRITICAL(sr);
    switch(opMode) {
    case RFLR_OPMODE_TRANSMITTER:
#if MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW)
        hal_gpio_write(SX1272_RXTX, 0);
        hal_gpio_write(SX1272_N_RXTX, 1);
#endif
#if MYNEWT_VAL(SX1272_HAS_ANT_SW)
        hal_gpio_write(SX1272_RXTX, 1);
#endif
        break;
    case RFLR_OPMODE_RECEIVER:
    case RFLR_OPMODE_RECEIVER_SINGLE:
    case RFLR_OPMODE_CAD:
    default:
#if MYNEWT_VAL(SX1272_HAS_COMP_ANT_SW)
        hal_gpio_write(SX1272_RXTX, 1);
        hal_gpio_write(SX1272_N_RXTX, 0);
#endif
#if MYNEWT_VAL(SX1272_HAS_ANT_SW)
        hal_gpio_write(SX1272_RXTX, 0);
#endif
        break;
    }
    OS_EXIT_CRITICAL(sr);
}
#else
void
SX1272SetAntSwLowPower(bool status)
{
    (void)status;
}

void
SX1272AntSwInit(void)
{
    /*
     * XXX: consider doing this to save power. Currently the gpio are
     * set to rx mode automatically in the IO init function.
     */
}

void
SX1272AntSwDeInit(void)
{
    /*
     * XXX: consider doing this to save power. Currently the gpio are
     * set to rx mode automatically in the IO init function.
     */
}

void
SX1272SetAntSw(uint8_t opMode)
{
    (void)opMode;
}
#endif

bool
SX1272CheckRfFrequency(uint32_t frequency)
{
    // Implement check. Currently all frequencies are supported
    return true;
}

uint32_t
SX1272GetBoardTcxoWakeupTime(void)
{
    return 0;
}

void
SX1272RxIoIrqDisable(void)
{
    hal_gpio_irq_disable(SX1272_DIO0);
    hal_gpio_irq_disable(SX1272_DIO1);
    hal_gpio_irq_disable(SX1272_DIO2);
    hal_gpio_irq_disable(SX1272_DIO3);
}

void
SX1272RxIoIrqEnable(void)
{
    hal_gpio_irq_enable(SX1272_DIO0);
    hal_gpio_irq_enable(SX1272_DIO1);
    hal_gpio_irq_enable(SX1272_DIO2);
    hal_gpio_irq_enable(SX1272_DIO3);
}
