zephyr/subsys/shell/shell_uart.c
Dominik Ermel 1b617a368f mgmt: smp: Change smp_shell_rx_byte to process data in bulk
The smp_shell_rx_byte has been renamed to smp_shell_rx_bytes and now
accepts data buffer pointer and its size as parameters. Return value
has been changed to size_t and represents number of bytes processed from
the given buffer.

The change has been done to more efficiently serve most common scenario
when the function is called in loop to process buffer, byte by byte.
Previously such operation required passing each byte separately,
with the change the function will work directly on source buffer
reducing number of calls and byte copy operations.

Signed-off-by: Dominik Ermel <dominik.ermel@nordicsemi.no>
2020-09-03 21:53:00 +02:00

306 lines
7.6 KiB
C

/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <shell/shell_uart.h>
#include <drivers/uart.h>
#include <init.h>
#include <logging/log.h>
#define LOG_MODULE_NAME shell_uart
LOG_MODULE_REGISTER(shell_uart);
#ifdef CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD
#define RX_POLL_PERIOD K_MSEC(CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD)
#else
#define RX_POLL_PERIOD K_NO_WAIT
#endif
SHELL_UART_DEFINE(shell_transport_uart,
CONFIG_SHELL_BACKEND_SERIAL_TX_RING_BUFFER_SIZE,
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE);
SHELL_DEFINE(shell_uart, CONFIG_SHELL_PROMPT_UART, &shell_transport_uart,
CONFIG_SHELL_BACKEND_SERIAL_LOG_MESSAGE_QUEUE_SIZE,
CONFIG_SHELL_BACKEND_SERIAL_LOG_MESSAGE_QUEUE_TIMEOUT,
SHELL_FLAG_OLF_CRLF);
#ifdef CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
static void uart_rx_handle(const struct device *dev,
const struct shell_uart *sh_uart)
{
uint8_t *data;
uint32_t len;
uint32_t rd_len;
bool new_data = false;
#ifdef CONFIG_MCUMGR_SMP_SHELL
struct smp_shell_data *const smp = &sh_uart->ctrl_blk->smp;
#endif
do {
len = ring_buf_put_claim(sh_uart->rx_ringbuf, &data,
sh_uart->rx_ringbuf->size);
if (len > 0) {
rd_len = uart_fifo_read(dev, data, len);
/* If there is any new data to be either taken into
* ring buffer or consumed by the SMP, signal the
* shell_thread.
*/
if (rd_len > 0) {
new_data = true;
}
#ifdef CONFIG_MCUMGR_SMP_SHELL
/* Divert bytes from shell handling if it is
* part of an mcumgr frame.
*/
size_t i = smp_shell_rx_bytes(smp, data, rd_len);
rd_len -= i;
if (rd_len) {
for (uint32_t j = 0; j < rd_len; j++) {
data[j] = data[i + j];
}
}
#endif /* CONFIG_MCUMGR_SMP_SHELL */
int err = ring_buf_put_finish(sh_uart->rx_ringbuf,
rd_len);
(void)err;
__ASSERT_NO_MSG(err == 0);
} else {
uint8_t dummy;
/* No space in the ring buffer - consume byte. */
LOG_WRN("RX ring buffer full.");
rd_len = uart_fifo_read(dev, &dummy, 1);
#ifdef CONFIG_MCUMGR_SMP_SHELL
/* If successful in getting byte from the fifo, try
* feeding it to SMP as a part of mcumgr frame.
*/
if ((rd_len != 0) &&
(smp_shell_rx_bytes(smp, &dummy, 1) == 1)) {
new_data = true;
}
#endif /* CONFIG_MCUMGR_SMP_SHELL */
}
} while (rd_len && (rd_len == len));
if (new_data) {
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_RX_RDY,
sh_uart->ctrl_blk->context);
}
}
static void uart_tx_handle(const struct device *dev,
const struct shell_uart *sh_uart)
{
uint32_t len;
int err;
const uint8_t *data;
len = ring_buf_get_claim(sh_uart->tx_ringbuf, (uint8_t **)&data,
sh_uart->tx_ringbuf->size);
if (len) {
len = uart_fifo_fill(dev, data, len);
err = ring_buf_get_finish(sh_uart->tx_ringbuf, len);
__ASSERT_NO_MSG(err == 0);
} else {
uart_irq_tx_disable(dev);
sh_uart->ctrl_blk->tx_busy = 0;
}
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_TX_RDY,
sh_uart->ctrl_blk->context);
}
static void uart_callback(const struct device *dev, void *user_data)
{
const struct shell_uart *sh_uart = (struct shell_uart *)user_data;
uart_irq_update(dev);
if (uart_irq_rx_ready(dev)) {
uart_rx_handle(dev, sh_uart);
}
if (uart_irq_tx_ready(dev)) {
uart_tx_handle(dev, sh_uart);
}
}
#endif /* CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN */
static void uart_irq_init(const struct shell_uart *sh_uart)
{
#ifdef CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
const struct device *dev = sh_uart->ctrl_blk->dev;
uart_irq_callback_user_data_set(dev, uart_callback, (void *)sh_uart);
uart_irq_rx_enable(dev);
#endif
}
static void timer_handler(struct k_timer *timer)
{
uint8_t c;
const struct shell_uart *sh_uart = k_timer_user_data_get(timer);
while (uart_poll_in(sh_uart->ctrl_blk->dev, &c) == 0) {
if (ring_buf_put(sh_uart->rx_ringbuf, &c, 1) == 0U) {
/* ring buffer full. */
LOG_WRN("RX ring buffer full.");
}
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_RX_RDY,
sh_uart->ctrl_blk->context);
}
}
static int init(const struct shell_transport *transport,
const void *config,
shell_transport_handler_t evt_handler,
void *context)
{
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
sh_uart->ctrl_blk->dev = (const struct device *)config;
sh_uart->ctrl_blk->handler = evt_handler;
sh_uart->ctrl_blk->context = context;
if (IS_ENABLED(CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN)) {
uart_irq_init(sh_uart);
} else {
k_timer_init(sh_uart->timer, timer_handler, NULL);
k_timer_user_data_set(sh_uart->timer, (void *)sh_uart);
k_timer_start(sh_uart->timer, RX_POLL_PERIOD, RX_POLL_PERIOD);
}
return 0;
}
static int uninit(const struct shell_transport *transport)
{
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
if (IS_ENABLED(CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN)) {
const struct device *dev = sh_uart->ctrl_blk->dev;
uart_irq_rx_disable(dev);
} else {
k_timer_stop(sh_uart->timer);
}
return 0;
}
static int enable(const struct shell_transport *transport, bool blocking_tx)
{
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
sh_uart->ctrl_blk->blocking_tx = blocking_tx;
if (blocking_tx) {
#ifdef CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
uart_irq_tx_disable(sh_uart->ctrl_blk->dev);
#endif
}
return 0;
}
static void irq_write(const struct shell_uart *sh_uart, const void *data,
size_t length, size_t *cnt)
{
*cnt = ring_buf_put(sh_uart->tx_ringbuf, data, length);
if (atomic_set(&sh_uart->ctrl_blk->tx_busy, 1) == 0) {
#ifdef CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN
uart_irq_tx_enable(sh_uart->ctrl_blk->dev);
#endif
}
}
static int write(const struct shell_transport *transport,
const void *data, size_t length, size_t *cnt)
{
const struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
const uint8_t *data8 = (const uint8_t *)data;
if (IS_ENABLED(CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN) &&
!sh_uart->ctrl_blk->blocking_tx) {
irq_write(sh_uart, data, length, cnt);
} else {
for (size_t i = 0; i < length; i++) {
uart_poll_out(sh_uart->ctrl_blk->dev, data8[i]);
}
*cnt = length;
sh_uart->ctrl_blk->handler(SHELL_TRANSPORT_EVT_TX_RDY,
sh_uart->ctrl_blk->context);
}
return 0;
}
static int read(const struct shell_transport *transport,
void *data, size_t length, size_t *cnt)
{
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
*cnt = ring_buf_get(sh_uart->rx_ringbuf, data, length);
return 0;
}
#ifdef CONFIG_MCUMGR_SMP_SHELL
static void update(const struct shell_transport *transport)
{
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
smp_shell_process(&sh_uart->ctrl_blk->smp);
}
#endif /* CONFIG_MCUMGR_SMP_SHELL */
const struct shell_transport_api shell_uart_transport_api = {
.init = init,
.uninit = uninit,
.enable = enable,
.write = write,
.read = read,
#ifdef CONFIG_MCUMGR_SMP_SHELL
.update = update,
#endif /* CONFIG_MCUMGR_SMP_SHELL */
};
static int enable_shell_uart(const struct device *arg)
{
ARG_UNUSED(arg);
const struct device *dev =
device_get_binding(CONFIG_UART_SHELL_ON_DEV_NAME);
bool log_backend = CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > 0;
uint32_t level =
(CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL > LOG_LEVEL_DBG) ?
CONFIG_LOG_MAX_LEVEL : CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL;
if (dev == NULL) {
return -ENODEV;
}
if (IS_ENABLED(CONFIG_MCUMGR_SMP_SHELL)) {
smp_shell_init();
}
shell_init(&shell_uart, dev, true, log_backend, level);
return 0;
}
SYS_INIT(enable_shell_uart, POST_KERNEL, 0);
const struct shell *shell_backend_uart_get_ptr(void)
{
return &shell_uart;
}