/*
 * This file is part of the bladeRF project:
 *   http://www.github.com/nuand/bladeRF
 *
 * Copyright (C) 2018 Nuand LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef BLADERF2_COMMON_H_
#define BLADERF2_COMMON_H_

#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
#include "log.h"
#endif

#include "bladerf2_common.h"
#include "helpers/version.h"
#include "streaming/sync.h"


/******************************************************************************/
/* Types */
/******************************************************************************/

enum bladerf2_vctcxo_trim_source {
    TRIM_SOURCE_NONE,
    TRIM_SOURCE_TRIM_DAC,
    TRIM_SOURCE_PLL,
    TRIM_SOURCE_AUXDAC
};

enum bladerf2_rfic_command_mode {
    RFIC_COMMAND_HOST, /**< Host-based control */
    RFIC_COMMAND_FPGA, /**< FPGA-based control */
};

struct controller_fns {
    bool (*is_present)(struct bladerf *dev);
    bool (*is_initialized)(struct bladerf *dev);
    bool (*is_standby)(struct bladerf *dev);
    int (*get_init_state)(struct bladerf *dev, bladerf_rfic_init_state *state);

    int (*initialize)(struct bladerf *dev);
    int (*standby)(struct bladerf *dev);
    int (*deinitialize)(struct bladerf *dev);

    int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable);

    int (*get_sample_rate)(struct bladerf *dev,
                           bladerf_channel ch,
                           bladerf_sample_rate *rate);
    int (*set_sample_rate)(struct bladerf *dev,
                           bladerf_channel ch,
                           bladerf_sample_rate rate);

    int (*get_frequency)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_frequency *frequency);
    int (*set_frequency)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_frequency frequency);
    int (*select_band)(struct bladerf *dev,
                       bladerf_channel ch,
                       bladerf_frequency frequency);

    int (*get_bandwidth)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_bandwidth *bandwidth);
    int (*set_bandwidth)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_bandwidth bandwidth,
                         bladerf_bandwidth *actual);

    int (*get_gain_mode)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_gain_mode *mode);
    int (*set_gain_mode)(struct bladerf *dev,
                         bladerf_channel ch,
                         bladerf_gain_mode mode);

    int (*get_gain)(struct bladerf *dev, bladerf_channel ch, int *gain);
    int (*set_gain)(struct bladerf *dev, bladerf_channel ch, int gain);

    int (*get_gain_stage)(struct bladerf *dev,
                          bladerf_channel ch,
                          char const *stage,
                          int *gain);
    int (*set_gain_stage)(struct bladerf *dev,
                          bladerf_channel ch,
                          char const *stage,
                          int gain);

    int (*get_rssi)(struct bladerf *dev,
                    bladerf_channel ch,
                    int *pre_rssi,
                    int *sym_rssi);

    int (*get_filter)(struct bladerf *dev,
                      bladerf_channel ch,
                      bladerf_rfic_rxfir *rxfir,
                      bladerf_rfic_txfir *txfir);
    int (*set_filter)(struct bladerf *dev,
                      bladerf_channel ch,
                      bladerf_rfic_rxfir rxfir,
                      bladerf_rfic_txfir txfir);

    int (*get_txmute)(struct bladerf *dev, bladerf_channel ch, bool *state);
    int (*set_txmute)(struct bladerf *dev, bladerf_channel ch, bool state);

    int (*store_fastlock_profile)(struct bladerf *dev,
                                  bladerf_channel ch,
                                  uint32_t profile);

    int (*save_fastlock_profile)(struct bladerf *dev,
                                 bladerf_channel ch,
                                 uint32_t profile,
                                 uint8_t *values);

    enum bladerf2_rfic_command_mode const command_mode;
};

struct bladerf2_board_data {
    /* Board state */
    enum {
        STATE_UNINITIALIZED,
        STATE_FIRMWARE_LOADED,
        STATE_FPGA_LOADED,
        STATE_INITIALIZED,
    } state;

    /* AD9361 PHY Handle */
    struct ad9361_rf_phy *phy;

    /* RFIC configuration parameters */
    void *rfic_init_params;

    /* Bitmask of capabilities determined by version numbers */
    uint64_t capabilities;

    /* Format currently being used with a module, or -1 if module is not used */
    bladerf_format module_format[NUM_MODULES];

    /* Which mode of operation we use for tuning */
    bladerf_tuning_mode tuning_mode;

    /* Board properties */
    bladerf_fpga_size fpga_size;
    /* Data message size */
    size_t msg_size;

    /* Version information */
    struct bladerf_version fpga_version;
    struct bladerf_version fw_version;
    char fpga_version_str[BLADERF_VERSION_STR_MAX + 1];
    char fw_version_str[BLADERF_VERSION_STR_MAX + 1];

    /* Synchronous interface handles */
    struct bladerf_sync sync[2];

    /* VCTCXO trim state */
    enum bladerf2_vctcxo_trim_source trim_source;
    uint16_t trimdac_last_value;   /**< saved running value */
    uint16_t trimdac_stored_value; /**< cached value read from SPI flash */

    /* Quick Tune Profile Status */
    uint16_t quick_tune_tx_profile;
    uint16_t quick_tune_rx_profile;

    /* RFIC backend command handling */
    struct controller_fns const *rfic;

    /* RFIC FIR Filter status */
    bladerf_rfic_rxfir rxfir;
    bladerf_rfic_txfir txfir;

    /* If true, RFIC control will be fully de-initialized on close, instead of
     * just put into a standby state. */
    bool rfic_reset_on_close;
};

struct bladerf_rfic_status_register {
    bool rfic_initialized;
    size_t write_queue_length;
};


/******************************************************************************/
/* Externs */
/******************************************************************************/

extern AD9361_InitParam bladerf2_rfic_init_params;
extern AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst;
extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config;
extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config;
extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2;
extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2;
extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4;
extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4;
extern const float ina219_r_shunt;


/******************************************************************************/
/* Constants */
/******************************************************************************/

extern char const *bladerf2_state_to_string[4];


/******************************************************************************/
/* Macros */
/******************************************************************************/

/* Macro for logging and returning an error status. This should be used for
 * errors defined in the \ref RETCODES list. */
#define RETURN_ERROR_STATUS(_what, _status)                   \
    do {                                                      \
        log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \
                  bladerf_strerror(_status));                 \
        return _status;                                       \
    } while (0)

/* Macro for converting, logging, and returning libad9361 error codes. */
#define RETURN_ERROR_AD9361(_what, _status)                           \
    do {                                                              \
        RETURN_ERROR_STATUS(_what, errno_ad9361_to_bladerf(_status)); \
    } while (0)

/* Macro for logging and returning ::BLADERF_ERR_INVAL */
#define RETURN_INVAL_ARG(_what, _arg, _why)                               \
    do {                                                                  \
        log_error("%s: %s '%s' invalid: %s\n", __FUNCTION__, _what, #_arg, \
                  _why);                                                  \
        return BLADERF_ERR_INVAL;                                         \
    } while (0)

#define RETURN_INVAL(_what, _why)                                     \
    do {                                                              \
        log_error("%s: %s invalid: %s\n", __FUNCTION__, _what, _why); \
        return BLADERF_ERR_INVAL;                                     \
    } while (0)

/**
 * @brief   Null test
 *
 * @param   _var  The variable to check
 *
 * @return  RETURN_INVAL if _var is null, continues otherwise
 */
#define NULL_CHECK(_var)                    \
    do {                                    \
        if (NULL == _var) {                 \
            RETURN_INVAL(#_var, "is null"); \
        }                                   \
    } while (0)

/**
 * @brief   Null test, with mutex unlock on failure
 *
 * @param   _var  The variable to check
 *
 * @return  RETURN_INVAL if _var is null, continues otherwise
 */
#define NULL_CHECK_LOCKED(_var)             \
    do {                                    \
        NULL_CHECK(dev);                    \
                                            \
        if (NULL == _var) {                 \
            MUTEX_UNLOCK(__lock);           \
            RETURN_INVAL(#_var, "is null"); \
        }                                   \
    } while (0)

/**
 * @brief   Board state check
 *
 * @param   _state  Minimum sufficient board state
 *
 * @return  BLADERF_ERR_NOT_INIT if board's state is less than _state, continues
 *          otherwise
 */
#define CHECK_BOARD_STATE(_state)                                         \
    do {                                                                  \
        NULL_CHECK(dev);                                                  \
        NULL_CHECK(dev->board);                                           \
                                                                          \
        struct bladerf2_board_data *_bd = dev->board_data;                \
                                                                          \
        if (_bd->state < _state) {                                        \
            log_error("%s: Board state insufficient for operation "       \
                      "(current \"%s\", requires \"%s\").\n",             \
                      __FUNCTION__, bladerf2_state_to_string[_bd->state], \
                      bladerf2_state_to_string[_state]);                  \
                                                                          \
            return BLADERF_ERR_NOT_INIT;                                  \
        }                                                                 \
    } while (0)

/**
 * @brief   Test if board is a bladeRF 2
 *
 * @param   _dev  Device handle
 *
 * @return  BLADERF_ERR_UNSUPPORTED if board is not a bladeRF 2, continues
 *          otherwise
 */
#define CHECK_BOARD_IS_BLADERF2(_dev)                                        \
    do {                                                                     \
        NULL_CHECK(_dev);                                                    \
        NULL_CHECK(_dev->board);                                             \
                                                                             \
        if (_dev->board != &bladerf2_board_fns) {                            \
            log_error("%s: Board type \"%s\" not supported\n", __FUNCTION__, \
                      _dev->board->name);                                    \
            return BLADERF_ERR_UNSUPPORTED;                                  \
        }                                                                    \
    } while (0)

/**
 * @brief   Call a function and return early if it fails
 *
 * @param   _fn   The function
 *
 * @return  function return value if less than zero; continues otherwise
 */
#define CHECK_STATUS(_fn)                  \
    do {                                   \
        int _s = _fn;                      \
        if (_s < 0) {                      \
            RETURN_ERROR_STATUS(#_fn, _s); \
        }                                  \
    } while (0)

/**
 * @brief   Call a function and goto error if it fails
 *
 * @param   _fn   The function
 *
 * @note    `int status` must be declared in the including scope
 */
#define CHECK_STATUS_GOTO(_fn)                             \
    do {                                                   \
        status = _fn;                                      \
        if (status < 0) {                                  \
            log_error("%s: %i failed: %s\n", #_fn, status, \
                      bladerf_strerror(status));           \
            goto error;                                    \
        }                                                  \
    } while (0)

/**
 * @brief   Call a function and, if it fails, unlock the mutex and return
 *
 * @param   _fn   The function
 *
 * @return  function return value if less than zero; continues otherwise
 */
#define CHECK_STATUS_LOCKED(_fn)           \
    do {                                   \
        int _s = _fn;                      \
        if (_s < 0) {                      \
            MUTEX_UNLOCK(__lock);          \
            RETURN_ERROR_STATUS(#_fn, _s); \
        }                                  \
    } while (0)

/**
 * @brief   Call a function and return early if it fails, with error translation
 *          from AD936x to bladeRF return codes
 *
 * @param   _fn   The function
 *
 * @return  function return value if less than zero; continues otherwise
 */
#define CHECK_AD936X(_fn)                  \
    do {                                   \
        int _s = _fn;                      \
        if (_s < 0) {                      \
            RETURN_ERROR_AD9361(#_fn, _s); \
        }                                  \
    } while (0)

/**
 * @brief   Call a function and, if it fails, unlock the mutex and return, with
 *          error translation from AD936x to bladeRF return codes
 *
 * @param   _fn   The function
 *
 * @return  function return value if less than zero; continues otherwise
 */
#define CHECK_AD936X_LOCKED(_fn)           \
    do {                                   \
        int _s = _fn;                      \
        if (_s < 0) {                      \
            MUTEX_UNLOCK(__lock);          \
            RETURN_ERROR_AD9361(#_fn, _s); \
        }                                  \
    } while (0)

/**
 * @brief   Execute a command block with a mutex lock
 *
 * @note    Variables declared within the including scope must be declared
 *          one-per-line.
 *
 * @param   _lock    Lock to hold
 * @param   _thing   Block to execute
 */
#define WITH_MUTEX(_lock, _thing) \
    do {                          \
        MUTEX *__lock = _lock;    \
                                  \
        MUTEX_LOCK(__lock);       \
        _thing;                   \
        MUTEX_UNLOCK(__lock);     \
    } while (0)

/**
 * @brief   Execute command block, conditional to _mode
 *
 * @param   _dev     Device handle
 * @param   _mode    Command mode
 * @param   _thing   Block to do if it happens
 */
#define IF_COMMAND_MODE(_dev, _mode, _thing)               \
    do {                                                   \
        NULL_CHECK(_dev);                                  \
        NULL_CHECK(_dev->board_data);                      \
                                                           \
        struct bladerf2_board_data *bd = _dev->board_data; \
                                                           \
        if (bd->rfic->command_mode == _mode) {             \
            _thing;                                        \
        };                                                 \
    } while (0)


/******************************************************************************/
/* Functions */
/******************************************************************************/

/**
 * Perform the neccessary device configuration for the specified format
 * (e.g., enabling/disabling timestamp support), first checking that the
 * requested format would not conflict with the other stream direction.
 *
 * @param           dev     Device handle
 * @param[in]       dir     Direction that is currently being configured
 * @param[in]       format  Format the channel is being configured for
 *
 * @return 0 on success, BLADERF_ERR_* on failure
 */
int perform_format_config(struct bladerf *dev,
                          bladerf_direction dir,
                          bladerf_format format);

/**
 * Deconfigure and update any state pertaining what a format that a stream
 * direction is no longer using.
 *
 * @param       dev     Device handle
 * @param[in]   dir     Direction that is currently being deconfigured
 *
 * @return 0 on success, BLADERF_ERR_* on failure
 */
int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir);

bool is_valid_fpga_size(struct bladerf *dev,
                        bladerf_fpga_size fpga,
                        size_t len);

bool is_valid_fw_size(size_t len);

bladerf_tuning_mode default_tuning_mode(struct bladerf *dev);

bool check_total_sample_rate(struct bladerf *dev);

bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir);

int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset);

#endif  // BLADERF2_COMMON_H_