/*
 * Copyright (C) 2016 PHYTEC Messtechnik GmbH
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License v2.1. See the file LICENSE in the top level directory for more
 * details.
 */

/**
 * @ingroup     drivers_kw2xrf
 * @{
 * @file
 * @brief       get/set functionality of kw2xrf driver
 *
 * @author      Johann Fischer <j.fischer@phytec.de>
 * @author      Jonas Remmert <j.remmert@phytec.de>
 * @author      Oliver Hahm <oliver.hahm@inria.fr>
 * @author      Sebastian Meiling <s@mlng.net>
 * @}
 */

#include "log.h"
#include "kw2xrf.h"
#include "kw2xrf_spi.h"
#include "kw2xrf_reg.h"
#include "kw2xrf_getset.h"
#include "kw2xrf_intern.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#define KW2XRF_LQI_HW_MAX           230      /**< LQI Saturation Level */

/* Modem_PA_PWR Register (PA Power Control) has a valid range from 3-31 */
#define KW2XRF_PA_RANGE_MAX      31       /**< Maximum value of PA Power Control Register */
#define KW2XRF_PA_RANGE_MIN      3        /**< Minimum value of PA Power Control Register */

#define KW2XRF_NUM_CHANNEL      (KW2XRF_MAX_CHANNEL - KW2XRF_MIN_CHANNEL + 1)

/* PLL integer and fractional lookup tables
 *
 * Fc = 2405 + 5(k - 11) , k = 11,12,...,26
 *
 * Equation for PLL frequency, MKW2xD Reference Manual, p.255 :
 * F = ((PLL_INT0 + 64) + (PLL_FRAC0/65536))32MHz
 *
 */
static const uint8_t pll_int_lt[16] = {
    11, 11, 11, 11,
    11, 11, 12, 12,
    12, 12, 12, 12,
    13, 13, 13, 13
};

static const uint16_t pll_frac_lt[16] = {
    10240, 20480, 30720, 40960,
    51200, 61440, 6144, 16384,
    26624, 36864, 47104, 57344,
    2048, 12288, 22528, 32768
};

static const uint8_t pow_lt[44] = {
    3, 4, 4, 5,
    6, 6, 7, 7,
    8, 9, 9, 10,
    11, 11, 12, 13,
    13, 14, 14, 15,
    16, 16, 17, 18,
    18, 19, 20, 20,
    21, 21, 22, 23,
    23, 24, 25, 25,
    26, 27, 27, 28,
    28, 29, 30, 31
};

void kw2xrf_set_tx_power(kw2xrf_t *dev, int16_t txpower)
{
    if (txpower > KW2XDRF_OUTPUT_POWER_MAX) {
        txpower = KW2XDRF_OUTPUT_POWER_MAX;
    }

    if (txpower < KW2XDRF_OUTPUT_POWER_MIN) {
        txpower = KW2XDRF_OUTPUT_POWER_MIN;
    }

    uint8_t level = pow_lt[txpower - KW2XDRF_OUTPUT_POWER_MIN];
    kw2xrf_write_dreg(dev, MKW2XDM_PA_PWR, MKW2XDM_PA_PWR(level));
    LOG_DEBUG("[kw2xrf] set txpower to: %d\n", txpower);
    dev->tx_power = txpower;
}

uint16_t kw2xrf_get_txpower(kw2xrf_t *dev)
{
    return dev->tx_power;
}

uint8_t kw2xrf_get_channel(kw2xrf_t *dev)
{
    uint8_t pll_int = kw2xrf_read_dreg(dev, MKW2XDM_PLL_INT0);
    uint16_t pll_frac = kw2xrf_read_dreg(dev, MKW2XDM_PLL_FRAC0_LSB);
    pll_frac |= ((uint16_t)kw2xrf_read_dreg(dev, MKW2XDM_PLL_FRAC0_MSB) << 8);

    for (unsigned i = 0; i < KW2XRF_NUM_CHANNEL; i++) {
        if ((pll_frac_lt[i] == pll_frac) && (pll_int_lt[i] == pll_int)) {
            return i + 11;
        }
    }
    return 0;
}

static int kw2xrf_get_sequence(kw2xrf_t *dev)
{
    int reg = 0;
    reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
    reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK;
    return reg;
}

int kw2xrf_set_channel(kw2xrf_t *dev, uint8_t channel)
{
    /* Save old sequence to restore this state later */
    uint8_t old_seq = kw2xrf_get_sequence(dev);

    if (channel < KW2XRF_MIN_CHANNEL || channel > KW2XRF_MAX_CHANNEL) {
        LOG_ERROR("[kw2xrf] Invalid channel %u\n", channel);
        return -1;
    }

    if (old_seq) {
        kw2xrf_abort_sequence(dev);
    }

    uint8_t tmp = channel - 11;
    kw2xrf_write_dreg(dev, MKW2XDM_PLL_INT0, MKW2XDM_PLL_INT0_VAL(pll_int_lt[tmp]));
    kw2xrf_write_dreg(dev, MKW2XDM_PLL_FRAC0_LSB, (uint8_t)pll_frac_lt[tmp]);
    kw2xrf_write_dreg(dev, MKW2XDM_PLL_FRAC0_MSB, (uint8_t)(pll_frac_lt[tmp] >> 8));

    dev->netdev.chan = channel;

    if (old_seq) {
        kw2xrf_set_sequence(dev, old_seq);
    }

    LOG_DEBUG("[kw2xrf] set channel to %u\n", channel);
    return 0;
}

void kw2xrf_abort_sequence(kw2xrf_t *dev)
{
    uint8_t regs[MKW2XDM_PHY_CTRL4 + 1];

    kw2xrf_mask_irq_b(dev);
    kw2xrf_read_dregs(dev, MKW2XDM_IRQSTS1, regs, (MKW2XDM_PHY_CTRL4 + 1));

    if ((regs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) != XCVSEQ_IDLE) {
        /* abort any ongoing sequence */
        regs[MKW2XDM_PHY_CTRL1] &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
        kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, regs[MKW2XDM_PHY_CTRL1]);
    }

    uint8_t state;
    do {
        state = kw2xrf_read_dreg(dev, MKW2XDM_SEQ_STATE);
        DEBUG("[kw2xrf] abort SEQ_STATE: %x\n", state);
    } while ((state & 0x1F) != 0);

    /* clear all IRQ bits */
    regs[MKW2XDM_IRQSTS1] = 0x7f;
    regs[MKW2XDM_IRQSTS2] = 0x03;
    regs[MKW2XDM_IRQSTS3] |= 0x0f;
    kw2xrf_write_dregs(dev, MKW2XDM_IRQSTS1, regs, 3);

    kw2xrf_enable_irq_b(dev);
}

/*
 * Simplified version for irq handling where the state of
 * the sequenz manager is known.
 */
void kw2xrf_set_idle_sequence(kw2xrf_t *dev)
{
    kw2xrf_mask_irq_b(dev);
    uint8_t reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);

    /* reset sequenz manager */
    reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
    kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);

    if (dev->pending_tx) {
        DEBUG("[kw2xrf] pending tx, cannot set idle sequenz\n");
        return;
    }

    /* start new sequenz */
    reg |= MKW2XDM_PHY_CTRL1_XCVSEQ(dev->idle_state);
    kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);

    switch (dev->idle_state) {
        case XCVSEQ_IDLE:
        /* for inexplicable reasons, the receive mode is also idle mode */
        case XCVSEQ_RECEIVE:
            dev->state = NETOPT_STATE_IDLE;
            break;

        case XCVSEQ_CONTINUOUS_CCA:
        case XCVSEQ_CCA:
            dev->state = NETOPT_STATE_RX;
            break;

        case XCVSEQ_TRANSMIT:
        case XCVSEQ_TX_RX:
            dev->state = NETOPT_STATE_TX;
            break;

        default:
            dev->state = NETOPT_STATE_IDLE;
    }
    kw2xrf_enable_irq_b(dev);
}

void kw2xrf_set_sequence(kw2xrf_t *dev, kw2xrf_physeq_t seq)
{
    uint8_t reg = 0;

    kw2xrf_abort_sequence(dev);

    switch (seq) {
        case XCVSEQ_IDLE:
        /* for inexplicable reasons, the receive mode is also idle mode */
        case XCVSEQ_RECEIVE:
            dev->state = NETOPT_STATE_IDLE;
            break;

        case XCVSEQ_CONTINUOUS_CCA:
        case XCVSEQ_CCA:
            dev->state = NETOPT_STATE_RX;
            break;

        case XCVSEQ_TRANSMIT:
        case XCVSEQ_TX_RX:
            dev->state = NETOPT_STATE_TX;
            break;

        default:
            DEBUG("[kw2xrf] undefined state assigned to phy\n");
            dev->state = NETOPT_STATE_IDLE;
    }

    DEBUG("[kw2xrf] set sequence to %i\n", seq);
    reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);
    reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
    reg |= MKW2XDM_PHY_CTRL1_XCVSEQ(seq);
    kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL1, reg);
}

void kw2xrf_set_pan(kw2xrf_t *dev, uint16_t pan)
{
    dev->netdev.pan = pan;

    uint8_t val_ar[2];
    val_ar[1] = (pan >> 8);
    val_ar[0] = (uint8_t)pan;
    kw2xrf_write_iregs(dev, MKW2XDMI_MACPANID0_LSB, val_ar, 2);
    LOG_DEBUG("[kw2xrf] set pan to: 0x%x\n", pan);
    dev->netdev.pan = pan;
}

void kw2xrf_set_addr_short(kw2xrf_t *dev, uint16_t addr)
{
    uint8_t val_ar[2];
    val_ar[0] = (addr >> 8);
    val_ar[1] = (uint8_t)addr;
    dev->netdev.short_addr[0] = val_ar[1];
    dev->netdev.short_addr[1] = val_ar[0];
#ifdef MODULE_SIXLOWPAN
    /* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to
     * 0 for unicast addresses */
    dev->netdev.short_addr[1] &= 0x7F;
#endif
    kw2xrf_write_iregs(dev, MKW2XDMI_MACSHORTADDRS0_LSB, val_ar,
                       IEEE802154_SHORT_ADDRESS_LEN);
}

void kw2xrf_set_addr_long(kw2xrf_t *dev, uint64_t addr)
{
    uint64_t tmp;
    uint8_t *ap = (uint8_t *)(&tmp);

    for (unsigned i = 0; i < IEEE802154_LONG_ADDRESS_LEN; i++) {
        dev->netdev.long_addr[i] = (uint8_t)(addr >> (i * 8));
        ap[i] = (addr >> ((IEEE802154_LONG_ADDRESS_LEN - 1 - i) * 8));
    }

    kw2xrf_write_iregs(dev, MKW2XDMI_MACLONGADDRS0_0, ap,
                       IEEE802154_LONG_ADDRESS_LEN);
}

uint16_t kw2xrf_get_addr_short(kw2xrf_t *dev)
{
    return (dev->netdev.short_addr[0] << 8) | dev->netdev.short_addr[1];
}

uint64_t kw2xrf_get_addr_long(kw2xrf_t *dev)
{
    uint64_t addr;
    uint8_t *ap = (uint8_t *)(&addr);

    kw2xrf_read_iregs(dev, MKW2XDMI_MACLONGADDRS0_0, ap,
                      IEEE802154_LONG_ADDRESS_LEN);

    return addr;
}

int8_t kw2xrf_get_cca_threshold(kw2xrf_t *dev)
{
    uint8_t tmp;
    kw2xrf_read_iregs(dev, MKW2XDMI_CCA1_THRESH, &tmp, 1);
    /* KW2x register value represents absolute value in dBm
     * default value: -75 dBm
     */
    return (-tmp);
}

void kw2xrf_set_cca_threshold(kw2xrf_t *dev, int8_t value)
{
    /* normalize to absolute value */
    if (value < 0) {
        value = -value;
    }
    kw2xrf_write_iregs(dev, MKW2XDMI_CCA1_THRESH, (uint8_t*)&value, 1);
}

void kw2xrf_set_cca_mode(kw2xrf_t *dev, uint8_t mode)
{
    uint8_t tmp;
    tmp = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL4);
    tmp &= ~MKW2XDM_PHY_CTRL4_CCATYPE_MASK;
    tmp |= MKW2XDM_PHY_CTRL4_CCATYPE(mode);
    kw2xrf_write_dreg(dev, MKW2XDM_PHY_CTRL4, tmp);
}

uint8_t kw2xrf_get_cca_mode(kw2xrf_t *dev)
{
    uint8_t tmp;
    tmp = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL4);
    return (tmp & MKW2XDM_PHY_CTRL4_CCATYPE_MASK) >> MKW2XDM_PHY_CTRL4_CCATYPE_SHIFT;
}

int16_t kw2xrf_get_rssi(uint32_t value)
{
    /* Get rssi (Received Signal Strength Indicator, unit is dBm)
     * from lqi (Link Quality Indicator) value.
     * There are two different equations for RSSI:
     * RF = (LQI - 286.6) / 2.69333 (MKW2xD Reference Manual)
     * RF = (LQI - 295.4) / 2.84 (MCR20A Reference Manual)
     * The last appears more to match the graphic (Figure 3-10).
     * Since RSSI value is always positive and we want to
     * avoid the floating point computation:
     * -RF * 65536 = (LQI / 2.84 - 295.4 / 2.84) * 65536
     * RF * 65536 = (295.4 * 65536 / 2.84) - (LQI * 65536 / 2.84)
     */
    uint32_t a = (uint32_t)(295.4 * 65536 / 2.84);
    uint32_t b = (uint32_t)(65536 / 2.84);
    return ((b * value) - a) >> 16;
}

void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state)
{
    DEBUG("[kw2xrf] set option %i to %i\n", option, state);

    /* set option field */
    if (state) {
        dev->netdev.flags |= option;

        /* trigger option specific actions */
        switch (option) {
            case KW2XRF_OPT_AUTOCCA:
                LOG_DEBUG("[kw2xrf] opt: enabling CCA before TX mode\n");
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_CCABFRTX);
                break;

            case KW2XRF_OPT_PROMISCUOUS:
                LOG_DEBUG("[kw2xrf] opt: enabling PROMISCUOUS mode\n");
                /* disable auto ACKs in promiscuous mode */
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_AUTOACK | MKW2XDM_PHY_CTRL1_RXACKRQD);
                /* enable promiscuous mode */
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL4,
                    MKW2XDM_PHY_CTRL4_PROMISCUOUS);
                break;

            case KW2XRF_OPT_AUTOACK:
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_AUTOACK);
                break;

            case KW2XRF_OPT_ACK_REQ:
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_RXACKRQD);
                break;

            case KW2XRF_OPT_TELL_RX_START:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_RX_WMRK_MSK);
                break;

            case KW2XRF_OPT_TELL_RX_END:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_RXMSK);
                break;

            case KW2XRF_OPT_TELL_TX_END:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_TXMSK);
                break;

            case KW2XRF_OPT_TELL_TX_START:
            default:
                /* do nothing */
                break;
        }
    }
    else {
        dev->netdev.flags &= ~(option);
        /* trigger option specific actions */
        switch (option) {
            case KW2XRF_OPT_AUTOCCA:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_CCABFRTX);
                break;

            case KW2XRF_OPT_PROMISCUOUS:
                /* disable promiscuous mode */
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL4,
                    MKW2XDM_PHY_CTRL4_PROMISCUOUS);
                /* re-enable AUTOACK only if the option is set */
                if (dev->netdev.flags & KW2XRF_OPT_AUTOACK) {
                    kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                        MKW2XDM_PHY_CTRL1_AUTOACK);
                }
                if (dev->netdev.flags & KW2XRF_OPT_ACK_REQ) {
                    kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                        MKW2XDM_PHY_CTRL1_RXACKRQD);
                }
                break;

            case KW2XRF_OPT_AUTOACK:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_AUTOACK);
                break;

            case KW2XRF_OPT_ACK_REQ:
                kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1,
                    MKW2XDM_PHY_CTRL1_RXACKRQD);
                break;

            case KW2XRF_OPT_TELL_RX_START:
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_RX_WMRK_MSK);
                break;

            case KW2XRF_OPT_TELL_RX_END:
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_RXMSK);
                break;

            case KW2XRF_OPT_TELL_TX_END:
                kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL2,
                    MKW2XDM_PHY_CTRL2_TXMSK);
                break;


            case KW2XRF_OPT_TELL_TX_START:
            default:
                /* do nothing */
                break;
        }
    }
}

netopt_state_t kw2xrf_get_status(kw2xrf_t *dev)
{
    uint8_t reg = kw2xrf_read_dreg(dev, MKW2XDM_PHY_CTRL1);

    switch (reg & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) {
        case XCVSEQ_RECEIVE:
            return NETOPT_STATE_RX;

        case XCVSEQ_TRANSMIT:
            return NETOPT_STATE_TX;

        case XCVSEQ_CCA:
            return NETOPT_STATE_RX;

        case XCVSEQ_TX_RX:
            return NETOPT_STATE_TX;

        case XCVSEQ_CONTINUOUS_CCA:
            return NETOPT_STATE_RX;

        case XCVSEQ_IDLE:
            return NETOPT_STATE_IDLE;
        default:
            break;
    }
    return NETOPT_STATE_IDLE;
}

int kw2xrf_cca(kw2xrf_t *dev)
{
    /* TODO: add Standalone CCA here */
    kw2xrf_seq_timeout_on(dev, 0x3ffff);
    kw2xrf_set_sequence(dev, XCVSEQ_CONTINUOUS_CCA);
    return 0;
}

void kw2xrf_set_rx_watermark(kw2xrf_t *dev, uint8_t value)
{
    kw2xrf_write_iregs(dev, MKW2XDMI_RX_WTR_MARK, &value, 1);
}
