Ver Fonte

implement serialX UART device framework

thewon86 há 2 anos atrás
pai
commit
67bc7e816c

+ 2 - 0
bsp/stm32/libraries/HAL_Drivers/SConscript

@@ -13,6 +13,8 @@ if GetDepend(['RT_USING_PIN']):
 if GetDepend(['RT_USING_SERIAL']):
     if GetDepend(['RT_USING_SERIAL_V2']):
         src += ['drv_usart_v2.c']
+    elif GetDepend(['RT_USING_SERIAL_X']):
+        src += ['drv_usartX.c']
     else:
         src += ['drv_usart.c']
 

+ 876 - 0
bsp/stm32/libraries/HAL_Drivers/drv_usartX.c

@@ -0,0 +1,876 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-04-10     THEWON       first version
+ */
+
+#include "board.h"
+#include "drv_usartX.h"
+#include "drv_config.h"
+
+#ifdef RT_USING_SERIAL
+
+//#define DRV_DEBUG
+#define LOG_TAG             "drv.usart"
+#include <drv_log.h>
+
+#if !defined(BSP_USING_UART1) && !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \
+    !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && !defined(BSP_USING_UART6) && \
+    !defined(BSP_USING_UART7) && !defined(BSP_USING_UART8) && !defined(BSP_USING_LPUART1)
+#error "Please define at least one BSP_USING_UARTx"
+/* this driver can be disabled at menuconfig -> RT-Thread Components -> Device Drivers */
+#endif
+
+#ifdef RT_SERIAL_USING_DMA
+static void stm32_dma_config(struct rt_serial_device *serial, rt_ubase_t flag);
+#endif
+
+enum
+{
+#ifdef BSP_USING_UART1
+    UART1_INDEX,
+#endif
+#ifdef BSP_USING_UART2
+    UART2_INDEX,
+#endif
+#ifdef BSP_USING_UART3
+    UART3_INDEX,
+#endif
+#ifdef BSP_USING_UART4
+    UART4_INDEX,
+#endif
+#ifdef BSP_USING_UART5
+    UART5_INDEX,
+#endif
+#ifdef BSP_USING_UART6
+    UART6_INDEX,
+#endif
+#ifdef BSP_USING_UART7
+    UART7_INDEX,
+#endif
+#ifdef BSP_USING_UART8
+    UART8_INDEX,
+#endif
+#ifdef BSP_USING_LPUART1
+    LPUART1_INDEX,
+#endif
+};
+
+static struct stm32_uart_config uart_config[] =
+{
+#ifdef BSP_USING_UART1
+    UART1_CONFIG,
+#endif
+#ifdef BSP_USING_UART2
+    UART2_CONFIG,
+#endif
+#ifdef BSP_USING_UART3
+    UART3_CONFIG,
+#endif
+#ifdef BSP_USING_UART4
+    UART4_CONFIG,
+#endif
+#ifdef BSP_USING_UART5
+    UART5_CONFIG,
+#endif
+#ifdef BSP_USING_UART6
+    UART6_CONFIG,
+#endif
+#ifdef BSP_USING_UART7
+    UART7_CONFIG,
+#endif
+#ifdef BSP_USING_UART8
+    UART8_CONFIG,
+#endif
+#ifdef BSP_USING_LPUART1
+    LPUART1_CONFIG,
+#endif
+};
+
+static struct stm32_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0};
+
+static rt_uint32_t stm32_uart_get_mask(rt_uint32_t word_length, rt_uint32_t parity)
+{
+    rt_uint32_t mask;
+    if (word_length == UART_WORDLENGTH_8B)
+    {
+        if (parity == UART_PARITY_NONE)
+        {
+            mask = 0x00FFU ;
+        }
+        else
+        {
+            mask = 0x007FU ;
+        }
+    }
+#ifdef UART_WORDLENGTH_9B
+    else if (word_length == UART_WORDLENGTH_9B)
+    {
+        if (parity == UART_PARITY_NONE)
+        {
+            mask = 0x01FFU ;
+        }
+        else
+        {
+            mask = 0x00FFU ;
+        }
+    }
+#endif
+#ifdef UART_WORDLENGTH_7B
+    else if (word_length == UART_WORDLENGTH_7B)
+    {
+        if (parity == UART_PARITY_NONE)
+        {
+            mask = 0x007FU ;
+        }
+        else
+        {
+            mask = 0x003FU ;
+        }
+    }
+    else
+    {
+        mask = 0x0000U;
+    }
+#endif
+    return mask;
+}
+
+static rt_err_t stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+    RT_ASSERT(cfg != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+    uart->handle.Instance          = uart->config->Instance;
+    uart->handle.Init.BaudRate     = cfg->baud_rate;
+    uart->handle.Init.Mode         = UART_MODE_TX_RX;
+    uart->handle.Init.OverSampling = UART_OVERSAMPLING_16;
+
+    switch (cfg->flowcontrol)
+    {
+    case RT_SERIAL_FLOWCONTROL_NONE:
+        uart->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+        break;
+    case RT_SERIAL_FLOWCONTROL_CTSRTS:
+        uart->handle.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
+        break;
+    default:
+        uart->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+        break;
+    }
+
+    switch (cfg->data_bits)
+    {
+    case DATA_BITS_8:
+        if (cfg->parity == PARITY_ODD || cfg->parity == PARITY_EVEN)
+            uart->handle.Init.WordLength = UART_WORDLENGTH_9B;
+        else
+            uart->handle.Init.WordLength = UART_WORDLENGTH_8B;
+        break;
+    case DATA_BITS_9:
+        uart->handle.Init.WordLength = UART_WORDLENGTH_9B;
+        break;
+    default:
+        uart->handle.Init.WordLength = UART_WORDLENGTH_8B;
+        break;
+    }
+
+    switch (cfg->stop_bits)
+    {
+    case STOP_BITS_1:
+        uart->handle.Init.StopBits   = UART_STOPBITS_1;
+        break;
+    case STOP_BITS_2:
+        uart->handle.Init.StopBits   = UART_STOPBITS_2;
+        break;
+    default:
+        uart->handle.Init.StopBits   = UART_STOPBITS_1;
+        break;
+    }
+
+    switch (cfg->parity)
+    {
+    case PARITY_NONE:
+        uart->handle.Init.Parity     = UART_PARITY_NONE;
+        break;
+    case PARITY_ODD:
+        uart->handle.Init.Parity     = UART_PARITY_ODD;
+        break;
+    case PARITY_EVEN:
+        uart->handle.Init.Parity     = UART_PARITY_EVEN;
+        break;
+    default:
+        uart->handle.Init.Parity     = UART_PARITY_NONE;
+        break;
+    }
+
+    uart->config->mask = stm32_uart_get_mask(uart->handle.Init.WordLength, uart->handle.Init.Parity);
+    if (HAL_UART_Init(&uart->handle) != HAL_OK)
+    {
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t stm32_control(struct rt_serial_device *serial, int cmd, void *arg)
+{
+    struct stm32_uart *uart;
+    rt_ubase_t ctrl_arg = (rt_ubase_t)arg;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_OPEN:
+        __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE);
+        __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE);
+        UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_RXNE);
+        UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TXE);
+        UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
+        /* enable interrupt */
+        HAL_NVIC_SetPriority(uart->config->irq_type, 1, 0);
+        HAL_NVIC_EnableIRQ(uart->config->irq_type);
+#ifdef RT_SERIAL_USING_DMA
+        uart->dmaTxing = RT_FALSE;
+        HAL_NVIC_EnableIRQ(uart->config->dma_conf_rx->dma_irq);
+        HAL_NVIC_EnableIRQ(uart->config->dma_conf_tx->dma_irq);
+#endif
+    break;
+    case RT_DEVICE_CTRL_CLOSE:
+        HAL_NVIC_DisableIRQ(uart->config->irq_type);
+#ifdef RT_SERIAL_USING_DMA
+        HAL_NVIC_DisableIRQ(uart->config->dma_conf_rx->dma_irq);
+        HAL_NVIC_DisableIRQ(uart->config->dma_conf_tx->dma_irq);
+#endif
+        __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE);
+        __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE);
+        if (HAL_UART_DeInit(&(uart->handle)) != HAL_OK )
+        {
+        }
+    break;
+    /* disable interrupt */
+    case RT_DEVICE_CTRL_CLR_INT:
+        /* disable interrupt */
+        if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) {
+            __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE);
+        }
+
+#ifdef RT_SERIAL_USING_DMA
+        /* disable DMA */
+        if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX)
+        {
+            HAL_NVIC_DisableIRQ(uart->config->dma_conf_rx->dma_irq);
+            if (HAL_DMA_Abort(&(uart->dma_rx.handle)) != HAL_OK)
+            {
+                RT_ASSERT(0);
+            }
+
+            if (HAL_DMA_DeInit(&(uart->dma_rx.handle)) != HAL_OK)
+            {
+                RT_ASSERT(0);
+            }
+        }
+        if(ctrl_arg & RT_DEVICE_FLAG_DMA_TX)
+        {
+            HAL_NVIC_DisableIRQ(uart->config->dma_conf_tx->dma_irq);
+            if (HAL_DMA_DeInit(&(uart->dma_tx.handle)) != HAL_OK)
+            {
+                RT_ASSERT(0);
+            }
+        }
+#endif
+    break;
+    /* enable interrupt */
+    case RT_DEVICE_CTRL_SET_INT:
+        /* enable rx irq */
+        if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) {
+            __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_RXNE);
+        }
+    break;
+#ifdef RT_SERIAL_USING_DMA
+    case RT_DEVICE_CTRL_CONFIG:
+        stm32_dma_config(serial, ctrl_arg);
+    break;
+#endif
+    default :
+    break;
+    }
+    return RT_EOK;
+}
+
+static int stm32_putc(struct rt_serial_device *serial, char c)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) == RESET);
+    UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
+#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
+    || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
+    || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32F3)
+    uart->handle.Instance->TDR = c;
+#else
+    uart->handle.Instance->DR = c;
+#endif
+    return 1;
+}
+
+static int stm32_getc(struct rt_serial_device *serial)
+{
+    int ch = -1;
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) == RESET)
+    {
+        return -1;
+    }
+#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
+    || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
+    || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3)
+    ch = uart->handle.Instance->RDR & uart->config->mask;
+#else
+    ch = uart->handle.Instance->DR & uart->config->mask;
+#endif
+    return ch;
+}
+
+static int stm32_flush(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+    while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET);
+
+    return 1;
+}
+
+static void stm32_start_tx(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+    __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_TXE);
+}
+
+static void stm32_stop_tx(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+    __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE);
+}
+
+#ifdef RT_SERIAL_USING_DMA
+static rt_bool_t stm32_is_dma_txing(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    return uart->dmaTxing;  //RT_FALSE;
+}
+
+static void stm32_start_dma_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size)
+{
+    struct stm32_uart *uart;
+    HAL_StatusTypeDef status;
+    DMA_HandleTypeDef *hdma;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    do {        // may fallin dead loop
+        status = HAL_UART_Transmit_DMA(&uart->handle, buf, size);
+    } while (status != HAL_OK);
+    uart->dmaTxing = RT_TRUE;
+}
+
+static void stm32_stop_dma_tx(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    if ((uart->dma_tx.handle.Instance->CR & DMA_SxCR_EN) == DMA_SxCR_EN) {
+        return;
+    }
+    __HAL_DMA_DISABLE(&uart->dma_tx.handle);
+    uart->dmaTxing = RT_FALSE;
+}
+#endif
+
+static void stm32_enable_interrupt(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    HAL_NVIC_EnableIRQ(uart->config->irq_type);
+#ifdef RT_SERIAL_USING_DMA
+    if (uart->uart_dma_flag) {
+        HAL_NVIC_EnableIRQ(uart->config->dma_conf_rx->dma_irq);
+    }
+#endif
+}
+
+static void stm32_disable_interrupt(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    HAL_NVIC_DisableIRQ(uart->config->irq_type);
+#ifdef RT_SERIAL_USING_DMA
+    if (uart->uart_dma_flag) {
+        HAL_NVIC_DisableIRQ(uart->config->dma_conf_rx->dma_irq);
+    }
+#endif
+}
+
+/**
+ * Uart common interrupt process. This need add to uart ISR.
+ *
+ * @param serial serial device
+ */
+static void uart_isr(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+#ifdef RT_SERIAL_USING_DMA
+    rt_size_t dma_cnt;
+#endif
+
+    RT_ASSERT(serial != RT_NULL);
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    /* UART in mode Receiver -------------------------------------------------*/
+    if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) &&
+        (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET))
+    {
+        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
+    }
+    if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) != RESET) &&
+        (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_TXE) != RESET))
+    {
+        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET)
+             && (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET))
+    {
+        __HAL_UART_CLEAR_IDLEFLAG(&uart->handle);
+        dma_cnt = RT_SERIAL_DMA_BUFSZ - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle));
+
+        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));
+    }
+    else if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) &&
+            (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_TC) != RESET))
+    {
+        if ((serial->parent.open_flag & RT_DEVICE_FLAG_DMA_TX) != 0)
+        {
+            HAL_UART_IRQHandler(&(uart->handle));
+        }
+    }
+#endif
+    else
+    {
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE) != RESET)
+        {
+            __HAL_UART_CLEAR_OREFLAG(&uart->handle);
+        }
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_NE) != RESET)
+        {
+            __HAL_UART_CLEAR_NEFLAG(&uart->handle);
+        }
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_FE) != RESET)
+        {
+            __HAL_UART_CLEAR_FEFLAG(&uart->handle);
+        }
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_PE) != RESET)
+        {
+            __HAL_UART_CLEAR_PEFLAG(&uart->handle);
+        }
+#if !defined(SOC_SERIES_STM32L4) && !defined(SOC_SERIES_STM32WL) && !defined(SOC_SERIES_STM32F7) && !defined(SOC_SERIES_STM32F0) \
+    && !defined(SOC_SERIES_STM32L0) && !defined(SOC_SERIES_STM32G0) && !defined(SOC_SERIES_STM32H7) \
+    && !defined(SOC_SERIES_STM32G4) && !defined(SOC_SERIES_STM32MP1) && !defined(SOC_SERIES_STM32WB)
+#ifdef SOC_SERIES_STM32F3
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_LBDF) != RESET)
+        {
+            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_LBDF);
+        }
+#else
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_LBD) != RESET)
+        {
+            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_LBD);
+        }
+#endif
+#endif
+        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_CTS) != RESET)
+        {
+            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_CTS);
+        }
+//        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) != RESET)
+//        {
+//            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TXE);
+//        }
+//        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) != RESET)
+//        {
+//            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
+//        }
+//        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET)
+//        {
+//            UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_RXNE);
+//        }
+    }
+}
+
+#ifdef RT_SERIAL_USING_DMA
+static void dma_isr(struct rt_serial_device *serial)
+{
+    struct stm32_uart *uart;
+    rt_size_t dma_cnt;
+
+    RT_ASSERT(serial != RT_NULL);
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) ||
+        (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET))
+    {
+        dma_cnt = RT_SERIAL_DMA_BUFSZ - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle));
+
+        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));
+    }
+}
+#endif
+
+#if defined(BSP_USING_UART1)
+void USART1_IRQHandler(void)
+{
+    /* enter interrupt */
+    rt_interrupt_enter();
+
+    uart_isr(&(uart_obj[UART1_INDEX].serial));
+
+    /* leave interrupt */
+    rt_interrupt_leave();
+}
+#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_RX_USING_DMA)
+void UART1_DMA_RX_IRQHandler(void)
+{
+    /* enter interrupt */
+    rt_interrupt_enter();
+
+    HAL_DMA_IRQHandler(&uart_obj[UART1_INDEX].dma_rx.handle);
+
+    /* leave interrupt */
+    rt_interrupt_leave();
+}
+#endif /* defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_RX_USING_DMA) */
+#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_TX_USING_DMA)
+void UART1_DMA_TX_IRQHandler(void)
+{
+    /* enter interrupt */
+    rt_interrupt_enter();
+
+    HAL_DMA_IRQHandler(&uart_obj[UART1_INDEX].dma_tx.handle);
+
+    /* leave interrupt */
+    rt_interrupt_leave();
+}
+#endif /* defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_TX_USING_DMA) */
+#endif /* BSP_USING_UART1 */
+
+#ifdef RT_SERIAL_USING_DMA
+static void stm32_uart_get_dma_config(void)
+{
+#ifdef BSP_USING_UART1
+    uart_obj[UART1_INDEX].uart_dma_flag = 0;
+#ifdef BSP_UART1_RX_USING_DMA
+    uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
+    static struct dma_config uart1_dma_rx = UART1_DMA_RX_CONFIG;
+    uart_config[UART1_INDEX].dma_conf_rx = &uart1_dma_rx;
+#endif
+#ifdef BSP_UART1_TX_USING_DMA
+    uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
+    static struct dma_config uart1_dma_tx = UART1_DMA_TX_CONFIG;
+    uart_config[UART1_INDEX].dma_conf_tx = &uart1_dma_tx;
+#endif
+#endif
+}
+
+static void stm32_dma_config(struct rt_serial_device *serial, rt_ubase_t flag)
+{
+    DMA_HandleTypeDef *DMA_Handle;
+    struct dma_config *dma_config;
+    struct stm32_uart *uart;
+
+    RT_ASSERT(serial != RT_NULL);
+    uart = rt_container_of(serial, struct stm32_uart, serial);
+
+    if (RT_DEVICE_FLAG_DMA_RX == flag)
+    {
+        DMA_Handle = &uart->dma_rx.handle;
+        dma_config = uart->config->dma_conf_rx;
+    }
+    else if (RT_DEVICE_FLAG_DMA_TX == flag)
+    {
+        DMA_Handle = &uart->dma_tx.handle;
+        dma_config = uart->config->dma_conf_tx;
+    }
+    LOG_D("%s dma config start", uart->config->name);
+
+    {
+        rt_uint32_t tmpreg = 0x00U;
+#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0) \
+    || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1)
+        /* enable DMA clock && Delay after an RCC peripheral clock enabling*/
+        SET_BIT(RCC->AHBENR, dma_config->dma_rcc);
+        tmpreg = READ_BIT(RCC->AHBENR, dma_config->dma_rcc);
+#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) \
+    || defined(SOC_SERIES_STM32G4)|| defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32WB)
+        /* enable DMA clock && Delay after an RCC peripheral clock enabling*/
+        SET_BIT(RCC->AHB1ENR, dma_config->dma_rcc);
+        tmpreg = READ_BIT(RCC->AHB1ENR, dma_config->dma_rcc);
+#elif defined(SOC_SERIES_STM32MP1)
+        /* enable DMA clock && Delay after an RCC peripheral clock enabling*/
+        SET_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc);
+        tmpreg = READ_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc);
+#endif
+
+#if (defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)) && defined(DMAMUX1)
+        /* enable DMAMUX clock for L4+ and G4 */
+        __HAL_RCC_DMAMUX1_CLK_ENABLE();
+#elif defined(SOC_SERIES_STM32MP1)
+        __HAL_RCC_DMAMUX_CLK_ENABLE();
+#endif
+
+        UNUSED(tmpreg);   /* To avoid compiler warnings */
+    }
+
+    if (RT_DEVICE_FLAG_DMA_RX == flag)
+    {
+        __HAL_LINKDMA(&(uart->handle), hdmarx, uart->dma_rx.handle);
+    }
+    else if (RT_DEVICE_FLAG_DMA_TX == flag)
+    {
+        __HAL_LINKDMA(&(uart->handle), hdmatx, uart->dma_tx.handle);
+    }
+
+#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1)
+    DMA_Handle->Instance                 = dma_config->Instance;
+#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7)
+    DMA_Handle->Instance                 = dma_config->Instance;
+    DMA_Handle->Init.Channel             = dma_config->channel;
+#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)\
+    || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1)
+    DMA_Handle->Instance                 = dma_config->Instance;
+    DMA_Handle->Init.Request             = dma_config->request;
+#endif
+    DMA_Handle->Init.PeriphInc           = DMA_PINC_DISABLE;
+    DMA_Handle->Init.MemInc              = DMA_MINC_ENABLE;
+    DMA_Handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
+    DMA_Handle->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
+
+    if (RT_DEVICE_FLAG_DMA_RX == flag)
+    {
+        DMA_Handle->Init.Direction           = DMA_PERIPH_TO_MEMORY;
+        DMA_Handle->Init.Mode                = DMA_CIRCULAR;
+    }
+    else if (RT_DEVICE_FLAG_DMA_TX == flag)
+    {
+        DMA_Handle->Init.Direction           = DMA_MEMORY_TO_PERIPH;
+        DMA_Handle->Init.Mode                = DMA_NORMAL;
+    }
+
+    DMA_Handle->Init.Priority            = DMA_PRIORITY_MEDIUM;
+#if defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1)
+    DMA_Handle->Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
+#endif
+    if (HAL_DMA_DeInit(DMA_Handle) != HAL_OK)
+    {
+        RT_ASSERT(0);
+    }
+
+    if (HAL_DMA_Init(DMA_Handle) != HAL_OK)
+    {
+        RT_ASSERT(0);
+    }
+
+    /* enable interrupt */
+    if (flag == RT_DEVICE_FLAG_DMA_RX)
+    {
+        /* Start DMA transfer */
+        if (HAL_UART_Receive_DMA(&(uart->handle), serial->serial_dma_rx, RT_SERIAL_DMA_BUFSZ) != HAL_OK)
+        {
+            /* Transfer error in reception process */
+            RT_ASSERT(0);
+        }
+        CLEAR_BIT(uart->handle.Instance->CR3, USART_CR3_EIE);
+        __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE);
+    }
+
+    /* DMA irq should set in DMA TX mode, or HAL_UART_TxCpltCallback function will not be called */
+    HAL_NVIC_SetPriority(dma_config->dma_irq, 0, 0);
+    HAL_NVIC_EnableIRQ(dma_config->dma_irq);
+}
+
+/**
+  * @brief  UART error callbacks
+  * @param  huart: UART handle
+  * @note   This example shows a simple way to report transfer error, and you can
+  *         add your own implementation.
+  * @retval None
+  */
+void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
+{
+    struct stm32_uart *uart
+
+    RT_ASSERT(huart != NULL);
+
+    uart = (struct stm32_uart *)huart;
+    LOG_D("%s: %s %d\n", __FUNCTION__, uart->config->name, huart->ErrorCode);
+    UNUSED(uart);
+}
+
+/**
+  * @brief  Rx Transfer completed callback
+  * @param  huart: UART handle
+  * @note   This example shows a simple way to report end of DMA Rx transfer, and
+  *         you can add your own implementation.
+  * @retval None
+  */
+void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(huart != NULL);
+
+    uart = (struct stm32_uart *)huart;
+    dma_isr(&uart->serial);
+}
+
+/**
+  * @brief  Rx Half transfer completed callback
+  * @param  huart: UART handle
+  * @note   This example shows a simple way to report end of DMA Rx Half transfer,
+  *         and you can add your own implementation.
+  * @retval None
+  */
+void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
+{
+    struct stm32_uart *uart;
+
+    RT_ASSERT(huart != NULL);
+
+    uart = (struct stm32_uart *)huart;
+    dma_isr(&uart->serial);
+}
+
+/**
+  * @brief  HAL_UART_TxCpltCallback
+  * @param  huart: UART handle
+  * @note   This callback can be called by two functions, first in UART_EndTransmit_IT when
+  *         UART Tx complete and second in UART_DMATransmitCplt function in DMA Circular mode.
+  * @retval None
+  */
+void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
+{
+    struct stm32_uart *uart;
+    rt_size_t dma_cnt;
+
+    RT_ASSERT(huart != NULL);
+
+    uart = (struct stm32_uart *)huart;
+
+    dma_cnt = __HAL_DMA_GET_COUNTER(&(uart->dma_tx.handle));
+
+    if (dma_cnt == 0)
+    {
+        rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE);
+    }
+}
+#endif  /* RT_SERIAL_USING_DMA */
+
+static const struct rt_uart_ops stm32_uart_ops =
+{
+    .configure = stm32_configure,
+    .control = stm32_control,
+    .putc = stm32_putc,
+    .getc = stm32_getc,
+    .flush = stm32_flush,
+    .start_tx = stm32_start_tx,
+    .stop_tx = stm32_stop_tx,
+#ifdef RT_SERIAL_USING_DMA
+    .is_dma_txing = stm32_is_dma_txing,
+    .start_dma_tx = stm32_start_dma_tx,
+    .stop_dma_tx = stm32_stop_dma_tx,
+#endif
+    .enable_interrupt = stm32_enable_interrupt,
+    .disable_interrupt = stm32_disable_interrupt,
+};
+
+int rt_hw_usart_init(void)
+{
+    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
+    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
+    rt_err_t result = 0;
+
+#ifdef RT_SERIAL_USING_DMA
+    stm32_uart_get_dma_config();
+#endif
+
+    for (int i = 0; i < obj_num; i++)
+    {
+        /* init UART object */
+        uart_obj[i].config = &uart_config[i];
+        uart_obj[i].serial.ops    = &stm32_uart_ops;
+        uart_obj[i].serial.config = config;
+
+        /* register UART device */
+        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
+                                       RT_DEVICE_FLAG_RDWR
+                                       | RT_DEVICE_FLAG_INT_RX
+                                       | RT_DEVICE_FLAG_INT_TX
+#ifdef RT_SERIAL_USING_DMA
+                                       | uart_obj[i].uart_dma_flag
+#endif
+                                       , NULL);
+        RT_ASSERT(result == RT_EOK);
+    }
+
+    return result;
+}
+
+#endif /* RT_USING_SERIAL */
+

+ 74 - 0
bsp/stm32/libraries/HAL_Drivers/drv_usartX.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-04-10     THEWON       first version
+ */
+
+#ifndef __DRV_USARTX_H__
+#define __DRV_USARTX_H__
+
+#include <rtthread.h>
+#include "rtdevice.h"
+#include <rthw.h>
+#include <drv_common.h>
+#include "drv_dma.h"
+
+int rt_hw_usart_init(void);
+
+#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) \
+    || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3)
+#define DMA_INSTANCE_TYPE              DMA_Channel_TypeDef
+#elif defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) \
+    || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1)
+#define DMA_INSTANCE_TYPE              DMA_Stream_TypeDef
+#endif /*  defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) */
+
+#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F2) \
+    || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) \
+    || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3)
+#define UART_INSTANCE_CLEAR_FUNCTION    __HAL_UART_CLEAR_FLAG
+#elif defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32H7) \
+    || defined(SOC_SERIES_STM32MP1)
+#define UART_INSTANCE_CLEAR_FUNCTION    __HAL_UART_CLEAR_IT
+#endif
+
+/* stm32 config class */
+struct stm32_uart_config
+{
+    const char *name;
+    USART_TypeDef *Instance;
+    IRQn_Type irq_type;
+    rt_uint32_t mask;
+#ifdef RT_SERIAL_USING_DMA
+    struct dma_config *dma_conf_rx;
+    struct dma_config *dma_conf_tx;
+#endif
+};
+
+/* stm32 uart dirver class */
+struct stm32_uart
+{
+    UART_HandleTypeDef handle;
+    struct rt_serial_device serial;
+    struct stm32_uart_config *config;
+
+#ifdef RT_SERIAL_USING_DMA
+    rt_bool_t dmaTxing;
+    struct
+    {
+        DMA_HandleTypeDef handle;
+    } dma_rx;
+    struct
+    {
+        DMA_HandleTypeDef handle;
+    } dma_tx;
+    rt_uint16_t uart_dma_flag;
+#endif
+};
+
+#endif  /* __DRV_USART_H__ */
+

+ 5 - 0
bsp/stm32/stm32f429-armfly-v6/board/Kconfig

@@ -93,6 +93,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART1"
                 default y
 
+            config BSP_UART1_TX_USING_DMA
+                bool "Enable UART1 TX DMA"
+                depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART1_RX_USING_DMA
                 bool "Enable UART1 RX DMA"
                 depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA

+ 15 - 0
bsp/stm32/stm32f429-atk-apollo/board/Kconfig

@@ -99,6 +99,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART1"
                 default y
 
+            config BSP_UART1_TX_USING_DMA
+                bool "Enable UART1 TX DMA"
+                depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART1_RX_USING_DMA
                 bool "Enable UART1 RX DMA"
                 depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
@@ -108,6 +113,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART2"
                 default n
 
+            config BSP_UART2_TX_USING_DMA
+                bool "Enable UART2 TX DMA"
+                depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART2_RX_USING_DMA
                 bool "Enable UART2 RX DMA"
                 depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA
@@ -116,7 +126,12 @@ menu "On-chip Peripheral Drivers"
             config BSP_USING_UART3
                 bool "Enable UART3"
                 default n
+
+            config BSP_UART3_TX_USING_DMA
+                bool "Enable UART3 TX DMA"
+                depends on BSP_USING_UART3 && RT_SERIAL_USING_DMA
                 depends on !BSP_USING_ETH
+                default n
 
             config BSP_UART3_RX_USING_DMA
                 bool "Enable UART3 RX DMA"

+ 10 - 0
bsp/stm32/stm32f429-fire-challenger/board/Kconfig

@@ -97,6 +97,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART1"
                 default y
 
+            config BSP_UART1_TX_USING_DMA
+                bool "Enable UART1 TX DMA"
+                depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART1_RX_USING_DMA
                 bool "Enable UART1 RX DMA"
                 depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
@@ -106,6 +111,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART2"
                 default n
 
+            config BSP_UART2_TX_USING_DMA
+                bool "Enable UART2 TX DMA"
+                depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART2_RX_USING_DMA
                 bool "Enable UART2 RX DMA"
                 depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA

+ 10 - 0
bsp/stm32/stm32f429-st-disco/board/Kconfig

@@ -32,6 +32,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART1"
                 default y
 
+            config BSP_UART1_TX_USING_DMA
+                bool "Enable UART1 TX DMA"
+                depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART1_RX_USING_DMA
                 bool "Enable UART1 RX DMA"
                 depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
@@ -41,6 +46,11 @@ menu "On-chip Peripheral Drivers"
                 bool "Enable UART2"
                 default n
 
+            config BSP_UART2_TX_USING_DMA
+                bool "Enable UART2 TX DMA"
+                depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA
+                default n
+
             config BSP_UART2_RX_USING_DMA
                 bool "Enable UART2 RX DMA"
                 depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA

+ 33 - 1
components/drivers/Kconfig

@@ -38,6 +38,10 @@ menuconfig RT_USING_SERIAL
                 bool "RT_USING_SERIAL_V1"
             config RT_USING_SERIAL_V2
                 bool "RT_USING_SERIAL_V2"
+            config RT_USING_SERIAL_X
+                bool "RT_USING_SERIAL_X"
+                help
+                    A serial driver that supporting True NonBlock.
         endchoice
         config RT_SERIAL_USING_DMA
             bool "Enable serial DMA mode"
@@ -45,8 +49,36 @@ menuconfig RT_USING_SERIAL
 
         config RT_SERIAL_RB_BUFSZ
             int "Set RX buffer size"
-            depends on !RT_USING_SERIAL_V2
+            depends on RT_USING_SERIAL_V1
             default 64
+
+        if RT_USING_SERIAL_X
+            config RT_SERIAL_FIFO_BUFSZ
+                int "Set SerialX FIFO buffer size"
+                default 128
+                help
+                    suggestion: RT_SERIAL_FIFO_BUFSZ == 2^N
+                    and RT_SERIAL_FIFO_BUFSZ >= 2*RT_SERIAL_DMA_BUFSZ
+
+            config RT_SERIAL_DMA_BUFSZ
+                int "Set SerialX DMA buffer size"
+                depends on RT_SERIAL_USING_DMA
+                default 64
+                help
+                    suggestion: RT_SERIAL_DMA_BUFSZ == 2^N
+                    and RT_SERIAL_FIFO_BUFSZ >= 2*RT_SERIAL_DMA_BUFSZ
+
+            config RT_SERIAL_HARD_FIFO
+                bool "Set Hard FIFO buffer size"
+                default n
+                help
+                    Useful only if the chip supported
+                    eg: NUC970 N9H30 serial chips
+                        The UART1/2/4/6/8/10 is built-in with a 64-byte transmitter FIFO (TX_FIFO)
+                        and a 64-byte receiver FIFO (RX_FIFO),
+                        and the UART0/3/5/7/9 are equipped 16-byte transmitter FIFO (TX_FIFO)
+                        and 16-byte receiver FIFO (RX_FIFO).
+        endif
     endif
 
 config RT_USING_TTY

+ 207 - 0
components/drivers/include/drivers/serialX.h

@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2022-04-10     THEWON       first version
+ */
+
+#ifndef __SERIALX_H__
+#define __SERIALX_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define BAUD_RATE_2400                  2400
+#define BAUD_RATE_4800                  4800
+#define BAUD_RATE_9600                  9600
+#define BAUD_RATE_19200                 19200
+#define BAUD_RATE_38400                 38400
+#define BAUD_RATE_57600                 57600
+#define BAUD_RATE_115200                115200
+#define BAUD_RATE_230400                230400
+#define BAUD_RATE_460800                460800
+#define BAUD_RATE_921600                921600
+#define BAUD_RATE_2000000               2000000
+#define BAUD_RATE_3000000               3000000
+
+#define DATA_BITS_5                     5
+#define DATA_BITS_6                     6
+#define DATA_BITS_7                     7
+#define DATA_BITS_8                     8
+#define DATA_BITS_9                     9
+
+#define STOP_BITS_1                     0
+#define STOP_BITS_2                     1
+#define STOP_BITS_3                     2
+#define STOP_BITS_4                     3
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#define PARITY_NONE                     0
+#define PARITY_ODD                      1
+#define PARITY_EVEN                     2
+#endif
+
+#define BIT_ORDER_LSB                   0
+#define BIT_ORDER_MSB                   1
+
+#define NRZ_NORMAL                      0       /* Non Return to Zero : normal mode */
+#define NRZ_INVERTED                    1       /* Non Return to Zero : inverted mode */
+
+#ifndef RT_SERIAL_FIFO_BUFSZ
+#define RT_SERIAL_FIFO_BUFSZ            128
+#endif
+
+#ifndef RT_SERIAL_DMA_BUFSZ
+#define RT_SERIAL_DMA_BUFSZ             32
+#endif
+
+#if RT_SERIAL_DMA_BUFSZ < 32
+#define RT_SERIAL_DMA_BUFSZ             32
+#endif
+
+#if RT_SERIAL_FIFO_BUFSZ < (RT_SERIAL_DMA_BUFSZ*2)
+#define RT_SERIAL_FIFO_BUFSZ            RT_SERIAL_DMA_BUFSZ*2
+#endif
+
+
+#define RT_SERIAL_EVENT_RX_IND          0x01    /* Rx indication */
+#define RT_SERIAL_EVENT_TX_DONE         0x02    /* Tx complete   */
+#define RT_SERIAL_EVENT_RX_DMADONE      0x03    /* Rx DMA transfer done */
+#define RT_SERIAL_EVENT_TX_DMADONE      0x04    /* Tx DMA transfer done */
+#define RT_SERIAL_EVENT_RX_TIMEOUT      0x05    /* Rx timeout    */
+
+#define RT_SERIAL_DMA_RX                0x01
+#define RT_SERIAL_DMA_TX                0x02
+
+#define RT_SERIAL_RX_INT                0x01
+#define RT_SERIAL_TX_INT                0x02
+
+#define RT_SERIAL_ERR_OVERRUN           0x01
+#define RT_SERIAL_ERR_FRAMING           0x02
+#define RT_SERIAL_ERR_PARITY            0x03
+
+#define RT_SERIAL_TX_DATAQUEUE_SIZE     2048
+#define RT_SERIAL_TX_DATAQUEUE_LWM      30
+
+#define RT_SERIAL_FLOWCONTROL_CTSRTS     1
+#define RT_SERIAL_FLOWCONTROL_NONE       0
+
+/* Default config for serial_configure structure */
+#define RT_SERIAL_CONFIG_DEFAULT           \
+{                                          \
+    BAUD_RATE_115200, /* 115200 bits/s */  \
+    DATA_BITS_8,      /* 8 databits */     \
+    STOP_BITS_1,      /* 1 stopbit */      \
+    PARITY_NONE,      /* No parity  */     \
+    BIT_ORDER_LSB,    /* LSB first sent */ \
+    NRZ_NORMAL,       /* Normal mode */    \
+    RT_SERIAL_FIFO_BUFSZ, /* Buffer size */  \
+    RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \
+    0                                      \
+}
+
+#define RT_SERIAL_EVENT_TXDONE (1 << 0)
+#define RT_SERIAL_EVENT_RXDONE (1 << 1)
+
+//#define RT_SERIAL_USE_EVENT                 // Just for test, maybe using event in the future
+
+struct rt_serial_device;
+
+typedef int (*cb_serial_tx)(struct rt_serial_device *serial, const rt_uint8_t *data, int length);
+typedef int (*cb_serial_rx)(struct rt_serial_device *serial, rt_uint8_t *data, int length);
+
+struct serial_configure
+{
+    rt_uint32_t baud_rate;
+
+    rt_uint32_t data_bits               :4;
+    rt_uint32_t stop_bits               :2;
+    rt_uint32_t parity                  :2;
+    rt_uint32_t bit_order               :1;
+    rt_uint32_t invert                  :1;
+    rt_uint32_t bufsz                   :16;
+    rt_uint32_t flowcontrol             :1;
+    rt_uint32_t reserved                :5;
+};
+
+/*
+ * Serial FIFO mode
+ */
+struct rt_serial_fifo
+{
+    rt_uint32_t buf_sz;
+    /* software fifo */
+    rt_uint8_t *buffer;
+
+    rt_uint16_t put_index, get_index;
+
+    rt_bool_t is_full;
+};
+
+struct rt_serial_device
+{
+    struct rt_device          parent;
+
+    const struct rt_uart_ops *ops;
+    struct serial_configure   config;
+
+    void *serial_rx;
+    void *serial_tx;
+
+#ifdef RT_SERIAL_USING_DMA
+    rt_size_t dma_idx_rx;
+    rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ];
+    rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ];
+#endif
+
+    cb_serial_rx _cb_rx;
+    cb_serial_tx _cb_tx;
+#ifndef RT_SERIAL_USE_EVENT
+    struct rt_completion completion_rx;
+    struct rt_completion completion_tx;
+#else
+    rt_event_t rx_done;
+    rt_event_t tx_done;
+#endif
+};
+typedef struct rt_serial_device rt_serial_t;
+
+/**
+ * uart operators
+ */
+struct rt_uart_ops
+{
+    rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
+    rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);
+
+    int (*putc)(struct rt_serial_device *serial, char c);
+    int (*getc)(struct rt_serial_device *serial);
+    int (*flush)(struct rt_serial_device *serial);
+
+    void (*start_tx)(struct rt_serial_device *serial);
+    void (*stop_tx)(struct rt_serial_device *serial);
+
+#ifdef RT_SERIAL_USING_DMA
+    rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial);
+    void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size);
+    void (*stop_dma_tx)(struct rt_serial_device *serial);
+#endif
+
+    void (*enable_interrupt)(struct rt_serial_device *serial);
+    void (*disable_interrupt)(struct rt_serial_device *serial);
+};
+
+void rt_hw_serial_isr(struct rt_serial_device *serial, int event);
+
+rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
+                               const char              *name,
+                               rt_uint32_t              flag,
+                               void                    *data);
+
+#endif
+

+ 4 - 1
components/drivers/include/rtdevice.h

@@ -7,6 +7,7 @@
  * Date           Author       Notes
  * 2012-01-08     bernard      first version.
  * 2014-07-12     bernard      Add workqueue implementation.
+ * 2022-04-10     THEWON       add serialX
  */
 
 #ifndef __RT_DEVICE_H__
@@ -57,7 +58,9 @@ extern "C" {
 #endif /* RT_USING_USB_HOST */
 
 #ifdef RT_USING_SERIAL
-#ifdef RT_USING_SERIAL_V2
+#ifdef RT_USING_SERIAL_X
+#include "drivers/serialX.h"
+#elif defined RT_USING_SERIAL_V2
 #include "drivers/serial_v2.h"
 #else
 #include "drivers/serial.h"

+ 3 - 0
components/drivers/serial/SConscript

@@ -7,6 +7,9 @@ if GetDepend(['RT_USING_SERIAL']):
     if GetDepend(['RT_USING_SERIAL_V2']):
         src = Glob('serial_v2.c')
         group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL_V2'], CPPPATH = CPPPATH)
+    elif  GetDepend(['RT_USING_SERIAL_X']):
+        src = Glob('serialX.c')
+        group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL_X'], CPPPATH = CPPPATH)
     else:
         src = Glob('serial.c')
         group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL'], CPPPATH = CPPPATH)

+ 1560 - 0
components/drivers/serial/serialX.c

@@ -0,0 +1,1560 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2006-03-13     bernard      first version
+ * 2012-05-15     lgnq         modified according bernard's implementation.
+ * 2012-05-28     bernard      code cleanup
+ * 2012-11-23     bernard      fix compiler warning.
+ * 2013-02-20     bernard      use RT_SERIAL_RB_BUFSZ to define
+ *                             the size of ring buffer.
+ * 2014-07-10     bernard      rewrite serial framework
+ * 2014-12-31     bernard      use open_flag for poll_tx stream mode.
+ * 2015-05-19     Quintin      fix DMA tx mod tx_dma->activated flag !=RT_FALSE BUG
+ *                             in open function.
+ * 2015-11-10     bernard      fix the poll rx issue when there is no data.
+ * 2016-05-10     armink       add fifo mode to DMA rx when serial->config.bufsz != 0.
+ * 2017-01-19     aubr.cool    prevent change serial rx bufsz when serial is opened.
+ * 2017-11-07     JasonJia     fix data bits error issue when using tcsetattr.
+ * 2017-11-15     JasonJia     fix poll rx issue when data is full.
+ *                             add TCFLSH and FIONREAD support.
+ * 2018-12-08     Ernest Chen  add DMA choice
+ * 2020-09-14     WillianChan  add a line feed to the carriage return character
+ *                             when using interrupt tx
+ * 2020-12-14     Meco Man     implement function of setting window's size(TIOCSWINSZ)
+ * 2021-08-22     Meco Man     implement function of getting window's size(TIOCGWINSZ)
+ * 2022-04-10     THEWON       serialX first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#ifdef RT_USING_SERIAL
+
+#define DBG_TAG    "UART"
+#define DBG_LVL    DBG_INFO
+#include <rtdbg.h>
+
+static rt_err_t rt_serial_flush(struct rt_device *dev);
+
+#ifdef RT_USING_POSIX_STDIO
+#include <dfs_posix.h>
+#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3)
+#include <dfs_poll.h>
+#else
+#include <poll.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef RT_USING_POSIX_TERMIOS
+#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3)
+#include <posix_termios.h>
+#else
+#include <termios.h>
+#endif
+#endif
+
+/* it's possible the 'getc/putc' is defined by stdio.h in gcc/newlib. */
+#ifdef getc
+#undef getc
+#endif
+
+#ifdef putc
+#undef putc
+#endif
+
+static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);
+
+    return RT_EOK;
+}
+
+/* fops for serial */
+static int serial_fops_open(struct dfs_fd *fd)
+{
+    rt_err_t ret = 0;
+    rt_uint16_t flags = 0;
+    rt_device_t device;
+
+    device = (rt_device_t)fd->data;
+    RT_ASSERT(device != RT_NULL);
+
+    switch (fd->flags & O_ACCMODE)
+    {
+        case O_RDONLY:
+            LOG_D("fops open: O_RDONLY!");
+            flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDONLY;
+            break;
+        case O_WRONLY:
+            LOG_D("fops open: O_WRONLY!");
+            flags = RT_DEVICE_FLAG_WRONLY;
+            break;
+        case O_RDWR:
+            LOG_D("fops open: O_RDWR!");
+            flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDWR;
+            break;
+        default:
+            LOG_E("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
+            break;
+    }
+
+    if ((fd->flags & O_ACCMODE) != O_WRONLY)
+        rt_device_set_rx_indicate(device, serial_fops_rx_ind);
+    ret = rt_device_open(device, flags);
+    if (ret == RT_EOK) return 0;
+
+    return ret;
+}
+
+static int serial_fops_close(struct dfs_fd *fd)
+{
+    rt_device_t device;
+
+    device = (rt_device_t)fd->data;
+
+    rt_device_set_rx_indicate(device, RT_NULL);
+    rt_device_close(device);
+
+    return 0;
+}
+
+static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
+{
+    rt_device_t device;
+
+    device = (rt_device_t)fd->data;
+    switch (cmd)
+    {
+    case FIONREAD:
+        break;
+    case FIONWRITE:
+        break;
+    }
+
+    return rt_device_control(device, cmd, args);
+}
+
+static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
+{
+    int size = 0;
+    rt_device_t device;
+
+    device = (rt_device_t)fd->data;
+
+    do
+    {
+        size = rt_device_read(device, -1, buf, count);
+        if (size <= 0)
+        {
+            if (fd->flags & O_NONBLOCK)
+            {
+                size = -EAGAIN;
+                break;
+            }
+
+            rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
+        }
+    }while (size <= 0);
+
+    return size;
+}
+
+static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
+{
+    rt_device_t device;
+
+    device = (rt_device_t)fd->data;
+    return rt_device_write(device, -1, buf, count);
+}
+
+static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req)
+{
+    int mask = 0;
+    int flags = 0;
+    rt_device_t device;
+    struct rt_serial_device *serial;
+
+    device = (rt_device_t)fd->data;
+    RT_ASSERT(device != RT_NULL);
+
+    serial = (struct rt_serial_device *)device;
+
+    /* only support POLLIN */
+    flags = fd->flags & O_ACCMODE;
+    if (flags == O_RDONLY || flags == O_RDWR)
+    {
+        register rt_base_t level;
+        struct rt_serial_fifo* rx_fifo;
+
+        rt_poll_add(&(device->wait_queue), req);
+
+        rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+
+        level = rt_hw_interrupt_disable();
+        if ((rx_fifo->get_index != rx_fifo->put_index) || (rx_fifo->get_index == rx_fifo->put_index && rx_fifo->is_full == RT_TRUE))
+            mask |= POLLIN;
+        rt_hw_interrupt_enable(level);
+    }
+
+    return mask;
+}
+
+const static struct dfs_file_ops _serial_fops =
+{
+    serial_fops_open,
+    serial_fops_close,
+    serial_fops_ioctl,
+    serial_fops_read,
+    serial_fops_write,
+    RT_NULL, /* flush */
+    RT_NULL, /* lseek */
+    RT_NULL, /* getdents */
+    serial_fops_poll,
+};
+#endif
+
+/**
+ * Calculate fifo data length.
+ *
+ * @param fifo the data fifo of serial device
+ *
+ * @return length
+ */
+rt_inline rt_size_t _serial_fifo_calc_data_len(struct rt_serial_fifo *fifo)
+{
+    rt_size_t size;
+    if (fifo->put_index == fifo->get_index) {
+        size = (fifo->is_full == RT_FALSE) ? 0 : fifo->buf_sz;
+    } else if (fifo->put_index > fifo->get_index) {
+        size = fifo->put_index - fifo->get_index;
+    } else {
+        size = fifo->buf_sz - (fifo->get_index - fifo->put_index);
+    }
+
+    return size;
+}
+
+rt_inline void _serial_fifo_push_data(struct rt_serial_fifo *fifo, rt_uint8_t ch)
+{
+    fifo->buffer[fifo->put_index] = ch;
+    fifo->put_index += 1;
+    if (fifo->put_index >= fifo->buf_sz) fifo->put_index = 0;
+}
+
+rt_inline rt_uint8_t _serial_fifo_pop_data(struct rt_serial_fifo *fifo)
+{
+    rt_uint8_t ch;
+
+    ch = fifo->buffer[fifo->get_index];
+    fifo->get_index += 1;
+    if (fifo->get_index >= fifo->buf_sz) fifo->get_index = 0;
+
+    return ch;
+}
+
+/*
+ * Serial poll routines
+ */
+rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
+{
+    int ch;
+    int size;
+
+    RT_ASSERT(serial != RT_NULL);
+    size = length;
+
+    while (length)
+    {
+        ch = serial->ops->getc(serial);
+        if (ch == -1) break;
+
+        *data = ch;
+        data++; length--;
+    }
+
+    return size - length;
+}
+
+rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+    int size;
+    rt_uint8_t last_char = 0;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    size = length;
+    while (length)
+    {
+        /*
+         * to be polite with serial console add a line feed
+         * to the carriage return character
+         */
+        if (*data == '\n' &&
+            (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+            last_char != '\r')
+        {
+            serial->ops->putc(serial, '\r');
+
+            last_char = 0;
+        } else if (*data == '\r') {
+            last_char = '\r';
+        } else {
+            last_char = 0;
+        }
+
+        serial->ops->putc(serial, *data);
+
+        data++; length--;
+    }
+
+    return size - length;
+}
+
+/*
+ * Serial interrupt routines
+ */
+rt_inline int _serial_fifo_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
+{
+    rt_size_t len, size;
+    struct rt_serial_fifo* rx_fifo;
+    register rt_base_t level;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    if (length == 0) return 0;
+
+    rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+
+    /* disable interrupt */
+    level = rt_hw_interrupt_disable();
+
+    len = _serial_fifo_calc_data_len(rx_fifo);
+
+    if ((len == 0) &&                // non-blocking io mode
+        (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+        return 0;
+    }
+    if ((len == 0) &&                // blocking io mode
+        (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+        rt_err_t ret;
+        do {
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+
+#ifndef RT_SERIAL_USE_EVENT
+            ret = rt_completion_wait(&(serial->completion_rx), RT_WAITING_FOREVER);
+#else
+            ret = rt_event_recv(serial->rx_done, RT_SERIAL_EVENT_RXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+            if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+            } else {
+                return size;
+            }
+
+            /* disable interrupt */
+            level = rt_hw_interrupt_disable();
+
+            len = _serial_fifo_calc_data_len(rx_fifo);
+        } while(len == 0);
+    }
+
+    if (len > length) {
+        len = length;
+    }
+
+    /* read from software FIFO */
+    for (size = 0; size < len; size++) {
+        /* otherwise there's the data: */
+        *data = _serial_fifo_pop_data(rx_fifo);
+        data++;
+    }
+
+    rx_fifo->is_full = RT_FALSE;
+
+    /* enable interrupt */
+    rt_hw_interrupt_enable(level);
+
+    return size;
+}
+
+rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+    rt_size_t len, length_t, size;
+    struct rt_serial_fifo *tx_fifo;
+    register rt_base_t level;
+    rt_uint8_t last_char = 0;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    if (length == 0) return 0;
+
+    tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+    size = 0;
+    do {
+        length_t = length - size;
+        /* disable interrupt */
+        level = rt_hw_interrupt_disable();
+
+        len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo);
+
+        if ((len == 0) &&                // non-blocking io mode
+            (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+            break;
+        }
+
+        if ((len == 0) &&                // blocking io mode
+            (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+            rt_err_t ret;
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+#ifndef RT_SERIAL_USE_EVENT
+            ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+            ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+            if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+                continue;
+            } else {
+                return size;
+            }
+        }
+
+        if (len > length_t) {
+            len = length_t;
+        }
+        /* copy to software FIFO */
+        while (len > 0) {
+            /*
+             * to be polite with serial console add a line feed
+             * to the carriage return character
+             */
+            if (*data == '\n' &&
+                (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+                last_char != '\r') {
+                _serial_fifo_push_data(tx_fifo, '\r');
+
+                last_char = 0;
+            } else if (*data == '\r') {
+                last_char = '\r';
+            } else {
+                last_char = 0;
+            }
+
+            _serial_fifo_push_data(tx_fifo, *data);
+
+            data++; len--; size++;
+        }
+
+        /* if the next position is read index, discard this 'read char' */
+        if (tx_fifo->put_index == tx_fifo->get_index) {
+            tx_fifo->is_full = RT_TRUE;
+        }
+
+        // TODO: start tx
+        serial->ops->start_tx(serial);
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+    } while(size < length);
+
+    return size;
+}
+
+#ifdef RT_SERIAL_USING_DMA
+/*
+ * Serial DMA routines
+ */
+rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+    rt_size_t len, length_t, size, i;
+    struct rt_serial_fifo *tx_fifo;
+    register rt_base_t level;
+    rt_uint8_t last_char = 0, ch;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    if (length == 0) return 0;
+
+    tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+    size = 0;
+    do {
+        length_t = length - size;
+        /* disable interrupt */
+        level = rt_hw_interrupt_disable();
+
+        len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo);
+
+        if ((len == 0) &&                // non-blocking io mode
+            (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+            break;
+        }
+
+        if ((len == 0) &&                // blocking io mode
+            (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+            rt_err_t ret;
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+
+#ifndef RT_SERIAL_USE_EVENT
+            ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+            ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+            if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+                continue;
+            } else {
+                return size;
+            }
+        }
+
+        if (len > length_t) {
+            len = length_t;
+        }
+        /* copy to software FIFO */
+        while (len > 0) {
+            /*
+             * to be polite with serial console add a line feed
+             * to the carriage return character
+             */
+            if (*data == '\n' &&
+                (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+                last_char != '\r') {
+                _serial_fifo_push_data(tx_fifo, '\r');
+
+                last_char = 0;
+            } else if (*data == '\r') {
+                last_char = '\r';
+            } else {
+                last_char = 0;
+            }
+
+            _serial_fifo_push_data(tx_fifo, *data);
+
+            data++; len--; size++;
+        }
+
+        /* if the next position is read index, discard this 'read char' */
+        if (tx_fifo->put_index == tx_fifo->get_index) {
+            tx_fifo->is_full = RT_TRUE;
+        }
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        // TODO: start tx
+            /* disable interrupt */
+            level = rt_hw_interrupt_disable();
+        if (serial->ops->is_dma_txing(serial) == RT_FALSE) {
+            /* calucate fifo data size */
+            len = _serial_fifo_calc_data_len(tx_fifo);
+
+            if (len > RT_SERIAL_DMA_BUFSZ) {
+                len = RT_SERIAL_DMA_BUFSZ;
+            }
+            /* read from software FIFO */
+            for (i = 0; i < len; i++) {
+                /* pop one byte data */
+                ch = _serial_fifo_pop_data(tx_fifo);
+
+                serial->serial_dma_tx[i] = ch;
+            }
+            tx_fifo->is_full = RT_FALSE;
+            serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len);
+
+        }
+            /* enable interrupt */
+            rt_hw_interrupt_enable(level);
+    } while(size < length);
+
+    return size;
+}
+#endif /* RT_SERIAL_USING_DMA */
+
+/* RT-Thread Device Interface */
+/*
+ * This function initializes serial device.
+ */
+static rt_err_t rt_serial_init(struct rt_device *dev)
+{
+    rt_err_t result = RT_EOK;
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    serial = (struct rt_serial_device *)dev;
+
+    /* initialize rx/tx */
+    serial->serial_rx = RT_NULL;
+    serial->serial_tx = RT_NULL;
+
+    /* apply configuration */
+    if (serial->ops->configure)
+        result = serial->ops->configure(serial, &serial->config);
+
+    return result;
+}
+
+static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+    rt_uint16_t stream_flag = 0;
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    serial = (struct rt_serial_device *)dev;
+
+    LOG_D("open serial device: 0x%08x with open flag: 0x%04x",
+        dev, oflag);
+    /* check device flag with the open flag */
+    if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
+        return -RT_EIO;
+    if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
+        return -RT_EIO;
+    if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
+        return -RT_EIO;
+    if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
+        return -RT_EIO;
+
+    /* keep steam flag */
+    if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
+        stream_flag = RT_DEVICE_FLAG_STREAM;
+
+    /* get open flags */
+    dev->open_flag = oflag & 0xff;
+
+    if (oflag & RT_DEVICE_FLAG_INT_RX)
+    {
+        /* initialize the Rx/Tx structure according to open flag */
+        if (serial->serial_rx == RT_NULL)
+        {
+            struct rt_serial_fifo* rx_fifo;
+
+            rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+                                                            serial->config.bufsz);
+            RT_ASSERT(rx_fifo != RT_NULL);
+            rx_fifo->buf_sz = serial->config.bufsz;
+            rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
+            rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz);
+            rx_fifo->put_index = 0;
+            rx_fifo->get_index = 0;
+            rx_fifo->is_full = RT_FALSE;
+
+            serial->serial_rx = rx_fifo;
+        }
+        dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
+
+        serial->_cb_rx = _serial_fifo_rx;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_rx));
+#else
+        serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO);
+#endif
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if (oflag & RT_DEVICE_FLAG_DMA_RX)
+    {
+        /* initialize the Rx/Tx structure according to open flag */
+        if (serial->serial_rx == RT_NULL)
+        {
+            struct rt_serial_fifo* rx_fifo;
+
+            rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+                                                            serial->config.bufsz);
+            RT_ASSERT(rx_fifo != RT_NULL);
+            rx_fifo->buf_sz = serial->config.bufsz;
+            rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
+            rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz);
+            rx_fifo->put_index = 0;
+            rx_fifo->get_index = 0;
+            rx_fifo->is_full = RT_FALSE;
+
+            serial->serial_rx = rx_fifo;
+        }
+        dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
+
+        serial->dma_idx_rx = 0;
+        serial->_cb_rx = _serial_fifo_rx;
+
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_rx));
+#else
+        serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO);
+#endif
+        /* configure fifo address and length to low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX);
+    }
+#endif /* RT_SERIAL_USING_DMA */
+    else
+    {
+        serial->serial_rx = RT_NULL;
+        serial->_cb_rx = _serial_poll_rx;
+    }
+
+    if (oflag & RT_DEVICE_FLAG_INT_TX)
+    {
+        if (serial->serial_tx == RT_NULL)
+        {
+            struct rt_serial_fifo *tx_fifo;
+
+            tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+                                                            serial->config.bufsz);
+            RT_ASSERT(tx_fifo != RT_NULL);
+            tx_fifo->buf_sz = serial->config.bufsz;
+            tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1);
+            rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz);
+            tx_fifo->put_index = 0;
+            tx_fifo->get_index = 0;
+            tx_fifo->is_full = RT_FALSE;
+#ifndef RT_SERIAL_USE_EVENT
+            rt_completion_init(&(serial->completion_tx));
+#else
+            serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO);
+#endif
+            serial->serial_tx = tx_fifo;
+        }
+        dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
+        /* configure low level device */
+//        serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
+
+        serial->_cb_tx = _serial_int_tx;
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if (oflag & RT_DEVICE_FLAG_DMA_TX)
+    {
+        if (serial->serial_tx == RT_NULL)
+        {
+            struct rt_serial_fifo *tx_fifo;
+
+            tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+                                                            serial->config.bufsz);
+            RT_ASSERT(tx_fifo != RT_NULL);
+            tx_fifo->buf_sz = serial->config.bufsz;
+            tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1);
+            rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz);
+            tx_fifo->put_index = 0;
+            tx_fifo->get_index = 0;
+            tx_fifo->is_full = RT_FALSE;
+
+#ifndef RT_SERIAL_USE_EVENT
+            rt_completion_init(&(serial->completion_tx));
+#else
+            serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO);
+#endif
+
+            serial->serial_tx = tx_fifo;
+        }
+        dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
+
+        serial->_cb_tx = _serial_dma_tx;
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX);
+    }
+#endif /* RT_SERIAL_USING_DMA */
+    else
+    {
+        serial->serial_tx = RT_NULL;
+        serial->_cb_tx = _serial_poll_tx;
+    }
+
+    serial->ops->control(serial, RT_DEVICE_CTRL_OPEN, (void *)0);
+
+    /* set stream flag */
+    dev->open_flag |= stream_flag;
+
+    return RT_EOK;
+}
+
+static rt_err_t rt_serial_close(struct rt_device *dev)
+{
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    serial = (struct rt_serial_device *)dev;
+
+    rt_serial_flush(dev);
+    serial->ops->control(serial, RT_DEVICE_CTRL_CLOSE, RT_NULL);
+
+    if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
+    {
+        struct rt_serial_fifo *rx_fifo;
+
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX);
+
+        dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
+
+        rx_fifo = (struct rt_serial_fifo *)serial->serial_rx;
+        rt_free(rx_fifo);
+
+        serial->serial_rx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_rx));
+#else
+        rt_event_delete(serial->rx_done);
+#endif
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
+    {
+        struct rt_serial_fifo *rx_fifo;
+
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX);
+
+        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX;
+
+        rx_fifo = (struct rt_serial_fifo *)serial->serial_rx;
+        rt_free(rx_fifo);
+
+        serial->serial_rx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_rx));
+#else
+        rt_event_delete(serial->rx_done);
+#endif
+    }
+#endif /* RT_SERIAL_USING_DMA */
+
+    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
+    {
+        struct rt_serial_fifo *tx_fifo;
+
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_TX);
+
+        dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
+
+        tx_fifo = (struct rt_serial_fifo *)serial->serial_tx;
+        rt_free(tx_fifo);
+
+        serial->serial_tx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_tx));
+#else
+        rt_event_delete(serial->tx_done);
+#endif
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
+    {
+        struct rt_serial_fifo *tx_fifo;
+
+        /* configure low level device */
+        serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_TX);
+
+        dev->open_flag &= ~RT_DEVICE_FLAG_DMA_TX;
+
+        tx_fifo = (struct rt_serial_fifo *)serial->serial_tx;
+        rt_free(tx_fifo);
+
+        serial->serial_tx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_init(&(serial->completion_tx));
+#else
+        rt_event_delete(serial->tx_done);
+#endif
+    }
+#endif /* RT_SERIAL_USING_DMA */
+
+    dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED;
+
+    return RT_EOK;
+}
+
+static rt_size_t rt_serial_read(struct rt_device *dev,
+                                rt_off_t          pos,
+                                void             *buffer,
+                                rt_size_t         size)
+{
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    if (size == 0) return 0;
+
+    serial = (struct rt_serial_device *)dev;
+
+    return serial->_cb_rx(serial, (rt_uint8_t *)buffer, size);
+}
+
+static rt_size_t rt_serial_write(struct rt_device *dev,
+                                 rt_off_t          pos,
+                                 const void       *buffer,
+                                 rt_size_t         size)
+{
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    if (size == 0) return 0;
+
+    serial = (struct rt_serial_device *)dev;
+
+    return serial->_cb_tx(serial, (const rt_uint8_t *)buffer, size);
+}
+
+static rt_err_t rt_serial_flush(struct rt_device *dev)
+{
+    struct rt_serial_device *serial;
+    rt_size_t len;
+    struct rt_serial_fifo *tx_fifo, *rx_fifo;
+    register rt_base_t level;
+
+    RT_ASSERT(dev != RT_NULL);
+
+    serial = (struct rt_serial_device *)dev;
+
+    if((dev->open_flag & RT_DEVICE_FLAG_INT_RX)
+#ifdef RT_SERIAL_USING_DMA
+        || (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
+#endif /* RT_SERIAL_USING_DMA */
+        ) {
+        rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+        level = rt_hw_interrupt_disable();
+        rx_fifo->get_index = rx_fifo->put_index = 0;
+        rx_fifo->is_full = RT_FALSE;
+        rt_hw_interrupt_enable(level);
+    }
+
+    if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX)
+#ifdef RT_SERIAL_USING_DMA
+     || (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
+#endif /* RT_SERIAL_USING_DMA */
+    ) {
+        tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+        while(1) {
+            /* disable interrupt */
+            level = rt_hw_interrupt_disable();
+
+            len = _serial_fifo_calc_data_len(tx_fifo);
+
+            if (len == 0) {
+                /* enable interrupt */
+                rt_hw_interrupt_enable(level);
+                break;
+            } else {
+                /* enable interrupt */
+                rt_hw_interrupt_enable(level);
+#ifndef RT_SERIAL_USE_EVENT
+                rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+                rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+            }
+        }
+
+    if (dev->open_flag & RT_DEVICE_FLAG_INT_TX) {
+        serial->ops->flush(serial);
+    }
+#ifdef RT_SERIAL_USING_DMA
+    else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) {
+    }
+#endif /* RT_SERIAL_USING_DMA */
+    }
+
+    return RT_EOK;
+}
+
+#ifdef RT_USING_POSIX_TERMIOS
+struct speed_baudrate_item
+{
+    speed_t speed;
+    int baudrate;
+};
+
+const static struct speed_baudrate_item _tbl[] =
+{
+    {B2400, BAUD_RATE_2400},
+    {B4800, BAUD_RATE_4800},
+    {B9600, BAUD_RATE_9600},
+    {B19200, BAUD_RATE_19200},
+    {B38400, BAUD_RATE_38400},
+    {B57600, BAUD_RATE_57600},
+    {B115200, BAUD_RATE_115200},
+    {B230400, BAUD_RATE_230400},
+    {B460800, BAUD_RATE_460800},
+    {B921600, BAUD_RATE_921600},
+    {B2000000, BAUD_RATE_2000000},
+    {B3000000, BAUD_RATE_3000000},
+};
+
+static speed_t _get_speed(int baudrate)
+{
+    int index;
+
+    for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
+    {
+        if (_tbl[index].baudrate == baudrate)
+            return _tbl[index].speed;
+    }
+
+    return B0;
+}
+
+static int _get_baudrate(speed_t speed)
+{
+    int index;
+
+    for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
+    {
+        if (_tbl[index].speed == speed)
+            return _tbl[index].baudrate;
+    }
+
+    return 0;
+}
+
+static void _tc_flush(struct rt_serial_device *serial, int queue)
+{
+    register rt_base_t level;
+    int ch = -1;
+    struct rt_serial_fifo *rx_fifo = RT_NULL;
+    struct rt_device *device = RT_NULL;
+
+    RT_ASSERT(serial != RT_NULL);
+
+    device = &(serial->parent);
+    rx_fifo = (struct rt_serial_fifo *) serial->serial_rx;
+
+    switch(queue)
+    {
+        case TCIFLUSH:
+        case TCIOFLUSH:
+
+            RT_ASSERT(rx_fifo != RT_NULL);
+
+            if((device->open_flag & RT_DEVICE_FLAG_INT_RX) || (device->open_flag & RT_DEVICE_FLAG_DMA_RX))
+            {
+                RT_ASSERT(RT_NULL != rx_fifo);
+                level = rt_hw_interrupt_disable();
+                rx_fifo->get_index = rx_fifo->put_index;
+                rx_fifo->is_full = RT_FALSE;
+                rt_hw_interrupt_enable(level);
+            }
+            else
+            {
+                while (1)
+                {
+                    ch = serial->ops->getc(serial);
+                    if (ch == -1) break;
+                }
+            }
+
+            break;
+
+         case TCOFLUSH:
+            break;
+    }
+
+}
+
+#endif
+
+static rt_err_t rt_serial_control(struct rt_device *dev,
+                                  int              cmd,
+                                  void             *args)
+{
+    rt_err_t ret = RT_EOK;
+    struct rt_serial_device *serial;
+
+    RT_ASSERT(dev != RT_NULL);
+    serial = (struct rt_serial_device *)dev;
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_SUSPEND:
+        /* suspend device */
+        dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
+    break;
+    case RT_DEVICE_CTRL_RESUME:
+        /* resume device */
+        dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
+    break;
+    case RT_DEVICE_CTRL_BLOCKING:
+        if (((rt_uint32_t)args & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+            dev->open_flag |= RT_DEVICE_OFLAG_NONBLOCKING;
+        } else {
+            dev->open_flag &= ~RT_DEVICE_OFLAG_NONBLOCKING;
+        }
+    break;
+    case RT_DEVICE_CTRL_CONFIG:
+        if (args)
+        {
+            struct serial_configure *pconfig = (struct serial_configure *) args;
+            if (pconfig->bufsz != serial->config.bufsz && serial->parent.ref_count)
+            {
+                /*can not change buffer size*/
+                return RT_EBUSY;
+            }
+            /* set serial configure */
+            serial->config = *pconfig;
+            /* serial device has been opened, to configure it */
+            serial->ops->configure(serial, pconfig);
+        }
+    break;
+#ifdef RT_USING_POSIX_STDIO
+#ifdef RT_USING_POSIX_TERMIOS
+        case TCGETA:
+            {
+                struct termios *tio = (struct termios*)args;
+                if (tio == RT_NULL) return -RT_EINVAL;
+
+                tio->c_iflag = 0;
+                tio->c_oflag = 0;
+                tio->c_lflag = 0;
+
+                /* update oflag for console device */
+                if (rt_console_get_device() == dev)
+                    tio->c_oflag = OPOST | ONLCR;
+
+                /* set cflag */
+                tio->c_cflag = 0;
+                if (serial->config.data_bits == DATA_BITS_5)
+                    tio->c_cflag = CS5;
+                else if (serial->config.data_bits == DATA_BITS_6)
+                    tio->c_cflag = CS6;
+                else if (serial->config.data_bits == DATA_BITS_7)
+                    tio->c_cflag = CS7;
+                else if (serial->config.data_bits == DATA_BITS_8)
+                    tio->c_cflag = CS8;
+
+                if (serial->config.stop_bits == STOP_BITS_2)
+                    tio->c_cflag |= CSTOPB;
+
+                if (serial->config.parity == PARITY_EVEN)
+                    tio->c_cflag |= PARENB;
+                else if (serial->config.parity == PARITY_ODD)
+                    tio->c_cflag |= (PARODD | PARENB);
+
+                cfsetospeed(tio, _get_speed(serial->config.baud_rate));
+            }
+            break;
+
+        case TCSETAW:
+        case TCSETAF:
+        case TCSETA:
+            {
+                int baudrate;
+                struct serial_configure config;
+
+                struct termios *tio = (struct termios*)args;
+                if (tio == RT_NULL) return -RT_EINVAL;
+
+                config = serial->config;
+
+                baudrate = _get_baudrate(cfgetospeed(tio));
+                config.baud_rate = baudrate;
+
+                switch (tio->c_cflag & CSIZE)
+                {
+                case CS5:
+                    config.data_bits = DATA_BITS_5;
+                    break;
+                case CS6:
+                    config.data_bits = DATA_BITS_6;
+                    break;
+                case CS7:
+                    config.data_bits = DATA_BITS_7;
+                    break;
+                default:
+                    config.data_bits = DATA_BITS_8;
+                    break;
+                }
+
+                if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2;
+                else config.stop_bits = STOP_BITS_1;
+
+                if (tio->c_cflag & PARENB)
+                {
+                    if (tio->c_cflag & PARODD) config.parity = PARITY_ODD;
+                    else config.parity = PARITY_EVEN;
+                }
+                else config.parity = PARITY_NONE;
+
+                serial->ops->configure(serial, &config);
+            }
+            break;
+        case TCFLSH:
+            {
+                int queue = (int)args;
+
+                _tc_flush(serial, queue);
+            }
+
+            break;
+        case TCXONC:
+            break;
+#endif /*RT_USING_POSIX_TERMIOS*/
+        case TIOCSWINSZ:
+            {
+                struct winsize* p_winsize;
+
+                p_winsize = (struct winsize*)args;
+                rt_kprintf("\x1b[8;%d;%dt", p_winsize->ws_col, p_winsize->ws_row);
+            }
+            break;
+        case TIOCGWINSZ:
+            {
+                struct winsize* p_winsize;
+                p_winsize = (struct winsize*)args;
+
+                if(rt_thread_self() != rt_thread_find("tshell"))
+                {
+                    /* only can be used in tshell thread; otherwise, return default size */
+                    p_winsize->ws_col = 80;
+                    p_winsize->ws_row = 24;
+                }
+                else
+                {
+                    #define _TIO_BUFLEN 20
+                    char _tio_buf[_TIO_BUFLEN];
+                    unsigned char cnt1, cnt2, cnt3, i;
+                    char row_s[4], col_s[4];
+                    char *p;
+
+                    rt_memset(_tio_buf, 0, _TIO_BUFLEN);
+
+                    /* send the command to terminal for getting the window size of the terminal */
+                    rt_kprintf("\033[18t");
+
+                    /* waiting for the response from the terminal */
+                    i = 0;
+                    while(i < _TIO_BUFLEN)
+                    {
+                        _tio_buf[i] = getchar();
+                        if(_tio_buf[i] != 't')
+                        {
+                            i ++;
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+                    if(i == _TIO_BUFLEN)
+                    {
+                        /* buffer overloaded, and return default size */
+                        p_winsize->ws_col = 80;
+                        p_winsize->ws_row = 24;
+                        break;
+                    }
+
+                    /* interpreting data eg: "\033[8;1;15t" which means row is 1 and col is 15 (unit: size of ONE character) */
+                    rt_memset(row_s,0,4);
+                    rt_memset(col_s,0,4);
+                    cnt1 = 0;
+                    while(_tio_buf[cnt1] != ';' && cnt1 < _TIO_BUFLEN)
+                    {
+                        cnt1++;
+                    }
+                    cnt2 = ++cnt1;
+                    while(_tio_buf[cnt2] != ';' && cnt2 < _TIO_BUFLEN)
+                    {
+                        cnt2++;
+                    }
+                    p = row_s;
+                    while(cnt1 < cnt2)
+                    {
+                        *p++ = _tio_buf[cnt1++];
+                    }
+                    p = col_s;
+                    cnt2++;
+                    cnt3 = rt_strlen(_tio_buf) - 1;
+                    while(cnt2 < cnt3)
+                    {
+                        *p++ = _tio_buf[cnt2++];
+                    }
+
+                    /* load the window size date */
+                    p_winsize->ws_col = atoi(col_s);
+                    p_winsize->ws_row = atoi(row_s);
+                #undef _TIO_BUFLEN
+                }
+
+                p_winsize->ws_xpixel = 0;/* unused */
+                p_winsize->ws_ypixel = 0;/* unused */
+            }
+            break;
+        case FIONREAD:
+            {
+                rt_size_t recved = 0;
+                register rt_base_t level;
+
+                level = rt_hw_interrupt_disable();
+                recved = _serial_fifo_calc_data_len(serial->serial_rx);
+                rt_hw_interrupt_enable(level);
+
+                *(rt_size_t *)args = recved;
+            }
+            break;
+#endif /*RT_USING_POSIX_STDIO*/
+    default :
+        /* control device */
+        ret = serial->ops->control(serial, cmd, args);
+    break;
+    }
+
+    return ret;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops serial_ops =
+{
+    rt_serial_init,
+    rt_serial_open,
+    rt_serial_close,
+    rt_serial_read,
+    rt_serial_write,
+    rt_serial_control,
+    rt_serial_flush,
+};
+#endif
+
+/*
+ * serial register
+ */
+rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
+                               const char              *name,
+                               rt_uint32_t              flag,
+                               void                    *data)
+{
+    rt_err_t ret;
+    struct rt_device *device;
+    RT_ASSERT(serial != RT_NULL);
+
+    device = &(serial->parent);
+
+    device->type        = RT_Device_Class_Char;
+    device->rx_indicate = RT_NULL;
+    device->tx_complete = RT_NULL;
+
+#ifdef RT_USING_DEVICE_OPS
+    device->ops         = &serial_ops;
+#else
+    device->init        = rt_serial_init;
+    device->open        = rt_serial_open;
+    device->close       = rt_serial_close;
+    device->read        = rt_serial_read;
+    device->write       = rt_serial_write;
+    device->flush       = rt_serial_flush;
+    device->control     = rt_serial_control;
+#endif
+    device->user_data   = data;
+
+    /* register a character device */
+    ret = rt_device_register(device, name, flag);
+
+#if defined(RT_USING_POSIX_STDIO)
+    /* set fops */
+    device->fops        = &_serial_fops;
+#endif
+
+    return ret;
+}
+
+/* ISR for serial interrupt */
+void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
+{
+    switch (event & 0xff)
+    {
+    case RT_SERIAL_EVENT_RX_IND:
+    {
+        int ch = -1;
+        struct rt_serial_fifo* rx_fifo;
+
+        /* interrupt mode receive */
+        rx_fifo = (struct rt_serial_fifo*)serial->serial_rx;
+
+        while (1) {
+            ch = serial->ops->getc(serial);
+            if (ch == -1) break;
+
+            /* if fifo is full, discard one byte first */
+            if (rx_fifo->is_full == RT_TRUE) {
+                rx_fifo->get_index += 1;
+                if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0;
+            }
+            /* push a new data */
+            _serial_fifo_push_data(rx_fifo, ch);
+
+            /* if put index equal to read index, fifo is full */
+            if (rx_fifo->put_index == rx_fifo->get_index)
+            {
+                rx_fifo->is_full = RT_TRUE;
+            }
+        }
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_done(&(serial->completion_rx));
+#else
+        rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE);
+#endif
+        /* invoke callback */
+        if (serial->parent.rx_indicate != RT_NULL) {
+            rt_size_t rx_length;
+
+            /* get rx length */
+            rx_length = _serial_fifo_calc_data_len(rx_fifo);
+            if (rx_length) {
+                serial->parent.rx_indicate(&serial->parent, rx_length);
+            }
+        }
+    }
+    break;
+    case RT_SERIAL_EVENT_TX_DONE:
+    {
+#if defined(RT_SERIAL_HARD_FIFO)
+        rt_size_t fifo_sz;
+#endif
+        rt_size_t len;
+        rt_uint8_t ch = 0;
+        struct rt_serial_fifo *tx_fifo;
+
+        tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+        /* calucate fifo data size */
+        len = _serial_fifo_calc_data_len(tx_fifo);
+        if (len == 0) {
+            // TODO: stop tx
+            serial->ops->stop_tx(serial);
+#ifndef RT_SERIAL_USE_EVENT
+            rt_completion_done(&(serial->completion_tx));
+#else
+            rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE);
+#endif
+            /* invoke callback */
+            if (serial->parent.tx_complete != RT_NULL) {
+                serial->parent.tx_complete(&serial->parent, (void*)len);
+            }
+            break;
+        }
+#if defined(RT_SERIAL_HARD_FIFO)
+        fifo_sz = event >> 8;
+        if (len > fifo_sz) {
+            len = fifo_sz;
+        }
+        /* read from software FIFO */
+        while (len > 0) {
+            /* pop one byte data */
+            ch = _serial_fifo_pop_data(tx_fifo);
+
+            serial->ops->putc(serial, ch);
+            len--;
+        }
+#else
+        /* pop one byte data */
+        ch = _serial_fifo_pop_data(tx_fifo);
+
+        serial->ops->putc(serial, ch);
+#endif
+        tx_fifo->is_full = RT_FALSE;
+    }
+    break;
+#ifdef RT_SERIAL_USING_DMA
+    case RT_SERIAL_EVENT_RX_DMADONE:
+    {
+        int dma_idx, ch = -1;
+        struct rt_serial_fifo* rx_fifo;
+
+        if (serial->dma_idx_rx == dma_idx) break;
+
+        dma_idx = event >> 8;
+
+        rx_fifo = (struct rt_serial_fifo*)serial->serial_rx;
+
+        while (serial->dma_idx_rx != dma_idx) {
+            ch = serial->serial_dma_rx[serial->dma_idx_rx];
+
+            /* if fifo is full, discard one byte first */
+            if (rx_fifo->is_full == RT_TRUE) {
+                rx_fifo->get_index += 1;
+                if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0;
+            }
+            /* push a new data */
+            _serial_fifo_push_data(rx_fifo, ch);
+
+            /* if put index equal to read index, fifo is full */
+            if (rx_fifo->put_index == rx_fifo->get_index)
+            {
+                rx_fifo->is_full = RT_TRUE;
+            }
+            serial->dma_idx_rx++;
+            if (serial->dma_idx_rx == RT_SERIAL_DMA_BUFSZ) {
+                serial->dma_idx_rx = 0;
+            }
+        }
+        serial->dma_idx_rx = dma_idx;
+#ifndef RT_SERIAL_USE_EVENT
+        rt_completion_done(&(serial->completion_rx));
+#else
+        rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE);
+#endif
+
+        /* invoke callback */
+        if (serial->parent.rx_indicate != RT_NULL) {
+            rt_size_t rx_length;
+
+            /* get rx length */
+            rx_length = _serial_fifo_calc_data_len(rx_fifo);
+            if (rx_length) {
+                serial->parent.rx_indicate(&serial->parent, rx_length);
+            }
+        }
+    }
+    break;
+    case RT_SERIAL_EVENT_TX_DMADONE:
+    {
+        int i;
+        rt_size_t len;
+        rt_uint8_t ch = 0;
+        struct rt_serial_fifo *tx_fifo;
+
+        tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+        /* calucate fifo data size */
+        len = _serial_fifo_calc_data_len(tx_fifo);
+        if (len == 0) {
+            // TODO: stop tx
+            serial->ops->stop_dma_tx(serial);
+#ifndef RT_SERIAL_USE_EVENT
+            rt_completion_done(&(serial->completion_tx));
+#else
+            rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE);
+#endif
+            /* invoke callback */
+            if (serial->parent.tx_complete != RT_NULL) {
+                serial->parent.tx_complete(&serial->parent, (void*)len);
+            }
+            break;
+        }
+
+        if (len > RT_SERIAL_DMA_BUFSZ) {
+            len = RT_SERIAL_DMA_BUFSZ;
+        }
+        /* read from software FIFO */
+        for (i = 0; i < len; i++) {
+            /* pop one byte data */
+            ch = _serial_fifo_pop_data(tx_fifo);
+
+            serial->serial_dma_tx[i] = ch;
+        }
+        tx_fifo->is_full = RT_FALSE;
+        serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len);
+    }
+    break;
+#endif /* RT_SERIAL_USING_DMA */
+    }
+}
+#endif /* RT_USING_SERIAL */
+

+ 9 - 0
include/rtdef.h

@@ -41,6 +41,7 @@
  * 2022-01-01     Gabriel      improve hooking method
  * 2022-01-07     Gabriel      move some __on_rt_xxxxx_hook to dedicated c source files
  * 2022-01-12     Meco Man     remove RT_THREAD_BLOCK
+ * 2022-04-10     THEWON       add flush for device & some device flags
  * 2022-04-20     Meco Man     change version number to v4.1.1
  * 2022-04-21     THEWON       add macro RT_VERSION_CHECK
  * 2022-06-29     Meco Man     add RT_USING_LIBC and standard libc headers
@@ -1165,6 +1166,10 @@ enum rt_device_class_type
 #define RT_DEVICE_OFLAG_WRONLY          0x002           /**< write only access */
 #define RT_DEVICE_OFLAG_RDWR            0x003           /**< read and write */
 #define RT_DEVICE_OFLAG_OPEN            0x008           /**< device is opened */
+
+#define RT_DEVICE_OFLAG_BLOCKING        0x000           /**< blocking io mode */
+#define RT_DEVICE_OFLAG_NONBLOCKING     0x004           /**< non-blocking io mode */
+
 #define RT_DEVICE_OFLAG_MASK            0xf0f           /**< mask of open flag */
 
 /**
@@ -1182,6 +1187,8 @@ enum rt_device_class_type
 #define RT_DEVICE_CTRL_CLR_INT          0x07            /**< clear interrupt */
 #define RT_DEVICE_CTRL_GET_INT          0x08            /**< get interrupt status */
 #define RT_DEVICE_CTRL_CONSOLE_OFLAG    0x09            /**< get console open flag */
+#define RT_DEVICE_CTRL_OPEN             0x0A            /**< open device */
+#define RT_DEVICE_CTRL_BLOCKING         0x0B            /**< blocking io */
 #define RT_DEVICE_CTRL_MASK             0x1f            /**< mask for contrl commands */
 
 /**
@@ -1220,6 +1227,7 @@ struct rt_device_ops
     rt_ssize_t (*read)  (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
     rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
     rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
+    rt_err_t  (*flush)  (rt_device_t dev);
 };
 #endif /* RT_USING_DEVICE_OPS */
 
@@ -1264,6 +1272,7 @@ struct rt_device
     rt_ssize_t (*read)  (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
     rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
     rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
+    rt_err_t  (*flush)  (rt_device_t dev);
 #endif /* RT_USING_DEVICE_OPS */
 
 #ifdef RT_USING_POSIX_DEVIO

+ 38 - 0
src/device.c

@@ -13,6 +13,7 @@
  * 2013-07-09     Grissiom     add ref_count support
  * 2016-04-02     Bernard      fix the open_flag initialization issue.
  * 2021-03-19     Meco Man     remove rt_device_init_all()
+ * 2022-04-10     THEWON       add flush for device
  */
 
 #include <rtthread.h>
@@ -28,6 +29,7 @@
 #define device_close    (dev->ops->close)
 #define device_read     (dev->ops->read)
 #define device_write    (dev->ops->write)
+#define device_flush    (dev->ops->flush)
 #define device_control  (dev->ops->control)
 #else
 #define device_init     (dev->init)
@@ -35,6 +37,7 @@
 #define device_close    (dev->close)
 #define device_read     (dev->read)
 #define device_write    (dev->write)
+#define device_flush    (dev->flush)
 #define device_control  (dev->control)
 #endif /* RT_USING_DEVICE_OPS */
 
@@ -290,7 +293,11 @@ rt_err_t rt_device_close(rt_device_t dev)
 
     /* set open flag */
     if (result == RT_EOK || result == -RT_ENOSYS)
+    {
         dev->open_flag = RT_DEVICE_OFLAG_CLOSE;
+        dev->rx_indicate = RT_NULL;
+        dev->tx_complete = RT_NULL;
+    }
 
     return result;
 }
@@ -382,6 +389,37 @@ rt_ssize_t rt_device_write(rt_device_t dev,
 }
 RTM_EXPORT(rt_device_write);
 
+/**
+ * @brief This function will flush a device's buffers.
+ *
+ * @param dev is the pointer of device driver structure.
+ *
+ * @return the result, RT_EOK on successfully.
+ */
+rt_err_t rt_device_flush(rt_device_t dev)
+{
+    RT_ASSERT(dev != RT_NULL);
+    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
+
+    if (dev->ref_count == 0)
+    {
+        rt_set_errno(-RT_ERROR);
+        return 0;
+    }
+
+    /* call device_write interface */
+    if (device_flush != RT_NULL)
+    {
+        return device_flush(dev);
+    }
+
+    /* set error code */
+    rt_set_errno(-RT_ENOSYS);
+
+    return 0;
+}
+RTM_EXPORT(rt_device_flush);
+
 /**
  * @brief This function will perform a variety of control functions on devices.
  *