/* Copyright Statement:
 *
 * (C) 2005-2016  MediaTek Inc. All rights reserved.
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws. The information contained herein
 * is confidential and proprietary to MediaTek Inc. ("MediaTek") and/or its licensors.
 * Without the prior written permission of MediaTek and/or its licensors,
 * any reproduction, modification, use or disclosure of MediaTek Software,
 * and information contained herein, in whole or in part, shall be strictly prohibited.
 * You may only use, reproduce, modify, or distribute (as applicable) MediaTek Software
 * if you have agreed to and been bound by the applicable license agreement with
 * MediaTek ("License Agreement") and been granted explicit permission to do so within
 * the License Agreement ("Permitted User").  If you are not a Permitted User,
 * please cease any access or use of MediaTek Software immediately.
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT MEDIATEK SOFTWARE RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES
 * ARE PROVIDED TO RECEIVER ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
 * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
 * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
 * SUPPLIED WITH MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
 * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
 * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
 * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
 * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
 * CUMULATIVE LIABILITY WITH RESPECT TO MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
 * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE MEDIATEK SOFTWARE AT ISSUE,
 * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
 * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
 */

#include "hal_uart.h"

#ifdef HAL_UART_MODULE_ENABLED
#include <string.h>
#include "mt7687.h"
#include "system_mt7687.h"
#include "core_cm4.h"
#include "uart.h"
#include "top.h"
#include "hal_log.h"
#ifdef HAL_SLEEP_MANAGER_ENABLED
#include "hal_gpt.h"
#include "hal_sleep_manager.h"
#endif
#include "hal_nvic.h"
#include "hal_nvic_internal.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef HAL_SLEEP_MANAGER_ENABLED
/* Sleep handler's name with pattern "uartx"  */
#define UART_SLEEP_HANDLER_NAME_LENGTH 5
#endif

typedef enum {
    UART_HWSTATUS_UNINITIALIZED,
    UART_HWSTATUS_POLL_INITIALIZED,
    UART_HWSTATUS_DMA_INITIALIZED,
} uart_hwstatus_t;

typedef struct {
    hal_uart_callback_t func;
    void *arg;
} uart_callback_t;

#ifdef HAL_SLEEP_MANAGER_ENABLED
typedef enum {
    UART_FLOWCONTROL_NONE,
    UART_FLOWCONTROL_SOFTWARE,
    UART_FLOWCONTROL_HARDWARE,
} uart_flowcontrol_t;

typedef struct {
    uint8_t xon;
    uint8_t xoff;
    uint8_t escape_character;
} uart_sw_flowcontrol_config_t;
#endif

static bool g_uart_global_data_initialized = false;
#ifdef HAL_SLEEP_MANAGER_ENABLED
static bool g_uart_frist_send_complete_interrupt[HAL_UART_MAX];
static bool g_uart_restore_init;
static bool g_uart_send_lock_status[HAL_UART_MAX];
static uint8_t g_uart_sleep_handler[HAL_UART_MAX];
static uart_flowcontrol_t g_uart_flowcontrol_status[HAL_UART_MAX];
static uart_sw_flowcontrol_config_t g_uart_sw_flowcontrol_config[HAL_UART_MAX];
static hal_uart_config_t g_uart_config[HAL_UART_MAX];
#endif
static volatile uart_hwstatus_t g_uart_hwstatus[HAL_UART_MAX];
static uart_callback_t g_uart_callback[HAL_UART_MAX];
static uart_dma_callback_data_t g_uart_dma_callback_data[HAL_UART_MAX * 2];
static hal_uart_dma_config_t g_uart_dma_config[HAL_UART_MAX];
static const uint32_t g_uart_baudrate_map[] = {110, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 5000000};

extern hal_nvic_irq_t g_uart_port_to_irq_num[];
#ifdef HAL_SLEEP_MANAGER_ENABLED
extern const char *const g_uart_sleep_handler_name[];
#endif

static bool uart_port_is_valid(hal_uart_port_t uart_port)
{
    return (uart_port < HAL_UART_MAX);
}

static bool uart_baudrate_is_valid(hal_uart_baudrate_t baudrate)
{
    return (baudrate < HAL_UART_BAUDRATE_MAX);
}

static bool uart_config_is_valid(const hal_uart_config_t *config)
{
    return ((config->baudrate < HAL_UART_BAUDRATE_MAX) &&
            (config->word_length <= HAL_UART_WORD_LENGTH_8) &&
            (config->stop_bit <= HAL_UART_STOP_BIT_2) &&
            (config->parity <= HAL_UART_PARITY_EVEN));
}

static uint32_t uart_translate_timeout(uint32_t timeout_us)
{
    uint32_t ticks_per_us;

    ticks_per_us = top_mcu_freq_get() / 1000000;

    return ticks_per_us * timeout_us;
}


/* triggered by vfifo dma rx thershold interrupt or UART receive timeout interrupt.
 * 1. When vfifo dma rx thershold interrupt happen,
 * this function is called with is_timeout=false.
 * then call suer's callback to notice that data can be fetched from receive buffer.
 * 2. When UART receive timeout interrupt happen,
 * this function is called with is_timeout=true.
 * then call suer's callback to notice that data can be fetched from receive buffer.
 */
void uart_receive_handler(hal_uart_port_t uart_port, bool is_timeout)
{
    vdma_channel_t channel;
    uint32_t avail_bytes;
    hal_uart_callback_t callback;
    void *arg;

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return;
    }

    channel = uart_port_to_dma_channel(uart_port, 1);

    vdma_get_available_receive_bytes(channel, &avail_bytes);

    if (avail_bytes == 0) {
        return;
    }

    callback = g_uart_callback[uart_port].func;
    arg = g_uart_callback[uart_port].arg;
    if (callback == NULL) {
        return;
    }
    callback(HAL_UART_EVENT_READY_TO_READ, arg);

    vdma_get_available_receive_bytes(channel, &avail_bytes);

    if (avail_bytes >= g_uart_dma_config[uart_port].receive_vfifo_threshold_size) {
        vdma_disable_interrupt(channel);
    }
}

/* triggered by vfifo dma tx thershold interrupt or uart transmit complete interrupt.
 * 1. When vfifo dma tx thershold interrupt happen,
 * this function is called with is_send_complete_trigger=false.
 * then call suer's callback to notice that data can be put in send buffer again.
 * 2. When UART transmit complete interrupt happen,
 * this function is called with is_send_complete_trigger=true.
 * Now all user data has been sent out, so we call hal_sleep_manager_unlock_sleep()
 * to unlock sleep.
 */
void uart_send_handler(hal_uart_port_t uart_port, bool is_send_complete_trigger)
{
    vdma_channel_t channel;
    uint32_t compare_space, avail_space;
    hal_uart_callback_t callback;
    void *arg;
#ifdef HAL_SLEEP_MANAGER_ENABLED
    hal_sleep_manager_status_t sleep_status;
    uint32_t irq_status;
#endif

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return;
    }

    channel = uart_port_to_dma_channel(uart_port, 0);

    if (is_send_complete_trigger == true) {
#ifdef HAL_SLEEP_MANAGER_ENABLED
        if (g_uart_frist_send_complete_interrupt[uart_port] == false) {
            irq_status = save_and_set_interrupt_mask();
            vdma_get_available_send_space(channel, &avail_space);

            if (g_uart_dma_config[uart_port].send_vfifo_buffer_size - avail_space == 0) {
                if (g_uart_send_lock_status[uart_port] == true) {
                    sleep_status = hal_sleep_manager_unlock_sleep(g_uart_sleep_handler[uart_port]);
                    if (sleep_status != HAL_SLEEP_MANAGER_OK) {
                    }
                    g_uart_send_lock_status[uart_port] = false;
                }
            }
            restore_interrupt_mask(irq_status);
        } else {
            g_uart_frist_send_complete_interrupt[uart_port] = false;
        }
        return;
#endif
    } else {
        callback = g_uart_callback[uart_port].func;
        arg = g_uart_callback[uart_port].arg;
        if (callback == NULL) {
            return;
        }
        callback(HAL_UART_EVENT_READY_TO_WRITE, arg);

        vdma_get_available_send_space(channel, &avail_space);

        compare_space = g_uart_dma_config[uart_port].send_vfifo_buffer_size
                        - g_uart_dma_config[uart_port].send_vfifo_threshold_size;
        if (avail_space >= compare_space) {
            vdma_disable_interrupt(channel);
        }
    }
}

/* Only triggered by UART error interrupt */
void uart_error_handler(hal_uart_port_t uart_port)
{
    hal_uart_callback_t callback;
    void *arg;

    if (!uart_verify_error(uart_port)) {
        uart_purge_fifo(uart_port, 1);
        uart_purge_fifo(uart_port, 0);
        callback = g_uart_callback[uart_port].func;
        arg = g_uart_callback[uart_port].arg;
        if (callback == NULL) {
            return;
        }
        callback(HAL_UART_EVENT_TRANSACTION_ERROR, arg);
    }
}

static void uart_dma_callback_handler(vdma_event_t event, void  *user_data)
{
    uart_dma_callback_data_t *callback_data = (uart_dma_callback_data_t *)user_data;

    if (callback_data->is_rx == true) {
        uart_receive_handler(callback_data->uart_port, false);
    } else {
        uart_send_handler(callback_data->uart_port, false);
    }
}

#ifdef HAL_SLEEP_MANAGER_ENABLED
void uart_backup_all_registers(void)
{
    hal_uart_port_t uart_port;
    uart_hwstatus_t uart_hwstatus;
    uart_flowcontrol_t uart_flowcontrol;
    hal_uart_callback_t uart_callback;
    void *uart_callback_arg;

    for (uart_port = HAL_UART_0; uart_port < HAL_UART_MAX; uart_port++) {
        if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_UNINITIALIZED) {
            uart_hwstatus = g_uart_hwstatus[uart_port];
            uart_flowcontrol = g_uart_flowcontrol_status[uart_port];
            uart_callback = g_uart_callback[uart_port].func;
            uart_callback_arg = g_uart_callback[uart_port].arg;

            hal_uart_deinit(uart_port);
            g_uart_hwstatus[uart_port] = uart_hwstatus;
            g_uart_flowcontrol_status[uart_port] = uart_flowcontrol;
            g_uart_callback[uart_port].func = uart_callback;
            g_uart_callback[uart_port].arg = uart_callback_arg;
        }
    }
}

void uart_restore_all_registers(void)
{
    uint32_t delay;
    hal_uart_port_t uart_port;

    g_uart_restore_init = true;

    for (uart_port = HAL_UART_0; uart_port < HAL_UART_MAX; uart_port++) {
        if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_POLL_INITIALIZED) {
            g_uart_hwstatus[uart_port] = UART_HWSTATUS_UNINITIALIZED;
            hal_uart_init(uart_port, &g_uart_config[uart_port]);
        } else if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_DMA_INITIALIZED) {
            g_uart_hwstatus[uart_port] = UART_HWSTATUS_UNINITIALIZED;
            hal_uart_init(uart_port, &g_uart_config[uart_port]);
            hal_uart_set_dma(uart_port, &g_uart_dma_config[uart_port]);
            hal_uart_register_callback(uart_port, g_uart_callback[uart_port].func, g_uart_callback[uart_port].arg);
        }
        /* Because of hardware limitation, we have to send XON manually
         * when software flow control is turn on for that port.
         */
        if (g_uart_flowcontrol_status[uart_port] == UART_FLOWCONTROL_SOFTWARE) {
            uart_put_char_block(uart_port, g_uart_sw_flowcontrol_config[uart_port].xon);
            /* must delay until xon character is sent out */
            delay = ((1000000 * 12) / g_uart_config[uart_port].baudrate) + 1;
            hal_gpt_delay_us(delay);
            hal_uart_set_software_flowcontrol(uart_port,
                                              g_uart_sw_flowcontrol_config[uart_port].xon,
                                              g_uart_sw_flowcontrol_config[uart_port].xoff,
                                              g_uart_sw_flowcontrol_config[uart_port].escape_character);
        } else if (g_uart_flowcontrol_status[uart_port] == UART_FLOWCONTROL_HARDWARE) {
            hal_uart_set_hardware_flowcontrol(uart_port);
        }
    }

    g_uart_restore_init = false;
}
#endif

hal_uart_status_t hal_uart_set_baudrate(hal_uart_port_t uart_port, hal_uart_baudrate_t baudrate)
{
    uint32_t actual_baudrate, irq_status;

    if ((!uart_port_is_valid(uart_port)) ||
            (!uart_baudrate_is_valid(baudrate))) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    irq_status = save_and_set_interrupt_mask();
#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_config[uart_port].baudrate = baudrate;
#endif
    restore_interrupt_mask(irq_status);

    actual_baudrate = g_uart_baudrate_map[baudrate];

    uart_set_baudrate(uart_port, actual_baudrate);

    return HAL_UART_STATUS_OK;
}

hal_uart_status_t hal_uart_set_format(hal_uart_port_t uart_port,
                                      const hal_uart_config_t *config)
{
    uint32_t irq_status;

    if ((!uart_port_is_valid(uart_port)) ||
            (!uart_config_is_valid(config))) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    irq_status = save_and_set_interrupt_mask();
#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_config[uart_port].baudrate = config->baudrate;
    g_uart_config[uart_port].word_length = config->word_length;
    g_uart_config[uart_port].stop_bit = config->stop_bit;
    g_uart_config[uart_port].parity = config->parity;
#endif
    restore_interrupt_mask(irq_status);

    hal_uart_set_baudrate(uart_port, config->baudrate);
    uart_set_format(uart_port, config->word_length, config->stop_bit, config->parity);

    return HAL_UART_STATUS_OK;
}

hal_uart_status_t hal_uart_init(hal_uart_port_t uart_port, hal_uart_config_t *uart_config)
{
    uint32_t i, actual_baudrate, irq_status;

    if ((!uart_port_is_valid(uart_port)) ||
            (!uart_config_is_valid(uart_config))) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    irq_status = save_and_set_interrupt_mask();
    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_UNINITIALIZED) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR_BUSY;
    }

#ifdef HAL_SLEEP_MANAGER_ENABLED
    /* Because hal_uart_init() may be called in two place,
       * We only get new handler when it's called not in wakeup flow.
       */
    if (g_uart_restore_init == false) {
        g_uart_sleep_handler[uart_port] = hal_sleep_manager_set_sleep_handle((char *)g_uart_sleep_handler_name[uart_port]);
    }
#endif

    if (g_uart_global_data_initialized == false) {
        for (i = 0; i < HAL_UART_MAX; i++) {
            g_uart_hwstatus[i] = UART_HWSTATUS_UNINITIALIZED;
#ifdef HAL_SLEEP_MANAGER_ENABLED
            g_uart_flowcontrol_status[i] = UART_FLOWCONTROL_NONE;
            g_uart_frist_send_complete_interrupt[i] = false;
            g_uart_send_lock_status[i] = false;
#endif
            g_uart_callback[i].arg = NULL;
            g_uart_callback[i].func = NULL;
        }
        g_uart_global_data_initialized = true;
    }
#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_config[uart_port].baudrate = uart_config->baudrate;
    g_uart_config[uart_port].word_length = uart_config->word_length;
    g_uart_config[uart_port].stop_bit = uart_config->stop_bit;
    g_uart_config[uart_port].parity = uart_config->parity;
#endif
    g_uart_hwstatus[uart_port] = UART_HWSTATUS_POLL_INITIALIZED;
    restore_interrupt_mask(irq_status);

    uart_reset_default_value(uart_port);
    actual_baudrate = g_uart_baudrate_map[uart_config->baudrate];
    uart_set_baudrate(uart_port, actual_baudrate);
    uart_set_format(uart_port, uart_config->word_length, uart_config->stop_bit, uart_config->parity);
    uart_set_fifo(uart_port);
#ifdef HAL_SLEEP_MANAGER_ENABLED
    uart_set_sleep_mode(uart_port);
#endif

    return HAL_UART_STATUS_OK;
}

hal_uart_status_t hal_uart_deinit(hal_uart_port_t uart_port)
{
    vdma_channel_t tx_dma_channel, rx_dma_channel;
    uint32_t irq_status;
#ifdef HAL_SLEEP_MANAGER_ENABLED
    hal_sleep_manager_status_t sleep_status;
#endif

    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    /* wait all left data sent out before deinit. */
    uart_query_empty(uart_port);

    /* unregister vdma module */
    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_DMA_INITIALIZED) {
        tx_dma_channel = uart_port_to_dma_channel(uart_port, 0);
        rx_dma_channel = uart_port_to_dma_channel(uart_port, 1);

        vdma_disable_interrupt(tx_dma_channel);
        vdma_disable_interrupt(rx_dma_channel);

        vdma_stop(tx_dma_channel);
        vdma_stop(rx_dma_channel);

        vdma_deinit(tx_dma_channel);
        vdma_deinit(rx_dma_channel);

        NVIC_DisableIRQ(g_uart_port_to_irq_num[uart_port]);
    }
    uart_reset_default_value(uart_port);

    irq_status = save_and_set_interrupt_mask();
    g_uart_callback[uart_port].func = NULL;
    g_uart_callback[uart_port].arg = NULL;

#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_frist_send_complete_interrupt[uart_port] = false;
    g_uart_flowcontrol_status[uart_port] = UART_FLOWCONTROL_NONE;
    if (g_uart_restore_init == false) {
        hal_sleep_manager_unlock_sleep(g_uart_sleep_handler[uart_port]);
        sleep_status = hal_sleep_manager_release_sleep_handle(g_uart_sleep_handler[uart_port]);
        if (sleep_status != HAL_SLEEP_MANAGER_OK) {
            restore_interrupt_mask(irq_status);
            return HAL_UART_STATUS_ERROR;
        }
    }
#endif

    g_uart_hwstatus[uart_port] = UART_HWSTATUS_UNINITIALIZED;

    restore_interrupt_mask(irq_status);

    return HAL_UART_STATUS_OK;
}

void hal_uart_put_char(hal_uart_port_t uart_port, char byte)
{
    if (!uart_port_is_valid(uart_port)) {
        return;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        return;
    }

    uart_put_char_block(uart_port, byte);
}

uint32_t hal_uart_send_polling(hal_uart_port_t uart_port, const uint8_t *data, uint32_t size)
{
    uint32_t i = 0;

    if ((!uart_port_is_valid(uart_port)) || (data == NULL)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        return 0;
    }

    for (i = 0; i < size; i++) {
        hal_uart_put_char(uart_port, *data);
        data++;
    }

    return size;
}

uint32_t hal_uart_send_dma(hal_uart_port_t uart_port, const uint8_t *data, uint32_t size)
{
    vdma_channel_t channel;
    uint32_t i, real_count, avail_space;
#ifdef HAL_SLEEP_MANAGER_ENABLED
    uint32_t irq_status;
    hal_sleep_manager_status_t sleep_status;
#endif

    if ((!uart_port_is_valid(uart_port)) || (data == NULL) || (size == 0)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return 0;
    }

#ifdef HAL_SLEEP_MANAGER_ENABLED
    irq_status = save_and_set_interrupt_mask();
    if (g_uart_send_lock_status[uart_port] == false) {
        sleep_status = hal_sleep_manager_lock_sleep(g_uart_sleep_handler[uart_port]);
        if (sleep_status != HAL_SLEEP_MANAGER_OK) {
            restore_interrupt_mask(irq_status);
            return 0;
        }
        g_uart_send_lock_status[uart_port] = true;
    }
    restore_interrupt_mask(irq_status);
#endif

    channel = uart_port_to_dma_channel(uart_port, 0);

    vdma_get_available_send_space(channel, &avail_space);

    if (avail_space >= size) {
        real_count = size;
    } else {
        real_count = avail_space;
    }
    for (i = 0; i < real_count; i++) {
        vdma_push_data(channel, data[i]);
    }

    /* If avail space is not enough, turn on TX IRQ
       * so that UART driver can notice user when user's data has been sent out.
       */
    if (real_count == avail_space) {
        vdma_enable_interrupt(channel);
    }

    return real_count;
}

char hal_uart_get_char(hal_uart_port_t uart_port)
{
    char data;

    if (!uart_port_is_valid(uart_port)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        return 0;
    }

    data = uart_get_char_block(uart_port);

    return data;
}

uint32_t hal_uart_receive_polling(hal_uart_port_t uart_port, uint8_t *buffer, uint32_t size)
{
    uint32_t i;
    uint8_t *pbuf = buffer;

    if ((!uart_port_is_valid(uart_port)) ||
            (buffer == NULL)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        return 0;
    }

    for (i = 0; i < size; i++) {
        pbuf[i] = hal_uart_get_char(uart_port);
    }

    return size;
}

uint32_t hal_uart_receive_dma(hal_uart_port_t uart_port, uint8_t *buffer, uint32_t size)
{
    vdma_channel_t channel;
    uint32_t receive_count, avail_count;
    uint32_t index;

    if ((!uart_port_is_valid(uart_port)) ||
            (buffer == NULL) ||
            (size == 0)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return 0;
    }

    channel = uart_port_to_dma_channel(uart_port, 1);

    vdma_get_available_receive_bytes(channel, &avail_count);

    if (avail_count < size) {
        receive_count = avail_count;
    } else {
        receive_count = size;
    }
    for (index = 0; index < receive_count; index++) {
        vdma_pop_data(channel, &buffer[index]);
    }

    /* If avail bytes is not enough, turn on RX IRQ
       * so that UART driver can notice user when new user's data has been received.
       */
    if (receive_count == avail_count) {
        vdma_enable_interrupt(channel);
    }

    return receive_count;
}

static void uart_start_dma_transmission(hal_uart_port_t uart_port)
{
    vdma_channel_t tx_dma_channel, rx_dma_channel;

    tx_dma_channel = uart_port_to_dma_channel(uart_port, 0);
    rx_dma_channel = uart_port_to_dma_channel(uart_port, 1);

#ifdef HAL_SLEEP_MANAGER_ENABLED
    uart_unmask_send_interrupt(uart_port);
    g_uart_frist_send_complete_interrupt[uart_port] = true;
#endif
    uart_unmask_receive_interrupt(uart_port);
    NVIC_EnableIRQ(g_uart_port_to_irq_num[uart_port]);
    vdma_enable_interrupt(tx_dma_channel);
    vdma_enable_interrupt(rx_dma_channel);

    vdma_start(tx_dma_channel);
    vdma_start(rx_dma_channel);

    uart_enable_dma(uart_port);
}

hal_uart_status_t hal_uart_register_callback(hal_uart_port_t uart_port,
        hal_uart_callback_t user_callback,
        void *user_data)
{
    vdma_channel_t tx_dma_channel, rx_dma_channel;
    uint32_t irq_status;
    vdma_status_t status;
    hal_nvic_status_t nvic_status;

    if ((!uart_port_is_valid(uart_port)) ||
            (user_callback == NULL)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    irq_status = save_and_set_interrupt_mask();
    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    tx_dma_channel = uart_port_to_dma_channel(uart_port, 0);
    rx_dma_channel = uart_port_to_dma_channel(uart_port, 1);

    g_uart_callback[uart_port].func = user_callback;
    g_uart_callback[uart_port].arg = user_data;

    uart_dma_channel_to_callback_data(tx_dma_channel, &g_uart_dma_callback_data[uart_port * 2]);
    status = vdma_register_callback(tx_dma_channel, uart_dma_callback_handler, &g_uart_dma_callback_data[uart_port * 2]);
    if (status != VDMA_OK) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR;
    }
    uart_dma_channel_to_callback_data(rx_dma_channel, &g_uart_dma_callback_data[(uart_port * 2) + 1]);
    status = vdma_register_callback(rx_dma_channel, uart_dma_callback_handler, &g_uart_dma_callback_data[(uart_port * 2) + 1]);
    if (status != VDMA_OK) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR;
    }
    //timeout callback
    status = vdma_register_timeout_callback(rx_dma_channel, uart_dma_callback_handler, &g_uart_dma_callback_data[(uart_port * 2) + 1]);
    if (status != VDMA_OK) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR;
    }
    nvic_status = hal_nvic_register_isr_handler(g_uart_port_to_irq_num[uart_port], uart_interrupt_handler);
    if (nvic_status != HAL_NVIC_STATUS_OK) {
        restore_interrupt_mask(irq_status);
        return HAL_UART_STATUS_ERROR;
    }
    NVIC_SetPriority((IRQn_Type)g_uart_port_to_irq_num[uart_port], DEFAULT_PRI);
    uart_start_dma_transmission(uart_port);

    g_uart_hwstatus[uart_port] = UART_HWSTATUS_DMA_INITIALIZED;

    restore_interrupt_mask(irq_status);

    return HAL_UART_STATUS_OK;
}

uint32_t hal_uart_get_available_send_space(hal_uart_port_t uart_port)
{
    vdma_channel_t channel;
    uint32_t roomleft;

    if (!uart_port_is_valid(uart_port)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return 0;
    }

    channel = uart_port_to_dma_channel(uart_port, 0);
    vdma_get_available_send_space(channel, &roomleft);

    return roomleft;
}

uint32_t hal_uart_get_available_receive_bytes(hal_uart_port_t uart_port)
{
    vdma_channel_t channel;
    uint32_t avail;

    if (!uart_port_is_valid(uart_port)) {
        return 0;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_DMA_INITIALIZED) {
        return 0;
    }

    channel = uart_port_to_dma_channel(uart_port, 1);
    vdma_get_available_receive_bytes(channel, &avail);

    return avail;
}

hal_uart_status_t hal_uart_set_hardware_flowcontrol(hal_uart_port_t uart_port)
{
    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    uart_set_hardware_flowcontrol(uart_port);

#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_flowcontrol_status[uart_port] = UART_FLOWCONTROL_HARDWARE;
#endif

    return HAL_UART_STATUS_OK;
}

hal_uart_status_t hal_uart_set_software_flowcontrol(hal_uart_port_t uart_port,
        uint8_t xon,
        uint8_t xoff,
        uint8_t escape_character)
{
    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    uart_set_software_flowcontrol(uart_port, xon, xoff, escape_character);

#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_flowcontrol_status[uart_port] = UART_FLOWCONTROL_SOFTWARE;
    g_uart_sw_flowcontrol_config[uart_port].xon = xon;
    g_uart_sw_flowcontrol_config[uart_port].xoff = xoff;
    g_uart_sw_flowcontrol_config[uart_port].escape_character = escape_character;
#endif

    return HAL_UART_STATUS_OK;
}

hal_uart_status_t hal_uart_disable_flowcontrol(hal_uart_port_t uart_port)
{
    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] == UART_HWSTATUS_UNINITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    uart_disable_flowcontrol(uart_port);

#ifdef HAL_SLEEP_MANAGER_ENABLED
    g_uart_flowcontrol_status[uart_port] = UART_FLOWCONTROL_NONE;
#endif

    return HAL_UART_STATUS_OK;
}



hal_uart_status_t hal_uart_set_dma(hal_uart_port_t uart_port, const hal_uart_dma_config_t *dma_config)
{
    uint32_t irq_status;
    vdma_config_t internal_dma_config;
    vdma_channel_t tx_dma_channel, rx_dma_channel;

    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }
    if ((dma_config->send_vfifo_buffer == NULL) ||
            (dma_config->receive_vfifo_buffer == NULL)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }
    if ((dma_config->send_vfifo_buffer_size >= UART_DMA_MAX_SETTING_VALUE) ||
            (dma_config->send_vfifo_threshold_size >= UART_DMA_MAX_SETTING_VALUE) ||
            (dma_config->send_vfifo_threshold_size > dma_config->send_vfifo_buffer_size)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }
    if ((dma_config->receive_vfifo_buffer_size >= UART_DMA_MAX_SETTING_VALUE) ||
            (dma_config->receive_vfifo_threshold_size >= UART_DMA_MAX_SETTING_VALUE) ||
            (dma_config->receive_vfifo_alert_size >= UART_DMA_MAX_SETTING_VALUE) ||
            (dma_config->receive_vfifo_threshold_size > dma_config->receive_vfifo_buffer_size) ||
            (dma_config->receive_vfifo_alert_size > dma_config->receive_vfifo_buffer_size)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    if (g_uart_hwstatus[uart_port] != UART_HWSTATUS_POLL_INITIALIZED) {
        return HAL_UART_STATUS_ERROR_UNINITIALIZED;
    }

    tx_dma_channel = uart_port_to_dma_channel(uart_port, 0);
    rx_dma_channel = uart_port_to_dma_channel(uart_port, 1);

    vdma_init(tx_dma_channel);

    internal_dma_config.base_address = (uint32_t)dma_config->send_vfifo_buffer;
    internal_dma_config.size = dma_config->send_vfifo_buffer_size;
    vdma_configure(tx_dma_channel, &internal_dma_config);

    vdma_set_threshold(tx_dma_channel, dma_config->send_vfifo_threshold_size);

    vdma_init(rx_dma_channel);

    internal_dma_config.base_address = (uint32_t)dma_config->receive_vfifo_buffer;
    internal_dma_config.size = dma_config->receive_vfifo_buffer_size;
    vdma_configure(rx_dma_channel, &internal_dma_config);

    vdma_set_threshold(rx_dma_channel, dma_config->receive_vfifo_threshold_size);

    #ifdef HAL_UART_FEATURE_VFIFO_DMA_TIMEOUT
    hal_uart_set_dma_timeout(uart_port, 1000);
#endif

    vdma_set_alert_length(rx_dma_channel, dma_config->receive_vfifo_alert_size);

    irq_status = save_and_set_interrupt_mask();
    g_uart_dma_config[uart_port].send_vfifo_buffer = dma_config->send_vfifo_buffer;
    g_uart_dma_config[uart_port].send_vfifo_buffer_size = dma_config->send_vfifo_buffer_size;
    g_uart_dma_config[uart_port].send_vfifo_threshold_size = dma_config->send_vfifo_threshold_size;
    g_uart_dma_config[uart_port].receive_vfifo_alert_size = dma_config->receive_vfifo_alert_size;
    g_uart_dma_config[uart_port].receive_vfifo_buffer = dma_config->receive_vfifo_buffer;
    g_uart_dma_config[uart_port].receive_vfifo_buffer_size = dma_config->receive_vfifo_buffer_size;
    g_uart_dma_config[uart_port].receive_vfifo_threshold_size = dma_config->receive_vfifo_threshold_size;
    restore_interrupt_mask(irq_status);

    return HAL_UART_STATUS_OK;
}
#ifdef HAL_UART_FEATURE_VFIFO_DMA_TIMEOUT
hal_uart_status_t hal_uart_set_dma_timeout(hal_uart_port_t uart_port, uint32_t timeout)
{
    vdma_channel_t channel;

    if (!uart_port_is_valid(uart_port)) {
        return HAL_UART_STATUS_ERROR_PARAMETER;
    }

    channel = uart_port_to_dma_channel(uart_port, 1);

    vdma_set_timeout(channel, uart_translate_timeout(timeout));

    return HAL_UART_STATUS_OK;
}
#endif


#endif