diff options
| author | ryancw <[email protected]> | 2024-09-04 10:39:10 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2025-05-16 09:42:11 +0800 |
| commit | ac8fba0129f98b5c6a7c6caaa55538a53b370db2 (patch) | |
| tree | ff4cfe35207900dba802b879da2f244a3adf5f5b /components/drivers | |
| parent | bf96f99d6bc3dfa3c7c9230d1b8e4415c02f19f0 (diff) | |
[drivers][serial_v2]允许阻塞接收超过rx缓冲区大小的数据、增加超时时间、flush、获取缓冲区数据长度命令、数据溢出逻辑修复、稳定性细节优化、添加更多serial_v2测试用例
[components][serial_v2] 优化txflush逻辑、对tx的activated做中断保护
[components][at] at_client适配新版serial_v2
[components][at] at_server适配新版serial_v2
[components][serial_v2] 测试用例增加循环调用,format测试用例
[components][serial_v2] poll模式判断逻辑错误
[components][serial_v2] 测试用例去掉一些非必要延时
[components][serial_v2] 测试例程使用menuconfig进行配置,更新readme
[components][at_client] at_client_getchar返回值错误、at_client解析线程优先级错误设置
[components][at] 错误码应该返回负值
[components][serial_v2] TCFLSH和FIONREAD完善、control函数增加错误返回值
[components][serial_v2] RT_SERIAL_CTRL_GET_RX_DATA_LEN更改为RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT
[utest][serial_v2] TC_UART_SEND_TIMES替换为RT_SERIAL_TC_SEND_ITERATIONS
[components][serial_v2] FIONREAD参数应该是无符号类型
[utest][serial_v2] 完善测试用例
[components][serial_v2] 避免使用三目运算符
[components][serial_v2] 使用clang-format格式化代码
[components][serial_v2] 添加get超时时间命令
[components][serial_v2] 完善posix接口
[components][serial_v2] 阻塞接口添加阻塞时间为0时的处理逻辑、优化RX阻塞接收逻辑
[components][serial_v2] 设置超时时间命令的参数改为指针形式
[components][serial_v2] nbuf发送添加超时时间为0时的逻辑
[components][serial_v2] 完善添加测试用例
[utest][serial_v2] 修复依赖关系
[components][serial_v2] 非阻塞模式下tx_flush错误修复
[components][serial_v2] activated使用原子API
[components][serial_v2] 优化DMA逻辑、没使能DMA时屏蔽DMA逻辑节约资源
[components][serial_v2] 提供写满时丢弃新数据和覆盖旧数据策略,写满丢弃策略效率更高
[components][serial_v2] 部分平台适配写满时两种策略功能
[components][serial_v2] DMA模式暂不支持丢弃新数据策略
[utest][serial_v2] 优化测试代码
[components][serial_v2] DMA模式下使用乒乓缓冲、DMA模式支持丢弃新数据策略
[utest][serial_v2] 适配DMA乒乓缓冲
[bsp][serial_v2] 部分bsp适配DMA下乒乓缓冲
[components][serial_v2] 使用spinlock替换中断,对部分结构体变量使用原子操作
[utest][serial_v2] 更新测试用例
[components][at] 适配new serialv2不再判断RTT版本号
[components][at] 删除多余的中文注释
[utest][serial_v2] 添加交叉echo示例,qemu环境下专用
[bsp][qemu] 适配串口v2并开启fifo
[components][at] 修复合并导致的错误
[bsp][n32] 适配serial_v2,没有经过测试
[components][serial_v2] 格式化代码
[utest][serial_v2] 删除无意义的打印
Diffstat (limited to 'components/drivers')
| -rw-r--r-- | components/drivers/include/drivers/dev_serial_v2.h | 65 | ||||
| -rw-r--r-- | components/drivers/serial/Kconfig | 11 | ||||
| -rw-r--r-- | components/drivers/serial/dev_serial_v2.c | 1874 |
3 files changed, 1251 insertions, 699 deletions
diff --git a/components/drivers/include/drivers/dev_serial_v2.h b/components/drivers/include/drivers/dev_serial_v2.h index ef163dff0c..d1b116ab1c 100644 --- a/components/drivers/include/drivers/dev_serial_v2.h +++ b/components/drivers/include/drivers/dev_serial_v2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -12,8 +12,7 @@ #define __DEV_SERIAL_V2_H__ #include <rtthread.h> - - +#include <rtdevice.h> /** * @addtogroup group_Drivers RTTHREAD Driver * @defgroup group_Serial_v2 Serial v2 @@ -169,6 +168,9 @@ #define NRZ_NORMAL 0 /* Non Return to Zero : normal mode */ #define NRZ_INVERTED 1 /* Non Return to Zero : inverted mode */ +/** + * device flag + */ #define RT_DEVICE_FLAG_RX_BLOCKING 0x1000 #define RT_DEVICE_FLAG_RX_NON_BLOCKING 0x2000 @@ -180,21 +182,42 @@ #define RT_SERIAL_TX_BLOCKING RT_DEVICE_FLAG_TX_BLOCKING #define RT_SERIAL_TX_NON_BLOCKING RT_DEVICE_FLAG_TX_NON_BLOCKING +/** + * hw device control commands + */ #define RT_DEVICE_CHECK_OPTMODE 0x20 +/** + * hw serial control commands + */ +#define RT_HW_SERIAL_CTRL_GETC 0x01 /* Tx irq get char */ +#define RT_HW_SERIAL_CTRL_PUTC 0x02 /* Rx irq put char */ +#define RT_HW_SERIAL_CTRL_GET_DMA_PING_BUF 0x03 /* Get DMA ping-pong buffer */ + +/** + * hw isr event + */ #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 */ + +/** + * device commands + * 0x40 - special device control commands + */ +#define RT_SERIAL_CTRL_SET_RX_TIMEOUT 0x41 /* set Rx timeout. Call before rt_device_read. not supported in poll mode */ +#define RT_SERIAL_CTRL_SET_TX_TIMEOUT 0x42 /* set Tx timeout. Call before rt_device_write. not supported in poll mode */ +#define RT_SERIAL_CTRL_GET_RX_TIMEOUT 0x43 /* get Rx timeout. not supported in poll mode */ +#define RT_SERIAL_CTRL_GET_TX_TIMEOUT 0x44 /* get Tx timeout. not supported in poll mode */ +#define RT_SERIAL_CTRL_RX_FLUSH 0x45 /* clear rx buffer. Discard all data */ +#define RT_SERIAL_CTRL_TX_FLUSH 0x46 /* clear tx buffer. Blocking and wait for the send buffer data to be sent. not supported in poll mode */ +#define RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT 0x47 /* get unread bytes count. not supported in poll mode */ #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_RX_MINBUFSZ 64 #define RT_SERIAL_TX_MINBUFSZ 64 @@ -216,7 +239,8 @@ RT_SERIAL_RX_MINBUFSZ, /* rxBuf size */ \ RT_SERIAL_TX_MINBUFSZ, /* txBuf size */ \ RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \ - 0 \ + 0, /* reserved */ \ + 0, /* dma_ping_bufsz */ \ } /** @@ -239,6 +263,10 @@ struct serial_configure rt_uint32_t tx_bufsz :16; rt_uint32_t flowcontrol :1; rt_uint32_t reserved :5; + +#ifdef RT_SERIAL_USING_DMA + rt_uint32_t dma_ping_bufsz :16; +#endif }; /** @@ -248,12 +276,15 @@ struct rt_serial_rx_fifo { struct rt_ringbuffer rb; +#ifdef RT_SERIAL_USING_DMA + struct rt_ringbuffer dma_ping_rb; +#endif + struct rt_completion rx_cpt; - rt_uint16_t rx_cpt_index; + rt_size_t rx_cpt_index; - /* software fifo */ - rt_uint8_t buffer[]; + rt_atomic_t rx_timeout; }; /** @@ -264,14 +295,13 @@ struct rt_serial_tx_fifo { struct rt_ringbuffer rb; - rt_size_t put_size; + struct rt_completion tx_cpt; - rt_bool_t activated; + rt_size_t put_size; - struct rt_completion tx_cpt; + rt_atomic_t tx_timeout; - /* software fifo */ - rt_uint8_t buffer[]; + rt_atomic_t activated; }; /** @@ -288,6 +318,8 @@ struct rt_serial_device void *serial_rx; void *serial_tx; + struct rt_spinlock spinlock; + struct rt_device_notify rx_notify; }; @@ -321,6 +353,7 @@ struct rt_uart_ops */ void rt_hw_serial_isr(struct rt_serial_device *serial, int event); +rt_err_t rt_hw_serial_control_isr(struct rt_serial_device *serial, int cmd, void *args); /** * @brief Register a serial device to device list diff --git a/components/drivers/serial/Kconfig b/components/drivers/serial/Kconfig index 349aaeab2f..cd6cf3f359 100644 --- a/components/drivers/serial/Kconfig +++ b/components/drivers/serial/Kconfig @@ -13,6 +13,17 @@ menuconfig RT_USING_SERIAL config RT_USING_SERIAL_V2 bool "RT_USING_SERIAL_V2" endchoice + + choice + prompt "Choice Serial version" + depends on RT_USING_SERIAL_V2 + default RT_SERIAL_BUF_STRATEGY_OVERWRITE + config RT_SERIAL_BUF_STRATEGY_DROP + bool "drop new incoming data when the buffer is full" + config RT_SERIAL_BUF_STRATEGY_OVERWRITE + bool "overwrite old data when the buffer is full" + endchoice + config RT_SERIAL_USING_DMA bool "Enable serial DMA mode" default y diff --git a/components/drivers/serial/dev_serial_v2.c b/components/drivers/serial/dev_serial_v2.c index 18f9c8970e..bf51d3fb4f 100644 --- a/components/drivers/serial/dev_serial_v2.c +++ b/components/drivers/serial/dev_serial_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -12,10 +12,22 @@ #include <rtthread.h> #include <rtdevice.h> -#define DBG_TAG "Serial" -#define DBG_LVL DBG_INFO +#define DBG_TAG "Serial" +#define DBG_LVL DBG_INFO #include <rtdbg.h> +#ifdef RT_SERIAL_BUF_STRATEGY_DROP +#define RT_SERIAL_FIFO_LOCK(spinlock) ((rt_base_t)0) +#define RT_SERIAL_FIFO_UNLOCK(spinlock, level) \ + do { \ + RT_UNUSED(spinlock); \ + RT_UNUSED(level); \ + } while (0) +#else +#define RT_SERIAL_FIFO_LOCK(spinlock) rt_spin_lock_irqsave(spinlock) +#define RT_SERIAL_FIFO_UNLOCK(spinlock, level) rt_spin_unlock_irqrestore(spinlock, level) +#endif /* RT_SERIAL_BUF_STRATEGY_DROP */ + #ifdef RT_USING_POSIX_STDIO #include <unistd.h> #include <fcntl.h> @@ -39,7 +51,7 @@ RT_OBJECT_HOOKLIST_DEFINE(rt_hw_serial_rxind); static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size) { - rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN); + rt_wqueue_wakeup(&dev->wait_queue, (void *)POLLIN); RT_OBJECT_HOOKLIST_CALL(rt_hw_serial_rxind, (dev, size)); @@ -49,7 +61,7 @@ static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size) /* fops for serial */ static int serial_fops_open(struct dfs_file *fd) { - rt_err_t ret = 0; + rt_err_t ret = 0; rt_uint16_t flags = 0; rt_device_t device; @@ -77,7 +89,7 @@ static int serial_fops_open(struct dfs_file *fd) if ((fd->flags & O_ACCMODE) != O_WRONLY) rt_device_set_rx_indicate(device, serial_fops_rx_ind); - ret = rt_device_open(device, flags); + ret = rt_device_open(device, flags | RT_SERIAL_RX_BLOCKING | RT_SERIAL_TX_BLOCKING); if (ret == RT_EOK) return 0; return ret; @@ -98,8 +110,8 @@ static int serial_fops_close(struct dfs_file *fd) static int serial_fops_ioctl(struct dfs_file *fd, int cmd, void *args) { rt_device_t device; - int flags = (int)(rt_base_t)args; - int mask = O_NONBLOCK | O_APPEND; + int flags = (int)(rt_base_t)args; + int mask = O_NONBLOCK | O_APPEND; device = (rt_device_t)fd->vnode->data; switch (cmd) @@ -109,7 +121,7 @@ static int serial_fops_ioctl(struct dfs_file *fd, int cmd, void *args) case FIONWRITE: break; case F_SETFL: - flags &= mask; + flags &= mask; fd->flags &= ~mask; fd->flags |= flags; break; @@ -124,25 +136,35 @@ static ssize_t serial_fops_read(struct dfs_file *fd, void *buf, size_t count, of static ssize_t serial_fops_read(struct dfs_file *fd, void *buf, size_t count) #endif { - int size = 0; + ssize_t size = 0; rt_device_t device; + rt_int32_t rx_timout; + + if (count == 0) return 0; + RT_ASSERT(fd != RT_NULL && buf != RT_NULL); device = (rt_device_t)fd->vnode->data; + RT_ASSERT(device != RT_NULL); - do + /* nonblock mode */ + if (fd->flags & O_NONBLOCK) { - size = rt_device_read(device, -1, buf, count); + rx_timout = RT_WAITING_NO; + rt_device_control(device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void *)&rx_timout); + 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); + size = -1; + rt_set_errno(EAGAIN); } - }while (size <= 0); + } + else + { + rx_timout = RT_WAITING_FOREVER; + rt_device_control(device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void *)&rx_timout); + size = rt_device_read(device, -1, buf, count); + } + return size; } @@ -153,17 +175,53 @@ static ssize_t serial_fops_write(struct dfs_file *fd, const void *buf, size_t co static ssize_t serial_fops_write(struct dfs_file *fd, const void *buf, size_t count) #endif { + ssize_t size = 0; rt_device_t device; + rt_int32_t tx_timeout; + + device = (rt_device_t)fd->vnode->data; + + if (fd->flags & O_NONBLOCK) + { + tx_timeout = RT_WAITING_NO; + rt_device_control(device, RT_SERIAL_CTRL_SET_TX_TIMEOUT, (void *)&tx_timeout); + size = rt_device_write(device, -1, buf, count); + if (size <= 0) + { + size = -1; + rt_set_errno(EAGAIN); + } + } + else + { + tx_timeout = RT_WAITING_FOREVER; + rt_device_control(device, RT_SERIAL_CTRL_SET_TX_TIMEOUT, (void *)&tx_timeout); + size = rt_device_write(device, -1, buf, count); + } + + return size; +} + +static int serial_fops_flush(struct dfs_file *fd) +{ + rt_device_t device; + struct rt_serial_device *serial; device = (rt_device_t)fd->vnode->data; - return rt_device_write(device, -1, buf, count); + RT_ASSERT(device != RT_NULL); + + serial = (struct rt_serial_device *)device; + rt_device_control(device, RT_SERIAL_CTRL_TX_FLUSH, (void *)RT_NULL); + rt_device_control(device, RT_SERIAL_CTRL_RX_FLUSH, (void *)RT_NULL); + + return 0; } static int serial_fops_poll(struct dfs_file *fd, struct rt_pollreq *req) { - int mask = 0; - int flags = 0; - rt_device_t device; + int mask = 0; + int flags = 0; + rt_device_t device; struct rt_serial_device *serial; device = (rt_device_t)fd->vnode->data; @@ -175,43 +233,41 @@ static int serial_fops_poll(struct dfs_file *fd, struct rt_pollreq *req) flags = fd->flags & O_ACCMODE; if (flags == O_RDONLY || flags == O_RDWR) { - rt_base_t level; - struct rt_serial_rx_fifo* rx_fifo; - - rt_poll_add(&(device->wait_queue), req); + rt_base_t level; + struct rt_serial_rx_fifo *rx_fifo; - rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx; + rt_poll_add(&device->wait_queue, req); - level = rt_hw_interrupt_disable(); + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); if (rt_ringbuffer_data_len(&rx_fifo->rb)) mask |= POLLIN; - rt_hw_interrupt_enable(level); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); } - // mask|=POLLOUT; - return mask; + /* mask|=POLLOUT; */ + return mask; } const static struct dfs_file_ops _serial_fops = -{ - .open = serial_fops_open, - .close = serial_fops_close, - .ioctl = serial_fops_ioctl, - .read = serial_fops_read, - .write = serial_fops_write, - .poll = serial_fops_poll, + { + .open = serial_fops_open, + .close = serial_fops_close, + .ioctl = serial_fops_ioctl, + .read = serial_fops_read, + .write = serial_fops_write, + .flush = serial_fops_flush, + .poll = serial_fops_poll, }; #endif /* RT_USING_POSIX_STDIO */ -static rt_ssize_t rt_serial_get_linear_buffer(struct rt_ringbuffer *rb, - rt_uint8_t **ptr) +static rt_ssize_t rt_serial_get_linear_buffer(struct rt_ringbuffer *rb, + rt_uint8_t **ptr) { rt_size_t size; RT_ASSERT(rb != RT_NULL); - *ptr = RT_NULL; - /* whether has enough data */ size = rt_ringbuffer_data_len(rb); @@ -221,7 +277,7 @@ static rt_ssize_t rt_serial_get_linear_buffer(struct rt_ringbuffer *rb, *ptr = &rb->buffer_ptr[rb->read_index]; - if(rb->buffer_size - rb->read_index > size) + if (rb->buffer_size - rb->read_index > size) { return size; } @@ -229,8 +285,9 @@ static rt_ssize_t rt_serial_get_linear_buffer(struct rt_ringbuffer *rb, return rb->buffer_size - rb->read_index; } -static rt_ssize_t rt_serial_update_read_index(struct rt_ringbuffer *rb, - rt_uint16_t read_index) +#ifdef RT_SERIAL_USING_DMA +static void rt_serial_update_read_index(struct rt_ringbuffer *rb, + rt_uint16_t length) { rt_size_t size; @@ -241,60 +298,98 @@ static rt_ssize_t rt_serial_update_read_index(struct rt_ringbuffer *rb, /* no data */ if (size == 0) - return 0; + return; /* less data */ - if(size < read_index) - read_index = size; + if (size < length) + length = size; - if(rb->buffer_size - rb->read_index > read_index) + if (rb->buffer_size - rb->read_index > length) { - rb->read_index += read_index; - return read_index; + rb->read_index += length; + return; } - read_index = rb->buffer_size - rb->read_index; - /* we are going into the other side of the mirror */ rb->read_mirror = ~rb->read_mirror; - rb->read_index = 0; + rb->read_index = length - (rb->buffer_size - rb->read_index); - return read_index; + return; } -static rt_ssize_t rt_serial_update_write_index(struct rt_ringbuffer *rb, - rt_uint16_t write_size) +static void rt_serial_update_write_index(struct rt_ringbuffer *rb, + rt_uint16_t length) { - rt_uint16_t size; +#ifdef RT_SERIAL_BUF_STRATEGY_DROP + rt_uint16_t space_length; + RT_ASSERT(rb != RT_NULL); /* whether has enough space */ - size = rt_ringbuffer_space_len(rb); + space_length = rt_ringbuffer_space_len(rb); + + /* no space */ + if (space_length == 0) + return; + + /* drop some data */ + if (space_length < length) + length = space_length; - /* no space, drop some data */ - if (size < write_size) + if (rb->buffer_size - rb->write_index > length) { - write_size = size; + /* this should not cause overflow because there is enough space for + * length of data in current mirror */ + rb->write_index += length; + return; + } + + /* we are going into the other side of the mirror */ + rb->write_mirror = ~rb->write_mirror; + rb->write_index = length - (rb->buffer_size - rb->write_index); + return; + +#else + rt_uint16_t space_length; + RT_ASSERT(rb != RT_NULL); + + /* whether has enough space */ + space_length = rt_ringbuffer_space_len(rb); + + if (length > rb->buffer_size) + { + length = rb->buffer_size; #if !defined(RT_USING_ULOG) || defined(ULOG_USING_ISR_LOG) LOG_W("The serial buffer (len %d) is overflow.", rb->buffer_size); #endif } - if (rb->buffer_size - rb->write_index > write_size) + if (rb->buffer_size - rb->write_index > length) { /* this should not cause overflow because there is enough space for * length of data in current mirror */ - rb->write_index += write_size; - return write_size; + rb->write_index += length; + + if (length > space_length) + rb->read_index = rb->write_index; + + return; } /* we are going into the other side of the mirror */ rb->write_mirror = ~rb->write_mirror; - rb->write_index = write_size - (rb->buffer_size - rb->write_index); + rb->write_index = length - (rb->buffer_size - rb->write_index); - return write_size; + if (length > space_length) + { + if (rb->write_index <= rb->read_index) + rb->read_mirror = ~rb->read_mirror; + rb->read_index = rb->write_index; + } + return; +#endif /* RT_SERIAL_BUF_STRATEGY_DROP */ } - +#endif /* RT_SERIAL_USING_DMA */ /** * @brief Serial polling receive data routine, This function will receive data @@ -305,32 +400,32 @@ static rt_ssize_t rt_serial_update_write_index(struct rt_ringbuffer *rb, * @param size Receive data buffer length. * @return Return the final length of data received. */ -rt_ssize_t _serial_poll_rx(struct rt_device *dev, - rt_off_t pos, - void *buffer, - rt_size_t size) +rt_ssize_t _serial_poll_rx(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) { struct rt_serial_device *serial; - rt_size_t getc_size; - int getc_element; /* Gets one byte of data received */ - rt_uint8_t *getc_buffer; /* Pointer to the receive data buffer */ + rt_size_t getc_size; + int getc_element; /* Gets one byte of data received */ + rt_uint8_t *getc_buffer; /* Pointer to the receive data buffer */ - RT_ASSERT(dev != RT_NULL); + if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); - serial = (struct rt_serial_device *)dev; - RT_ASSERT(serial != RT_NULL); + serial = (struct rt_serial_device *)dev; getc_buffer = (rt_uint8_t *)buffer; - getc_size = size; + getc_size = size; - while(size) + while (size) { getc_element = serial->ops->getc(serial); if (getc_element == -1) break; *getc_buffer = getc_element; - ++ getc_buffer; - -- size; + ++getc_buffer; + --size; if (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) { @@ -340,7 +435,7 @@ rt_ssize_t _serial_poll_rx(struct rt_device *dev, } } - return getc_size - size; + return getc_size - size; } /** @@ -350,23 +445,23 @@ rt_ssize_t _serial_poll_rx(struct rt_device *dev, * @param pos Empty parameter. * @param buffer Transmit data buffer. * @param size Transmit data buffer length. - * @return Return the final length of data received. + * @return Return the final length of data transmit. */ -rt_ssize_t _serial_poll_tx(struct rt_device *dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) +rt_ssize_t _serial_poll_tx(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) { struct rt_serial_device *serial; - rt_size_t putc_size; - rt_uint8_t *putc_buffer; /* Pointer to the transmit data buffer */ - RT_ASSERT(dev != RT_NULL); + rt_size_t putc_size; + rt_uint8_t *putc_buffer; /* Pointer to the transmit data buffer */ - serial = (struct rt_serial_device *)dev; - RT_ASSERT(serial != RT_NULL); + if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); + serial = (struct rt_serial_device *)dev; putc_buffer = (rt_uint8_t *)buffer; - putc_size = size; + putc_size = size; while (size) { @@ -379,73 +474,109 @@ rt_ssize_t _serial_poll_tx(struct rt_device *dev, } serial->ops->putc(serial, *putc_buffer); - ++ putc_buffer; - -- size; + ++putc_buffer; + --size; } - return putc_size - size; + return putc_size - size; } /** * @brief Serial receive data routines, This function will receive * data by using fifo + * + * @note In blocking mode, the function will wait until the specified amount of data is received or until a timeout occurs. + * In non-blocking mode, the function will immediately attempt to retrieve as much data as possible from the ring buffer and return. + * * @param dev The pointer of device driver structure * @param pos Empty parameter. * @param buffer Receive data buffer. * @param size Receive data buffer length. - * @return Return the final length of data received. + * @return Returns the actual length of data received. If a timeout occurs in blocking mode, it returns -RT_ETIMEOUT. */ -static rt_ssize_t _serial_fifo_rx(struct rt_device *dev, - rt_off_t pos, - void *buffer, - rt_size_t size) +static rt_ssize_t _serial_fifo_rx(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_rx_fifo *rx_fifo; - rt_base_t level; - rt_size_t recv_len; /* The length of data from the ringbuffer */ + rt_base_t level; + rt_size_t recv_size = 0; - RT_ASSERT(dev != RT_NULL); if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); - serial = (struct rt_serial_device *)dev; - - RT_ASSERT((serial != RT_NULL) && (buffer != RT_NULL)); - - rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + serial = (struct rt_serial_device *)dev; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; if (dev->open_flag & RT_SERIAL_RX_BLOCKING) { - if (size > serial->config.rx_bufsz) - { - LOG_W("(%s) serial device received data:[%d] larger than " - "rx_bufsz:[%d], please increase the BSP_UARTx_RX_BUFSIZE option", - dev->parent.name, size, serial->config.rx_bufsz); + rt_size_t data_len = 0; + rt_size_t rx_bufsz_third = serial->config.rx_bufsz / 2; + rt_int32_t base_rx_timeout = rt_atomic_load(&rx_fifo->rx_timeout); + rt_int32_t rx_timeout = base_rx_timeout; + rt_tick_t now_tick = 0; + rt_tick_t begin_tick = rt_tick_get(); - return 0; - } - /* Get the length of the data from the ringbuffer */ - recv_len = rt_ringbuffer_data_len(&(rx_fifo->rb)); - - if (recv_len < size) + while (1) { - /* When recv_len is less than size, rx_cpt_index is updated to the size - * and rt_current_thread is suspend until rx_cpt_index is equal to 0 */ - rx_fifo->rx_cpt_index = size; - rt_completion_wait(&(rx_fifo->rx_cpt), RT_WAITING_FOREVER); - } - } + if (rx_timeout != RT_WAITING_NO) + { + level = rt_spin_lock_irqsave(&serial->spinlock); + data_len = rt_ringbuffer_data_len(&rx_fifo->rb); + if (data_len < size - recv_size) + { + if (size - (recv_size + data_len) >= rx_bufsz_third) + { + rx_fifo->rx_cpt_index = rx_bufsz_third; + } + else + { + rx_fifo->rx_cpt_index = size - (recv_size + data_len); + } + rt_completion_wait(&rx_fifo->rx_cpt, 0); + } + rt_spin_unlock_irqrestore(&serial->spinlock, level); + } - /* This part of the code is open_flag as RT_SERIAL_RX_NON_BLOCKING */ + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); + recv_size += rt_ringbuffer_get(&rx_fifo->rb, (rt_uint8_t *)buffer + recv_size, size - recv_size); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); + if (recv_size == size || rx_timeout == RT_WAITING_NO) + { + break; + } - level = rt_hw_interrupt_disable(); - /* When open_flag is RT_SERIAL_RX_NON_BLOCKING, - * the data is retrieved directly from the ringbuffer and returned */ - recv_len = rt_ringbuffer_get(&(rx_fifo->rb), buffer, size); - rt_hw_interrupt_enable(level); + rt_completion_wait(&rx_fifo->rx_cpt, rx_timeout); + if (rx_timeout != RT_WAITING_FOREVER) + { + now_tick = rt_tick_get(); + if (now_tick - begin_tick >= base_rx_timeout) + { + return -RT_ETIMEOUT; + } + else + { + if (now_tick > begin_tick) + rx_timeout = base_rx_timeout - (now_tick - begin_tick); + else + rx_timeout = begin_tick + base_rx_timeout - now_tick + 1; + } + } + } + } + else if (dev->open_flag & RT_SERIAL_RX_NON_BLOCKING) + { + /* When open_flag is RT_SERIAL_RX_NON_BLOCKING, + * the data is retrieved directly from the ringbuffer and returned */ + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); + recv_size = rt_ringbuffer_get(&rx_fifo->rb, buffer, size); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); + } - return recv_len; + return recv_size; } /** @@ -455,23 +586,23 @@ static rt_ssize_t _serial_fifo_rx(struct rt_device *dev, * @param pos Empty parameter. * @param buffer Transmit data buffer. * @param size Transmit data buffer length. - * @return Return the final length of data transmit. + * @return Returns the actual length of data transmitted. If a timeout occurs, it returns -RT_ETIMEOUT. */ -static rt_ssize_t _serial_fifo_tx_blocking_nbuf(struct rt_device *dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) +static rt_ssize_t _serial_fifo_tx_blocking_nbuf(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) { - struct rt_serial_device *serial; - struct rt_serial_tx_fifo *tx_fifo = RT_NULL; - rt_ssize_t rst; + struct rt_serial_device *serial; + struct rt_serial_tx_fifo *tx_fifo; + rt_ssize_t send_size; + rt_err_t ret; - RT_ASSERT(dev != RT_NULL); if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); - serial = (struct rt_serial_device *)dev; - RT_ASSERT((serial != RT_NULL) && (buffer != RT_NULL)); - tx_fifo = (struct rt_serial_tx_fifo *) serial->serial_tx; + serial = (struct rt_serial_device *)dev; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; RT_ASSERT(tx_fifo != RT_NULL); if (rt_thread_self() == RT_NULL || (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) @@ -482,19 +613,42 @@ static rt_ssize_t _serial_fifo_tx_blocking_nbuf(struct rt_device *dev, /* When serial transmit in tx_blocking mode, * if the activated mode is RT_TRUE, it will return directly */ - if (tx_fifo->activated == RT_TRUE) return 0; + if (rt_atomic_flag_test_and_set(&tx_fifo->activated)) + { + return 0; + } + + /* clear tx_cpt flag */ + rt_completion_wait(&tx_fifo->tx_cpt, 0); - tx_fifo->activated = RT_TRUE; /* Call the transmit interface for transmission */ - rst = serial->ops->transmit(serial, - (rt_uint8_t *)buffer, - size, - RT_SERIAL_TX_BLOCKING); + send_size = serial->ops->transmit(serial, + (rt_uint8_t *)buffer, + size, + RT_SERIAL_TX_BLOCKING); + + if (rt_atomic_load(&tx_fifo->tx_timeout) == RT_WAITING_NO) + { + return send_size; + } + /* Waiting for the transmission to complete */ - rt_completion_wait(&(tx_fifo->tx_cpt), RT_WAITING_FOREVER); + ret = rt_completion_wait(&tx_fifo->tx_cpt, rt_atomic_load(&tx_fifo->tx_timeout)); + if (ret != RT_EOK) + { + if (ret == -RT_ETIMEOUT) + { + return ret; + } + else + { + return 0; + } + } + /* Inactive tx mode flag */ - tx_fifo->activated = RT_FALSE; - return rst; + rt_atomic_flag_clear(&tx_fifo->activated); + return send_size; } /** @@ -504,25 +658,23 @@ static rt_ssize_t _serial_fifo_tx_blocking_nbuf(struct rt_device *dev, * @param pos Empty parameter. * @param buffer Transmit data buffer. * @param size Transmit data buffer length. - * @return Return the final length of data transmit. + * @return Returns the final length of data transmitted. If not all data can be sent within the timeout period, returns -RT_ETIMEOUT. */ -static rt_ssize_t _serial_fifo_tx_blocking_buf(struct rt_device *dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) +static rt_ssize_t _serial_fifo_tx_blocking_buf(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) { - struct rt_serial_device *serial; - struct rt_serial_tx_fifo *tx_fifo = RT_NULL; - rt_size_t length = size; - rt_size_t offset = 0; + struct rt_serial_device *serial; + struct rt_serial_tx_fifo *tx_fifo; + rt_base_t level; + rt_size_t send_size = 0; if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); - RT_ASSERT(dev != RT_NULL); - serial = (struct rt_serial_device *)dev; - RT_ASSERT((serial != RT_NULL) && (buffer != RT_NULL)); - - tx_fifo = (struct rt_serial_tx_fifo *) serial->serial_tx; + serial = (struct rt_serial_device *)dev; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; RT_ASSERT(tx_fifo != RT_NULL); if (rt_thread_self() == RT_NULL || (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) @@ -530,34 +682,70 @@ static rt_ssize_t _serial_fifo_tx_blocking_buf(struct rt_device *dev, /* using poll tx when the scheduler not startup or in stream mode */ return _serial_poll_tx(dev, pos, buffer, size); } + /* When serial transmit in tx_blocking mode, * if the activated mode is RT_TRUE, it will return directly */ - if (tx_fifo->activated == RT_TRUE) return 0; - tx_fifo->activated = RT_TRUE; + if (rt_atomic_flag_test_and_set(&tx_fifo->activated)) + { + return 0; + } - while (size) + rt_int32_t base_tx_timeout = rt_atomic_load(&tx_fifo->tx_timeout); + rt_int32_t tx_timeout = base_tx_timeout; + rt_tick_t now_tick = 0; + rt_tick_t begin_tick = rt_tick_get(); + + while (send_size != size) { /* Copy one piece of data into the ringbuffer at a time * until the length of the data is equal to size */ - tx_fifo->put_size = rt_ringbuffer_put(&(tx_fifo->rb), - (rt_uint8_t *)buffer + offset, - size); + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); + tx_fifo->put_size = rt_ringbuffer_put(&tx_fifo->rb, + (rt_uint8_t *)buffer + send_size, + size - send_size); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); + + /* clear tx_cpt flag */ + rt_completion_wait(&tx_fifo->tx_cpt, 0); - /* Call the transmit interface for transmission */ + /* Call the transmit interface for transmission again */ + /* Note that in interrupt mode, buffer and tx_fifo->put_size + * are inactive parameters */ serial->ops->transmit(serial, - (rt_uint8_t *)buffer + offset, - tx_fifo->put_size, - RT_SERIAL_TX_BLOCKING); + (rt_uint8_t *)buffer + send_size, + tx_fifo->put_size, + RT_SERIAL_TX_BLOCKING); + + send_size += tx_fifo->put_size; + + if (tx_timeout == RT_WAITING_NO) + { + break; + } - offset += tx_fifo->put_size; - size -= tx_fifo->put_size; /* Waiting for the transmission to complete */ - rt_completion_wait(&(tx_fifo->tx_cpt), RT_WAITING_FOREVER); + rt_completion_wait(&tx_fifo->tx_cpt, tx_timeout); + if (tx_timeout != RT_WAITING_FOREVER) + { + now_tick = rt_tick_get(); + if (now_tick - begin_tick >= base_tx_timeout) + { + return -RT_ETIMEOUT; + } + else + { + if (now_tick > begin_tick) + tx_timeout = base_tx_timeout - (now_tick - begin_tick); + else + tx_timeout = begin_tick + base_tx_timeout - now_tick + 1; + } + } } + /* Finally Inactivate the tx->fifo */ - tx_fifo->activated = RT_FALSE; + rt_atomic_flag_clear(&tx_fifo->activated); - return length; + return send_size; } /** @@ -569,46 +757,49 @@ static rt_ssize_t _serial_fifo_tx_blocking_buf(struct rt_device *dev, * @param size Transmit data buffer length. * @return Return the final length of data transmit. */ -static rt_ssize_t _serial_fifo_tx_nonblocking(struct rt_device *dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) +static rt_ssize_t _serial_fifo_tx_nonblocking(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_tx_fifo *tx_fifo; - rt_base_t level; - rt_size_t length; + rt_uint8_t *put_ptr; + rt_base_t level; + rt_size_t send_size = 0; - RT_ASSERT(dev != RT_NULL); if (size == 0) return 0; + RT_ASSERT(dev != RT_NULL && buffer != RT_NULL); - serial = (struct rt_serial_device *)dev; - RT_ASSERT((serial != RT_NULL) && (buffer != RT_NULL)); - tx_fifo = (struct rt_serial_tx_fifo *) serial->serial_tx; - - level = rt_hw_interrupt_disable(); + serial = (struct rt_serial_device *)dev; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); - if (tx_fifo->activated == RT_FALSE) - { - /* When serial transmit in tx_non_blocking mode, if the activated mode is RT_FALSE, + /* When serial transmit in tx_non_blocking mode, if the activated mode is RT_FALSE, * start copying data into the ringbuffer */ - tx_fifo->activated = RT_TRUE; + if (!rt_atomic_flag_test_and_set(&tx_fifo->activated)) + { + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); /* Copying data into the ringbuffer */ - length = rt_ringbuffer_put(&(tx_fifo->rb), buffer, size); + send_size = rt_ringbuffer_put(&tx_fifo->rb, buffer, size); - rt_hw_interrupt_enable(level); - - rt_uint8_t *put_ptr = RT_NULL; /* Get the linear length buffer from ringbuffer */ - tx_fifo->put_size = rt_serial_get_linear_buffer(&(tx_fifo->rb), &put_ptr); - /* Call the transmit interface for transmission */ + tx_fifo->put_size = rt_serial_get_linear_buffer(&tx_fifo->rb, &put_ptr); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); + + /* clear tx_cpt flag */ + rt_completion_wait(&tx_fifo->tx_cpt, 0); + + /* Call the transmit interface for transmission again */ + /* Note that in interrupt mode, put_ptr and tx_fifo->put_size + * are inactive parameters */ serial->ops->transmit(serial, put_ptr, tx_fifo->put_size, RT_SERIAL_TX_NON_BLOCKING); /* In tx_nonblocking mode, there is no need to call rt_completion_wait() APIs to wait * for the rt_current_thread to resume */ - return length; + return send_size; } /* If the activated mode is RT_TRUE, it means that serial device is transmitting, @@ -616,12 +807,12 @@ static rt_ssize_t _serial_fifo_tx_nonblocking(struct rt_device *dev, * Note that this part of the code requires disable interrupts * to prevent multi thread reentrant */ + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); /* Copying data into the ringbuffer */ - length = rt_ringbuffer_put(&(tx_fifo->rb), buffer, size); - - rt_hw_interrupt_enable(level); + send_size = rt_ringbuffer_put(&tx_fifo->rb, buffer, size); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); - return length; + return send_size; } @@ -631,10 +822,10 @@ static rt_ssize_t _serial_fifo_tx_nonblocking(struct rt_device *dev, * @param rx_oflag The flag of that the serial port opens. * @return Return the status of the operation. */ -static rt_err_t rt_serial_tx_enable(struct rt_device *dev, - rt_uint16_t tx_oflag) +static rt_err_t rt_serial_tx_enable(struct rt_device *dev, + rt_uint16_t tx_oflag) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_tx_fifo *tx_fifo = RT_NULL; RT_ASSERT(dev != RT_NULL); @@ -646,7 +837,7 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, if (tx_oflag == RT_SERIAL_TX_NON_BLOCKING) { LOG_E("(%s) serial device with misconfigure: tx_bufsz = 0", - dev->parent.name); + dev->parent.name); return -RT_EINVAL; } @@ -665,7 +856,7 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, { /* When using RT_SERIAL_TX_BLOCKING, it is necessary to determine * whether serial device needs to use buffer */ - rt_err_t optmode; /* The operating mode used by serial device */ + rt_err_t optmode; /* The operating mode used by serial device */ /* Call the Control() API to get the operating mode */ optmode = serial->ops->control(serial, RT_DEVICE_CHECK_OPTMODE, @@ -673,13 +864,12 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, if (optmode == RT_SERIAL_TX_BLOCKING_BUFFER) { /* If use RT_SERIAL_TX_BLOCKING_BUFFER, the ringbuffer is initialized */ - tx_fifo = (struct rt_serial_tx_fifo *) rt_malloc - (sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); + tx_fifo = (struct rt_serial_tx_fifo *)rt_malloc(sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); RT_ASSERT(tx_fifo != RT_NULL); - - rt_ringbuffer_init(&(tx_fifo->rb), - tx_fifo->buffer, - serial->config.tx_bufsz); + rt_memset(tx_fifo, RT_NULL, sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); + rt_ringbuffer_init(&tx_fifo->rb, + (rt_uint8_t *)tx_fifo + sizeof(struct rt_serial_tx_fifo), + serial->config.tx_bufsz); serial->serial_tx = tx_fifo; #ifndef RT_USING_DEVICE_OPS @@ -690,13 +880,11 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, { /* If not use RT_SERIAL_TX_BLOCKING_BUFFER, * the control() API is called to configure the serial device */ - tx_fifo = (struct rt_serial_tx_fifo*) rt_malloc - (sizeof(struct rt_serial_tx_fifo)); + tx_fifo = (struct rt_serial_tx_fifo *)rt_malloc(sizeof(struct rt_serial_tx_fifo)); RT_ASSERT(tx_fifo != RT_NULL); - + rt_memset(tx_fifo, RT_NULL, sizeof(struct rt_serial_tx_fifo)); /* Init rb.buffer_ptr to RT_NULL, in rt_serial_write() need check it * otherwise buffer_ptr maybe a random value, as rt_malloc not init memory */ - tx_fifo->rb.buffer_ptr = RT_NULL; serial->serial_tx = tx_fifo; #ifndef RT_USING_DEVICE_OPS @@ -705,41 +893,45 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, /* Call the control() API to configure the serial device by RT_SERIAL_TX_BLOCKING*/ serial->ops->control(serial, - RT_DEVICE_CTRL_CONFIG, - (void *)RT_SERIAL_TX_BLOCKING); - rt_memset(&tx_fifo->rb, RT_NULL, sizeof(tx_fifo->rb)); + RT_DEVICE_CTRL_CONFIG, + (void *)RT_SERIAL_TX_BLOCKING); } - - tx_fifo->activated = RT_FALSE; + rt_atomic_flag_clear(&tx_fifo->activated); tx_fifo->put_size = 0; - rt_completion_init(&(tx_fifo->tx_cpt)); + rt_atomic_store(&tx_fifo->tx_timeout, RT_WAITING_FOREVER); + + rt_completion_init(&tx_fifo->tx_cpt); dev->open_flag |= RT_SERIAL_TX_BLOCKING; return RT_EOK; } + /* When using RT_SERIAL_TX_NON_BLOCKING, ringbuffer needs to be initialized, * and initialize the tx_fifo->activated value is RT_FALSE. */ - tx_fifo = (struct rt_serial_tx_fifo *) rt_malloc - (sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); + tx_fifo = (struct rt_serial_tx_fifo *)rt_malloc(sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); RT_ASSERT(tx_fifo != RT_NULL); + rt_memset(tx_fifo, RT_NULL, sizeof(struct rt_serial_tx_fifo) + serial->config.tx_bufsz); - tx_fifo->activated = RT_FALSE; - tx_fifo->put_size = 0; - rt_ringbuffer_init(&(tx_fifo->rb), - tx_fifo->buffer, - serial->config.tx_bufsz); + rt_ringbuffer_init(&tx_fifo->rb, + (rt_uint8_t *)tx_fifo + sizeof(struct rt_serial_tx_fifo), + serial->config.tx_bufsz); serial->serial_tx = tx_fifo; + rt_atomic_flag_clear(&tx_fifo->activated); + tx_fifo->put_size = 0; + #ifndef RT_USING_DEVICE_OPS dev->write = _serial_fifo_tx_nonblocking; #endif + rt_completion_init(&tx_fifo->tx_cpt); dev->open_flag |= RT_SERIAL_TX_NON_BLOCKING; + /* Call the control() API to configure the serial device by RT_SERIAL_TX_NON_BLOCKING*/ serial->ops->control(serial, - RT_DEVICE_CTRL_CONFIG, - (void *)RT_SERIAL_TX_NON_BLOCKING); + RT_DEVICE_CTRL_CONFIG, + (void *)RT_SERIAL_TX_NON_BLOCKING); return RT_EOK; } @@ -751,11 +943,12 @@ static rt_err_t rt_serial_tx_enable(struct rt_device *dev, * @param rx_oflag The flag of that the serial port opens. * @return Return the status of the operation. */ -static rt_err_t rt_serial_rx_enable(struct rt_device *dev, - rt_uint16_t rx_oflag) +static rt_err_t rt_serial_rx_enable(struct rt_device *dev, + rt_uint16_t rx_oflag) { - struct rt_serial_device *serial; - struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + struct rt_serial_device *serial; + struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + rt_size_t rx_fifo_size = 0; RT_ASSERT(dev != RT_NULL); serial = (struct rt_serial_device *)dev; @@ -766,7 +959,7 @@ static rt_err_t rt_serial_rx_enable(struct rt_device *dev, if (rx_oflag == RT_SERIAL_RX_NON_BLOCKING) { LOG_E("(%s) serial device with misconfigure: rx_bufsz = 0", - dev->parent.name); + dev->parent.name); return -RT_EINVAL; } @@ -781,11 +974,30 @@ static rt_err_t rt_serial_rx_enable(struct rt_device *dev, if (serial->config.rx_bufsz < RT_SERIAL_RX_MINBUFSZ) serial->config.rx_bufsz = RT_SERIAL_RX_MINBUFSZ; - rx_fifo = (struct rt_serial_rx_fifo *) rt_malloc - (sizeof(struct rt_serial_rx_fifo) + serial->config.rx_bufsz); +#ifdef RT_SERIAL_USING_DMA + if (serial->config.dma_ping_bufsz > 0 && serial->config.dma_ping_bufsz < RT_SERIAL_RX_MINBUFSZ / 2) + serial->config.dma_ping_bufsz = RT_SERIAL_RX_MINBUFSZ / 2; + rx_fifo_size = sizeof(struct rt_serial_rx_fifo) + serial->config.rx_bufsz + serial->config.dma_ping_bufsz; +#else + rx_fifo_size = sizeof(struct rt_serial_rx_fifo) + serial->config.rx_bufsz; +#endif + rx_fifo = (struct rt_serial_rx_fifo *)rt_malloc(rx_fifo_size); RT_ASSERT(rx_fifo != RT_NULL); - rt_ringbuffer_init(&(rx_fifo->rb), rx_fifo->buffer, serial->config.rx_bufsz); + rt_memset(rx_fifo, RT_NULL, rx_fifo_size); + + rt_ringbuffer_init(&rx_fifo->rb, + (rt_uint8_t *)rx_fifo + sizeof(struct rt_serial_rx_fifo), + serial->config.rx_bufsz); + +#ifdef RT_SERIAL_USING_DMA + if (serial->config.dma_ping_bufsz > 0) + { + rt_ringbuffer_init(&rx_fifo->dma_ping_rb, + (rt_uint8_t *)rx_fifo + sizeof(struct rt_serial_rx_fifo) + serial->config.rx_bufsz, + serial->config.dma_ping_bufsz); + } +#endif serial->serial_rx = rx_fifo; @@ -798,19 +1010,22 @@ static rt_err_t rt_serial_rx_enable(struct rt_device *dev, dev->open_flag |= RT_SERIAL_RX_NON_BLOCKING; /* Call the control() API to configure the serial device by RT_SERIAL_RX_NON_BLOCKING*/ serial->ops->control(serial, - RT_DEVICE_CTRL_CONFIG, - (void *) RT_SERIAL_RX_NON_BLOCKING); + RT_DEVICE_CTRL_CONFIG, + (void *)RT_SERIAL_RX_NON_BLOCKING); return RT_EOK; } + /* When using RT_SERIAL_RX_BLOCKING, rt_completion_init() and rx_cpt_index are initialized */ rx_fifo->rx_cpt_index = 0; - rt_completion_init(&(rx_fifo->rx_cpt)); + rt_atomic_store(&rx_fifo->rx_timeout, RT_WAITING_FOREVER); + + rt_completion_init(&rx_fifo->rx_cpt); dev->open_flag |= RT_SERIAL_RX_BLOCKING; /* Call the control() API to configure the serial device by RT_SERIAL_RX_BLOCKING*/ serial->ops->control(serial, - RT_DEVICE_CTRL_CONFIG, - (void *) RT_SERIAL_RX_BLOCKING); + RT_DEVICE_CTRL_CONFIG, + (void *)RT_SERIAL_RX_BLOCKING); return RT_EOK; } @@ -821,10 +1036,10 @@ static rt_err_t rt_serial_rx_enable(struct rt_device *dev, * @param rx_oflag The flag of that the serial port opens. * @return Return the status of the operation. */ -static rt_err_t rt_serial_rx_disable(struct rt_device *dev, - rt_uint16_t rx_oflag) +static rt_err_t rt_serial_rx_disable(struct rt_device *dev, + rt_uint16_t rx_oflag) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_rx_fifo *rx_fifo; RT_ASSERT(dev != RT_NULL); @@ -836,22 +1051,20 @@ static rt_err_t rt_serial_rx_disable(struct rt_device *dev, if (serial->serial_rx == RT_NULL) return RT_EOK; - do + if (rx_oflag == RT_SERIAL_RX_NON_BLOCKING) { - if (rx_oflag == RT_SERIAL_RX_NON_BLOCKING) - { - dev->open_flag &= ~ RT_SERIAL_RX_NON_BLOCKING; - serial->ops->control(serial, - RT_DEVICE_CTRL_CLR_INT, - (void *)RT_SERIAL_RX_NON_BLOCKING); - break; - } - - dev->open_flag &= ~ RT_SERIAL_RX_BLOCKING; + dev->open_flag &= ~RT_SERIAL_RX_NON_BLOCKING; serial->ops->control(serial, - RT_DEVICE_CTRL_CLR_INT, - (void *)RT_SERIAL_RX_BLOCKING); - } while (0); + RT_DEVICE_CTRL_CLR_INT, + (void *)RT_SERIAL_RX_NON_BLOCKING); + } + else + { + dev->open_flag &= ~RT_SERIAL_RX_BLOCKING; + serial->ops->control(serial, + RT_DEVICE_CTRL_CLR_INT, + (void *)RT_SERIAL_RX_BLOCKING); + } rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; RT_ASSERT(rx_fifo != RT_NULL); @@ -867,10 +1080,10 @@ static rt_err_t rt_serial_rx_disable(struct rt_device *dev, * @param rx_oflag The flag of that the serial port opens. * @return Return the status of the operation. */ -static rt_err_t rt_serial_tx_disable(struct rt_device *dev, - rt_uint16_t tx_oflag) +static rt_err_t rt_serial_tx_disable(struct rt_device *dev, + rt_uint16_t tx_oflag) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_tx_fifo *tx_fifo; RT_ASSERT(dev != RT_NULL); @@ -885,30 +1098,26 @@ static rt_err_t rt_serial_tx_disable(struct rt_device *dev, tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; RT_ASSERT(tx_fifo != RT_NULL); - do + if (tx_oflag == RT_SERIAL_TX_NON_BLOCKING) { - if (tx_oflag == RT_SERIAL_TX_NON_BLOCKING) - { - dev->open_flag &= ~ RT_SERIAL_TX_NON_BLOCKING; - - serial->ops->control(serial, - RT_DEVICE_CTRL_CLR_INT, - (void *)RT_SERIAL_TX_NON_BLOCKING); - break; - } + dev->open_flag &= ~RT_SERIAL_TX_NON_BLOCKING; - rt_completion_done(&(tx_fifo->tx_cpt)); - dev->open_flag &= ~ RT_SERIAL_TX_BLOCKING; serial->ops->control(serial, - RT_DEVICE_CTRL_CLR_INT, - (void *)RT_SERIAL_TX_BLOCKING); - } while (0); + RT_DEVICE_CTRL_CLR_INT, + (void *)RT_SERIAL_TX_NON_BLOCKING); + } + else + { + rt_completion_done(&tx_fifo->tx_cpt); + dev->open_flag &= ~RT_SERIAL_TX_BLOCKING; + serial->ops->control(serial, + RT_DEVICE_CTRL_CLR_INT, + (void *)RT_SERIAL_TX_BLOCKING); + } + rt_free(tx_fifo); serial->serial_tx = RT_NULL; - - rt_memset(&serial->rx_notify, 0, sizeof(struct rt_device_notify)); - return RT_EOK; } @@ -919,7 +1128,7 @@ static rt_err_t rt_serial_tx_disable(struct rt_device *dev, */ static rt_err_t rt_serial_init(struct rt_device *dev) { - rt_err_t result = RT_EOK; + rt_err_t result = RT_EOK; struct rt_serial_device *serial; RT_ASSERT(dev != RT_NULL); @@ -958,7 +1167,7 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) } LOG_D("open serial device: 0x%08x with open flag: 0x%04x", - dev, oflag); + dev, oflag); /* By default, the receive mode of a serial devide is RT_SERIAL_RX_NON_BLOCKING */ if ((oflag & RT_SERIAL_RX_BLOCKING) == RT_SERIAL_RX_BLOCKING) @@ -973,19 +1182,16 @@ static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) dev->open_flag |= RT_SERIAL_TX_BLOCKING; /* set steam flag */ - if ((oflag & RT_DEVICE_FLAG_STREAM) || - (dev->open_flag & RT_DEVICE_FLAG_STREAM)) + if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM)) dev->open_flag |= RT_DEVICE_FLAG_STREAM; /* initialize the Rx structure according to open flag */ if (serial->serial_rx == RT_NULL) - rt_serial_rx_enable(dev, dev->open_flag & - (RT_SERIAL_RX_BLOCKING | RT_SERIAL_RX_NON_BLOCKING)); + rt_serial_rx_enable(dev, dev->open_flag & (RT_SERIAL_RX_BLOCKING | RT_SERIAL_RX_NON_BLOCKING)); /* initialize the Tx structure according to open flag */ if (serial->serial_tx == RT_NULL) - rt_serial_tx_enable(dev, dev->open_flag & - (RT_SERIAL_TX_BLOCKING | RT_SERIAL_TX_NON_BLOCKING)); + rt_serial_tx_enable(dev, dev->open_flag & (RT_SERIAL_TX_BLOCKING | RT_SERIAL_TX_NON_BLOCKING)); return RT_EOK; } @@ -1006,16 +1212,16 @@ static rt_err_t rt_serial_close(struct rt_device *dev) /* this device has more reference count */ if (dev->ref_count > 1) return -RT_ERROR; /* Disable serial receive mode. */ - rt_serial_rx_disable(dev, dev->open_flag & - (RT_SERIAL_RX_BLOCKING | RT_SERIAL_RX_NON_BLOCKING)); + rt_serial_rx_disable(dev, dev->open_flag & (RT_SERIAL_RX_BLOCKING | RT_SERIAL_RX_NON_BLOCKING)); /* Disable serial tranmit mode. */ - rt_serial_tx_disable(dev, dev->open_flag & - (RT_SERIAL_TX_BLOCKING | RT_SERIAL_TX_NON_BLOCKING)); + rt_serial_tx_disable(dev, dev->open_flag & (RT_SERIAL_TX_BLOCKING | RT_SERIAL_TX_NON_BLOCKING)); /* Clear the callback function */ serial->parent.rx_indicate = RT_NULL; serial->parent.tx_complete = RT_NULL; + rt_memset(&serial->rx_notify, RT_NULL, sizeof(struct rt_device_notify)); + /* Call the control() API to close the serial device */ serial->ops->control(serial, RT_DEVICE_CTRL_CLOSE, RT_NULL); dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED; @@ -1023,35 +1229,108 @@ static rt_err_t rt_serial_close(struct rt_device *dev) return RT_EOK; } +static void _serial_rx_flush(struct rt_serial_device *serial) +{ + rt_base_t level; + struct rt_serial_rx_fifo *rx_fifo; + RT_ASSERT(serial != RT_NULL); + + if (serial->config.rx_bufsz == 0) + { + while (serial->ops->getc(serial) != -1) + { + } + } + else + { + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + level = rt_spin_lock_irqsave(&serial->spinlock); + rt_completion_wait(&rx_fifo->rx_cpt, 0); + rt_ringbuffer_reset(&rx_fifo->rb); + rx_fifo->rx_cpt_index = 0; +#ifdef RT_SERIAL_USING_DMA + if (serial->config.dma_ping_bufsz > 0) + { + rt_ringbuffer_reset(&rx_fifo->dma_ping_rb); + } +#endif + rt_spin_unlock_irqrestore(&serial->spinlock, level); + } +} + +static void _serial_tx_flush(struct rt_serial_device *serial) +{ + struct rt_serial_tx_fifo *tx_fifo; + RT_ASSERT(serial != RT_NULL); + + if (serial->config.tx_bufsz != 0) + { + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); + + if (rt_atomic_load(&tx_fifo->activated)) + { + rt_completion_wait(&tx_fifo->tx_cpt, RT_WAITING_FOREVER); + return; + } + } +} + +static rt_err_t _serial_get_unread_bytes_count(struct rt_serial_device *serial, rt_ssize_t *unread_bytes) +{ + rt_base_t level; + struct rt_serial_rx_fifo *rx_fifo; + RT_ASSERT(serial != RT_NULL); + + if (serial->config.rx_bufsz == 0) + { + LOG_W("get unread bytes count not support in poll mode."); + *unread_bytes = -1; + return -RT_EPERM; + } + else + { + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + level = RT_SERIAL_FIFO_LOCK(&serial->spinlock); + *unread_bytes = rt_ringbuffer_data_len(&rx_fifo->rb); + RT_SERIAL_FIFO_UNLOCK(&serial->spinlock, level); + } + return RT_EOK; +} + #ifdef RT_USING_POSIX_TERMIOS struct speed_baudrate_item { speed_t speed; - int baudrate; + 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}, - {B500000, BAUD_RATE_500000}, - {B921600, BAUD_RATE_921600}, - {B2000000, BAUD_RATE_2000000}, - {B3000000, BAUD_RATE_3000000}, + { + { 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}, + { B500000, BAUD_RATE_500000}, + { 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 ++) + for (index = 0; index < sizeof(_tbl) / sizeof(_tbl[0]); index++) { if (_tbl[index].baudrate == baudrate) return _tbl[index].speed; @@ -1064,7 +1343,7 @@ static int _get_baudrate(speed_t speed) { int index; - for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++) + for (index = 0; index < sizeof(_tbl) / sizeof(_tbl[0]); index++) { if (_tbl[index].speed == speed) return _tbl[index].baudrate; @@ -1075,44 +1354,17 @@ static int _get_baudrate(speed_t speed) static void _tc_flush(struct rt_serial_device *serial, int queue) { - rt_base_t level; - int ch = -1; - struct rt_serial_rx_fifo *rx_fifo = RT_NULL; - struct rt_device *device = RT_NULL; - RT_ASSERT(serial != RT_NULL); - device = &(serial->parent); - rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; - - switch(queue) + if (queue == TCIFLUSH || queue == TCIOFLUSH) { - 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->rx_cpt_index = 0; - rt_hw_interrupt_enable(level); - } - else - { - while (1) - { - ch = serial->ops->getc(serial); - if (ch == -1) break; - } - } - - break; - - case TCOFLUSH: - break; + _serial_rx_flush(serial); } + if (queue == TCOFLUSH || queue == TCIOFLUSH) + { + _serial_tx_flush(serial); + } } #endif /* RT_USING_POSIX_TERMIOS */ @@ -1127,7 +1379,7 @@ static rt_err_t rt_serial_control(struct rt_device *dev, int cmd, void *args) { - rt_err_t ret = RT_EOK; + rt_err_t ret = RT_EOK; struct rt_serial_device *serial; RT_ASSERT(dev != RT_NULL); @@ -1135,254 +1387,381 @@ static rt_err_t rt_serial_control(struct rt_device *dev, switch (cmd) { - case RT_DEVICE_CTRL_SUSPEND: - /* suspend device */ - dev->flag |= RT_DEVICE_FLAG_SUSPENDED; - break; + 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_RESUME: + /* resume device */ + dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; + break; + + case RT_DEVICE_CTRL_CONFIG: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + struct serial_configure *pconfig = (struct serial_configure *)args; + if (((pconfig->rx_bufsz != serial->config.rx_bufsz) + || (pconfig->tx_bufsz != serial->config.tx_bufsz) +#ifdef RT_SERIAL_USING_DMA + || (pconfig->dma_ping_bufsz != serial->config.dma_ping_bufsz) +#endif + ) + && serial->parent.ref_count != 0) + { + /*can not change buffer size*/ + ret = -RT_EBUSY; + break; + } + /* set serial configure */ + serial->config = *pconfig; + serial->ops->configure(serial, (struct serial_configure *)args); + } + break; + case RT_DEVICE_CTRL_NOTIFY_SET: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify)); + } + break; + + case RT_DEVICE_CTRL_CONSOLE_OFLAG: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + *(rt_uint16_t *)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM; + } + break; - case RT_DEVICE_CTRL_CONFIG: - if (args != RT_NULL) + /* Call before rt_device_read */ + case RT_SERIAL_CTRL_SET_RX_TIMEOUT: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + if (serial->config.rx_bufsz == 0) { - struct serial_configure *pconfig = (struct serial_configure *) args; - if (((pconfig->rx_bufsz != serial->config.rx_bufsz) || (pconfig->tx_bufsz != serial->config.tx_bufsz)) - && serial->parent.ref_count) - { - /*can not change buffer size*/ - return -RT_EBUSY; - } - /* set serial configure */ - serial->config = *pconfig; - serial->ops->configure(serial, (struct serial_configure *) args); + ret = -RT_EPERM; } - break; - case RT_DEVICE_CTRL_NOTIFY_SET: - if (args) + + struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_atomic_store(&rx_fifo->rx_timeout, *(rt_int32_t *)args); + } + break; + + /* Call before rt_device_write */ + case RT_SERIAL_CTRL_SET_TX_TIMEOUT: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + if (serial->config.tx_bufsz == 0) { - rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify)); + ret = -RT_EPERM; } - break; - case RT_DEVICE_CTRL_CONSOLE_OFLAG: - if (args) + struct rt_serial_tx_fifo *tx_fifo = RT_NULL; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); + + rt_atomic_store(&tx_fifo->tx_timeout, *(rt_int32_t *)args); + } + break; + + case RT_SERIAL_CTRL_GET_RX_TIMEOUT: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + if (serial->config.rx_bufsz == 0) { - *(rt_uint16_t*)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM; + ret = -RT_EPERM; } - break; -#ifdef RT_USING_POSIX_STDIO -#ifdef RT_USING_POSIX_TERMIOS - case TCGETA: + + struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + *(rt_int32_t *)args = rt_atomic_load(&rx_fifo->rx_timeout); + } + break; + + case RT_SERIAL_CTRL_GET_TX_TIMEOUT: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + if (serial->config.tx_bufsz == 0) { - struct termios *tio = (struct termios*)args; - if (tio == RT_NULL) return -RT_EINVAL; + ret = -RT_EPERM; + } - tio->c_iflag = 0; - tio->c_oflag = 0; - tio->c_lflag = 0; + struct rt_serial_tx_fifo *tx_fifo = RT_NULL; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); - /* update oflag for console device */ - if (rt_console_get_device() == dev) - tio->c_oflag = OPOST | ONLCR; + *(rt_int32_t *)args = rt_atomic_load(&tx_fifo->tx_timeout); + } + break; - /* 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; + /* Discard all data */ + case RT_SERIAL_CTRL_RX_FLUSH: + _serial_rx_flush(serial); + break; - if (serial->config.stop_bits == STOP_BITS_2) - tio->c_cflag |= CSTOPB; + /* Blocking and wait for the send buffer data to be sent. */ + case RT_SERIAL_CTRL_TX_FLUSH: + _serial_tx_flush(serial); + break; - if (serial->config.parity == PARITY_EVEN) - tio->c_cflag |= PARENB; - else if (serial->config.parity == PARITY_ODD) - tio->c_cflag |= (PARODD | PARENB); + /* get unread bytes count. */ + case RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + ret = _serial_get_unread_bytes_count(serial, (rt_ssize_t *)args); + } + break; - if (serial->config.flowcontrol == RT_SERIAL_FLOWCONTROL_CTSRTS) - tio->c_cflag |= CRTSCTS; +#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; - cfsetospeed(tio, _get_speed(serial->config.baud_rate)); - } - break; + tio->c_iflag = 0; + tio->c_oflag = 0; + tio->c_lflag = 0; - case TCSETAW: - case TCSETAF: - case TCSETA: - { - int baudrate; - struct serial_configure config; + /* update oflag for console device */ + if (rt_console_get_device() == dev) + tio->c_oflag = OPOST | ONLCR; - struct termios *tio = (struct termios*)args; - if (tio == RT_NULL) return -RT_EINVAL; + /* 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; - config = serial->config; + if (serial->config.stop_bits == STOP_BITS_2) + tio->c_cflag |= CSTOPB; - baudrate = _get_baudrate(cfgetospeed(tio)); - config.baud_rate = baudrate; + if (serial->config.parity == PARITY_EVEN) + tio->c_cflag |= PARENB; + else if (serial->config.parity == PARITY_ODD) + tio->c_cflag |= (PARODD | PARENB); - 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 (serial->config.flowcontrol == RT_SERIAL_FLOWCONTROL_CTSRTS) + tio->c_cflag |= CRTSCTS; - if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2; - else config.stop_bits = STOP_BITS_1; + cfsetospeed(tio, _get_speed(serial->config.baud_rate)); + } + break; - if (tio->c_cflag & PARENB) - { - if (tio->c_cflag & PARODD) config.parity = PARITY_ODD; - else config.parity = PARITY_EVEN; - } - else config.parity = PARITY_NONE; + case TCSETAW: + case TCSETAF: + case TCSETA: { + int baudrate; + struct serial_configure config; - if (tio->c_cflag & CRTSCTS) config.flowcontrol = RT_SERIAL_FLOWCONTROL_CTSRTS; - else config.flowcontrol = RT_SERIAL_FLOWCONTROL_NONE; + struct termios *tio = (struct termios *)args; + if (tio == RT_NULL) return -RT_EINVAL; - /* set serial configure */ - serial->config = config; - serial->ops->configure(serial, &config); - } - break; - case TCFLSH: - { - int queue = (int)args; + config = serial->config; - _tc_flush(serial, queue); - } + baudrate = _get_baudrate(cfgetospeed(tio)); + config.baud_rate = baudrate; + switch (tio->c_cflag & CSIZE) + { + case CS5: + config.data_bits = DATA_BITS_5; break; - case TCXONC: + case CS6: + config.data_bits = DATA_BITS_6; 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); - } + case CS7: + config.data_bits = DATA_BITS_7; break; - case TIOCGWINSZ: - { - struct winsize* p_winsize; - p_winsize = (struct winsize*)args; + default: + config.data_bits = DATA_BITS_8; + break; + } - if(rt_thread_self() != rt_thread_find(FINSH_THREAD_NAME)) - { - /* only can be used in tshell thread; otherwise, return default size */ - p_winsize->ws_col = 80; - p_winsize->ws_row = 24; - } - else - { - #include <shell.h> - #define _TIO_BUFLEN 20 - char _tio_buf[_TIO_BUFLEN]; - unsigned char cnt1, cnt2, cnt3, i; - char row_s[4], col_s[4]; - char *p; + if (tio->c_cflag & CSTOPB) + config.stop_bits = STOP_BITS_2; + else + config.stop_bits = STOP_BITS_1; - rt_memset(_tio_buf, 0, _TIO_BUFLEN); + if (tio->c_cflag & PARENB) + { + if (tio->c_cflag & PARODD) + config.parity = PARITY_ODD; + else + config.parity = PARITY_EVEN; + } + else + config.parity = PARITY_NONE; - /* send the command to terminal for getting the window size of the terminal */ - rt_kprintf("\033[18t"); + if (tio->c_cflag & CRTSCTS) + config.flowcontrol = RT_SERIAL_FLOWCONTROL_CTSRTS; + else + config.flowcontrol = RT_SERIAL_FLOWCONTROL_NONE; - /* waiting for the response from the terminal */ - i = 0; - while(i < _TIO_BUFLEN) - { - _tio_buf[i] = finsh_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; - } + /* set serial configure */ + serial->config = config; + serial->ops->configure(serial, &config); + } + break; + case TCFLSH: { + int queue = (int)args; - /* 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(cnt1 < _TIO_BUFLEN && _tio_buf[cnt1] != ';') - { - cnt1++; - } - cnt2 = ++cnt1; - while(cnt2 < _TIO_BUFLEN && _tio_buf[cnt2] != ';') - { - 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++]; - } + _tc_flush(serial, queue); + } + + break; + case TCXONC: + break; +#endif /*RT_USING_POSIX_TERMIOS*/ + case TIOCSWINSZ: { + struct winsize *p_winsize; - /* load the window size date */ - p_winsize->ws_col = atoi(col_s); - p_winsize->ws_row = atoi(row_s); - #undef _TIO_BUFLEN + 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(FINSH_THREAD_NAME)) + { + /* only can be used in tshell thread; otherwise, return default size */ + p_winsize->ws_col = 80; + p_winsize->ws_row = 24; + } + else + { +#include <shell.h> +#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] = finsh_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; + } - p_winsize->ws_xpixel = 0;/* unused */ - p_winsize->ws_ypixel = 0;/* unused */ + /* 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 (cnt1 < _TIO_BUFLEN && _tio_buf[cnt1] != ';') + { + cnt1++; } - break; - case FIONREAD: + cnt2 = ++cnt1; + while (cnt2 < _TIO_BUFLEN && _tio_buf[cnt2] != ';') + { + cnt2++; + } + p = row_s; + while (cnt1 < cnt2) + { + *p++ = _tio_buf[cnt1++]; + } + p = col_s; + cnt2++; + cnt3 = rt_strlen(_tio_buf) - 1; + while (cnt2 < cnt3) { - rt_size_t recved = 0; - rt_base_t level; - struct rt_serial_rx_fifo * rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + *p++ = _tio_buf[cnt2++]; + } - level = rt_hw_interrupt_disable(); - recved = rt_ringbuffer_data_len(&(rx_fifo->rb)); - rt_hw_interrupt_enable(level); + /* load the window size date */ + p_winsize->ws_col = atoi(col_s); + p_winsize->ws_row = atoi(row_s); +#undef _TIO_BUFLEN + } - *(rt_size_t *)args = recved; - } - break; + p_winsize->ws_xpixel = 0; /* unused */ + p_winsize->ws_ypixel = 0; /* unused */ + } + break; + case FIONREAD: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + rt_ssize_t unread_bytes = 0; + ret = _serial_get_unread_bytes_count(serial, &unread_bytes); + if (ret == RT_EOK) + *(rt_size_t *)args = (rt_size_t)unread_bytes; + else + *(rt_size_t *)args = 0; + } + break; #endif /* RT_USING_POSIX_STDIO */ - default : - /* control device */ - ret = serial->ops->control(serial, cmd, args); - break; + default: + /* control device */ + ret = serial->ops->control(serial, cmd, args); + break; } return ret; @@ -1390,9 +1769,9 @@ static rt_err_t rt_serial_control(struct rt_device *dev, #ifdef RT_USING_DEVICE_OPS static rt_ssize_t rt_serial_read(struct rt_device *dev, - rt_off_t pos, - void *buffer, - rt_size_t size) + rt_off_t pos, + void *buffer, + rt_size_t size) { struct rt_serial_device *serial; @@ -1411,11 +1790,11 @@ static rt_ssize_t rt_serial_read(struct rt_device *dev, static rt_ssize_t rt_serial_write(struct rt_device *dev, - rt_off_t pos, - const void *buffer, - rt_size_t size) + rt_off_t pos, + const void *buffer, + rt_size_t size) { - struct rt_serial_device *serial; + struct rt_serial_device *serial; struct rt_serial_tx_fifo *tx_fifo; RT_ASSERT(dev != RT_NULL); @@ -1423,7 +1802,7 @@ static rt_ssize_t rt_serial_write(struct rt_device *dev, serial = (struct rt_serial_device *)dev; RT_ASSERT((serial != RT_NULL) && (buffer != RT_NULL)); - tx_fifo = (struct rt_serial_tx_fifo *) serial->serial_tx; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; if (serial->config.tx_bufsz == 0) { @@ -1432,6 +1811,7 @@ static rt_ssize_t rt_serial_write(struct rt_device *dev, if (dev->open_flag & RT_SERIAL_TX_BLOCKING) { + RT_ASSERT(tx_fifo != RT_NULL); if ((tx_fifo->rb.buffer_ptr) == RT_NULL) { return _serial_fifo_tx_blocking_nbuf(dev, pos, buffer, size); @@ -1444,14 +1824,13 @@ static rt_ssize_t rt_serial_write(struct rt_device *dev, } 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_init, + rt_serial_open, + rt_serial_close, + rt_serial_read, + rt_serial_write, + rt_serial_control}; #endif /** @@ -1467,34 +1846,36 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, rt_uint32_t flag, void *data) { - rt_err_t ret; + rt_err_t ret; struct rt_device *device; RT_ASSERT(serial != RT_NULL); - device = &(serial->parent); + rt_spin_lock_init(&serial->spinlock); + + 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; + device->ops = &serial_ops; #else - device->init = rt_serial_init; - device->open = rt_serial_open; - device->close = rt_serial_close; - device->read = RT_NULL; - device->write = RT_NULL; - device->control = rt_serial_control; + device->init = rt_serial_init; + device->open = rt_serial_open; + device->close = rt_serial_close; + device->read = RT_NULL; + device->write = RT_NULL; + device->control = rt_serial_control; #endif - device->user_data = data; + device->user_data = data; /* register a character device */ ret = rt_device_register(device, name, flag); #ifdef RT_USING_POSIX_STDIO /* set fops */ - device->fops = &_serial_fops; + device->fops = &_serial_fops; #endif return ret; } @@ -1504,139 +1885,266 @@ rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, * @param serial RT-thread serial device. * @param event ISR event type. */ -void rt_hw_serial_isr(struct rt_serial_device *serial, int event) +rt_err_t rt_hw_serial_control_isr(struct rt_serial_device *serial, int cmd, void *args) { RT_ASSERT(serial != RT_NULL); + rt_err_t ret = RT_EOK; - switch (event & 0xff) + switch (cmd) { - /* Interrupt receive event */ - case RT_SERIAL_EVENT_RX_IND: - case RT_SERIAL_EVENT_RX_DMADONE: + case RT_HW_SERIAL_CTRL_PUTC: + if (args == RT_NULL) { - struct rt_serial_rx_fifo *rx_fifo; - rt_size_t rx_length = 0; - rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; - rt_base_t level; + ret = -RT_EINVAL; + } + else + { + struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; RT_ASSERT(rx_fifo != RT_NULL); - /* If the event is RT_SERIAL_EVENT_RX_IND, rx_length is equal to 0 */ - rx_length = (event & (~0xff)) >> 8; +#ifdef RT_SERIAL_BUF_STRATEGY_DROP + rt_ringbuffer_putchar(&rx_fifo->rb, *(rt_uint8_t *)args); +#else + rt_ringbuffer_putchar_force(&rx_fifo->rb, *(rt_uint8_t *)args); +#endif /* RT_SERIAL_BUF_STRATEGY_DROP */ + } + break; - if (rx_length) - { /* RT_SERIAL_EVENT_RX_DMADONE MODE */ - level = rt_hw_interrupt_disable(); - rt_serial_update_write_index(&(rx_fifo->rb), rx_length); - rt_hw_interrupt_enable(level); + case RT_HW_SERIAL_CTRL_GETC: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + struct rt_serial_tx_fifo *tx_fifo = RT_NULL; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); + if (rt_ringbuffer_getchar(&tx_fifo->rb, (rt_uint8_t *)args) == 0) + { + ret = -RT_EEMPTY; } + } + break; - /* Get the length of the data from the ringbuffer */ - rx_length = rt_ringbuffer_data_len(&rx_fifo->rb); - if (rx_length == 0) break; +#ifdef RT_SERIAL_USING_DMA + case RT_HW_SERIAL_CTRL_GET_DMA_PING_BUF: + if (args == RT_NULL) + { + ret = -RT_EINVAL; + } + else + { + struct rt_serial_rx_fifo *rx_fifo = RT_NULL; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + *(rt_uint8_t **)args = rx_fifo->dma_ping_rb.buffer_ptr; + } + break; +#endif + + default: + ret = -RT_EINVAL; + break; + } + + return ret; +} + +/** + * @brief ISR for serial interrupt + * @param serial RT-thread serial device. + * @param event ISR event type. + */ +void rt_hw_serial_isr(struct rt_serial_device *serial, int event) +{ + RT_ASSERT(serial != RT_NULL); + + switch (event & 0xff) + { + /* Interrupt receive event */ + case RT_SERIAL_EVENT_RX_IND: + case RT_SERIAL_EVENT_RX_DMADONE: { + struct rt_serial_rx_fifo *rx_fifo; + rt_size_t rx_length = 0; + rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); - if (serial->parent.open_flag & RT_SERIAL_RX_BLOCKING) +#ifdef RT_SERIAL_USING_DMA + rt_base_t level; + /* If the event is RT_SERIAL_EVENT_RX_IND, rx_length is equal to 0 */ + rx_length = event >> 8; + + /* RT_SERIAL_EVENT_RX_DMADONE MODE */ + if (rx_length != 0) + { +#ifdef RT_SERIAL_BUF_STRATEGY_DROP + rt_uint8_t *ptr; + rt_size_t size; + rt_size_t space_len; + /* UART_IT_IDLE and dma isr */ + level = rt_spin_lock_irqsave(&serial->spinlock); + rt_serial_update_write_index(&rx_fifo->dma_ping_rb, rx_length); + do { - if (rx_fifo->rx_cpt_index && rx_length >= rx_fifo->rx_cpt_index ) - { - rx_fifo->rx_cpt_index = 0; - rt_completion_done(&(rx_fifo->rx_cpt)); - } - } - /* Trigger the receiving completion callback */ - if (serial->parent.rx_indicate != RT_NULL) - serial->parent.rx_indicate(&(serial->parent), rx_length); + space_len = rt_ringbuffer_space_len(&rx_fifo->rb); + if (space_len == 0) + break; - if (serial->rx_notify.notify) + size = rt_ringbuffer_peek(&rx_fifo->dma_ping_rb, &ptr); + if (size == 0) + break; + + space_len -= rt_ringbuffer_put(&rx_fifo->rb, ptr, size); + if (space_len == 0) + break; + + size = rt_ringbuffer_peek(&rx_fifo->dma_ping_rb, &ptr); + if (size == 0) + break; + + rt_ringbuffer_put(&rx_fifo->rb, ptr, size); + } while (0); + if (space_len == 0) + rt_serial_update_read_index(&rx_fifo->dma_ping_rb, rt_ringbuffer_get_size(&rx_fifo->dma_ping_rb)); + rt_spin_unlock_irqrestore(&serial->spinlock, level); +#else + rt_uint8_t *ptr; + rt_size_t size; + /* UART_IT_IDLE and dma isr */ + level = rt_spin_lock_irqsave(&serial->spinlock); + rt_serial_update_write_index(&rx_fifo->dma_ping_rb, rx_length); + do { - serial->rx_notify.notify(serial->rx_notify.dev); - } - break; + size = rt_ringbuffer_peek(&rx_fifo->dma_ping_rb, &ptr); + if (size == 0) + break; + + rt_ringbuffer_put_force(&rx_fifo->rb, ptr, size); + + size = rt_ringbuffer_peek(&rx_fifo->dma_ping_rb, &ptr); + if (size == 0) + break; + + rt_ringbuffer_put_force(&rx_fifo->rb, ptr, size); + } while (0); + rt_spin_unlock_irqrestore(&serial->spinlock, level); +#endif /* RT_SERIAL_BUF_STRATEGY_DROP */ } +#endif /* RT_SERIAL_USING_DMA */ - /* Interrupt transmit event */ - case RT_SERIAL_EVENT_TX_DONE: + rx_length = rt_ringbuffer_data_len(&rx_fifo->rb); + if (rx_length == 0) { - struct rt_serial_tx_fifo *tx_fifo; - rt_size_t tx_length = 0; - tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; - RT_ASSERT(tx_fifo != RT_NULL); + break; + } - /* Get the length of the data from the ringbuffer */ - tx_length = rt_ringbuffer_data_len(&tx_fifo->rb); - /* If there is no data in tx_ringbuffer, - * then the transmit completion callback is triggered*/ - if (tx_length == 0) + if (serial->parent.open_flag & RT_SERIAL_RX_BLOCKING) + { + if (rx_fifo->rx_cpt_index && rx_length >= rx_fifo->rx_cpt_index) { - /* Trigger the transmit completion callback */ - if (serial->parent.tx_complete != RT_NULL) - serial->parent.tx_complete(&serial->parent, RT_NULL); - - /* Maybe some datas left in the buffer still need to be sent in block mode, - * so tx_fifo->activated should be RT_TRUE */ - if (serial->parent.open_flag & RT_SERIAL_TX_BLOCKING) - { - rt_completion_done(&(tx_fifo->tx_cpt)); - } - else - { - tx_fifo->activated = RT_FALSE; - } - - break; + rx_fifo->rx_cpt_index = 0; + rt_completion_done(&rx_fifo->rx_cpt); } + } - /* Call the transmit interface for transmission again */ - /* Note that in interrupt mode, tx_fifo->buffer and tx_length - * are inactive parameters */ - serial->ops->transmit(serial, - tx_fifo->buffer, - tx_length, - serial->parent.open_flag & ( \ - RT_SERIAL_TX_BLOCKING | \ - RT_SERIAL_TX_NON_BLOCKING)); - break; + /* Trigger the receiving completion callback */ + if (serial->parent.rx_indicate != RT_NULL) + { + serial->parent.rx_indicate(&serial->parent, rx_length); } - case RT_SERIAL_EVENT_TX_DMADONE: + if (serial->rx_notify.notify != RT_NULL) { - struct rt_serial_tx_fifo *tx_fifo; - tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; - RT_ASSERT(tx_fifo != RT_NULL); + serial->rx_notify.notify(serial->rx_notify.dev); + } + break; + } - tx_fifo->activated = RT_FALSE; + /* Interrupt transmit event */ + case RT_SERIAL_EVENT_TX_DONE: { + struct rt_serial_tx_fifo *tx_fifo; + rt_size_t tx_length = 0; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); + + /* Get the length of the data from the ringbuffer */ + tx_length = rt_ringbuffer_data_len(&tx_fifo->rb); + + /* If there is no data in tx_ringbuffer, + * then the transmit completion callback is triggered*/ + if (tx_length == 0) + { + rt_completion_done(&tx_fifo->tx_cpt); /* Trigger the transmit completion callback */ if (serial->parent.tx_complete != RT_NULL) - serial->parent.tx_complete(&serial->parent, RT_NULL); - - if (serial->parent.open_flag & RT_SERIAL_TX_BLOCKING) { - rt_completion_done(&(tx_fifo->tx_cpt)); - break; + serial->parent.tx_complete(&serial->parent, RT_NULL); } + rt_atomic_flag_clear(&tx_fifo->activated); + break; + } + + rt_atomic_flag_test_and_set(&tx_fifo->activated); + /* Call the transmit interface for transmission again */ + /* Note that in interrupt mode, tx_fifo->buffer and tx_length + * are inactive parameters */ + serial->ops->transmit(serial, + tx_fifo->rb.buffer_ptr, + tx_length, + serial->parent.open_flag & (RT_SERIAL_TX_BLOCKING | RT_SERIAL_TX_NON_BLOCKING)); + break; + } + +#ifdef RT_SERIAL_USING_DMA + case RT_SERIAL_EVENT_TX_DMADONE: { + struct rt_serial_tx_fifo *tx_fifo; + rt_size_t tx_length = 0; + tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx; + RT_ASSERT(tx_fifo != RT_NULL); + if ((serial->parent.open_flag & RT_SERIAL_TX_BLOCKING) != RT_SERIAL_TX_BLOCKING || rt_ringbuffer_get_size(&tx_fifo->rb) != 0) + { + // 每次进来中断就说明之前的put_size已经被发送完毕了 rt_serial_update_read_index(&tx_fifo->rb, tx_fifo->put_size); - /* Get the length of the data from the ringbuffer. - * If there is some data in tx_ringbuffer, - * then call the transmit interface for transmission again */ - if (rt_ringbuffer_data_len(&tx_fifo->rb)) + + /* Get the length of the data from the ringbuffer */ + tx_length = rt_ringbuffer_data_len(&tx_fifo->rb); + + if (tx_length != 0) { - tx_fifo->activated = RT_TRUE; + /* If there is some data in tx_ringbuffer, + * then call the transmit interface for transmission again */ + rt_atomic_flag_test_and_set(&tx_fifo->activated); - rt_uint8_t *put_ptr = RT_NULL; + rt_uint8_t *put_ptr = RT_NULL; /* Get the linear length buffer from ringbuffer */ - tx_fifo->put_size = rt_serial_get_linear_buffer(&(tx_fifo->rb), &put_ptr); + tx_fifo->put_size = rt_serial_get_linear_buffer(&tx_fifo->rb, &put_ptr); + /* Call the transmit interface for transmission again */ serial->ops->transmit(serial, - put_ptr, - tx_fifo->put_size, - RT_SERIAL_TX_NON_BLOCKING); + put_ptr, + tx_fifo->put_size, + RT_SERIAL_TX_NON_BLOCKING); + break; } + } - break; + rt_completion_done(&tx_fifo->tx_cpt); + + /* Trigger the transmit completion callback */ + if (serial->parent.tx_complete != RT_NULL) + { + serial->parent.tx_complete(&serial->parent, RT_NULL); } - default: - break; + rt_atomic_flag_clear(&tx_fifo->activated); + break; + } +#endif /* RT_SERIAL_USING_DMA */ + default: + break; } } |
