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

#include "lmcal_cfg.h"
#include "lmcal_uart.h"
#include "lmcal_uart_port.h"

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

#define UART_CH_CONFIG_FILL(nChNum)                             \
{                                                               \
    .enable         = (bool)LMCAL_UART_CH##nChNum##_ENABLE,     \
    .async_mode     = (u32) LMCAL_UART_CH##nChNum##_ASYNC_MODE, \
    .uart_num       = (u32) LMCAL_UART_CH##nChNum##_UART_NUM,   \
    .port_num_tx    = (u32) LMCAL_UART_CH##nChNum##_TX_PORT,    \
    .pin_num_tx     = (u32) LMCAL_UART_CH##nChNum##_TX_PIN,     \
    .port_num_rx    = (u32) LMCAL_UART_CH##nChNum##_RX_PORT,    \
    .pin_num_rx     = (u32) LMCAL_UART_CH##nChNum##_RX_PIN,     \
    .port_num_clk   = (u32) LMCAL_UART_CH##nChNum##_CLK_PORT,   \
    .pin_num_clk    = (u32) LMCAL_UART_CH##nChNum##_CLK_PIN,    \
    .parity         = (u32) LMCAL_UART_CH##nChNum##_PARITY,     \
    .baudrate       = (u32) LMCAL_UART_CH##nChNum##_BAUDRATE    \
}



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


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

// Конфигурация канала
typedef struct
{
    bool enable;
    u32  async_mode;
    u32  uart_num;
    u32  port_num_tx;
    u32  pin_num_tx;
    u32  port_num_rx;
    u32  pin_num_rx;
    u32  port_num_clk;
    u32  pin_num_clk;
    u32  parity;
    u32  baudrate;
} uart_ch_config_t;


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

extern uart_ch_service_t lmcal_uart_port_service_var[LMCAL_UART_CHANNEL_QTY];


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

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

static const uart_ch_config_t uart_st_ch_config[LMCAL_UART_CHANNEL_QTY] =
{ 
    UART_CH_CONFIG_FILL(0),
    UART_CH_CONFIG_FILL(1),
    UART_CH_CONFIG_FILL(2),
};


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

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

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

    return err;
}
#endif

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

fun_res_t lmcal_uart_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_uart_port_init(UART_PRE_OP);
    L_CHECK_ERR(err);

    // Инициализация всех включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_UART_CHANNEL_QTY; cnt++)
    {
        if (uart_st_ch_config[cnt].enable == true)
        {            
            err = lmcal_uart_port_init_periph(uart_st_ch_config[cnt].uart_num,
                                              uart_st_ch_config[cnt].async_mode,
                                              uart_st_ch_config[cnt].port_num_tx,
                                              uart_st_ch_config[cnt].pin_num_tx,
                                              uart_st_ch_config[cnt].port_num_rx,
                                              uart_st_ch_config[cnt].pin_num_rx,
                                              uart_st_ch_config[cnt].port_num_clk,
                                              uart_st_ch_config[cnt].pin_num_clk,
                                              uart_st_ch_config[cnt].parity,
                                              uart_st_ch_config[cnt].baudrate);
            L_CHECK_ERR(err);

            // Сброс сервисного массива
            lmcal_uart_port_service_var[cnt].init = true;
            lmcal_uart_port_service_var[cnt].busy = false;
            lmcal_uart_port_service_var[cnt].error = false;
            lmcal_uart_port_service_var[cnt].tx_callback = NULL;
            lmcal_uart_port_service_var[cnt].rx_callback = NULL;
            lmcal_uart_port_service_var[cnt].err_callback = NULL;
            lmcal_uart_port_service_var[cnt].uart_num = uart_st_ch_config[cnt].uart_num;
        }
    }

    err = lmcal_uart_port_init(UART_POST_OP);
    L_CHECK_ERR(err);

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

    L_EXIT;
    return err;
}



fun_res_t lmcal_uart_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_uart_port_deinit(UART_PRE_OP);
    L_CHECK_ERR(err);

    // Выполняем деинициализацию только включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_UART_CHANNEL_QTY; cnt++)
    {
        if (uart_st_ch_config[cnt].enable == true)
        {
            err = lmcal_uart_port_deinit_periph(uart_st_ch_config[cnt].uart_num,
                                                uart_st_ch_config[cnt].port_num_tx,
                                                uart_st_ch_config[cnt].pin_num_tx,
                                                uart_st_ch_config[cnt].port_num_rx,
                                                uart_st_ch_config[cnt].pin_num_rx,
                                                uart_st_ch_config[cnt].port_num_clk,
                                                uart_st_ch_config[cnt].pin_num_clk);
            L_CHECK_ERR(err);
        }
    }

    err = lmcal_uart_port_deinit(UART_POST_OP);
    L_CHECK_ERR(err);
    
    #if (LMCAL_DEBUG == 1)
    b_init_done = false;
    #endif

    L_EXIT;
    return err;
}



err_or_res_t lmcal_uart_get_status(lmcal_uart_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 == uart_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

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

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

    sts = (lmcal_uart_port_service_var[channel].busy == true) ? (CH_STS_BUSY) : (0);
    L_CHECK_VAL(sts);
    
    // Если асинхронный режим, то проверяем аппаратный флаг статуса
    if (uart_st_ch_config[channel].async_mode == 1)
    {
        err = lmcal_uart_port_get_status(uart_st_ch_config[channel].uart_num, &sts);
    }
    else
    {
        sts = CH_STS_IDLE;
    }

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



fun_res_t lmcal_uart_enable(lmcal_uart_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 == uart_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_uart_port_enable(uart_st_ch_config[channel].uart_num, new_state);

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

    return err;
}



fun_res_t lmcal_uart_set_callback(lmcal_uart_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 == uart_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    switch(type)
    {
        case(CB_INT_RX_CALLBACK):
            lmcal_uart_port_service_var[channel].rx_callback = function;
        break;
        case(CB_INT_TX_CALLBACK):
            lmcal_uart_port_service_var[channel].tx_callback = function;
        break;
        case(CB_INT_ERROR_CALLBACK):
            lmcal_uart_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_uart_write(lmcal_uart_channel_t channel, u16 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 == uart_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

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

        err = lmcal_uart_port_write(uart_st_ch_config[channel].uart_num, 
                                    uart_st_ch_config[channel].async_mode,
                                    data);
        
        if (uart_st_ch_config[channel].async_mode == 0)
        {
            // Сброс флага при синхронном режиме
            lmcal_uart_port_service_var[channel].busy = false;
        }
        else 
        {
            // сброс флага должен быть выполнен в прерывании
        }

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

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



err_or_res_t lmcal_uart_read(lmcal_uart_channel_t channel)
{
    err_or_res_t res = ERR_OK;
    fun_res_t err = ERR_OK;
    u16 data;

    #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 == uart_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_uart_port_read(uart_st_ch_config[channel].uart_num, 
                               uart_st_ch_config[channel].async_mode,
                               &data);

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    res = (err_or_res_t)((err == ERR_OK) ? (data) : (err));
    return res;
}



fun_res_t lmcal_uart_write_buf(lmcal_uart_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 == uart_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_uart_port_write_buf(uart_st_ch_config[channel].uart_num, 
                                    uart_st_ch_config[channel].async_mode,
                                    data,
                                    size);
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



fun_res_t lmcal_uart_read_buf(lmcal_uart_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 == uart_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_uart_port_read_buf(uart_st_ch_config[channel].uart_num, 
                                    uart_st_ch_config[channel].async_mode,
                                    data,
                                    size);
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



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