Skip to content

Commit 4962618

Browse files
nordic-krchnashif
authored andcommitted
shell: shell_uart: add ring_buffers and interrupt support
Improved RX path to use ring buffer for incoming data instead of single byte buffer. Improved TX path to use ring buffer. Added support for asynchronous UART API (interrupts). Signed-off-by: Krzysztof Chruscinski <[email protected]>
1 parent 7909509 commit 4962618

File tree

4 files changed

+237
-37
lines changed

4 files changed

+237
-37
lines changed

include/shell/shell_uart.h

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,65 @@
88
#define SHELL_UART_H__
99

1010
#include <shell/shell.h>
11+
#include <ring_buffer.h>
12+
#include <atomic.h>
1113

1214
#ifdef __cplusplus
1315
extern "C" {
1416
#endif
1517

1618
extern const struct shell_transport_api shell_uart_transport_api;
1719

18-
struct shell_uart {
20+
/** @brief Shell UART transport instance control block (RW data). */
21+
struct shell_uart_ctrl_blk {
1922
struct device *dev;
2023
shell_transport_handler_t handler;
21-
struct k_timer timer;
2224
void *context;
23-
u8_t rx[1];
24-
size_t rx_cnt;
25+
atomic_t tx_busy;
26+
bool blocking;
27+
};
28+
29+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
30+
#define UART_SHELL_TX_RINGBUF_DECLARE(_name, _size) \
31+
RING_BUF_DECLARE(_name##_tx_ringbuf, _size)
32+
33+
#define UART_SHELL_TX_BUF_DECLARE(_name) \
34+
u8_t _name##_txbuf[SHELL_UART_TX_BUF_SIZE]
35+
36+
#define UART_SHELL_RX_TIMER_DECLARE(_name) /* Empty */
37+
38+
#define UART_SHELL_TX_RINGBUF_PTR(_name) (&_name##_tx_ringbuf)
39+
40+
#define UART_SHELL_RX_TIMER_PTR(_name) NULL
41+
42+
#else /* CONFIG_UART_INTERRUPT_DRIVEN */
43+
#define UART_SHELL_TX_RINGBUF_DECLARE(_name, _size) /* Empty */
44+
#define UART_SHELL_TX_BUF_DECLARE(_name) /* Empty */
45+
#define UART_SHELL_RX_TIMER_DECLARE(_name) static struct k_timer _name##_timer
46+
#define UART_SHELL_TX_RINGBUF_PTR(_name) NULL
47+
#define UART_SHELL_RX_TIMER_PTR(_name) (&_name##_timer)
48+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
49+
50+
/** @brief Shell UART transport instance structure. */
51+
struct shell_uart {
52+
struct shell_uart_ctrl_blk *ctrl_blk;
53+
struct k_timer *timer;
54+
struct ring_buf *tx_ringbuf;
55+
struct ring_buf *rx_ringbuf;
2556
};
2657

27-
#define SHELL_UART_DEFINE(_name) \
28-
static struct shell_uart _name##_shell_uart; \
58+
/** @brief Macro for creating shell UART transport instance. */
59+
#define SHELL_UART_DEFINE(_name, _tx_ringbuf_size, _rx_ringbuf_size) \
60+
static struct shell_uart_ctrl_blk _name##_ctrl_blk; \
61+
UART_SHELL_RX_TIMER_DECLARE(_name); \
62+
UART_SHELL_TX_RINGBUF_DECLARE(_name, _tx_ringbuf_size); \
63+
RING_BUF_DECLARE(_name##_rx_ringbuf, _rx_ringbuf_size); \
64+
static const struct shell_uart _name##_shell_uart = { \
65+
.ctrl_blk = &_name##_ctrl_blk, \
66+
.timer = UART_SHELL_RX_TIMER_PTR(_name), \
67+
.tx_ringbuf = UART_SHELL_TX_RINGBUF_PTR(_name), \
68+
.rx_ringbuf = &_name##_rx_ringbuf, \
69+
}; \
2970
struct shell_transport _name = { \
3071
.api = &shell_uart_transport_api, \
3172
.ctx = (struct shell_uart *)&_name##_shell_uart \

samples/net/echo_server/overlay-netusb.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ CONFIG_USB_DEVICE_NETWORK_ECM=y
99
CONFIG_USB_DRIVER_LOG_LEVEL_INF=y
1010
CONFIG_USB_DEVICE_LOG_LEVEL_INF=y
1111
CONFIG_INIT_STACKS=n
12+
13+
# Disable shell built-in commands to reduce ROM footprint
14+
CONFIG_SHELL_CMDS=n

subsys/shell/Kconfig.backends

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,46 @@ config SHELL_BACKEND_SERIAL
1818
bool "Enable serial backends."
1919
default y
2020
select SERIAL
21+
select RING_BUFFER
2122
help
2223
Enable serial backends.
2324

25+
# Internal config to enable UART interrupts if supported.
26+
config SHELL_BACKEND_SERIAL_FORCE_INTERRUPTS
27+
bool
28+
default y
29+
depends on SERIAL_SUPPORT_INTERRUPT
30+
imply UART_INTERRUPT_DRIVEN
31+
32+
if SHELL_BACKEND_SERIAL
33+
34+
config SHELL_BACKEND_SERIAL_TX_RING_BUFFER_SIZE
35+
int "Set TX ring buffer size"
36+
default 8
37+
depends on UART_INTERRUPT_DRIVEN
38+
help
39+
If UART is utilizing DMA transfers then increasing ring buffer size
40+
increases transfers length and reduces number of interrupts.
41+
42+
config SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE
43+
int "Set RX ring buffer size"
44+
default 64
45+
help
46+
RX ring buffer size impacts accepted latency of handling incoming
47+
bytes by shell. If shell input is coming from the keyboard then it is
48+
usually enough if ring buffer is few bytes (more than one due to
49+
escape sequences). However, if bulk data is transferred it may be
50+
required to increase it.
51+
52+
config SHELL_BACKEND_SERIAL_RX_POLL_PERIOD
53+
int "RX polling period (in milliseconds)"
54+
default 10
55+
depends on !UART_INTERRUPT_DRIVEN
56+
help
57+
Determines how often UART is polled for RX byte.
58+
59+
endif #SHELL_BACKEND_SERIAL
60+
2461
config SHELL_BACKEND_RTT
2562
bool "Enable RTT backend."
2663
select RTT_CONSOLE

subsys/shell/shell_uart.c

Lines changed: 150 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,131 @@
77
#include <shell/shell_uart.h>
88
#include <uart.h>
99
#include <init.h>
10+
#include <logging/log.h>
1011

11-
SHELL_UART_DEFINE(shell_transport_uart);
12+
#define LOG_MODULE_NAME shell_uart
13+
LOG_MODULE_REGISTER(shell_uart);
14+
15+
#ifdef CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD
16+
#define RX_POLL_PERIOD CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD
17+
#else
18+
#define RX_POLL_PERIOD 0
19+
#endif
20+
21+
SHELL_UART_DEFINE(shell_transport_uart,
22+
CONFIG_SHELL_BACKEND_SERIAL_TX_RING_BUFFER_SIZE,
23+
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE);
1224
SHELL_DEFINE(uart_shell, "uart:~$ ", &shell_transport_uart, 10,
1325
SHELL_FLAG_OLF_CRLF);
1426

15-
static void timer_handler(struct k_timer *timer)
27+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
28+
static void uart_rx_handle(const struct shell_uart *sh_uart)
1629
{
17-
struct shell_uart *sh_uart =
18-
CONTAINER_OF(timer, struct shell_uart, timer);
30+
u8_t *data;
31+
u32_t len;
32+
u32_t rd_len;
33+
bool new_data = false;
34+
35+
do {
36+
len = ring_buf_put_claim(sh_uart->rx_ringbuf, &data,
37+
sh_uart->rx_ringbuf->size);
38+
rd_len = uart_fifo_read(sh_uart->ctrl_blk->dev, data, len);
39+
40+
if (rd_len) {
41+
new_data = true;
42+
}
43+
44+
ring_buf_put_finish(sh_uart->rx_ringbuf, rd_len);
45+
} while (rd_len && (rd_len == len));
46+
47+
if (new_data) {
48+
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_RX_RDY,
49+
sh_uart->ctrl_blk->context);
50+
}
51+
}
1952

20-
if (uart_poll_in(sh_uart->dev, sh_uart->rx) == 0) {
21-
sh_uart->rx_cnt = 1;
22-
sh_uart->handler(SHELL_TRANSPORT_EVT_RX_RDY, sh_uart->context);
53+
static void uart_tx_handle(const struct shell_uart *sh_uart)
54+
{
55+
u32_t len;
56+
u8_t *data;
57+
int err;
58+
struct device *dev = sh_uart->ctrl_blk->dev;
59+
60+
len = ring_buf_get_claim(sh_uart->tx_ringbuf, &data,
61+
sh_uart->tx_ringbuf->size);
62+
if (len) {
63+
len = uart_fifo_fill(dev, data, len);
64+
err = ring_buf_get_finish(sh_uart->tx_ringbuf, len);
65+
__ASSERT_NO_MSG(err == 0);
66+
} else {
67+
uart_irq_tx_disable(dev);
68+
sh_uart->ctrl_blk->tx_busy = 0;
2369
}
70+
71+
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_TX_RDY,
72+
sh_uart->ctrl_blk->context);
2473
}
2574

75+
static void uart_callback(void *user_data)
76+
{
77+
const struct shell_uart *sh_uart = (struct shell_uart *)user_data;
78+
struct device *dev = sh_uart->ctrl_blk->dev;
79+
80+
uart_irq_update(dev);
81+
82+
if (uart_irq_rx_ready(dev)) {
83+
uart_rx_handle(sh_uart);
84+
}
85+
86+
if (uart_irq_tx_ready(dev)) {
87+
uart_tx_handle(sh_uart);
88+
}
89+
}
90+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
91+
92+
static void uart_irq_init(const struct shell_uart *sh_uart)
93+
{
94+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
95+
struct device *dev = sh_uart->ctrl_blk->dev;
96+
97+
uart_irq_callback_user_data_set(dev, uart_callback, (void *)sh_uart);
98+
uart_irq_rx_enable(dev);
99+
#endif
100+
}
101+
102+
static void timer_handler(struct k_timer *timer)
103+
{
104+
u8_t c;
105+
const struct shell_uart *sh_uart = k_timer_user_data_get(timer);
106+
107+
while (uart_poll_in(sh_uart->ctrl_blk->dev, &c) == 0) {
108+
if (ring_buf_put(sh_uart->rx_ringbuf, &c, 1) == 0) {
109+
/* ring buffer full. */
110+
LOG_WRN("RX ring buffer full.");
111+
}
112+
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_RX_RDY,
113+
sh_uart->ctrl_blk->context);
114+
}
115+
}
26116

27117
static int init(const struct shell_transport *transport,
28118
const void *config,
29119
shell_transport_handler_t evt_handler,
30120
void *context)
31121
{
32-
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
33-
34-
sh_uart->dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
35-
sh_uart->handler = evt_handler;
36-
sh_uart->context = context;
122+
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
37123

38-
k_timer_init(&sh_uart->timer, timer_handler, NULL);
124+
sh_uart->ctrl_blk->dev = (struct device *)config;
125+
sh_uart->ctrl_blk->handler = evt_handler;
126+
sh_uart->ctrl_blk->context = context;
39127

40-
k_timer_start(&sh_uart->timer, 20, 20);
128+
if (IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)) {
129+
uart_irq_init(sh_uart);
130+
} else {
131+
k_timer_init(sh_uart->timer, timer_handler, NULL);
132+
k_timer_user_data_set(sh_uart->timer, (void *)sh_uart);
133+
k_timer_start(sh_uart->timer, RX_POLL_PERIOD, RX_POLL_PERIOD);
134+
}
41135

42136
return 0;
43137
}
@@ -49,22 +143,54 @@ static int uninit(const struct shell_transport *transport)
49143

50144
static int enable(const struct shell_transport *transport, bool blocking)
51145
{
146+
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
147+
148+
sh_uart->ctrl_blk->blocking = blocking;
149+
150+
if (blocking) {
151+
if (!IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN)) {
152+
k_timer_stop(sh_uart->timer);
153+
}
154+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
155+
uart_irq_rx_disable(sh_uart->ctrl_blk->dev);
156+
uart_irq_tx_disable(sh_uart->ctrl_blk->dev);
157+
#endif
158+
}
159+
52160
return 0;
53161
}
54162

163+
static void irq_write(const struct shell_uart *sh_uart, const void *data,
164+
size_t length, size_t *cnt)
165+
{
166+
*cnt = ring_buf_put(sh_uart->tx_ringbuf, data, length);
167+
168+
if (atomic_set(&sh_uart->ctrl_blk->tx_busy, 1) == 0) {
169+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
170+
uart_irq_tx_enable(sh_uart->ctrl_blk->dev);
171+
#endif
172+
}
173+
}
174+
55175
static int write(const struct shell_transport *transport,
56176
const void *data, size_t length, size_t *cnt)
57177
{
58-
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
178+
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
59179
const u8_t *data8 = (const u8_t *)data;
60180

61-
for (size_t i = 0; i < length; i++) {
62-
uart_poll_out(sh_uart->dev, data8[i]);
63-
}
181+
if (IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) &&
182+
!sh_uart->ctrl_blk->blocking) {
183+
irq_write(sh_uart, data, length, cnt);
184+
} else {
185+
for (size_t i = 0; i < length; i++) {
186+
uart_poll_out(sh_uart->ctrl_blk->dev, data8[i]);
187+
}
64188

65-
*cnt = length;
189+
*cnt = length;
66190

67-
sh_uart->handler(SHELL_TRANSPORT_EVT_TX_RDY, sh_uart->context);
191+
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_TX_RDY,
192+
sh_uart->ctrl_blk->context);
193+
}
68194

69195
return 0;
70196
}
@@ -73,17 +199,8 @@ static int read(const struct shell_transport *transport,
73199
void *data, size_t length, size_t *cnt)
74200
{
75201
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
76-
u32_t key;
77202

78-
key = irq_lock();
79-
if (sh_uart->rx_cnt) {
80-
memcpy(data, sh_uart->rx, 1);
81-
sh_uart->rx_cnt = 0;
82-
*cnt = 1;
83-
} else {
84-
*cnt = 0;
85-
}
86-
irq_unlock(key);
203+
*cnt = ring_buf_get(sh_uart->rx_ringbuf, data, length);
87204

88205
return 0;
89206
}
@@ -99,7 +216,9 @@ const struct shell_transport_api shell_uart_transport_api = {
99216
static int enable_shell_uart(struct device *arg)
100217
{
101218
ARG_UNUSED(arg);
102-
shell_init(&uart_shell, NULL, true, true, LOG_LEVEL_INF);
219+
struct device *dev =
220+
device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
221+
shell_init(&uart_shell, dev, true, true, LOG_LEVEL_INF);
103222
return 0;
104223
}
105224
SYS_INIT(enable_shell_uart, POST_KERNEL, 0);

0 commit comments

Comments
 (0)