/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright (c) 2016-2021, NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "Defines.h"
#include "ComPortDriver.h"
#include "Application.h"
#include "fsl_port.h"
#include "fsl_uart.h"
#include "PowerModes.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define UARTHandler     UART0_UART1_UART2_UART3_IRQHandler
/*******************************************************************************
* Prototypes
******************************************************************************/
volatile uint8  UartPortStatus[NUM_UART_CHANNEL];
uint8  UartProtocol[NUM_UART_CHANNEL];
uint16 UARTRxBufIndex[NUM_UART_CHANNEL];
uint16 UARTTxBufIndex[NUM_UART_CHANNEL];
uint16 UARTBytesToTx[NUM_UART_CHANNEL];
uint16 UARTTimeout[NUM_UART_CHANNEL];

void UARTHandler(void);

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Initializes the UART instances which are required for communication 
 * with meter reader clients. A local port is accessed via optical/IR isolated port.
 * Another port is called electrical/RS232 port with isolation typically in meter hw only.
 * electrical port is useful any remote connectivity of the meter through a GPRS/RF module.
 * This function initializes instances of UARTs. Can also initialized any 
 * additional GPIO function(e.g., VCC Enable, Reset module etc) to enable 
 * UART based modules as described above.
 */
void UartModuleInit(void)
{
  uart_config_t uartConfig;
  
  /* Optical UART  */
  /*
  * uartConfig.baudRate_Bps = 9600;
  * uartConfig.parityMode = kUART_ParityDisabled;
  * uartConfig.stopBitCount = kUART_OneStopBit;
  * uartConfig.txFifoWatermark = 0;
  * uartConfig.rxFifoWatermark = 1;
  * uartConfig.enableTx = false;
  * uartConfig.enableRx = false;
  */
  UART_GetDefaultConfig(&uartConfig);
  uartConfig.baudRate_Bps = 9600;
  uartConfig.enableTx     = false;
  uartConfig.enableRx     = true;
  /* 
   Chose SystemState.SysClk for UART1, UART3. 
   SystemState.BusClk for UART0, UART2.
  */
  UART_Init(UART_BASE_PTR_OPTICAL, &uartConfig, UART_OPTICAL_CLOCK);
  /* Enable RX interrupt. */
  UART_EnableInterrupts(UART_BASE_PTR_OPTICAL, kUART_RxDataRegFullInterruptEnable | kUART_RxOverrunInterruptEnable);
  NVIC_SetPriority(UART0_UART1_UART2_UART3_IRQn, UART_INTERRUPT_PRIORITY);
  EnableIRQ(UART0_UART1_UART2_UART3_IRQn);
  
  UartPortStatus[UART_OPTICAL_INDEX] = UART_IDLE;
  UARTRxBufIndex[UART_OPTICAL_INDEX] = 0;
  UARTTxBufIndex[UART_OPTICAL_INDEX] = 0;
  /* Configure port as per meter hw */
  UART_OPTICAL_TXPORT_INIT;
  UART_OPTICAL_RXPORT_INIT;
  UART_EnableTxFIFO(UART_BASE_PTR_OPTICAL, true);  // Enable and flush Tx FIFO
  
  UartProtocol[UART_OPTICAL_INDEX] = UARTPROTOCOL_CUSTOM;
}

/*!
 * @brief Disables Tx port and enables Rx port of the optical/IR communication UART port.
 */
void DTxERx_UART_Optical(void)
{
  /* Disable transmit */
  UART_DisableInterrupts(UART_BASE_PTR_OPTICAL, kUART_TransmissionCompleteInterruptEnable);
  UART_EnableTx(UART_BASE_PTR_OPTICAL, false);
  UART_BASE_PTR_OPTICAL->CFIFO |= 0xC0; // Flush FIFO
  /* Enable receive */
  UART_EnableRx(UART_BASE_PTR_OPTICAL, true);
  UART_EnableInterrupts(UART_BASE_PTR_OPTICAL, kUART_RxDataRegFullInterruptEnable);
  UARTTxBufIndex[UART_OPTICAL_INDEX] = 0;
  UARTRxBufIndex[UART_OPTICAL_INDEX] = 0;
}

/*!
 * @brief Disables Rx port and enables Tx port of the optical/IR communication UART port.
 */
void DRxETx_UART_Optical(void)
{
  /* Disable receive */
  UART_DisableInterrupts(UART_BASE_PTR_OPTICAL, kUART_RxDataRegFullInterruptEnable);
  UART_EnableRx(UART_BASE_PTR_OPTICAL, false);
  UART_BASE_PTR_OPTICAL->CFIFO |= 0xC0; // Flush FIFO
  /* Enable transmit */
  UART_EnableTx(UART_BASE_PTR_OPTICAL, true);
  UARTTxBufIndex[UART_OPTICAL_INDEX] = 0;
  UARTRxBufIndex[UART_OPTICAL_INDEX] = 0;
}

/*!
 * @brief Callback function for any UART instances in MCU.
 * - serves callback function being invoked from the ISR of these UART instances.
 * - Rx operation: copies data bytes from UART DATA/FIFO to system RAM buffer. 
 * Indicates to application tasks about the current state of Rx process.
 * - Tx operation: writes UART DATA register from system RAM buffer.
 * Indicates to application tasks about the current state of Tx process.
 */
void UARTHandler(void)
{
  /* Variable to store the received byte */
  uint8 RxByte;
  volatile uint8 temp;
  
  /* If new data arrived. */
  if ((kUART_RxDataRegFullFlag | kUART_RxOverrunFlag) & UART_GetStatusFlags(UART_BASE_PTR_OPTICAL))
  {
    /* Optical/IR port interrupt */
    UARTTimeout[UART_OPTICAL_INDEX] = 3;
    
    RxByte = UART_OPTICAL_GETCHAR;
    
    /* Check if its the 1st byte of a receive sequence */
    if (UartPortStatus[UART_OPTICAL_INDEX] == UART_IDLE)
    {
      UartPortStatus[UART_OPTICAL_INDEX] = UART_RXING;
      
        UartProtocol[UART_OPTICAL_INDEX] = UARTPROTOCOL_CUSTOM;
    }
    
    if (UartPortStatus[UART_OPTICAL_INDEX] == UART_RXING)
    {
      if (UartProtocol[UART_OPTICAL_INDEX] == UARTPROTOCOL_CUSTOM)
      {
        UARTBuffer[UARTRxBufIndex[UART_OPTICAL_INDEX]++] = RxByte;
        if (UARTRxBufIndex[UART_OPTICAL_INDEX] == zUARTBUFFER)
        {
          UARTRxBufIndex[UART_OPTICAL_INDEX] = 0;
        }
        if ((DontCheckEOLCount == 0) && (RxByte == EOL))
        {
          UartPortStatus[UART_OPTICAL_INDEX] = UART_PROCESSING;
          DRxETx_UART_Optical();
        }
        else
        {
          if(DontCheckEOLCount)
          {
            DontCheckEOLCount--;
          }
        }
      }    
    }
  }
  else if ((kUART_RxOverrunFlag | kUART_FramingErrorFlag | kUART_NoiseErrorFlag | kUART_ParityErrorFlag) & UART_GetStatusFlags(UART_BASE_PTR_OPTICAL))
  {
    temp = UART_OPTICAL_GETCHAR;
  }
  else if ((kUART_TransmissionCompleteFlag) & UART_GetStatusFlags(UART_BASE_PTR_OPTICAL))
  {
    UARTBytesToTx[UART_OPTICAL_INDEX]--;
    if (UARTBytesToTx[UART_OPTICAL_INDEX] == 0)
    {
      UartPortStatus[UART_OPTICAL_INDEX] = UART_IDLE;
      UARTTimeout[UART_OPTICAL_INDEX] = 0;
      DTxERx_UART_Optical();
    }
    else
    {        
      UART_OPTICAL_PUTCHAR(UARTBuffer[UARTTxBufIndex[UART_OPTICAL_INDEX]++]);          
    }
  }
  SDK_ISR_EXIT_BARRIER;
}


/*!
 * @brief Initiates a character buffer data transmission through optical/IR port. 
 * Only the 1st byte is written to UART DATA register while the rest of the buffer 
 * data are sent from the ISR callback.
 */
 void UART_Transmit_Optical(uint8* txBuff, uint16 txLength)
{
  UARTBytesToTx[UART_OPTICAL_INDEX] = txLength;
  UartPortStatus[UART_OPTICAL_INDEX] = UART_TXING;
  DRxETx_UART_Optical();
  UARTTimeout[UART_OPTICAL_INDEX] = 3;
  UART_OPTICAL_PUTCHAR(txBuff[UARTTxBufIndex[UART_OPTICAL_INDEX]++]);
  UART_EnableInterrupts(UART_BASE_PTR_OPTICAL, kUART_TransmissionCompleteInterruptEnable);
}
