/*
 * File	: uart.c
 * Copyright (C) 2013 - 2016, Espressif Systems
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of version 3 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see .
 */
#include "ets_sys.h"
#include "osapi.h"
#include "driver/uart.h"
#include "osapi.h"
#include "driver/uart_register.h"
#include "mem.h"
#include "os_type.h"
#ifdef _ENABLE_RING_BUFFER
    static ringbuf_t rxBuff;
    static ringbuf_t txBuff;
#endif
#ifdef _ENABLE_CONSOLE_INTEGRATION
    uint8_t linked_to_console = 0;
#endif
extern UartDevice    UartDev;
/* Local variables */
static uint8 uart_recvTaskPrio = 0;
/* Internal Functions */
static  void ICACHE_FLASH_ATTR uart_config(uint8 uart_no);
static  void uart0_rx_intr_handler(void *para);
/* Public APIs */
void ICACHE_FLASH_ATTR UART_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 recv_task_priority);
#define ENABLE_DEBUG
#ifdef ENABLE_DEBUG
    #define DBG
    #define DBG1 os_printf
    #define DBG2 os_printf
#else
    #define DBG
    #define DBG1
    #define DBG2
#endif
#if _ENABLE_CONSOLE_INTEGRATION == 1
void ICACHE_FLASH_ATTR UART_init_console(UartBautRate uart0_br,
                                         uint8 recv_task_priority,
                                         ringbuf_t rxbuffer,
                                         ringbuf_t txBuffer)
{
    /* Set the task which should receive the signals once the data is received */
    uart_recvTaskPrio = recv_task_priority;
    UartDev.baut_rate = uart0_br;
    rxBuff = rxbuffer;
    txBuff = txBuffer;
    linked_to_console = 1;
    uart_config(UART0);
    UART_SetPrintPort(UART0);
    UartDev.baut_rate = uart0_br;
    uart_config(UART1);
    ETS_UART_INTR_ENABLE();
}
#endif
void ICACHE_FLASH_ATTR UART_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 recv_task_priority)
{
    /* Set the task which should receive the signals once the data is received */
    uart_recvTaskPrio = recv_task_priority;
    UartDev.baut_rate = uart0_br;
    uart_config(UART0);
    UART_SetPrintPort(UART0);
    UartDev.baut_rate = uart1_br;
    uart_config(UART1);
    ETS_UART_INTR_ENABLE();
    #if _ENABLE_CONSOLE_INTEGRATION == 0
        #if _ENABLE_RING_BUFFER == 1
            rxBuff = ringbuf_new(RX_RING_BUFFER_SIZE);
        #endif
    #endif
}
/******************************************************************************
 * FunctionName : uart_config
 * Description  : Internal used function
 *                UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled
 *                UART1 just used for debug output
 * Parameters   : uart_no, use UART0 or UART1 defined ahead
 * Returns      : NONE
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR uart_config(uint8 uart_no)
{
    if (uart_no == UART1)
    {
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
    }
    else
    {
        /* rcv_buff size if 0x100 */
        ETS_UART_INTR_ATTACH(uart0_rx_intr_handler,  &(UartDev.rcv_buff));
        PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
        #if UART_HW_RTS
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS);   //HW FLOW CONTROL RTS PIN
        #endif
        #if UART_HW_CTS
        PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_U0CTS);   //HW FLOW CONTROL CTS PIN
        #endif
    }
    uart_div_modify(uart_no, UART_CLK_FREQ / (UartDev.baut_rate));//SET BAUDRATE
    WRITE_PERI_REG(UART_CONF0(uart_no), ((UartDev.exist_parity & UART_PARITY_EN_M)  <<  UART_PARITY_EN_S) //SET BIT AND PARITY MODE
                                                                        | ((UartDev.parity & UART_PARITY_M)  <>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
    if (fifo_len)
    {
        max_unload = (fifo_len>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
        //DBG1("RX FIFO FULL [%d]\r\n", fifo_len );
        if (fifo_len)
        {
            //DBG1("Rx Fifo contains %d characters have to unload\r\n", fifo_len);
            for (index=0;index>UART_RXFIFO_CNT_S)& UART_RXFIFO_CNT);
        uart_rx_intr_disable(UART0);
        WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);
        system_os_post(uart_recvTaskPrio, SIG_UART0, 0);
        #endif
        goto end_int_handler;
    }
    if(UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST))
    {
        /* The Time out threshold for Rx/Tx is being execeeded */
        DBG1("Rx Timeout Threshold not being met \r\n");
        uart_rx_intr_disable(UART0);
        WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR);
        system_os_post(uart_recvTaskPrio, SIG_UART0, 0);
        goto end_int_handler;
    }
    if(UART_TXFIFO_EMPTY_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_TXFIFO_EMPTY_INT_ST))
    {
        /* The Tx FIFO is empty, the FIFO needs to be fed with new data */
        DBG1("Tx FIFO is empty\r\n");
        CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
        #if UART_BUFF_EN
            tx_start_uart_buffer(UART0);
        #endif
        //system_os_post(uart_recvTaskPrio, 1, 0);
        WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
    }
end_int_handler:
    return;
}
/******************************************************************************
 * FunctionName : uart_tx_one_char_no_wait
 * Description  : uart tx a single char without waiting for fifo
 * Parameters   : uint8 uart - uart port
 *                uint8 TxChar - char to tx
 * Returns      : STATUS
*******************************************************************************/
STATUS uart_tx_one_char(uint8 uart, uint8 TxChar)
{
    while (true)
	{
		uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
			break;
		}
	}
	WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
	return OK;
}
/******************************************************************************
 * FunctionName : uart_tx_one_char_no_wait
 * Description  : uart tx a single char without waiting for fifo
 * Parameters   : uint8 uart - uart port
 *                uint8 TxChar - char to tx
 * Returns      : STATUS
*******************************************************************************/
STATUS uart_tx_one_char_no_wait(uint8 uart, uint8 TxChar)
{
    uint8 fifo_cnt = (( READ_PERI_REG(UART_STATUS(uart))>>UART_TXFIFO_CNT_S)& UART_TXFIFO_CNT);
    if (fifo_cnt < 126)
    {
        WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
    }
    return OK;
}
/******************************************************************************
 * FunctionName : uart0_write_char_no_wait
 * Description  : tx a single char without waiting for uart 0.
                  helper function for os_printf output to fifo or tx buffer
 * Parameters   : uint8 uart - uart port
 *                uint8 TxChar - char to tx
 * Returns      : STATUS
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR uart0_write_char_no_wait(char c)
{
#if UART_BUFF_EN    //send to uart0 fifo but do not wait
    uint8 chr;
    if (c == '\n'){
        chr = '\r';
        tx_buff_enq(&chr, 1);
        chr = '\n';
        tx_buff_enq(&chr, 1);
    }else if (c == '\r'){
    }else{
        tx_buff_enq(&c,1);
    }
#else //send to uart tx buffer
    if (c == '\n')
    {
        uart_tx_one_char(UART0, '\r');
        uart_tx_one_char(UART0, '\n');
        //uart_tx_one_char_no_wait(UART0, '\r');
        //uart_tx_one_char_no_wait(UART0, '\n');
    }
    else
        if (c == '\r')
        {
            uart_tx_one_char(UART0, c);
        }
        else
        {
            uart_tx_one_char(UART0, c);
            //uart_tx_one_char_no_wait(UART0, c);
        }
#endif
}
/******************************************************************************
 * FunctionName : UART_SetPrintPort
 * Description  :
 *
 * Parameters   :
 * Returns      : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR UART_SetPrintPort(uint8 uart_no)
{
    if(uart_no==1)
    {
        //os_install_putc1(uart1_write_char);
    }
    else
    {
        /*option 1: do not wait if uart fifo is full,drop current character*/
        //os_install_putc1(uart0_write_char_no_wait);
        /*option 2: wait for a while if uart fifo is full*/
        //os_install_putc1(uart0_write_char);
    }
}