//******************************************** Включаемые файлы *********************************************

#include "lmcal_cfg.h"
#include "lmcal_spi.h"
#include "lmcal_spi_port.h"

//******************************************* Локальные константы *******************************************

#define SPI_CH_CONFIG_FILL(nChNum)                              \
{                                                               \
    .enable         = (bool)LMCAL_SPI_CH##nChNum##_ENABLE,      \
    .async_mode     = (u32) LMCAL_SPI_CH##nChNum##_ASYNC_MODE,  \
    .mode           = (u32) LMCAL_SPI_CH##nChNum##_MODE,        \
    .spi_num        = (u32) LMCAL_SPI_CH##nChNum##_SPI_NUM,     \
    .baudrate       = (u32) LMCAL_SPI_CH##nChNum##_BAUDRATE,    \
    .frame_size     = (u32) LMCAL_SPI_CH##nChNum##_FRAME_SIZE,  \
    .bit_order      = (u32) LMCAL_SPI_CH##nChNum##_BIT_ORDER,   \
    .clk_pol        = (u32) LMCAL_SPI_CH##nChNum##_CLK_POL,     \
    .clk_pha        = (u32) LMCAL_SPI_CH##nChNum##_CLK_PHA,     \
    .mosi_port      = (u32) LMCAL_SPI_CH##nChNum##_MOSI_PORT,   \
    .mosi_pin       = (u32) LMCAL_SPI_CH##nChNum##_MOSI_PIN,    \
    .miso_port      = (u32) LMCAL_SPI_CH##nChNum##_MISO_PORT,   \
    .miso_pin       = (u32) LMCAL_SPI_CH##nChNum##_MISO_PIN,    \
    .clk_port       = (u32) LMCAL_SPI_CH##nChNum##_CLK_PORT,    \
    .clk_pin        = (u32) LMCAL_SPI_CH##nChNum##_CLK_PIN,     \
    .cs_port        = (u32) LMCAL_SPI_CH##nChNum##_CS_PORT,     \
    .cs_pin         = (u32) LMCAL_SPI_CH##nChNum##_CS_PIN       \
}


//******************************************* Проверка параметров *******************************************


//******************************************** Определения типов ********************************************

// Конфигурация канала
typedef struct
{
    bool enable;
    u32  async_mode;
    u32  mode;
    u32  spi_num;
    u32  baudrate;
    u32  frame_size;
    u32  bit_order;
    u32  clk_pol;
    u32  clk_pha;
    u32  mosi_port;
    u32  mosi_pin;
    u32  miso_port;
    u32  miso_pin;
    u32  clk_port;
    u32  clk_pin;
    u32  cs_port;
    u32  cs_pin;
} spi_ch_config_t;



//************************************ Глобальные (публичные) переменные ************************************

extern spi_ch_service_t lmcal_spi_port_service_var[LMCAL_SPI_CHANNEL_QTY];


//************************************* Локальные (приватные) переменные ************************************

#if (LMCAL_DEBUG == 1)
static bool b_init_done = false;
#endif

static const spi_ch_config_t spi_st_ch_config[LMCAL_SPI_CHANNEL_QTY] =
{ 
    SPI_CH_CONFIG_FILL(0),
    SPI_CH_CONFIG_FILL(1),
    SPI_CH_CONFIG_FILL(2),
    SPI_CH_CONFIG_FILL(3),
};



//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************

#if (LMCAL_DEBUG == 1)
static fun_res_t f_check_channel(lmcal_spi_channel_t channel)
{
    fun_res_t err = ERR_OK;

    if (channel > LMCAL_SPI_CHANNEL_QTY)
    {
        err = ERR_INVALID_ARGUMENT;
    }

    return err;
}
#endif


//***********************************************************************************************************
//******************************** Определения процедур обработки прерываний ********************************
//***********************************************************************************************************


//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

fun_res_t lmcal_spi_init(void)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(false == b_init_done);
        L_CHECK_ERR(err);
    #endif
    
    err = lmcal_spi_port_init(SPI_PRE_OP);
    L_CHECK_ERR(err);

    // Инициализация всех включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_SPI_CHANNEL_QTY; cnt++)
    {
        if (spi_st_ch_config[cnt].enable == true)
        {
            err = lmcal_spi_port_init_periph(spi_st_ch_config[cnt].spi_num,
                                             spi_st_ch_config[cnt].async_mode,
                                             spi_st_ch_config[cnt].mode,
                                             spi_st_ch_config[cnt].baudrate,
                                             spi_st_ch_config[cnt].frame_size,
                                             spi_st_ch_config[cnt].bit_order,
                                             spi_st_ch_config[cnt].clk_pol,
                                             spi_st_ch_config[cnt].clk_pha,
                                             spi_st_ch_config[cnt].mosi_port,
                                             spi_st_ch_config[cnt].mosi_pin,
                                             spi_st_ch_config[cnt].miso_port,
                                             spi_st_ch_config[cnt].miso_pin,
                                             spi_st_ch_config[cnt].clk_port,
                                             spi_st_ch_config[cnt].clk_pin,
                                             spi_st_ch_config[cnt].cs_port,
                                             spi_st_ch_config[cnt].cs_pin);
            L_CHECK_ERR(err);

            // Сброс сервисного массива
            lmcal_spi_port_service_var[cnt].init = true;
            lmcal_spi_port_service_var[cnt].busy = false;
            lmcal_spi_port_service_var[cnt].error = false;
            lmcal_spi_port_service_var[cnt].tx_callback = NULL;
            lmcal_spi_port_service_var[cnt].rx_callback = NULL;
            lmcal_spi_port_service_var[cnt].err_callback = NULL;
            lmcal_spi_port_service_var[cnt].spi_num = spi_st_ch_config[cnt].spi_num;
            lmcal_spi_port_service_var[cnt].cs_port = spi_st_ch_config[cnt].cs_port;
            lmcal_spi_port_service_var[cnt].cs_pin  = spi_st_ch_config[cnt].cs_pin;
        }
    }

    err = lmcal_spi_port_init(SPI_POST_OP);
    L_CHECK_ERR(err);

    #if (LMCAL_DEBUG == 1)
    b_init_done = true;
    #endif

    L_EXIT;
    return err;
}



fun_res_t lmcal_spi_deinit(void)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
    #endif
    
    err = lmcal_spi_port_deinit(SPI_PRE_OP);
    L_CHECK_ERR(err);

    // Выполняем деинициализацию только включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_SPI_CHANNEL_QTY; cnt++)
    {
        if (spi_st_ch_config[cnt].enable == true)
        {
            err = lmcal_spi_port_deinit_periph(spi_st_ch_config[cnt].spi_num,
                                               spi_st_ch_config[cnt].mosi_port,
                                               spi_st_ch_config[cnt].mosi_pin,
                                               spi_st_ch_config[cnt].miso_port,
                                               spi_st_ch_config[cnt].miso_pin,
                                               spi_st_ch_config[cnt].clk_port,
                                               spi_st_ch_config[cnt].clk_pin,
                                               spi_st_ch_config[cnt].cs_port,
                                               spi_st_ch_config[cnt].cs_pin);
            L_CHECK_ERR(err);
        }
    }

    err = lmcal_spi_port_deinit(SPI_POST_OP);
    L_CHECK_ERR(err);
    
    #if (LMCAL_DEBUG == 1)
    b_init_done = false;
    #endif

    L_EXIT;
    return err;
}



err_or_res_t lmcal_spi_get_status(lmcal_spi_channel_t channel)
{
    err_or_res_t res = ERR_OK;
    fun_res_t err = ERR_OK;
    lmcal_ch_sts_t sts = 0;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    // Проверка инициализации канала
    sts = (lmcal_spi_port_service_var[channel].init == false) ? (CH_STS_NOT_INIT) : (0);
    L_CHECK_VAL(sts);

    sts = (lmcal_spi_port_service_var[channel].error == true) ? (CH_STS_ERROR) : (0);
    L_CHECK_VAL(sts);

    sts = (lmcal_spi_port_service_var[channel].busy == true) ? (CH_STS_BUSY) : (0);
    L_CHECK_VAL(sts);
    
    // Проверяем статус т.к. один SPI может быть у нескольких каналов одновременно
    err = lmcal_spi_port_get_status(spi_st_ch_config[channel].spi_num, 
                                    spi_st_ch_config[channel].mode,
                                    &sts);

    L_EXIT;
    res = (err_or_res_t)((err == ERR_OK) ? (sts) : (err));
    return res;
}



fun_res_t lmcal_spi_enable(lmcal_spi_channel_t channel, bool new_state)
{
    fun_res_t err = ERR_OK;
    
    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_spi_port_enable(spi_st_ch_config[channel].spi_num, new_state);

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif

    return err;
}



fun_res_t lmcal_spi_set_callback(lmcal_spi_channel_t channel, 
                                  lmcal_int_callback_type_t type,
                                  void_fun_t function)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    switch(type)
    {
        case(CB_INT_RX_CALLBACK):
            lmcal_spi_port_service_var[channel].rx_callback = function;
        break;
        case(CB_INT_TX_CALLBACK):
            lmcal_spi_port_service_var[channel].tx_callback = function;
        break;
        case(CB_INT_ERROR_CALLBACK):
            lmcal_spi_port_service_var[channel].err_callback = function;
        break;

        default:
            err = ERR_INVALID_ARGUMENT;
        break;
    }
    
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif

    return err;
}



fun_res_t lmcal_spi_write(lmcal_spi_channel_t channel, u32 data)
{
    fun_res_t err = ERR_OK;
    lmcal_ch_sts_t sts = 0;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
        // Проверяем статус т.к. один SPI может быть у нескольких каналов одновременно
        err = lmcal_spi_port_get_status(spi_st_ch_config[channel].spi_num, 
                                        spi_st_ch_config[channel].mode,
                                        &sts);
        L_CHECK_ERR(err);
        err = (sts == CH_STS_IDLE) ? (ERR_OK) : (ERR_RUNTIME_ERROR);
        L_CHECK_ERR(err);
    #endif

    if (lmcal_spi_port_service_var[channel].busy == false)
    {
        lmcal_spi_port_service_var[channel].busy = true;

        err = lmcal_spi_port_write(spi_st_ch_config[channel].spi_num, 
                                   spi_st_ch_config[channel].async_mode,
                                   spi_st_ch_config[channel].mode,
                                   spi_st_ch_config[channel].cs_port,
                                   spi_st_ch_config[channel].cs_pin,
                                   data);
        
        if (spi_st_ch_config[channel].async_mode == 0)
        {
            // Сброс флага при синхронном режиме
            lmcal_spi_port_service_var[channel].busy = false;
        }
        else 
        {
            // сброс флага должен быть выполнен в прерывании
        }

        if (err != ERR_OK)
        {
            // Ошибка
            lmcal_spi_port_service_var[channel].busy = false;
        }
    }
    else
    {
        err = ERR_RUNTIME_ERROR;
    }

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



fun_res_t lmcal_spi_read(lmcal_spi_channel_t channel, u32* data)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_spi_port_read(spi_st_ch_config[channel].spi_num, 
                              spi_st_ch_config[channel].async_mode,
                              spi_st_ch_config[channel].mode,
                              data);

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



fun_res_t lmcal_spi_write_buf(lmcal_spi_channel_t channel, u8* data, u16 size)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
        err = L_ASSERT(data != NULL);
        L_CHECK_ERR(err);
        err = L_ASSERT(size > 0);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_spi_port_write_buf(spi_st_ch_config[channel].spi_num, 
                                    spi_st_ch_config[channel].async_mode,
                                    spi_st_ch_config[channel].mode,
                                    data,
                                    size);
                                    
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



fun_res_t lmcal_spi_read_buf(lmcal_spi_channel_t channel, u8* data, u16 size)
{
    fun_res_t err = ERR_OK;
    
    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == spi_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
        err = L_ASSERT(data != NULL);
        L_CHECK_ERR(err);
        err = L_ASSERT(size > 0);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_spi_port_read_buf(spi_st_ch_config[channel].spi_num, 
                                  spi_st_ch_config[channel].async_mode,
                                  spi_st_ch_config[channel].mode,
                                  data,
                                  size);
    
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}


//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
