/* mbed Microcontroller Library
 *******************************************************************************
 * Copyright (c) 2014, Realtek Semiconductor Corp.
 * All rights reserved.
 *
 *******************************************************************************
 */

#include "objects.h"
//#include "mbed_assert.h"
#include "serial_api.h"
#include "serial_ex_api.h"

#if CONFIG_UART_EN

//#include "cmsis.h"
#include "pinmap.h"
#include <string.h>

static const PinMap PinMap_UART_TX[] = {
    {PC_3,  RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)},
    {PE_0,  RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)},
    {PA_7,  RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)}, // None RTL8710AF
    {PD_3,  RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)}, // None RTL8710AF, RTL8711AM
    {PE_4,  RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)},
    {PB_5,  RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)}, // None RTL8710AF, RTL8711AM
    {PA_4,  RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)}, // None RTL8711AM
    {PC_9,  RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)}, // None RTL8710AF, RTL8711AM
    {PD_7,  RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)}, // None RTL8710AF, RTL8711AM
    {NC,    NC,     0}
};

static const PinMap PinMap_UART_RX[] = {
    {PC_0,  RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)}, // No Interrupt Source?
    {PE_3,  RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)}, 
    {PA_6,  RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)}, // None RTL8710AF, // No Interrupt Source?
    {PD_0,  RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)}, // None RTL8710AF, RTL8711AM
    {PE_7,  RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)}, // None RTL8710AF, RTL8711AM
    {PB_4,  RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)}, // None RTL8710AF, RTL8711AM
    {PA_0,  RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)}, // None RTL8711AM
    {PC_6,  RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)}, // None RTL8710AF, RTL8711AM
    {PD_4,  RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)}, // None RTL8710AF, RTL8711AM
    {NC,    NC,     0}
};

#define UART_NUM (3) 
#define SERIAL_TX_IRQ_EN        0x01
#define SERIAL_RX_IRQ_EN        0x02
#define SERIAL_TX_DMA_EN        0x01
#define SERIAL_RX_DMA_EN        0x02

static uint32_t serial_irq_ids[UART_NUM] = {0, 0, 0}; // , 0

static uart_irq_handler irq_handler[UART_NUM];
static uint32_t serial_irq_en[UART_NUM] = {0, 0, 0};  // , 0

#ifdef CONFIG_GDMA_EN
static uint32_t serial_dma_en[UART_NUM] = {0, 0, 0}; // , 0
static HAL_GDMA_OP UartGdmaOp;
#endif

#ifdef CONFIG_MBED_ENABLED
int stdio_uart_inited = 0;
serial_t stdio_uart;
#endif

static void SerialTxDoneCallBack(VOID *pAdapter);
static void SerialRxDoneCallBack(VOID *pAdapter);

void serial_init(serial_t *obj, PinName tx, PinName rx) 
{
    uint32_t uart_tx, uart_rx;
    uint32_t uart_sel;
    uint8_t uart_idx;
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter;
#ifdef CONFIG_GDMA_EN
    PUART_DMA_CONFIG   pHalRuartDmaCfg;
    PHAL_GDMA_OP pHalGdmaOp=&UartGdmaOp;
#endif

    // Determine the UART to use (UART0, UART1, or UART3)
    uart_tx = pinmap_peripheral(tx, PinMap_UART_TX);
    uart_rx = pinmap_peripheral(rx, PinMap_UART_RX);

    uart_sel = pinmap_merge(uart_tx, uart_rx);
    uart_idx = RTL_GET_PERI_IDX(uart_sel);
    if (unlikely(uart_idx == (uint8_t)NC)) {
        DBG_UART_ERR("%s: Cannot find matched UART\n", __FUNCTION__);
        return;
    }

    pHalRuartOp = &(obj->hal_uart_op);
    pHalRuartAdapter = &(obj->hal_uart_adp);

    if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter)) {
        DBG_UART_ERR("%s: Allocate Adapter Failed\n", __FUNCTION__);
        return;
    }
    
    HalRuartOpInit((VOID*)pHalRuartOp);

#ifdef CONFIG_GDMA_EN
    HalGdmaOpInit((VOID*)pHalGdmaOp);
    pHalRuartDmaCfg = &obj->uart_gdma_cfg;
    pHalRuartDmaCfg->pHalGdmaOp = pHalGdmaOp;
    pHalRuartDmaCfg->pTxHalGdmaAdapter = &obj->uart_gdma_adp_tx;
    pHalRuartDmaCfg->pRxHalGdmaAdapter = &obj->uart_gdma_adp_rx;
    pHalRuartDmaCfg->pTxDmaBlkList = &obj->gdma_multiblk_list_tx;
    pHalRuartDmaCfg->pRxDmaBlkList = &obj->gdma_multiblk_list_rx;
    _memset((void*)(pHalRuartDmaCfg->pTxHalGdmaAdapter), 0, sizeof(HAL_GDMA_ADAPTER));
    _memset((void*)(pHalRuartDmaCfg->pRxHalGdmaAdapter), 0, sizeof(HAL_GDMA_ADAPTER));
    _memset((void*)(pHalRuartDmaCfg->pTxDmaBlkList), 0, sizeof(UART_DMA_MULTIBLK));
    _memset((void*)(pHalRuartDmaCfg->pRxDmaBlkList), 0, sizeof(UART_DMA_MULTIBLK));
#endif

    pHalRuartOp->HalRuartAdapterLoadDef(pHalRuartAdapter, uart_idx);
    pHalRuartAdapter->PinmuxSelect = RTL_GET_PERI_SEL(uart_sel);
    pHalRuartAdapter->BaudRate = 9600;
    pHalRuartAdapter->IrqHandle.Priority = 6;

    // Configure the UART pins
    // TODO:
//    pinmap_pinout(tx, PinMap_UART_TX);
//    pinmap_pinout(rx, PinMap_UART_RX);
//    pin_mode(tx, PullUp);
//    pin_mode(rx, PullUp);
    
    if (HalRuartInit(pHalRuartAdapter) != HAL_OK) {
        DBG_UART_ERR("serial_init Err!\n");
        return;
    }
    pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);    
    pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);

#ifdef CONFIG_MBED_ENABLED
    // For stdio management
    if (uart_idx == STDIO_UART) {
        stdio_uart_inited = 1;
        memcpy(&stdio_uart, obj, sizeof(serial_t));
    }
#endif
}

void serial_free(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
#ifdef CONFIG_GDMA_EN
    u8  uart_idx;
    PUART_DMA_CONFIG   pHalRuartDmaCfg;
#endif

    pHalRuartAdapter = &(obj->hal_uart_adp);

    HalRuartDeInit(pHalRuartAdapter);

#ifdef CONFIG_GDMA_EN
    uart_idx = pHalRuartAdapter->UartIndex;
    pHalRuartDmaCfg = &obj->uart_gdma_cfg;
    if (serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN) {
        HalRuartRxGdmaDeInit(pHalRuartDmaCfg);
        serial_dma_en[uart_idx] &= ~SERIAL_RX_DMA_EN;
    }

    if (serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN) {
        HalRuartTxGdmaDeInit(pHalRuartDmaCfg);
        serial_dma_en[uart_idx] &= ~SERIAL_TX_DMA_EN;
    }    
#endif
}

void serial_baud(serial_t *obj, int baudrate) {
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    //PHAL_RUART_OP      pHalRuartOp;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    //pHalRuartOp = &(obj->hal_uart_op);

    pHalRuartAdapter->BaudRate = baudrate;
//  HalRuartInit(pHalRuartAdapter);
    HalRuartSetBaudRate((VOID*)pHalRuartAdapter);
}

void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    //PHAL_RUART_OP      pHalRuartOp;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    //pHalRuartOp = &(obj->hal_uart_op);
    
    if (data_bits == 8) {
        pHalRuartAdapter->WordLen = RUART_WLS_8BITS;
    } else {
        pHalRuartAdapter->WordLen = RUART_WLS_7BITS;
    }


    switch (parity) {
        case ParityOdd:
        case ParityForced0:
            pHalRuartAdapter->Parity = RUART_PARITY_ENABLE;
            pHalRuartAdapter->ParityType = RUART_ODD_PARITY;
            break;
        case ParityEven:
        case ParityForced1:
            pHalRuartAdapter->Parity = RUART_PARITY_ENABLE;
            pHalRuartAdapter->ParityType = RUART_EVEN_PARITY;
            break;
        default: // ParityNone
            pHalRuartAdapter->Parity = RUART_PARITY_DISABLE;
            break;
    }

    if (stop_bits == 2) {
        pHalRuartAdapter->StopBit = RUART_STOP_BIT_2;
    } else {
        pHalRuartAdapter->StopBit = RUART_STOP_BIT_1;
    }

    HalRuartInit(pHalRuartAdapter);
}

/******************************************************************************
 * INTERRUPTS HANDLING
 ******************************************************************************/

static void SerialTxDoneCallBack(VOID *pAdapter)
{
    PHAL_RUART_ADAPTER pHalRuartAdapter = pAdapter;
    u8 uart_idx = pHalRuartAdapter->UartIndex;

    // Mask UART TX FIFO empty
    pHalRuartAdapter->Interrupts &= ~RUART_IER_ETBEI;
    HalRuartSetIMRRtl8195a (pHalRuartAdapter);

    if (irq_handler[uart_idx] != NULL) {
        irq_handler[uart_idx](serial_irq_ids[uart_idx], TxIrq);
    }
}

static void SerialRxDoneCallBack(VOID *pAdapter)
{
    PHAL_RUART_ADAPTER pHalRuartAdapter = pAdapter;
    u8 uart_idx = pHalRuartAdapter->UartIndex;
    
    if (irq_handler[uart_idx] != NULL) {
        irq_handler[uart_idx](serial_irq_ids[uart_idx], RxIrq);
    }
}

void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
//    PHAL_RUART_OP pHalRuartOp;
    u8 uart_idx;

    pHalRuartAdapter = &(obj->hal_uart_adp);
//    pHalRuartOp = &(obj->hal_uart_op);
    
    uart_idx = pHalRuartAdapter->UartIndex;
    
    irq_handler[uart_idx] = handler;
    serial_irq_ids[uart_idx] = id;        

    pHalRuartAdapter->TxTDCallback = SerialTxDoneCallBack;
    pHalRuartAdapter->TxTDCbPara = (void*)pHalRuartAdapter;
    pHalRuartAdapter->RxDRCallback = SerialRxDoneCallBack;
    pHalRuartAdapter->RxDRCbPara = (void*)pHalRuartAdapter;    

//    pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);
//    pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);
}


void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    PHAL_RUART_OP pHalRuartOp;
    u8 uart_idx;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    pHalRuartOp = &(obj->hal_uart_op);
    uart_idx = pHalRuartAdapter->UartIndex;
    
    if (enable) {
        if (irq == RxIrq) {
            pHalRuartAdapter->Interrupts |= RUART_IER_ERBI | RUART_IER_ELSI;
            serial_irq_en[uart_idx] |= SERIAL_RX_IRQ_EN;
            HalRuartSetIMRRtl8195a (pHalRuartAdapter);
        }
        else {
            serial_irq_en[uart_idx] |= SERIAL_TX_IRQ_EN;
        }
        pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter);
        pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter);
    } 
    else { // disable
        if (irq == RxIrq) {
            pHalRuartAdapter->Interrupts &= ~(RUART_IER_ERBI | RUART_IER_ELSI);
            serial_irq_en[uart_idx] &= ~SERIAL_RX_IRQ_EN;
        }
        else {
            pHalRuartAdapter->Interrupts &= ~RUART_IER_ETBEI;
            serial_irq_en[uart_idx] &= ~SERIAL_TX_IRQ_EN;
        }
        HalRuartSetIMRRtl8195a (pHalRuartAdapter);
        if (pHalRuartAdapter->Interrupts == 0) {
            InterruptUnRegister(&pHalRuartAdapter->IrqHandle);
            InterruptDis(&pHalRuartAdapter->IrqHandle);
        }
    }
}

/******************************************************************************
 * READ/WRITE
 ******************************************************************************/

int serial_getc(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;

    while (!serial_readable(obj));
    return (int)((HAL_RUART_READ32(uart_idx, RUART_REV_BUF_REG_OFF)) & 0xFF);
}

void serial_putc(serial_t *obj, int c) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    
    while (!serial_writable(obj));
    HAL_RUART_WRITE32(uart_idx, RUART_TRAN_HOLD_REG_OFF, (c & 0xFF));

    if (serial_irq_en[uart_idx] & SERIAL_TX_IRQ_EN) {
        // UnMask TX FIFO empty IRQ
        pHalRuartAdapter->Interrupts |= RUART_IER_ETBEI;
        HalRuartSetIMRRtl8195a (pHalRuartAdapter);
    }
}

int serial_readable(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;

    if ((HAL_RUART_READ32(uart_idx, RUART_LINE_STATUS_REG_OFF)) & RUART_LINE_STATUS_REG_DR) {
        return 1;
    }
    else {
        return 0;
    }
}

int serial_writable(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;

    if (HAL_RUART_READ32(uart_idx, RUART_LINE_STATUS_REG_OFF) & 
                        (RUART_LINE_STATUS_REG_THRE)) {
       return 1;
    }
    else {
        return 0;
    }
}

void serial_clear(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    HalRuartResetTRxFifo((VOID *)pHalRuartAdapter);
}

void serial_clear_tx(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    HalRuartResetTxFifo((VOID *)pHalRuartAdapter);
}

void serial_clear_rx(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    HalRuartResetRxFifo((VOID *)pHalRuartAdapter);
}

void serial_pinout_tx(PinName tx) 
{
    pinmap_pinout(tx, PinMap_UART_TX);
}

void serial_break_set(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    u32 RegValue;

    RegValue = HAL_RUART_READ32(uart_idx, RUART_LINE_CTL_REG_OFF);
    RegValue |= BIT_UART_LCR_BREAK_CTRL;
    HAL_RUART_WRITE32(uart_idx, RUART_LINE_CTL_REG_OFF, RegValue);
}

void serial_break_clear(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    u32 RegValue;

    RegValue = HAL_RUART_READ32(uart_idx, RUART_LINE_CTL_REG_OFF);
    RegValue &= ~(BIT_UART_LCR_BREAK_CTRL);
    HAL_RUART_WRITE32(uart_idx, RUART_LINE_CTL_REG_OFF, RegValue);
}

void serial_send_comp_handler(serial_t *obj, void *handler, uint32_t id) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    pHalRuartAdapter->TxCompCallback = (void(*)(void*))handler;
    pHalRuartAdapter->TxCompCbPara = (void*)id;
}

void serial_recv_comp_handler(serial_t *obj, void *handler, uint32_t id) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    pHalRuartAdapter->RxCompCallback = (void(*)(void*))handler;
    pHalRuartAdapter->RxCompCbPara = (void*)id;
}

void serial_set_flow_control(serial_t *obj, FlowControl type) // , PinName rxflow, PinName txflow)
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    // Our UART cannot specify the RTS/CTS pin seprately, so the ignore the rxflow, txflow pin
    // We just use the hardware auto flow control, so cannot do flow-control single direction only
    pHalRuartAdapter = &(obj->hal_uart_adp);

    // RTS low active
    // RTS_pin = autoflow_en ? (~rts | (RX_FIFO_Level_Trigger)) : ~rts
    switch(type) {
        case FlowControlRTSCTS:
            pHalRuartAdapter->FlowControl = AUTOFLOW_ENABLE;
            pHalRuartAdapter->RTSCtrl = 1;
            break;
            
        case FlowControlRTS:    // to indicate peer that it's ready for RX
            // It seems cannot only enable RTS
            pHalRuartAdapter->FlowControl = AUTOFLOW_ENABLE;
            pHalRuartAdapter->RTSCtrl = 1;
            break;
        
        case FlowControlCTS:    // to check is the peer ready for RX: if can start TX ?
            // need to check CTS before TX
            pHalRuartAdapter->FlowControl = AUTOFLOW_ENABLE;
            pHalRuartAdapter->RTSCtrl = 1;
            break;

        case FlowControlNone:
        default:
            pHalRuartAdapter->FlowControl = AUTOFLOW_DISABLE;
            pHalRuartAdapter->RTSCtrl = 1;  // RTS pin allways Low, peer can send data
            break;
                
    }

    HalRuartFlowCtrl((VOID *)pHalRuartAdapter);
}

// Blocked(busy wait) receive, return received bytes count
int32_t serial_recv_blocked (serial_t *obj, char *prxbuf, uint32_t len, uint32_t timeout_ms)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    obj->rx_len = len;
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = pHalRuartOp->HalRuartRecv(pHalRuartAdapter, (u8*)prxbuf, len, timeout_ms);
    HalRuartExitCritical(pHalRuartAdapter);
    
    return (ret);
}

// Blocked(busy wait) send, return transmitted bytes count
int32_t serial_send_blocked (serial_t *obj, char *ptxbuf, uint32_t len, uint32_t timeout_ms)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    obj->tx_len = len;
    ret = pHalRuartOp->HalRuartSend(pHalRuartAdapter, (u8*)ptxbuf, len, timeout_ms);
    return (ret);
}

int32_t serial_recv_stream (serial_t *obj, char *prxbuf, uint32_t len)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    obj->rx_len = len;
    ret = pHalRuartOp->HalRuartIntRecv(pHalRuartAdapter, (u8*)prxbuf, len);
    return (ret);
}

int32_t serial_send_stream (serial_t *obj, char *ptxbuf, uint32_t len)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    obj->tx_len = len;
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = pHalRuartOp->HalRuartIntSend(pHalRuartAdapter, (u8*)ptxbuf, len);
    HalRuartExitCritical(pHalRuartAdapter);
    return (ret);
}

#ifdef CONFIG_GDMA_EN

int32_t serial_recv_stream_dma (serial_t *obj, char *prxbuf, uint32_t len)
{
//    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    int32_t ret;

//    pHalRuartOp = &(obj->hal_uart_op);
    if ((serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN)==0) {
        PUART_DMA_CONFIG   pHalRuartDmaCfg;

        pHalRuartDmaCfg = &obj->uart_gdma_cfg;
        if (HAL_OK == HalRuartRxGdmaInit(pHalRuartAdapter, pHalRuartDmaCfg, 0)) {
            serial_dma_en[uart_idx] |= SERIAL_RX_DMA_EN;
        }
        else {
            return HAL_BUSY;
        }
    }
    
    obj->rx_len = len;
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = HalRuartDmaRecv(pHalRuartAdapter, (u8*)prxbuf, len);
    HalRuartExitCritical(pHalRuartAdapter);
    return (ret);
}

int32_t serial_send_stream_dma (serial_t *obj, char *ptxbuf, uint32_t len)
{
//    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    int32_t ret;

//    pHalRuartOp = &(obj->hal_uart_op);

    if ((serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN)==0) {
        PUART_DMA_CONFIG   pHalRuartDmaCfg;
        
        pHalRuartDmaCfg = &obj->uart_gdma_cfg;
        if (HAL_OK == HalRuartTxGdmaInit(pHalRuartAdapter, pHalRuartDmaCfg, 0)) {
            serial_dma_en[uart_idx] |= SERIAL_TX_DMA_EN;
        }
        else {
            return HAL_BUSY;
        }
    }
    obj->tx_len = len;
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = HalRuartDmaSend(pHalRuartAdapter, (u8*)ptxbuf, len);
    HalRuartExitCritical(pHalRuartAdapter);
    return (ret);
}

int32_t serial_recv_stream_dma_timeout (serial_t *obj, char *prxbuf, uint32_t len, uint32_t timeout_ms, void *force_cs)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    u8  uart_idx = pHalRuartAdapter->UartIndex;
    uint32_t TimeoutCount=0, StartCount;
    int ret;
    void (*task_yield)(void);

    pHalRuartOp = &(obj->hal_uart_op);
    if ((serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN)==0) {
        PUART_DMA_CONFIG   pHalRuartDmaCfg;

        pHalRuartDmaCfg = &obj->uart_gdma_cfg;
        if (HAL_OK == HalRuartRxGdmaInit(pHalRuartAdapter, pHalRuartDmaCfg, 0)) {
            serial_dma_en[uart_idx] |= SERIAL_RX_DMA_EN;
        }
        else {
            return HAL_BUSY;
        }
    }
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = HalRuartDmaRecv(pHalRuartAdapter, (u8*)prxbuf, len);
    HalRuartExitCritical(pHalRuartAdapter);
    
    if ((ret == HAL_OK) && (timeout_ms > 0)) {
        TimeoutCount = (timeout_ms*1000/TIMER_TICK_US);
        StartCount = HalTimerOp.HalTimerReadCount(1);
        task_yield = (void (*)(void))force_cs;
        pHalRuartAdapter->Status = HAL_UART_STATUS_OK;
        while (pHalRuartAdapter->State & HAL_UART_STATE_BUSY_RX) {
            if (HAL_TIMEOUT == RuartIsTimeout(StartCount, TimeoutCount)) {
                ret = pHalRuartOp->HalRuartStopRecv((VOID*)pHalRuartAdapter);
                ret = pHalRuartOp->HalRuartResetRxFifo((VOID*)pHalRuartAdapter);
                pHalRuartAdapter->Status = HAL_UART_STATUS_TIMEOUT;
                break;
            }
            if (NULL != task_yield) {
               task_yield();
            }
        }
        if (pHalRuartAdapter->Status == HAL_UART_STATUS_TIMEOUT) {
            return (len - pHalRuartAdapter->RxCount);
        } else {
            return len;
        }
    } else {
        return (-ret);
    }
}


#endif  // end of "#ifdef CONFIG_GDMA_EN"

int32_t serial_send_stream_abort (serial_t *obj)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = pHalRuartOp->HalRuartStopSend((VOID*)pHalRuartAdapter);
    HalRuartExitCritical(pHalRuartAdapter);
    if (HAL_OK != ret) {
        return -ret;
    }
    HalRuartResetTxFifo((VOID*)pHalRuartAdapter);

    ret = obj->tx_len - pHalRuartAdapter->TxCount;
    
    return (ret);
}

int32_t serial_recv_stream_abort (serial_t *obj)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    int ret;

    pHalRuartOp = &(obj->hal_uart_op);
    
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = pHalRuartOp->HalRuartStopRecv((VOID*)pHalRuartAdapter);
    HalRuartExitCritical(pHalRuartAdapter);
    if (HAL_OK != ret) {
        return -ret;
    }

//    pHalRuartOp->HalRuartResetRxFifo((VOID*)pHalRuartAdapter);
    
    ret = obj->rx_len - pHalRuartAdapter->RxCount;
    return (ret);
}

void serial_disable (serial_t *obj)
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);

    HalRuartDisable((VOID*)pHalRuartAdapter);
}

void serial_enable (serial_t *obj)
{
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);

    HalRuartEnable((VOID*)pHalRuartAdapter);
}

// return the byte count received before timeout, or error(<0)
int32_t serial_recv_stream_timeout (serial_t *obj, char *prxbuf, uint32_t len, uint32_t timeout_ms, void *force_cs)
{
    PHAL_RUART_OP      pHalRuartOp;
    PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp);
    uint32_t TimeoutCount=0, StartCount;
    int ret;
    void (*task_yield)(void);

    task_yield = NULL;
    pHalRuartOp = &(obj->hal_uart_op);
    HalRuartEnterCritical(pHalRuartAdapter);
    ret = pHalRuartOp->HalRuartIntRecv(pHalRuartAdapter, (u8*)prxbuf, len);
    HalRuartExitCritical(pHalRuartAdapter);
    if ((ret == HAL_OK) && (timeout_ms > 0)) {
        TimeoutCount = (timeout_ms*1000/TIMER_TICK_US);
        StartCount = HalTimerOp.HalTimerReadCount(1);
        task_yield = (void (*)(void))force_cs;
        while (pHalRuartAdapter->State & HAL_UART_STATE_BUSY_RX) {
            if (HAL_TIMEOUT == RuartIsTimeout(StartCount, TimeoutCount)) {
                ret = pHalRuartOp->HalRuartStopRecv((VOID*)pHalRuartAdapter);
                ret = pHalRuartOp->HalRuartResetRxFifo((VOID*)pHalRuartAdapter);
                pHalRuartAdapter->Status = HAL_UART_STATUS_TIMEOUT;
                break;
            }
            if (NULL != task_yield) {
               task_yield();
            }
        }
        return (len - pHalRuartAdapter->RxCount);
    } else {
        return (-ret);
    }
}

// to hook lock/unlock function for multiple-thread application
void serial_hook_lock(serial_t *obj, void *lock, void *unlock, uint32_t id) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;

    pHalRuartAdapter = &(obj->hal_uart_adp);
    pHalRuartAdapter->EnterCritical = (void (*)(void))lock;
    pHalRuartAdapter->ExitCritical = (void (*)(void))unlock;
}

// to read Line-Status register
// Bit 0: RX Data Ready
// Bit 1: Overrun Error
// Bit 2: Parity Error
// Bit 3: Framing Error
// Bit 4: Break Interrupt (received data input is held in 0 state for a longer than a full word tx time)
// Bit 5: TX FIFO empty (THR empty)
// Bit 6: TX FIFO empty (THR & TSR both empty)
// Bit 7: RX Error (parity error, framing error or break indication)
uint8_t serial_raed_lsr(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    uint8_t RegValue;
    
    pHalRuartAdapter = &(obj->hal_uart_adp);
    RegValue = HAL_RUART_READ8(pHalRuartAdapter->UartIndex, RUART_LINE_STATUS_REG_OFF);
    return RegValue;
}

// to read Modem-Status register
// Bit 0: DCTS, The CTS line has changed its state
// Bit 1: DDSR, The DSR line has changed its state
// Bit 2: TERI, RI line has changed its state from low to high state
// Bit 3: DDCD, DCD line has changed its state
// Bit 4: Complement of the CTS input 
// Bit 5: Complement of the DSR input 
// Bit 6: Complement of the RI input 
// Bit 7: Complement of the DCD input 
uint8_t serial_raed_msr(serial_t *obj) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    uint8_t RegValue;
    
    pHalRuartAdapter = &(obj->hal_uart_adp);
    RegValue = HAL_RUART_READ8(pHalRuartAdapter->UartIndex, RUART_MODEM_STATUS_REG_OFF);
    return RegValue;
}

// to set the RX FIFO level to trigger RX interrupt/RTS de-assert
// FifoLv:
//     0: 1-Byte
//     1: 4-Byte
//     2: 8-Byte
//     3: 14-Byte
void serial_rx_fifo_level(serial_t *obj, SerialFifoLevel FifoLv) 
{
    PHAL_RUART_ADAPTER pHalRuartAdapter;
    uint8_t RegValue;
    
    pHalRuartAdapter = &(obj->hal_uart_adp);
    RegValue = (RUART_FIFO_CTL_REG_DMA_ENABLE | RUART_FIFO_CTL_REG_FIFO_ENABLE) | (((uint8_t)FifoLv&0x03) << 6);
    HAL_RUART_WRITE8(pHalRuartAdapter->UartIndex, RUART_FIFO_CTL_REG_OFF, RegValue);
}

#endif
