You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
5.6 KiB
247 lines
5.6 KiB
/* |
|
* 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_mcumgr_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_mcumgr_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_mcumgr_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_mcumgr_recv_cb = cb; |
|
|
|
if (device_is_ready(uart_mcumgr_dev)) { |
|
uart_mcumgr_setup(uart_mcumgr_dev); |
|
} |
|
}
|
|
|