/*********************************************************************************************************//**
 * @file    uart_module.c
 * @version $Rev:: 465          $
 * @date    $Date:: 2025-05-06 #$
 * @brief   The UART Module (interrupt mode with buffer).
 *************************************************************************************************************
 * @attention
 *
 * Firmware Disclaimer Information
 *
 * 1. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, which is supplied by Holtek Semiconductor Inc., (hereinafter referred to as "HOLTEK") is the
 *    proprietary and confidential intellectual property of HOLTEK, and is protected by copyright law and
 *    other intellectual property laws.
 *
 * 2. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, is confidential information belonging to HOLTEK, and must not be disclosed to any third parties
 *    other than HOLTEK and the customer.
 *
 * 3. The program technical documentation, including the code, is provided "as is" and for customer reference
 *    only. After delivery by HOLTEK, the customer shall use the program technical documentation, including
 *    the code, at their own risk. HOLTEK disclaims any expressed, implied or statutory warranties, including
 *    the warranties of merchantability, satisfactory quality and fitness for a particular purpose.
 *
 * <h2><center>Copyright (C) Holtek Semiconductor Inc. All rights reserved</center></h2>
 ************************************************************************************************************/

/* Includes ------------------------------------------------------------------------------------------------*/
#include "ht32.h"
#include "ht32_board_config.h"
#include "uart_module.h"
#include "common/ring_buffer.h"

/* Private types -------------------------------------------------------------------------------------------*/
typedef struct
{
  usart_type *pUARTx;
  u8 uIsTxFinished;
} UARTModule_StateTypeDef;

/* Private constants ---------------------------------------------------------------------------------------*/
#define _UART_CNT_CH1             (0)
#define _UART_CNT_CH2             (0)
#define _UART_CNT_CH3             (0)

#define _TX0_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM0_TX_GPIO_PORT)
#define _TX0_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM0_TX_GPIO_PIN)
#define _RX0_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM0_RX_GPIO_PORT)
#define _RX0_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM0_RX_GPIO_PIN)
#define _RX0_GPIO_PORT            STRCAT2(GPIO,   HTCFG_UARTM0_RX_GPIO_PORT)
#define _RX0_GPIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM0_RX_GPIO_PIN)
#define _UART_CNT_CH0             (1)

#ifdef HTCFG_UARTM_CH1
#define _TX1_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM1_TX_GPIO_PORT)
#define _TX1_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM1_TX_GPIO_PIN)
#define _RX1_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM1_RX_GPIO_PORT)
#define _RX1_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM1_RX_GPIO_PIN)
#define _RX1_GPIO_PORT            STRCAT2(GPIO,   HTCFG_UARTM1_RX_GPIO_PORT)
#define _RX1_GPIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM1_RX_GPIO_PIN)
#define _UART_CNT_CH1             (1)
#endif

#ifdef HTCFG_UARTM_CH2
#define _TX2_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM2_TX_GPIO_PORT)
#define _TX2_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM2_TX_GPIO_PIN)
#define _RX2_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM2_RX_GPIO_PORT)
#define _RX2_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM2_RX_GPIO_PIN)
#define _RX2_GPIO_PORT            STRCAT2(GPIO,   HTCFG_UARTM2_RX_GPIO_PORT)
#define _RX2_GPIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM2_RX_GPIO_PIN)
#define _UART_CNT_CH2             (1)
#endif

#ifdef HTCFG_UARTM_CH3
#define _TX3_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM3_TX_GPIO_PORT)
#define _TX3_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM3_TX_GPIO_PIN)
#define _RX3_GPIO_ID              STRCAT2(GPIO,    HTCFG_UARTM3_RX_GPIO_PORT)
#define _RX3_AFIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM3_RX_GPIO_PIN)
#define _RX3_GPIO_PORT            STRCAT2(GPIO,   HTCFG_UARTM3_RX_GPIO_PORT)
#define _RX3_GPIO_PIN             STRCAT2(GPIO_PINS_, HTCFG_UARTM3_RX_GPIO_PIN)
#define _UART_CNT_CH3             (1)
#endif

#define UART_MODULE_SUPPORT_CH                (_UART_CNT_CH0+_UART_CNT_CH1+_UART_CNT_CH2+_UART_CNT_CH3)

/* Private function prototypes -----------------------------------------------------------------------------*/
static void _UARTM_IRQHandler(u32 CH);

/* Private macro -------------------------------------------------------------------------------------------*/
#define __DBG_Printf(...)

#if (UART_MODULE_DEBUG_MODE == 1)
  #undef __DBG_Printf
  #define __DBG_Printf printf
  #warning "================================ Debug Mode Warning ===================================="
  #warning " Debug mode has been enable which degrade the performance.                              "
  #warning " After all debug operation is finished, please remember to turn off debug mode.         "
  #warning "========================================================================================"
#endif

/* Global variables ----------------------------------------------------------------------------------------*/
Buffer_TypeDef gUART_Tx[UART_MODULE_SUPPORT_CH];
Buffer_TypeDef gUART_Rx[UART_MODULE_SUPPORT_CH];
__ALIGN4 u8 guUART_TxBuffer[UART_MODULE_SUPPORT_CH][HTCFG_UARTM0_TX_BUFFER_SIZE];
__ALIGN4 u8 guUART_RxBuffer[UART_MODULE_SUPPORT_CH][HTCFG_UARTM0_RX_BUFFER_SIZE];

/* Private variables ---------------------------------------------------------------------------------------*/
static UARTModule_StateTypeDef gUARTModuleState[UART_MODULE_SUPPORT_CH];

/* Global functions ----------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
  * @brief  UART Module init function.
  * @param  CH
  * @param  pUART_Init
  * @param  uRxTimeOutValue
  * @retval None
  ***********************************************************************************************************/
void UARTM_Init(u32 CH, USART_InitTypeDef *pUART_Init, u32 Unused)
{
  gpio_init_type gpio_init_struct;

  gUARTModuleState[UARTM_CH0].pUARTx = HTCFG_UARTM_CH0;
  #ifdef HTCFG_UARTM_CH1
  gUARTModuleState[UARTM_CH1].pUARTx = HTCFG_UARTM_CH1;
  #endif
  #ifdef HTCFG_UARTM_CH2
  gUARTModuleState[UARTM_CH2].pUARTx = HTCFG_UARTM_CH2;
  #endif
  #ifdef HTCFG_UARTM_CH3
  gUARTModuleState[UARTM_CH3].pUARTx = HTCFG_UARTM_CH3;
  #endif

  if (CH == UARTM_CH0)
  {
    Buffer_Init(&gUART_Tx[CH], guUART_TxBuffer[CH], HTCFG_UARTM0_TX_BUFFER_SIZE);
    Buffer_Init(&gUART_Rx[CH], guUART_RxBuffer[CH], HTCFG_UARTM0_RX_BUFFER_SIZE);

    /* Enable UART & AFIO APB clock                                                                         */
    crm_periph_clock_enable(HTCFG_UARTM0_UART_CLK, TRUE);
    crm_periph_clock_enable(HTCFG_UARTM0_RX_GPIO_CLK, TRUE);

    gpio_default_para_init(&gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = _TX0_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(_TX0_GPIO_ID, &gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
#if defined(USE_HT32F49163_SK) || defined(USE_HT32F49041_SK)
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
#endif
#if defined(USE_HT32F49395_SK)
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
#endif
    gpio_init_struct.gpio_pins = _RX0_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(_RX0_GPIO_ID, &gpio_init_struct);

#if defined(USE_HT32F49163_SK) || defined(USE_HT32F49041_SK)
    gpio_pin_mux_config(_TX0_GPIO_ID, HTCFG_UARTM0_TX_PIN_SOURCE, HTCFG_UARTM0_PIN_SOURCE);
    gpio_pin_mux_config(_RX0_GPIO_ID, HTCFG_UARTM0_RX_PIN_SOURCE, HTCFG_UARTM0_PIN_SOURCE);
#endif

    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    nvic_irq_enable(HTCFG_UARTM0_IRQn, 0, 0);
  }
  #ifdef HTCFG_UARTM_CH1
  else if (CH == UARTM_CH1)
  {
    Buffer_Init(&gUART_Tx[CH], guUART_TxBuffer[CH], HTCFG_UARTM1_TX_BUFFER_SIZE);
    Buffer_Init(&gUART_Rx[CH], guUART_RxBuffer[CH], HTCFG_UARTM1_RX_BUFFER_SIZE);

    /* Enable UART & AFIO APB clock                                                                         */
    crm_periph_clock_enable(HTCFG_UARTM1_UART_CLK, TRUE);
    crm_periph_clock_enable(HTCFG_UARTM1_RX_GPIO_CLK, TRUE);

    gpio_default_para_init(&gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = _TX1_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(_TX1_GPIO_ID, &gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pins = _RX1_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(_RX1_GPIO_ID, &gpio_init_struct);

    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    nvic_irq_enable(HTCFG_UARTM1_IRQn, 0, 0);
  }
  #endif
  #ifdef HTCFG_UARTM_CH2
  else if (CH == UARTM_CH2)
  {
    Buffer_Init(&gUART_Tx[CH], guUART_TxBuffer[CH], HTCFG_UARTM2_TX_BUFFER_SIZE);
    Buffer_Init(&gUART_Rx[CH], guUART_RxBuffer[CH], HTCFG_UARTM2_RX_BUFFER_SIZE);

    /* Enable UART & AFIO APB clock                                                                         */
    crm_periph_clock_enable(HTCFG_UARTM2_UART_CLK, TRUE);
    crm_periph_clock_enable(HTCFG_UARTM2_RX_GPIO_CLK, TRUE);

    gpio_default_para_init(&gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = _TX2_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(_TX2_GPIO_ID, &gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pins = _RX2_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(_RX2_GPIO_ID, &gpio_init_struct);

    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    nvic_irq_enable(HTCFG_UARTM2_IRQn, 0, 0);
  }
  #endif
  #ifdef HTCFG_UARTM_CH3
  else if (CH == UARTM_CH3)
  {
    Buffer_Init(&gUART_Tx[CH], guUART_TxBuffer[CH], HTCFG_UARTM3_TX_BUFFER_SIZE);
    Buffer_Init(&gUART_Rx[CH], guUART_RxBuffer[CH], HTCFG_UARTM3_RX_BUFFER_SIZE);

    /* Enable UART & AFIO APB clock                                                                         */
    crm_periph_clock_enable(HTCFG_UARTM3_UART_CLK, TRUE);
    crm_periph_clock_enable(HTCFG_UARTM3_RX_GPIO_CLK, TRUE);

    gpio_default_para_init(&gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = _TX3_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(_TX3_GPIO_ID, &gpio_init_struct);

    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pins = _RX3_AFIO_PIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(_RX3_GPIO_ID, &gpio_init_struct);

    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    nvic_irq_enable(HTCFG_UARTM3_IRQn, 0, 0);
  }
  #endif

  usart_init(gUARTModuleState[CH].pUARTx, pUART_Init->USART_BaudRate, pUART_Init->USART_WordLength, pUART_Init->USART_StopBits);
  usart_parity_selection_config(gUARTModuleState[CH].pUARTx, pUART_Init->USART_Parity);
  usart_transmitter_enable(gUARTModuleState[CH].pUARTx, TRUE);
  usart_receiver_enable(gUARTModuleState[CH].pUARTx, TRUE);

  usart_interrupt_enable(gUARTModuleState[CH].pUARTx, USART_RDBF_INT, TRUE);
  usart_enable(gUARTModuleState[CH].pUARTx, TRUE);
}

/*********************************************************************************************************//**
  * @brief  UART module write byte (Tx).
  * @param  CH
  * @param  uData
  * @retval SUCCESS or ERROR
  ***********************************************************************************************************/
u32 UARTM_WriteByte(u32 CH, u8 uData)
{
  u32 uResult;
  uResult = Buffer_WriteByte(&gUART_Tx[CH], uData);
  if (uResult == TRUE)
  {
    gUARTModuleState[CH].uIsTxFinished = FALSE;
    usart_interrupt_enable(gUARTModuleState[CH].pUARTx, USART_TDC_INT | USART_TDBE_INT, TRUE);
  }

  return uResult;
}

/*********************************************************************************************************//**
  * @brief  UART module write (Tx).
  * @param  CH
  * @param  pBuffer
  * @param  uLength
  * @retval success write count
  ***********************************************************************************************************/
u32 UARTM_Write(u32 CH, u8 *pBuffer, u32 uLength)
{
  u32 uResult;
  u32 uFreeSpace = gUART_Tx[CH].uSize - Buffer_GetLength(&gUART_Tx[CH]);
  if (uLength > uFreeSpace)
  {
    return 0;
  }
  uResult = Buffer_Write(&gUART_Tx[CH], pBuffer, uLength);
  if (uResult > 0)
  {
    gUARTModuleState[CH].uIsTxFinished = FALSE;
    usart_interrupt_enable(gUARTModuleState[CH].pUARTx, USART_TDC_INT | USART_TDBE_INT, TRUE);
  }

  return uResult;
}

/*********************************************************************************************************//**
  * @brief  UART module read byte (Rx).
  * @param  CH
  * @param  pData
  * @retval SUCCESS or ERROR
  ***********************************************************************************************************/
u32 UARTM_ReadByte(u32 CH, u8 *pData)
{
  return (Buffer_ReadByte(&gUART_Rx[CH], pData));
}

/*********************************************************************************************************//**
  * @brief  UART module read (Rx).
  * @param  CH
  * @param  pBuffer
  * @param  uLength
  * @retval Read count
  ***********************************************************************************************************/
u32 UARTM_Read(u32 CH, u8 *pBuffer, u32 uLength)
{
  return (Buffer_Read(&gUART_Rx[CH], pBuffer, uLength));
}

/*********************************************************************************************************//**
  * @brief  Get Read buffer length (Rx).
  * @param  CH
  * @retval uLength
  ***********************************************************************************************************/
u32 UARTM_GetReadBufferLength(u32 CH)
{
  return (Buffer_GetLength(&gUART_Rx[CH]));
}

/*********************************************************************************************************//**
  * @brief  Get Write buffer length (Tx).
  * @param  CH
  * @retval uLength
  ***********************************************************************************************************/
u32 UARTM_GetWriteBufferLength(u32 CH)
{
  return (Buffer_GetLength(&gUART_Tx[CH]));
}

/*********************************************************************************************************//**
  * @brief  Get Tx Status.
  * @param  CH
  * @retval TRUE for TX is Finished, FALSE for TX is not Finished
  ***********************************************************************************************************/
u8 UARTM_IsTxFinished(u32 CH)
{
  return (gUARTModuleState[CH].uIsTxFinished);
}

/*********************************************************************************************************//**
  * @brief  Discard Read buffer.
  * @param  CH
  * @retval None
  ***********************************************************************************************************/
void UARTM_DiscardReadBuffer(u32 CH)
{
  Buffer_Discard(&gUART_Rx[CH]);
}

/*********************************************************************************************************//**
 * @brief   This function handles UART interrupt.
 * @retval  None
 ************************************************************************************************************/
void UARTM_CH0_IRQHandler(void)
{
  _UARTM_IRQHandler(UARTM_CH0);
}

#ifdef HTCFG_UARTM_CH1
/*********************************************************************************************************//**
 * @brief   This function handles UART interrupt.
 * @retval  None
 ************************************************************************************************************/
void UARTM_CH1_IRQHandler(void)
{
  _UARTM_IRQHandler(UARTM_CH1);
}
#endif

#ifdef HTCFG_UARTM_CH2
/*********************************************************************************************************//**
 * @brief   This function handles UART interrupt.
 * @retval  None
 ************************************************************************************************************/
void UARTM_CH2_IRQHandler(void)
{
  _UARTM_IRQHandler(UARTM_CH2);
}
#endif

#ifdef HTCFG_UARTM_CH3
/*********************************************************************************************************//**
 * @brief   This function handles UART interrupt.
 * @retval  None
 ************************************************************************************************************/
void UARTM_CH3_IRQHandler(void)
{
  _UARTM_IRQHandler(UARTM_CH3);
}
#endif

/*********************************************************************************************************//**
  * @brief  This function handles UART interrupt.
  * @param  CH
  * @retval None
  ***********************************************************************************************************/
static void _UARTM_IRQHandler(u32 CH)
{
  u8 uTempData;
  UARTModule_StateTypeDef *pUARTMState = &gUARTModuleState[CH];

  if (usart_flag_get(pUARTMState->pUARTx, USART_ROERR_FLAG))
  {
    __DBG_Printf("Rx Over Run!\n\r");
    while (1);
  }

  if (usart_flag_get(pUARTMState->pUARTx, USART_TDBE_FLAG))
  {
    if (Buffer_isEmpty(&gUART_Tx[CH]))
    {
      usart_interrupt_enable(gUARTModuleState[CH].pUARTx, USART_TDC_INT | USART_TDBE_INT, TRUE);
    }
    else
    {
      Buffer_ReadByte(&gUART_Tx[CH], &uTempData);
      pUARTMState->pUARTx->dt = uTempData;
    }
  }

  if (usart_flag_get(pUARTMState->pUARTx, USART_RDBF_FLAG))
  {
    #if (UART_MODULE_DEBUG_MODE == 1)
    if (Buffer_isFull(&gUART_Rx[CH]))
    {
      /*----------------------------------------------------------------------------------------------------*/
      /* Should not reach here! It means the buffer for USART is full.                                      */
      /*----------------------------------------------------------------------------------------------------*/
      __DBG_Printf("UART Rx Buffer Full!\n\r");
      while (1);
    }
    #endif
    Buffer_WriteByte(&gUART_Rx[CH], (pUARTMState->pUARTx->dt & (u32)0x000000FF));
  }

  if (usart_flag_get(pUARTMState->pUARTx, USART_TDC_FLAG))
  {
    usart_interrupt_enable(pUARTMState->pUARTx, USART_TDC_INT | USART_TDBE_INT, FALSE);
    pUARTMState->uIsTxFinished = TRUE;
  }

}
