summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorthewon86 <[email protected]>2023-02-21 12:30:29 +0800
committerGitHub <[email protected]>2023-02-20 23:30:29 -0500
commit67bc7e816cb95cd7fb4e80829d16cf7eb3ee35f5 (patch)
tree4842baf19f886ccdf381cfaa9c3b1fbdb13b5a51 /components
parent7f588dc3a98fd48be7079ee18581e37176e4ca67 (diff)
implement serialX UART device framework
Diffstat (limited to 'components')
-rwxr-xr-xcomponents/drivers/Kconfig34
-rw-r--r--components/drivers/include/drivers/serialX.h207
-rw-r--r--components/drivers/include/rtdevice.h5
-rw-r--r--components/drivers/serial/SConscript3
-rw-r--r--components/drivers/serial/serialX.c1560
5 files changed, 1807 insertions, 2 deletions
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 <rtthread.h>
+#include <rtdevice.h>
+
+#define BAUD_RATE_2400 2400
+#define BAUD_RATE_4800 4800
+#define BAUD_RATE_9600 9600
+#define BAUD_RATE_19200 19200
+#define BAUD_RATE_38400 38400
+#define BAUD_RATE_57600 57600
+#define BAUD_RATE_115200 115200
+#define BAUD_RATE_230400 230400
+#define BAUD_RATE_460800 460800
+#define BAUD_RATE_921600 921600
+#define BAUD_RATE_2000000 2000000
+#define BAUD_RATE_3000000 3000000
+
+#define DATA_BITS_5 5
+#define DATA_BITS_6 6
+#define DATA_BITS_7 7
+#define DATA_BITS_8 8
+#define DATA_BITS_9 9
+
+#define STOP_BITS_1 0
+#define STOP_BITS_2 1
+#define STOP_BITS_3 2
+#define STOP_BITS_4 3
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#define PARITY_NONE 0
+#define PARITY_ODD 1
+#define PARITY_EVEN 2
+#endif
+
+#define BIT_ORDER_LSB 0
+#define BIT_ORDER_MSB 1
+
+#define NRZ_NORMAL 0 /* Non Return to Zero : normal mode */
+#define NRZ_INVERTED 1 /* Non Return to Zero : inverted mode */
+
+#ifndef RT_SERIAL_FIFO_BUFSZ
+#define RT_SERIAL_FIFO_BUFSZ 128
+#endif
+
+#ifndef RT_SERIAL_DMA_BUFSZ
+#define RT_SERIAL_DMA_BUFSZ 32
+#endif
+
+#if RT_SERIAL_DMA_BUFSZ < 32
+#define RT_SERIAL_DMA_BUFSZ 32
+#endif
+
+#if RT_SERIAL_FIFO_BUFSZ < (RT_SERIAL_DMA_BUFSZ*2)
+#define RT_SERIAL_FIFO_BUFSZ RT_SERIAL_DMA_BUFSZ*2
+#endif
+
+
+#define RT_SERIAL_EVENT_RX_IND 0x01 /* Rx indication */
+#define RT_SERIAL_EVENT_TX_DONE 0x02 /* Tx complete */
+#define RT_SERIAL_EVENT_RX_DMADONE 0x03 /* Rx DMA transfer done */
+#define RT_SERIAL_EVENT_TX_DMADONE 0x04 /* Tx DMA transfer done */
+#define RT_SERIAL_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */
+
+#define RT_SERIAL_DMA_RX 0x01
+#define RT_SERIAL_DMA_TX 0x02
+
+#define RT_SERIAL_RX_INT 0x01
+#define RT_SERIAL_TX_INT 0x02
+
+#define RT_SERIAL_ERR_OVERRUN 0x01
+#define RT_SERIAL_ERR_FRAMING 0x02
+#define RT_SERIAL_ERR_PARITY 0x03
+
+#define RT_SERIAL_TX_DATAQUEUE_SIZE 2048
+#define RT_SERIAL_TX_DATAQUEUE_LWM 30
+
+#define RT_SERIAL_FLOWCONTROL_CTSRTS 1
+#define RT_SERIAL_FLOWCONTROL_NONE 0
+
+/* Default config for serial_configure structure */
+#define RT_SERIAL_CONFIG_DEFAULT \
+{ \
+ BAUD_RATE_115200, /* 115200 bits/s */ \
+ DATA_BITS_8, /* 8 databits */ \
+ STOP_BITS_1, /* 1 stopbit */ \
+ PARITY_NONE, /* No parity */ \
+ BIT_ORDER_LSB, /* LSB first sent */ \
+ NRZ_NORMAL, /* Normal mode */ \
+ RT_SERIAL_FIFO_BUFSZ, /* Buffer size */ \
+ RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \
+ 0 \
+}
+
+#define RT_SERIAL_EVENT_TXDONE (1 << 0)
+#define RT_SERIAL_EVENT_RXDONE (1 << 1)
+
+//#define RT_SERIAL_USE_EVENT // Just for test, maybe using event in the future
+
+struct rt_serial_device;
+
+typedef int (*cb_serial_tx)(struct rt_serial_device *serial, const rt_uint8_t *data, int length);
+typedef int (*cb_serial_rx)(struct rt_serial_device *serial, rt_uint8_t *data, int length);
+
+struct serial_configure
+{
+ rt_uint32_t baud_rate;
+
+ rt_uint32_t data_bits :4;
+ rt_uint32_t stop_bits :2;
+ rt_uint32_t parity :2;
+ rt_uint32_t bit_order :1;
+ rt_uint32_t invert :1;
+ rt_uint32_t bufsz :16;
+ rt_uint32_t flowcontrol :1;
+ rt_uint32_t reserved :5;
+};
+
+/*
+ * Serial FIFO mode
+ */
+struct rt_serial_fifo
+{
+ rt_uint32_t buf_sz;
+ /* software fifo */
+ rt_uint8_t *buffer;
+
+ rt_uint16_t put_index, get_index;
+
+ rt_bool_t is_full;
+};
+
+struct rt_serial_device
+{
+ struct rt_device parent;
+
+ const struct rt_uart_ops *ops;
+ struct serial_configure config;
+
+ void *serial_rx;
+ void *serial_tx;
+
+#ifdef RT_SERIAL_USING_DMA
+ rt_size_t dma_idx_rx;
+ rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ];
+ rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ];
+#endif
+
+ cb_serial_rx _cb_rx;
+ cb_serial_tx _cb_tx;
+#ifndef RT_SERIAL_USE_EVENT
+ struct rt_completion completion_rx;
+ struct rt_completion completion_tx;
+#else
+ rt_event_t rx_done;
+ rt_event_t tx_done;
+#endif
+};
+typedef struct rt_serial_device rt_serial_t;
+
+/**
+ * uart operators
+ */
+struct rt_uart_ops
+{
+ rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
+ rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);
+
+ int (*putc)(struct rt_serial_device *serial, char c);
+ int (*getc)(struct rt_serial_device *serial);
+ int (*flush)(struct rt_serial_device *serial);
+
+ void (*start_tx)(struct rt_serial_device *serial);
+ void (*stop_tx)(struct rt_serial_device *serial);
+
+#ifdef RT_SERIAL_USING_DMA
+ rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial);
+ void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size);
+ void (*stop_dma_tx)(struct rt_serial_device *serial);
+#endif
+
+ void (*enable_interrupt)(struct rt_serial_device *serial);
+ void (*disable_interrupt)(struct rt_serial_device *serial);
+};
+
+void rt_hw_serial_isr(struct rt_serial_device *serial, int event);
+
+rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
+ const char *name,
+ rt_uint32_t flag,
+ void *data);
+
+#endif
+
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 <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#ifdef RT_USING_SERIAL
+
+#define DBG_TAG "UART"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static rt_err_t rt_serial_flush(struct rt_device *dev);
+
+#ifdef RT_USING_POSIX_STDIO
+#include <dfs_posix.h>
+#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3)
+#include <dfs_poll.h>
+#else
+#include <poll.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef RT_USING_POSIX_TERMIOS
+#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3)
+#include <posix_termios.h>
+#else
+#include <termios.h>
+#endif
+#endif
+
+/* it's possible the 'getc/putc' is defined by stdio.h in gcc/newlib. */
+#ifdef getc
+#undef getc
+#endif
+
+#ifdef putc
+#undef putc
+#endif
+
+static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size)
+{
+ rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);
+
+ return RT_EOK;
+}
+
+/* fops for serial */
+static int serial_fops_open(struct dfs_fd *fd)
+{
+ rt_err_t ret = 0;
+ rt_uint16_t flags = 0;
+ rt_device_t device;
+
+ device = (rt_device_t)fd->data;
+ RT_ASSERT(device != RT_NULL);
+
+ switch (fd->flags & O_ACCMODE)
+ {
+ case O_RDONLY:
+ LOG_D("fops open: O_RDONLY!");
+ flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDONLY;
+ break;
+ case O_WRONLY:
+ LOG_D("fops open: O_WRONLY!");
+ flags = RT_DEVICE_FLAG_WRONLY;
+ break;
+ case O_RDWR:
+ LOG_D("fops open: O_RDWR!");
+ flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDWR;
+ break;
+ default:
+ LOG_E("fops open: unknown mode - %d!", fd->flags & O_ACCMODE);
+ break;
+ }
+
+ if ((fd->flags & O_ACCMODE) != O_WRONLY)
+ rt_device_set_rx_indicate(device, serial_fops_rx_ind);
+ ret = rt_device_open(device, flags);
+ if (ret == RT_EOK) return 0;
+
+ return ret;
+}
+
+static int serial_fops_close(struct dfs_fd *fd)
+{
+ rt_device_t device;
+
+ device = (rt_device_t)fd->data;
+
+ rt_device_set_rx_indicate(device, RT_NULL);
+ rt_device_close(device);
+
+ return 0;
+}
+
+static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
+{
+ rt_device_t device;
+
+ device = (rt_device_t)fd->data;
+ switch (cmd)
+ {
+ case FIONREAD:
+ break;
+ case FIONWRITE:
+ break;
+ }
+
+ return rt_device_control(device, cmd, args);
+}
+
+static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
+{
+ int size = 0;
+ rt_device_t device;
+
+ device = (rt_device_t)fd->data;
+
+ do
+ {
+ size = rt_device_read(device, -1, buf, count);
+ if (size <= 0)
+ {
+ if (fd->flags & O_NONBLOCK)
+ {
+ size = -EAGAIN;
+ break;
+ }
+
+ rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
+ }
+ }while (size <= 0);
+
+ return size;
+}
+
+static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
+{
+ rt_device_t device;
+
+ device = (rt_device_t)fd->data;
+ return rt_device_write(device, -1, buf, count);
+}
+
+static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req)
+{
+ int mask = 0;
+ int flags = 0;
+ rt_device_t device;
+ struct rt_serial_device *serial;
+
+ device = (rt_device_t)fd->data;
+ RT_ASSERT(device != RT_NULL);
+
+ serial = (struct rt_serial_device *)device;
+
+ /* only support POLLIN */
+ flags = fd->flags & O_ACCMODE;
+ if (flags == O_RDONLY || flags == O_RDWR)
+ {
+ register rt_base_t level;
+ struct rt_serial_fifo* rx_fifo;
+
+ rt_poll_add(&(device->wait_queue), req);
+
+ rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+
+ level = rt_hw_interrupt_disable();
+ if ((rx_fifo->get_index != rx_fifo->put_index) || (rx_fifo->get_index == rx_fifo->put_index && rx_fifo->is_full == RT_TRUE))
+ mask |= POLLIN;
+ rt_hw_interrupt_enable(level);
+ }
+
+ return mask;
+}
+
+const static struct dfs_file_ops _serial_fops =
+{
+ serial_fops_open,
+ serial_fops_close,
+ serial_fops_ioctl,
+ serial_fops_read,
+ serial_fops_write,
+ RT_NULL, /* flush */
+ RT_NULL, /* lseek */
+ RT_NULL, /* getdents */
+ serial_fops_poll,
+};
+#endif
+
+/**
+ * Calculate fifo data length.
+ *
+ * @param fifo the data fifo of serial device
+ *
+ * @return length
+ */
+rt_inline rt_size_t _serial_fifo_calc_data_len(struct rt_serial_fifo *fifo)
+{
+ rt_size_t size;
+ if (fifo->put_index == fifo->get_index) {
+ size = (fifo->is_full == RT_FALSE) ? 0 : fifo->buf_sz;
+ } else if (fifo->put_index > fifo->get_index) {
+ size = fifo->put_index - fifo->get_index;
+ } else {
+ size = fifo->buf_sz - (fifo->get_index - fifo->put_index);
+ }
+
+ return size;
+}
+
+rt_inline void _serial_fifo_push_data(struct rt_serial_fifo *fifo, rt_uint8_t ch)
+{
+ fifo->buffer[fifo->put_index] = ch;
+ fifo->put_index += 1;
+ if (fifo->put_index >= fifo->buf_sz) fifo->put_index = 0;
+}
+
+rt_inline rt_uint8_t _serial_fifo_pop_data(struct rt_serial_fifo *fifo)
+{
+ rt_uint8_t ch;
+
+ ch = fifo->buffer[fifo->get_index];
+ fifo->get_index += 1;
+ if (fifo->get_index >= fifo->buf_sz) fifo->get_index = 0;
+
+ return ch;
+}
+
+/*
+ * Serial poll routines
+ */
+rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
+{
+ int ch;
+ int size;
+
+ RT_ASSERT(serial != RT_NULL);
+ size = length;
+
+ while (length)
+ {
+ ch = serial->ops->getc(serial);
+ if (ch == -1) break;
+
+ *data = ch;
+ data++; length--;
+ }
+
+ return size - length;
+}
+
+rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+ int size;
+ rt_uint8_t last_char = 0;
+
+ RT_ASSERT(serial != RT_NULL);
+
+ size = length;
+ while (length)
+ {
+ /*
+ * to be polite with serial console add a line feed
+ * to the carriage return character
+ */
+ if (*data == '\n' &&
+ (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+ last_char != '\r')
+ {
+ serial->ops->putc(serial, '\r');
+
+ last_char = 0;
+ } else if (*data == '\r') {
+ last_char = '\r';
+ } else {
+ last_char = 0;
+ }
+
+ serial->ops->putc(serial, *data);
+
+ data++; length--;
+ }
+
+ return size - length;
+}
+
+/*
+ * Serial interrupt routines
+ */
+rt_inline int _serial_fifo_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
+{
+ rt_size_t len, size;
+ struct rt_serial_fifo* rx_fifo;
+ register rt_base_t level;
+
+ RT_ASSERT(serial != RT_NULL);
+
+ if (length == 0) return 0;
+
+ rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+
+ len = _serial_fifo_calc_data_len(rx_fifo);
+
+ if ((len == 0) && // non-blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ return 0;
+ }
+ if ((len == 0) && // blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+ rt_err_t ret;
+ do {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+
+#ifndef RT_SERIAL_USE_EVENT
+ ret = rt_completion_wait(&(serial->completion_rx), RT_WAITING_FOREVER);
+#else
+ ret = rt_event_recv(serial->rx_done, RT_SERIAL_EVENT_RXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+ if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+ } else {
+ return size;
+ }
+
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+
+ len = _serial_fifo_calc_data_len(rx_fifo);
+ } while(len == 0);
+ }
+
+ if (len > length) {
+ len = length;
+ }
+
+ /* read from software FIFO */
+ for (size = 0; size < len; size++) {
+ /* otherwise there's the data: */
+ *data = _serial_fifo_pop_data(rx_fifo);
+ data++;
+ }
+
+ rx_fifo->is_full = RT_FALSE;
+
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+
+ return size;
+}
+
+rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+ rt_size_t len, length_t, size;
+ struct rt_serial_fifo *tx_fifo;
+ register rt_base_t level;
+ rt_uint8_t last_char = 0;
+
+ RT_ASSERT(serial != RT_NULL);
+
+ if (length == 0) return 0;
+
+ tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+ size = 0;
+ do {
+ length_t = length - size;
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+
+ len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo);
+
+ if ((len == 0) && // non-blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ break;
+ }
+
+ if ((len == 0) && // blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+ rt_err_t ret;
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+#ifndef RT_SERIAL_USE_EVENT
+ ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+ ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+ if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+ continue;
+ } else {
+ return size;
+ }
+ }
+
+ if (len > length_t) {
+ len = length_t;
+ }
+ /* copy to software FIFO */
+ while (len > 0) {
+ /*
+ * to be polite with serial console add a line feed
+ * to the carriage return character
+ */
+ if (*data == '\n' &&
+ (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+ last_char != '\r') {
+ _serial_fifo_push_data(tx_fifo, '\r');
+
+ last_char = 0;
+ } else if (*data == '\r') {
+ last_char = '\r';
+ } else {
+ last_char = 0;
+ }
+
+ _serial_fifo_push_data(tx_fifo, *data);
+
+ data++; len--; size++;
+ }
+
+ /* if the next position is read index, discard this 'read char' */
+ if (tx_fifo->put_index == tx_fifo->get_index) {
+ tx_fifo->is_full = RT_TRUE;
+ }
+
+ // TODO: start tx
+ serial->ops->start_tx(serial);
+
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ } while(size < length);
+
+ return size;
+}
+
+#ifdef RT_SERIAL_USING_DMA
+/*
+ * Serial DMA routines
+ */
+rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
+{
+ rt_size_t len, length_t, size, i;
+ struct rt_serial_fifo *tx_fifo;
+ register rt_base_t level;
+ rt_uint8_t last_char = 0, ch;
+
+ RT_ASSERT(serial != RT_NULL);
+
+ if (length == 0) return 0;
+
+ tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+ size = 0;
+ do {
+ length_t = length - size;
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+
+ len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo);
+
+ if ((len == 0) && // non-blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ break;
+ }
+
+ if ((len == 0) && // blocking io mode
+ (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) {
+ rt_err_t ret;
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+
+#ifndef RT_SERIAL_USE_EVENT
+ ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+ ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+ if (ret == RT_EOK || ret == -RT_ETIMEOUT) {
+ continue;
+ } else {
+ return size;
+ }
+ }
+
+ if (len > length_t) {
+ len = length_t;
+ }
+ /* copy to software FIFO */
+ while (len > 0) {
+ /*
+ * to be polite with serial console add a line feed
+ * to the carriage return character
+ */
+ if (*data == '\n' &&
+ (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM &&
+ last_char != '\r') {
+ _serial_fifo_push_data(tx_fifo, '\r');
+
+ last_char = 0;
+ } else if (*data == '\r') {
+ last_char = '\r';
+ } else {
+ last_char = 0;
+ }
+
+ _serial_fifo_push_data(tx_fifo, *data);
+
+ data++; len--; size++;
+ }
+
+ /* if the next position is read index, discard this 'read char' */
+ if (tx_fifo->put_index == tx_fifo->get_index) {
+ tx_fifo->is_full = RT_TRUE;
+ }
+
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+
+ // TODO: start tx
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+ if (serial->ops->is_dma_txing(serial) == RT_FALSE) {
+ /* calucate fifo data size */
+ len = _serial_fifo_calc_data_len(tx_fifo);
+
+ if (len > RT_SERIAL_DMA_BUFSZ) {
+ len = RT_SERIAL_DMA_BUFSZ;
+ }
+ /* read from software FIFO */
+ for (i = 0; i < len; i++) {
+ /* pop one byte data */
+ ch = _serial_fifo_pop_data(tx_fifo);
+
+ serial->serial_dma_tx[i] = ch;
+ }
+ tx_fifo->is_full = RT_FALSE;
+ serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len);
+
+ }
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ } while(size < length);
+
+ return size;
+}
+#endif /* RT_SERIAL_USING_DMA */
+
+/* RT-Thread Device Interface */
+/*
+ * This function initializes serial device.
+ */
+static rt_err_t rt_serial_init(struct rt_device *dev)
+{
+ rt_err_t result = RT_EOK;
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ serial = (struct rt_serial_device *)dev;
+
+ /* initialize rx/tx */
+ serial->serial_rx = RT_NULL;
+ serial->serial_tx = RT_NULL;
+
+ /* apply configuration */
+ if (serial->ops->configure)
+ result = serial->ops->configure(serial, &serial->config);
+
+ return result;
+}
+
+static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+ rt_uint16_t stream_flag = 0;
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ serial = (struct rt_serial_device *)dev;
+
+ LOG_D("open serial device: 0x%08x with open flag: 0x%04x",
+ dev, oflag);
+ /* check device flag with the open flag */
+ if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
+ return -RT_EIO;
+ if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
+ return -RT_EIO;
+ if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
+ return -RT_EIO;
+ if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
+ return -RT_EIO;
+
+ /* keep steam flag */
+ if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
+ stream_flag = RT_DEVICE_FLAG_STREAM;
+
+ /* get open flags */
+ dev->open_flag = oflag & 0xff;
+
+ if (oflag & RT_DEVICE_FLAG_INT_RX)
+ {
+ /* initialize the Rx/Tx structure according to open flag */
+ if (serial->serial_rx == RT_NULL)
+ {
+ struct rt_serial_fifo* rx_fifo;
+
+ rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+ serial->config.bufsz);
+ RT_ASSERT(rx_fifo != RT_NULL);
+ rx_fifo->buf_sz = serial->config.bufsz;
+ rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
+ rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz);
+ rx_fifo->put_index = 0;
+ rx_fifo->get_index = 0;
+ rx_fifo->is_full = RT_FALSE;
+
+ serial->serial_rx = rx_fifo;
+ }
+ dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
+
+ serial->_cb_rx = _serial_fifo_rx;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_rx));
+#else
+ serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO);
+#endif
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
+ }
+#ifdef RT_SERIAL_USING_DMA
+ else if (oflag & RT_DEVICE_FLAG_DMA_RX)
+ {
+ /* initialize the Rx/Tx structure according to open flag */
+ if (serial->serial_rx == RT_NULL)
+ {
+ struct rt_serial_fifo* rx_fifo;
+
+ rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+ serial->config.bufsz);
+ RT_ASSERT(rx_fifo != RT_NULL);
+ rx_fifo->buf_sz = serial->config.bufsz;
+ rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
+ rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz);
+ rx_fifo->put_index = 0;
+ rx_fifo->get_index = 0;
+ rx_fifo->is_full = RT_FALSE;
+
+ serial->serial_rx = rx_fifo;
+ }
+ dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
+
+ serial->dma_idx_rx = 0;
+ serial->_cb_rx = _serial_fifo_rx;
+
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_rx));
+#else
+ serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO);
+#endif
+ /* configure fifo address and length to low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX);
+ }
+#endif /* RT_SERIAL_USING_DMA */
+ else
+ {
+ serial->serial_rx = RT_NULL;
+ serial->_cb_rx = _serial_poll_rx;
+ }
+
+ if (oflag & RT_DEVICE_FLAG_INT_TX)
+ {
+ if (serial->serial_tx == RT_NULL)
+ {
+ struct rt_serial_fifo *tx_fifo;
+
+ tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+ serial->config.bufsz);
+ RT_ASSERT(tx_fifo != RT_NULL);
+ tx_fifo->buf_sz = serial->config.bufsz;
+ tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1);
+ rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz);
+ tx_fifo->put_index = 0;
+ tx_fifo->get_index = 0;
+ tx_fifo->is_full = RT_FALSE;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_tx));
+#else
+ serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO);
+#endif
+ serial->serial_tx = tx_fifo;
+ }
+ dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
+ /* configure low level device */
+// serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
+
+ serial->_cb_tx = _serial_int_tx;
+ }
+#ifdef RT_SERIAL_USING_DMA
+ else if (oflag & RT_DEVICE_FLAG_DMA_TX)
+ {
+ if (serial->serial_tx == RT_NULL)
+ {
+ struct rt_serial_fifo *tx_fifo;
+
+ tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) +
+ serial->config.bufsz);
+ RT_ASSERT(tx_fifo != RT_NULL);
+ tx_fifo->buf_sz = serial->config.bufsz;
+ tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1);
+ rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz);
+ tx_fifo->put_index = 0;
+ tx_fifo->get_index = 0;
+ tx_fifo->is_full = RT_FALSE;
+
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_tx));
+#else
+ serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO);
+#endif
+
+ serial->serial_tx = tx_fifo;
+ }
+ dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
+
+ serial->_cb_tx = _serial_dma_tx;
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX);
+ }
+#endif /* RT_SERIAL_USING_DMA */
+ else
+ {
+ serial->serial_tx = RT_NULL;
+ serial->_cb_tx = _serial_poll_tx;
+ }
+
+ serial->ops->control(serial, RT_DEVICE_CTRL_OPEN, (void *)0);
+
+ /* set stream flag */
+ dev->open_flag |= stream_flag;
+
+ return RT_EOK;
+}
+
+static rt_err_t rt_serial_close(struct rt_device *dev)
+{
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ serial = (struct rt_serial_device *)dev;
+
+ rt_serial_flush(dev);
+ serial->ops->control(serial, RT_DEVICE_CTRL_CLOSE, RT_NULL);
+
+ if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
+ {
+ struct rt_serial_fifo *rx_fifo;
+
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX);
+
+ dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX;
+
+ rx_fifo = (struct rt_serial_fifo *)serial->serial_rx;
+ rt_free(rx_fifo);
+
+ serial->serial_rx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_rx));
+#else
+ rt_event_delete(serial->rx_done);
+#endif
+ }
+#ifdef RT_SERIAL_USING_DMA
+ else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
+ {
+ struct rt_serial_fifo *rx_fifo;
+
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX);
+
+ dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX;
+
+ rx_fifo = (struct rt_serial_fifo *)serial->serial_rx;
+ rt_free(rx_fifo);
+
+ serial->serial_rx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_rx));
+#else
+ rt_event_delete(serial->rx_done);
+#endif
+ }
+#endif /* RT_SERIAL_USING_DMA */
+
+ if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
+ {
+ struct rt_serial_fifo *tx_fifo;
+
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_TX);
+
+ dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX;
+
+ tx_fifo = (struct rt_serial_fifo *)serial->serial_tx;
+ rt_free(tx_fifo);
+
+ serial->serial_tx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_tx));
+#else
+ rt_event_delete(serial->tx_done);
+#endif
+ }
+#ifdef RT_SERIAL_USING_DMA
+ else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
+ {
+ struct rt_serial_fifo *tx_fifo;
+
+ /* configure low level device */
+ serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_TX);
+
+ dev->open_flag &= ~RT_DEVICE_FLAG_DMA_TX;
+
+ tx_fifo = (struct rt_serial_fifo *)serial->serial_tx;
+ rt_free(tx_fifo);
+
+ serial->serial_tx = RT_NULL;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_init(&(serial->completion_tx));
+#else
+ rt_event_delete(serial->tx_done);
+#endif
+ }
+#endif /* RT_SERIAL_USING_DMA */
+
+ dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED;
+
+ return RT_EOK;
+}
+
+static rt_size_t rt_serial_read(struct rt_device *dev,
+ rt_off_t pos,
+ void *buffer,
+ rt_size_t size)
+{
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ if (size == 0) return 0;
+
+ serial = (struct rt_serial_device *)dev;
+
+ return serial->_cb_rx(serial, (rt_uint8_t *)buffer, size);
+}
+
+static rt_size_t rt_serial_write(struct rt_device *dev,
+ rt_off_t pos,
+ const void *buffer,
+ rt_size_t size)
+{
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ if (size == 0) return 0;
+
+ serial = (struct rt_serial_device *)dev;
+
+ return serial->_cb_tx(serial, (const rt_uint8_t *)buffer, size);
+}
+
+static rt_err_t rt_serial_flush(struct rt_device *dev)
+{
+ struct rt_serial_device *serial;
+ rt_size_t len;
+ struct rt_serial_fifo *tx_fifo, *rx_fifo;
+ register rt_base_t level;
+
+ RT_ASSERT(dev != RT_NULL);
+
+ serial = (struct rt_serial_device *)dev;
+
+ if((dev->open_flag & RT_DEVICE_FLAG_INT_RX)
+#ifdef RT_SERIAL_USING_DMA
+ || (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
+#endif /* RT_SERIAL_USING_DMA */
+ ) {
+ rx_fifo = (struct rt_serial_fifo*) serial->serial_rx;
+ level = rt_hw_interrupt_disable();
+ rx_fifo->get_index = rx_fifo->put_index = 0;
+ rx_fifo->is_full = RT_FALSE;
+ rt_hw_interrupt_enable(level);
+ }
+
+ if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX)
+#ifdef RT_SERIAL_USING_DMA
+ || (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
+#endif /* RT_SERIAL_USING_DMA */
+ ) {
+ tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+ while(1) {
+ /* disable interrupt */
+ level = rt_hw_interrupt_disable();
+
+ len = _serial_fifo_calc_data_len(tx_fifo);
+
+ if (len == 0) {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+ break;
+ } else {
+ /* enable interrupt */
+ rt_hw_interrupt_enable(level);
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER);
+#else
+ rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
+#endif
+ }
+ }
+
+ if (dev->open_flag & RT_DEVICE_FLAG_INT_TX) {
+ serial->ops->flush(serial);
+ }
+#ifdef RT_SERIAL_USING_DMA
+ else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) {
+ }
+#endif /* RT_SERIAL_USING_DMA */
+ }
+
+ return RT_EOK;
+}
+
+#ifdef RT_USING_POSIX_TERMIOS
+struct speed_baudrate_item
+{
+ speed_t speed;
+ int baudrate;
+};
+
+const static struct speed_baudrate_item _tbl[] =
+{
+ {B2400, BAUD_RATE_2400},
+ {B4800, BAUD_RATE_4800},
+ {B9600, BAUD_RATE_9600},
+ {B19200, BAUD_RATE_19200},
+ {B38400, BAUD_RATE_38400},
+ {B57600, BAUD_RATE_57600},
+ {B115200, BAUD_RATE_115200},
+ {B230400, BAUD_RATE_230400},
+ {B460800, BAUD_RATE_460800},
+ {B921600, BAUD_RATE_921600},
+ {B2000000, BAUD_RATE_2000000},
+ {B3000000, BAUD_RATE_3000000},
+};
+
+static speed_t _get_speed(int baudrate)
+{
+ int index;
+
+ for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
+ {
+ if (_tbl[index].baudrate == baudrate)
+ return _tbl[index].speed;
+ }
+
+ return B0;
+}
+
+static int _get_baudrate(speed_t speed)
+{
+ int index;
+
+ for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++)
+ {
+ if (_tbl[index].speed == speed)
+ return _tbl[index].baudrate;
+ }
+
+ return 0;
+}
+
+static void _tc_flush(struct rt_serial_device *serial, int queue)
+{
+ register rt_base_t level;
+ int ch = -1;
+ struct rt_serial_fifo *rx_fifo = RT_NULL;
+ struct rt_device *device = RT_NULL;
+
+ RT_ASSERT(serial != RT_NULL);
+
+ device = &(serial->parent);
+ rx_fifo = (struct rt_serial_fifo *) serial->serial_rx;
+
+ switch(queue)
+ {
+ case TCIFLUSH:
+ case TCIOFLUSH:
+
+ RT_ASSERT(rx_fifo != RT_NULL);
+
+ if((device->open_flag & RT_DEVICE_FLAG_INT_RX) || (device->open_flag & RT_DEVICE_FLAG_DMA_RX))
+ {
+ RT_ASSERT(RT_NULL != rx_fifo);
+ level = rt_hw_interrupt_disable();
+ rx_fifo->get_index = rx_fifo->put_index;
+ rx_fifo->is_full = RT_FALSE;
+ rt_hw_interrupt_enable(level);
+ }
+ else
+ {
+ while (1)
+ {
+ ch = serial->ops->getc(serial);
+ if (ch == -1) break;
+ }
+ }
+
+ break;
+
+ case TCOFLUSH:
+ break;
+ }
+
+}
+
+#endif
+
+static rt_err_t rt_serial_control(struct rt_device *dev,
+ int cmd,
+ void *args)
+{
+ rt_err_t ret = RT_EOK;
+ struct rt_serial_device *serial;
+
+ RT_ASSERT(dev != RT_NULL);
+ serial = (struct rt_serial_device *)dev;
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_SUSPEND:
+ /* suspend device */
+ dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
+ break;
+ case RT_DEVICE_CTRL_RESUME:
+ /* resume device */
+ dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
+ break;
+ case RT_DEVICE_CTRL_BLOCKING:
+ if (((rt_uint32_t)args & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) {
+ dev->open_flag |= RT_DEVICE_OFLAG_NONBLOCKING;
+ } else {
+ dev->open_flag &= ~RT_DEVICE_OFLAG_NONBLOCKING;
+ }
+ break;
+ case RT_DEVICE_CTRL_CONFIG:
+ if (args)
+ {
+ struct serial_configure *pconfig = (struct serial_configure *) args;
+ if (pconfig->bufsz != serial->config.bufsz && serial->parent.ref_count)
+ {
+ /*can not change buffer size*/
+ return RT_EBUSY;
+ }
+ /* set serial configure */
+ serial->config = *pconfig;
+ /* serial device has been opened, to configure it */
+ serial->ops->configure(serial, pconfig);
+ }
+ break;
+#ifdef RT_USING_POSIX_STDIO
+#ifdef RT_USING_POSIX_TERMIOS
+ case TCGETA:
+ {
+ struct termios *tio = (struct termios*)args;
+ if (tio == RT_NULL) return -RT_EINVAL;
+
+ tio->c_iflag = 0;
+ tio->c_oflag = 0;
+ tio->c_lflag = 0;
+
+ /* update oflag for console device */
+ if (rt_console_get_device() == dev)
+ tio->c_oflag = OPOST | ONLCR;
+
+ /* set cflag */
+ tio->c_cflag = 0;
+ if (serial->config.data_bits == DATA_BITS_5)
+ tio->c_cflag = CS5;
+ else if (serial->config.data_bits == DATA_BITS_6)
+ tio->c_cflag = CS6;
+ else if (serial->config.data_bits == DATA_BITS_7)
+ tio->c_cflag = CS7;
+ else if (serial->config.data_bits == DATA_BITS_8)
+ tio->c_cflag = CS8;
+
+ if (serial->config.stop_bits == STOP_BITS_2)
+ tio->c_cflag |= CSTOPB;
+
+ if (serial->config.parity == PARITY_EVEN)
+ tio->c_cflag |= PARENB;
+ else if (serial->config.parity == PARITY_ODD)
+ tio->c_cflag |= (PARODD | PARENB);
+
+ cfsetospeed(tio, _get_speed(serial->config.baud_rate));
+ }
+ break;
+
+ case TCSETAW:
+ case TCSETAF:
+ case TCSETA:
+ {
+ int baudrate;
+ struct serial_configure config;
+
+ struct termios *tio = (struct termios*)args;
+ if (tio == RT_NULL) return -RT_EINVAL;
+
+ config = serial->config;
+
+ baudrate = _get_baudrate(cfgetospeed(tio));
+ config.baud_rate = baudrate;
+
+ switch (tio->c_cflag & CSIZE)
+ {
+ case CS5:
+ config.data_bits = DATA_BITS_5;
+ break;
+ case CS6:
+ config.data_bits = DATA_BITS_6;
+ break;
+ case CS7:
+ config.data_bits = DATA_BITS_7;
+ break;
+ default:
+ config.data_bits = DATA_BITS_8;
+ break;
+ }
+
+ if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2;
+ else config.stop_bits = STOP_BITS_1;
+
+ if (tio->c_cflag & PARENB)
+ {
+ if (tio->c_cflag & PARODD) config.parity = PARITY_ODD;
+ else config.parity = PARITY_EVEN;
+ }
+ else config.parity = PARITY_NONE;
+
+ serial->ops->configure(serial, &config);
+ }
+ break;
+ case TCFLSH:
+ {
+ int queue = (int)args;
+
+ _tc_flush(serial, queue);
+ }
+
+ break;
+ case TCXONC:
+ break;
+#endif /*RT_USING_POSIX_TERMIOS*/
+ case TIOCSWINSZ:
+ {
+ struct winsize* p_winsize;
+
+ p_winsize = (struct winsize*)args;
+ rt_kprintf("\x1b[8;%d;%dt", p_winsize->ws_col, p_winsize->ws_row);
+ }
+ break;
+ case TIOCGWINSZ:
+ {
+ struct winsize* p_winsize;
+ p_winsize = (struct winsize*)args;
+
+ if(rt_thread_self() != rt_thread_find("tshell"))
+ {
+ /* only can be used in tshell thread; otherwise, return default size */
+ p_winsize->ws_col = 80;
+ p_winsize->ws_row = 24;
+ }
+ else
+ {
+ #define _TIO_BUFLEN 20
+ char _tio_buf[_TIO_BUFLEN];
+ unsigned char cnt1, cnt2, cnt3, i;
+ char row_s[4], col_s[4];
+ char *p;
+
+ rt_memset(_tio_buf, 0, _TIO_BUFLEN);
+
+ /* send the command to terminal for getting the window size of the terminal */
+ rt_kprintf("\033[18t");
+
+ /* waiting for the response from the terminal */
+ i = 0;
+ while(i < _TIO_BUFLEN)
+ {
+ _tio_buf[i] = getchar();
+ if(_tio_buf[i] != 't')
+ {
+ i ++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if(i == _TIO_BUFLEN)
+ {
+ /* buffer overloaded, and return default size */
+ p_winsize->ws_col = 80;
+ p_winsize->ws_row = 24;
+ break;
+ }
+
+ /* interpreting data eg: "\033[8;1;15t" which means row is 1 and col is 15 (unit: size of ONE character) */
+ rt_memset(row_s,0,4);
+ rt_memset(col_s,0,4);
+ cnt1 = 0;
+ while(_tio_buf[cnt1] != ';' && cnt1 < _TIO_BUFLEN)
+ {
+ cnt1++;
+ }
+ cnt2 = ++cnt1;
+ while(_tio_buf[cnt2] != ';' && cnt2 < _TIO_BUFLEN)
+ {
+ cnt2++;
+ }
+ p = row_s;
+ while(cnt1 < cnt2)
+ {
+ *p++ = _tio_buf[cnt1++];
+ }
+ p = col_s;
+ cnt2++;
+ cnt3 = rt_strlen(_tio_buf) - 1;
+ while(cnt2 < cnt3)
+ {
+ *p++ = _tio_buf[cnt2++];
+ }
+
+ /* load the window size date */
+ p_winsize->ws_col = atoi(col_s);
+ p_winsize->ws_row = atoi(row_s);
+ #undef _TIO_BUFLEN
+ }
+
+ p_winsize->ws_xpixel = 0;/* unused */
+ p_winsize->ws_ypixel = 0;/* unused */
+ }
+ break;
+ case FIONREAD:
+ {
+ rt_size_t recved = 0;
+ register rt_base_t level;
+
+ level = rt_hw_interrupt_disable();
+ recved = _serial_fifo_calc_data_len(serial->serial_rx);
+ rt_hw_interrupt_enable(level);
+
+ *(rt_size_t *)args = recved;
+ }
+ break;
+#endif /*RT_USING_POSIX_STDIO*/
+ default :
+ /* control device */
+ ret = serial->ops->control(serial, cmd, args);
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops serial_ops =
+{
+ rt_serial_init,
+ rt_serial_open,
+ rt_serial_close,
+ rt_serial_read,
+ rt_serial_write,
+ rt_serial_control,
+ rt_serial_flush,
+};
+#endif
+
+/*
+ * serial register
+ */
+rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
+ const char *name,
+ rt_uint32_t flag,
+ void *data)
+{
+ rt_err_t ret;
+ struct rt_device *device;
+ RT_ASSERT(serial != RT_NULL);
+
+ device = &(serial->parent);
+
+ device->type = RT_Device_Class_Char;
+ device->rx_indicate = RT_NULL;
+ device->tx_complete = RT_NULL;
+
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &serial_ops;
+#else
+ device->init = rt_serial_init;
+ device->open = rt_serial_open;
+ device->close = rt_serial_close;
+ device->read = rt_serial_read;
+ device->write = rt_serial_write;
+ device->flush = rt_serial_flush;
+ device->control = rt_serial_control;
+#endif
+ device->user_data = data;
+
+ /* register a character device */
+ ret = rt_device_register(device, name, flag);
+
+#if defined(RT_USING_POSIX_STDIO)
+ /* set fops */
+ device->fops = &_serial_fops;
+#endif
+
+ return ret;
+}
+
+/* ISR for serial interrupt */
+void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
+{
+ switch (event & 0xff)
+ {
+ case RT_SERIAL_EVENT_RX_IND:
+ {
+ int ch = -1;
+ struct rt_serial_fifo* rx_fifo;
+
+ /* interrupt mode receive */
+ rx_fifo = (struct rt_serial_fifo*)serial->serial_rx;
+
+ while (1) {
+ ch = serial->ops->getc(serial);
+ if (ch == -1) break;
+
+ /* if fifo is full, discard one byte first */
+ if (rx_fifo->is_full == RT_TRUE) {
+ rx_fifo->get_index += 1;
+ if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0;
+ }
+ /* push a new data */
+ _serial_fifo_push_data(rx_fifo, ch);
+
+ /* if put index equal to read index, fifo is full */
+ if (rx_fifo->put_index == rx_fifo->get_index)
+ {
+ rx_fifo->is_full = RT_TRUE;
+ }
+ }
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_done(&(serial->completion_rx));
+#else
+ rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE);
+#endif
+ /* invoke callback */
+ if (serial->parent.rx_indicate != RT_NULL) {
+ rt_size_t rx_length;
+
+ /* get rx length */
+ rx_length = _serial_fifo_calc_data_len(rx_fifo);
+ if (rx_length) {
+ serial->parent.rx_indicate(&serial->parent, rx_length);
+ }
+ }
+ }
+ break;
+ case RT_SERIAL_EVENT_TX_DONE:
+ {
+#if defined(RT_SERIAL_HARD_FIFO)
+ rt_size_t fifo_sz;
+#endif
+ rt_size_t len;
+ rt_uint8_t ch = 0;
+ struct rt_serial_fifo *tx_fifo;
+
+ tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+ /* calucate fifo data size */
+ len = _serial_fifo_calc_data_len(tx_fifo);
+ if (len == 0) {
+ // TODO: stop tx
+ serial->ops->stop_tx(serial);
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_done(&(serial->completion_tx));
+#else
+ rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE);
+#endif
+ /* invoke callback */
+ if (serial->parent.tx_complete != RT_NULL) {
+ serial->parent.tx_complete(&serial->parent, (void*)len);
+ }
+ break;
+ }
+#if defined(RT_SERIAL_HARD_FIFO)
+ fifo_sz = event >> 8;
+ if (len > fifo_sz) {
+ len = fifo_sz;
+ }
+ /* read from software FIFO */
+ while (len > 0) {
+ /* pop one byte data */
+ ch = _serial_fifo_pop_data(tx_fifo);
+
+ serial->ops->putc(serial, ch);
+ len--;
+ }
+#else
+ /* pop one byte data */
+ ch = _serial_fifo_pop_data(tx_fifo);
+
+ serial->ops->putc(serial, ch);
+#endif
+ tx_fifo->is_full = RT_FALSE;
+ }
+ break;
+#ifdef RT_SERIAL_USING_DMA
+ case RT_SERIAL_EVENT_RX_DMADONE:
+ {
+ int dma_idx, ch = -1;
+ struct rt_serial_fifo* rx_fifo;
+
+ if (serial->dma_idx_rx == dma_idx) break;
+
+ dma_idx = event >> 8;
+
+ rx_fifo = (struct rt_serial_fifo*)serial->serial_rx;
+
+ while (serial->dma_idx_rx != dma_idx) {
+ ch = serial->serial_dma_rx[serial->dma_idx_rx];
+
+ /* if fifo is full, discard one byte first */
+ if (rx_fifo->is_full == RT_TRUE) {
+ rx_fifo->get_index += 1;
+ if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0;
+ }
+ /* push a new data */
+ _serial_fifo_push_data(rx_fifo, ch);
+
+ /* if put index equal to read index, fifo is full */
+ if (rx_fifo->put_index == rx_fifo->get_index)
+ {
+ rx_fifo->is_full = RT_TRUE;
+ }
+ serial->dma_idx_rx++;
+ if (serial->dma_idx_rx == RT_SERIAL_DMA_BUFSZ) {
+ serial->dma_idx_rx = 0;
+ }
+ }
+ serial->dma_idx_rx = dma_idx;
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_done(&(serial->completion_rx));
+#else
+ rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE);
+#endif
+
+ /* invoke callback */
+ if (serial->parent.rx_indicate != RT_NULL) {
+ rt_size_t rx_length;
+
+ /* get rx length */
+ rx_length = _serial_fifo_calc_data_len(rx_fifo);
+ if (rx_length) {
+ serial->parent.rx_indicate(&serial->parent, rx_length);
+ }
+ }
+ }
+ break;
+ case RT_SERIAL_EVENT_TX_DMADONE:
+ {
+ int i;
+ rt_size_t len;
+ rt_uint8_t ch = 0;
+ struct rt_serial_fifo *tx_fifo;
+
+ tx_fifo = (struct rt_serial_fifo*) serial->serial_tx;
+
+ /* calucate fifo data size */
+ len = _serial_fifo_calc_data_len(tx_fifo);
+ if (len == 0) {
+ // TODO: stop tx
+ serial->ops->stop_dma_tx(serial);
+#ifndef RT_SERIAL_USE_EVENT
+ rt_completion_done(&(serial->completion_tx));
+#else
+ rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE);
+#endif
+ /* invoke callback */
+ if (serial->parent.tx_complete != RT_NULL) {
+ serial->parent.tx_complete(&serial->parent, (void*)len);
+ }
+ break;
+ }
+
+ if (len > RT_SERIAL_DMA_BUFSZ) {
+ len = RT_SERIAL_DMA_BUFSZ;
+ }
+ /* read from software FIFO */
+ for (i = 0; i < len; i++) {
+ /* pop one byte data */
+ ch = _serial_fifo_pop_data(tx_fifo);
+
+ serial->serial_dma_tx[i] = ch;
+ }
+ tx_fifo->is_full = RT_FALSE;
+ serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len);
+ }
+ break;
+#endif /* RT_SERIAL_USING_DMA */
+ }
+}
+#endif /* RT_USING_SERIAL */
+