From 67bc7e816cb95cd7fb4e80829d16cf7eb3ee35f5 Mon Sep 17 00:00:00 2001 From: thewon86 Date: Tue, 21 Feb 2023 12:30:29 +0800 Subject: implement serialX UART device framework --- bsp/stm32/libraries/HAL_Drivers/SConscript | 2 + bsp/stm32/libraries/HAL_Drivers/drv_usartX.c | 876 ++++++++++++ bsp/stm32/libraries/HAL_Drivers/drv_usartX.h | 74 + bsp/stm32/stm32f429-armfly-v6/board/Kconfig | 5 + bsp/stm32/stm32f429-atk-apollo/board/Kconfig | 15 + bsp/stm32/stm32f429-fire-challenger/board/Kconfig | 10 + bsp/stm32/stm32f429-st-disco/board/Kconfig | 10 + components/drivers/Kconfig | 34 +- components/drivers/include/drivers/serialX.h | 207 +++ components/drivers/include/rtdevice.h | 5 +- components/drivers/serial/SConscript | 3 + components/drivers/serial/serialX.c | 1560 +++++++++++++++++++++ include/rtdef.h | 9 + src/device.c | 38 + 14 files changed, 2846 insertions(+), 2 deletions(-) create mode 100644 bsp/stm32/libraries/HAL_Drivers/drv_usartX.c create mode 100644 bsp/stm32/libraries/HAL_Drivers/drv_usartX.h create mode 100644 components/drivers/include/drivers/serialX.h create mode 100644 components/drivers/serial/serialX.c diff --git a/bsp/stm32/libraries/HAL_Drivers/SConscript b/bsp/stm32/libraries/HAL_Drivers/SConscript index 7057495e6e..9b11c36808 100644 --- a/bsp/stm32/libraries/HAL_Drivers/SConscript +++ b/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'] diff --git a/bsp/stm32/libraries/HAL_Drivers/drv_usartX.c b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.c new file mode 100644 index 0000000000..a76ded231c --- /dev/null +++ b/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 + +#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 */ + diff --git a/bsp/stm32/libraries/HAL_Drivers/drv_usartX.h b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.h new file mode 100644 index 0000000000..89ed24b280 --- /dev/null +++ b/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 +#include "rtdevice.h" +#include +#include +#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__ */ + diff --git a/bsp/stm32/stm32f429-armfly-v6/board/Kconfig b/bsp/stm32/stm32f429-armfly-v6/board/Kconfig index 0aa90398d8..1a6bb75370 100644 --- a/bsp/stm32/stm32f429-armfly-v6/board/Kconfig +++ b/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 diff --git a/bsp/stm32/stm32f429-atk-apollo/board/Kconfig b/bsp/stm32/stm32f429-atk-apollo/board/Kconfig index 0e2fa4d833..36be2e532c 100644 --- a/bsp/stm32/stm32f429-atk-apollo/board/Kconfig +++ b/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" diff --git a/bsp/stm32/stm32f429-fire-challenger/board/Kconfig b/bsp/stm32/stm32f429-fire-challenger/board/Kconfig index 36af05fe92..8c5873c395 100644 --- a/bsp/stm32/stm32f429-fire-challenger/board/Kconfig +++ b/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 diff --git a/bsp/stm32/stm32f429-st-disco/board/Kconfig b/bsp/stm32/stm32f429-st-disco/board/Kconfig index fddfaa9616..6934a0b4e5 100644 --- a/bsp/stm32/stm32f429-st-disco/board/Kconfig +++ b/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 diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 1b6fc17ebf..10d26e5043 100755 --- a/components/drivers/Kconfig +++ b/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 diff --git a/components/drivers/include/drivers/serialX.h b/components/drivers/include/drivers/serialX.h new file mode 100644 index 0000000000..ff2b3e1c24 --- /dev/null +++ b/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 +#include + +#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 +#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 + diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index c7a61c1994..97acc06e0b 100644 --- a/components/drivers/include/rtdevice.h +++ b/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" diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript index 81d791803a..6eaa27a468 100644 --- a/components/drivers/serial/SConscript +++ b/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) diff --git a/components/drivers/serial/serialX.c b/components/drivers/serial/serialX.c new file mode 100644 index 0000000000..7c14fa1651 --- /dev/null +++ b/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 +#include +#include + +#ifdef RT_USING_SERIAL + +#define DBG_TAG "UART" +#define DBG_LVL DBG_INFO +#include + +static rt_err_t rt_serial_flush(struct rt_device *dev); + +#ifdef RT_USING_POSIX_STDIO +#include +#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3) +#include +#else +#include +#include +#endif + +#ifdef RT_USING_POSIX_TERMIOS +#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3) +#include +#else +#include +#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 */ + diff --git a/include/rtdef.h b/include/rtdef.h index 4a65c27842..9c7ddb9528 100644 --- a/include/rtdef.h +++ b/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 diff --git a/src/device.c b/src/device.c index 9665788c77..e150c97f88 100644 --- a/src/device.c +++ b/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 @@ -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. * -- cgit v1.3.1