zephyr/drivers/console/uart_mcumgr.c
Jamie McCrae 1b364c1422 drivers: console: uart_mcumgr: Skip reading FIFO during setup
Drops calling the UART FIFO read function during the setup
function (when not in async mode) which could cause issues on
some devices since this function is not called in an ISR.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2023-11-27 09:12:36 +00:00

248 lines
5.6 KiB
C

/*
* Copyright Runtime.io 2018. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief A driver for sending and receiving mcumgr packets over UART.
*/
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/mgmt/mcumgr/transport/serial.h>
#include <zephyr/drivers/console/uart_mcumgr.h>
static const struct device *const uart_mcumgr_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr));
/** Callback to execute when a valid fragment has been received. */
static uart_mcumgr_recv_fn *uart_mgumgr_recv_cb;
/** Contains the fragment currently being received. */
static struct uart_mcumgr_rx_buf *uart_mcumgr_cur_buf;
/**
* Whether the line currently being read should be ignored. This is true if
* the line is too long or if there is no buffer available to hold it.
*/
static bool uart_mcumgr_ignoring;
/** Contains buffers to hold incoming request fragments. */
K_MEM_SLAB_DEFINE(uart_mcumgr_slab, sizeof(struct uart_mcumgr_rx_buf),
CONFIG_UART_MCUMGR_RX_BUF_COUNT, 1);
#if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC)
uint8_t async_buffer[CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUFS]
[CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUF_SIZE];
static int async_current;
#endif
static struct uart_mcumgr_rx_buf *uart_mcumgr_alloc_rx_buf(void)
{
struct uart_mcumgr_rx_buf *rx_buf;
void *block;
int rc;
rc = k_mem_slab_alloc(&uart_mcumgr_slab, &block, K_NO_WAIT);
if (rc != 0) {
return NULL;
}
rx_buf = block;
rx_buf->length = 0;
return rx_buf;
}
void uart_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf *rx_buf)
{
void *block;
block = rx_buf;
k_mem_slab_free(&uart_mcumgr_slab, block);
}
#if !defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC)
/**
* Reads a chunk of received data from the UART.
*/
static int uart_mcumgr_read_chunk(void *buf, int capacity)
{
if (!uart_irq_rx_ready(uart_mcumgr_dev)) {
return 0;
}
return uart_fifo_read(uart_mcumgr_dev, buf, capacity);
}
#endif
/**
* Processes a single incoming byte.
*/
static struct uart_mcumgr_rx_buf *uart_mcumgr_rx_byte(uint8_t byte)
{
struct uart_mcumgr_rx_buf *rx_buf;
if (!uart_mcumgr_ignoring) {
if (uart_mcumgr_cur_buf == NULL) {
uart_mcumgr_cur_buf = uart_mcumgr_alloc_rx_buf();
if (uart_mcumgr_cur_buf == NULL) {
/* Insufficient buffers; drop this fragment. */
uart_mcumgr_ignoring = true;
}
}
}
rx_buf = uart_mcumgr_cur_buf;
if (!uart_mcumgr_ignoring) {
if (rx_buf->length >= sizeof(rx_buf->data)) {
/* Line too long; drop this fragment. */
uart_mcumgr_free_rx_buf(uart_mcumgr_cur_buf);
uart_mcumgr_cur_buf = NULL;
uart_mcumgr_ignoring = true;
} else {
rx_buf->data[rx_buf->length++] = byte;
}
}
if (byte == '\n') {
/* Fragment complete. */
if (uart_mcumgr_ignoring) {
uart_mcumgr_ignoring = false;
} else {
uart_mcumgr_cur_buf = NULL;
return rx_buf;
}
}
return NULL;
}
#if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC)
static void uart_mcumgr_async(const struct device *dev, struct uart_event *evt, void *user_data)
{
struct uart_mcumgr_rx_buf *rx_buf;
uint8_t *p;
int len;
ARG_UNUSED(dev);
switch (evt->type) {
case UART_TX_DONE:
case UART_TX_ABORTED:
break;
case UART_RX_RDY:
len = evt->data.rx.len;
p = &evt->data.rx.buf[evt->data.rx.offset];
for (int i = 0; i < len; i++) {
rx_buf = uart_mcumgr_rx_byte(p[i]);
if (rx_buf != NULL) {
uart_mgumgr_recv_cb(rx_buf);
}
}
break;
case UART_RX_DISABLED:
async_current = 0;
break;
case UART_RX_BUF_REQUEST:
/*
* Note that when buffer gets filled, the UART_RX_BUF_RELEASED will be reported,
* aside to UART_RX_RDY. The UART_RX_BUF_RELEASED is not processed because
* it has been assumed that the mcumgr will be able to consume bytes faster
* than UART will receive them and, since there is nothing to release, only
* UART_RX_BUF_REQUEST is processed.
*/
++async_current;
async_current %= CONFIG_MCUMGR_TRANSPORT_UART_ASYNC_BUFS;
uart_rx_buf_rsp(dev, async_buffer[async_current],
sizeof(async_buffer[async_current]));
break;
case UART_RX_BUF_RELEASED:
case UART_RX_STOPPED:
break;
}
}
#else
/**
* ISR that is called when UART bytes are received.
*/
static void uart_mcumgr_isr(const struct device *unused, void *user_data)
{
struct uart_mcumgr_rx_buf *rx_buf;
uint8_t buf[32];
int chunk_len;
int i;
ARG_UNUSED(unused);
ARG_UNUSED(user_data);
while (uart_irq_update(uart_mcumgr_dev) &&
uart_irq_is_pending(uart_mcumgr_dev)) {
chunk_len = uart_mcumgr_read_chunk(buf, sizeof(buf));
if (chunk_len == 0) {
continue;
}
for (i = 0; i < chunk_len; i++) {
rx_buf = uart_mcumgr_rx_byte(buf[i]);
if (rx_buf != NULL) {
uart_mgumgr_recv_cb(rx_buf);
}
}
}
}
#endif
/**
* Sends raw data over the UART.
*/
static int uart_mcumgr_send_raw(const void *data, int len)
{
const uint8_t *u8p;
u8p = data;
while (len--) {
uart_poll_out(uart_mcumgr_dev, *u8p++);
}
return 0;
}
int uart_mcumgr_send(const uint8_t *data, int len)
{
return mcumgr_serial_tx_pkt(data, len, uart_mcumgr_send_raw);
}
#if defined(CONFIG_MCUMGR_TRANSPORT_UART_ASYNC)
static void uart_mcumgr_setup(const struct device *uart)
{
uart_callback_set(uart, uart_mcumgr_async, NULL);
uart_rx_enable(uart, async_buffer[0], sizeof(async_buffer[0]), 0);
}
#else
static void uart_mcumgr_setup(const struct device *uart)
{
uart_irq_rx_disable(uart);
uart_irq_tx_disable(uart);
uart_irq_callback_set(uart, uart_mcumgr_isr);
uart_irq_rx_enable(uart);
}
#endif
void uart_mcumgr_register(uart_mcumgr_recv_fn *cb)
{
uart_mgumgr_recv_cb = cb;
if (device_is_ready(uart_mcumgr_dev)) {
uart_mcumgr_setup(uart_mcumgr_dev);
}
}