subsys: mgmt: UART transport for SMP (mcumgr).

Add a UART driver dedicated to transporting mcumgr SMP requests and
responses.

Signed-off-by: Christopher Collins <ccollins@apache.org>
This commit is contained in:
Christopher Collins 2018-01-17 18:01:52 -08:00 committed by Carles Cufí
parent 76bf5646d5
commit 14735116d1
7 changed files with 566 additions and 20 deletions

View file

@ -3,6 +3,7 @@ zephyr_sources_if_kconfig(ram_console.c)
zephyr_sources_if_kconfig(rtt_console.c)
zephyr_sources_if_kconfig(ipm_console_receiver.c)
zephyr_sources_if_kconfig(ipm_console_sender.c)
zephyr_sources_if_kconfig(uart_mcumgr.c)
zephyr_sources_if_kconfig(uart_pipe.c)
zephyr_sources_if_kconfig(telnet_console.c)
zephyr_sources_if_kconfig(xtensa_sim_console.c)

View file

@ -79,6 +79,17 @@ config UART_CONSOLE_DEBUG_SERVER_HOOKS
they are some sort of control characters, or let the regular console
code handle them if they are of no special significance to it.
config UART_CONSOLE_MCUMGR
bool
prompt "Enable UART console mcumgr passthrough"
default n
depends on UART_CONSOLE
help
Enables the UART console to receive mcumgr frames for image upgrade
and device management. When enabled, the UART console does not
process mcumgr frames, but it hands them up to a higher level module
(e.g., the shell). If unset, incoming mcumgr frames are dropped.
config USB_UART_CONSOLE
bool
prompt "Use USB port for console outputs"
@ -189,6 +200,51 @@ config UART_PIPE_ON_DEV_NAME
for pipe UART.
endif
config UART_MCUMGR
bool
prompt "Enable mcumgr UART driver"
select UART_INTERRUPT_DRIVEN
default n
help
Enable the mcumgr UART driver. This driver allows the application to
communicate over UART using the mcumgr protocol for image upgrade and
device management. The driver doesn't inspect received data (as
contrary to console UART driver) and all aspects of received protocol
data are handled by an application provided callback.
if UART_MCUMGR
if !HAS_DTS
config UART_MCUMGR_ON_DEV_NAME
string "Device Name of UART Device for mcumgr UART"
default "UART_0"
depends on UART_MCUMGR
help
This option specifies the name of UART device to be used
for mcumgr UART.
endif # !HAS_DTS
config UART_MCUMGR_RX_BUF_SIZE
int
prompt "Size of receive buffer for mcumgr fragments received over UART, in bytes"
default 128
help
Specifies the size of the mcumgr UART receive buffer, in bytes. This
value must be large enough to accommodate any line sent by an mcumgr
client.
config UART_MCUMGR_RX_BUF_COUNT
int
prompt "Number of receive buffers for mcumgr fragments received over UART"
default 2
help
Specifies the number of the mcumgr UART receive buffers. Receive
buffers hold received mcumgr fragments prior to reassembly. This
setting's value must satisfy the following relation:
UART_MCUMGR_RX_BUF_COUNT * UART_MCUMGR_RX_BUF_SIZE >=
MCUMGR_SMP_UART_MTU
endif # UART_MCUMGR
config XTENSA_SIM_CONSOLE
bool
prompt "Use Xtensa simulator console"

View file

@ -32,6 +32,9 @@
#include <linker/sections.h>
#include <atomic.h>
#include <misc/printk.h>
#ifdef CONFIG_UART_CONSOLE_MCUMGR
#include "mgmt/serial.h"
#endif
static struct device *uart_console_dev;
@ -43,8 +46,7 @@ void uart_console_in_debug_hook_install(uart_console_in_debug_hook_t hook)
debug_hook_in = hook;
}
static UART_CONSOLE_OUT_DEBUG_HOOK_SIG(debug_hook_out_nop)
{
static UART_CONSOLE_OUT_DEBUG_HOOK_SIG(debug_hook_out_nop) {
ARG_UNUSED(c);
return !UART_CONSOLE_DEBUG_HOOK_HANDLED;
}
@ -101,7 +103,7 @@ static int console_out(int c)
return c;
}
#endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
#endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
if ('\n' == c) {
uart_poll_out(uart_console_dev, '\r');
@ -116,16 +118,16 @@ static int console_out(int c)
#if defined(CONFIG_STDOUT_CONSOLE)
extern void __stdout_hook_install(int (*hook)(int));
#else
#define __stdout_hook_install(x) \
do {/* nothing */ \
#define __stdout_hook_install(x) \
do { /* nothing */ \
} while ((0))
#endif
#if defined(CONFIG_PRINTK)
extern void __printk_hook_install(int (*fn)(int));
#else
#define __printk_hook_install(x) \
do {/* nothing */ \
#define __printk_hook_install(x) \
do { /* nothing */ \
} while ((0))
#endif
@ -239,7 +241,13 @@ enum {
ESC_ANSI,
ESC_ANSI_FIRST,
ESC_ANSI_VAL,
ESC_ANSI_VAL_2
ESC_ANSI_VAL_2,
#ifdef CONFIG_UART_CONSOLE_MCUMGR
ESC_MCUMGR_PKT_1,
ESC_MCUMGR_PKT_2,
ESC_MCUMGR_FRAG_1,
ESC_MCUMGR_FRAG_2,
#endif
};
static atomic_t esc_state;
@ -335,6 +343,113 @@ ansi_cmd:
atomic_clear_bit(&esc_state, ESC_ANSI);
}
#ifdef CONFIG_UART_CONSOLE_MCUMGR
static void clear_mcumgr(void)
{
atomic_clear_bit(&esc_state, ESC_MCUMGR_PKT_1);
atomic_clear_bit(&esc_state, ESC_MCUMGR_PKT_2);
atomic_clear_bit(&esc_state, ESC_MCUMGR_FRAG_1);
atomic_clear_bit(&esc_state, ESC_MCUMGR_FRAG_2);
}
/**
* These states indicate whether an mcumgr frame is being received.
*/
#define CONSOLE_MCUMGR_STATE_NONE 1
#define CONSOLE_MCUMGR_STATE_HEADER 2
#define CONSOLE_MCUMGR_STATE_PAYLOAD 3
static int read_mcumgr_byte(uint8_t byte)
{
bool frag_1;
bool frag_2;
bool pkt_1;
bool pkt_2;
pkt_1 = atomic_test_bit(&esc_state, ESC_MCUMGR_PKT_1);
pkt_2 = atomic_test_bit(&esc_state, ESC_MCUMGR_PKT_2);
frag_1 = atomic_test_bit(&esc_state, ESC_MCUMGR_FRAG_1);
frag_2 = atomic_test_bit(&esc_state, ESC_MCUMGR_FRAG_2);
if (pkt_2 || frag_2) {
/* Already fully framed. */
return CONSOLE_MCUMGR_STATE_PAYLOAD;
}
if (pkt_1) {
if (byte == MCUMGR_SERIAL_HDR_PKT_2) {
/* Final framing byte received. */
atomic_set_bit(&esc_state, ESC_MCUMGR_PKT_2);
return CONSOLE_MCUMGR_STATE_PAYLOAD;
}
} else if (frag_1) {
if (byte == MCUMGR_SERIAL_HDR_FRAG_2) {
/* Final framing byte received. */
atomic_set_bit(&esc_state, ESC_MCUMGR_FRAG_2);
return CONSOLE_MCUMGR_STATE_PAYLOAD;
}
} else {
if (byte == MCUMGR_SERIAL_HDR_PKT_1) {
/* First framing byte received. */
atomic_set_bit(&esc_state, ESC_MCUMGR_PKT_1);
return CONSOLE_MCUMGR_STATE_HEADER;
} else if (byte == MCUMGR_SERIAL_HDR_FRAG_1) {
/* First framing byte received. */
atomic_set_bit(&esc_state, ESC_MCUMGR_FRAG_1);
return CONSOLE_MCUMGR_STATE_HEADER;
}
}
/* Non-mcumgr byte received. */
return CONSOLE_MCUMGR_STATE_NONE;
}
/**
* @brief Attempts to process a received byte as part of an mcumgr frame.
*
* @param cmd The console command currently being received.
* @param byte The byte just received.
*
* @return true if the command being received is an mcumgr frame; false if it
* is a plain console command.
*/
static bool handle_mcumgr(struct console_input *cmd, uint8_t byte)
{
int mcumgr_state;
mcumgr_state = read_mcumgr_byte(byte);
if (mcumgr_state == CONSOLE_MCUMGR_STATE_NONE) {
/* Not an mcumgr command; let the normal console handling
* process the byte.
*/
cmd->is_mcumgr = 0;
return false;
}
/* The received byte is part of an mcumgr command. Process the byte
* and return true to indicate that normal console handling should
* ignore it.
*/
if (cur + end < sizeof(cmd->line) - 1) {
cmd->line[cur++] = byte;
}
if (mcumgr_state == CONSOLE_MCUMGR_STATE_PAYLOAD && byte == '\n') {
cmd->line[cur + end] = '\0';
cmd->is_mcumgr = 1;
k_fifo_put(lines_queue, cmd);
clear_mcumgr();
cmd = NULL;
cur = 0;
end = 0;
}
return true;
}
#endif /* CONFIG_UART_CONSOLE_MCUMGR */
void uart_console_isr(struct device *unused)
{
ARG_UNUSED(unused);
@ -373,6 +488,15 @@ void uart_console_isr(struct device *unused)
}
}
#ifdef CONFIG_UART_CONSOLE_MCUMGR
/* Divert this byte from normal console handling if it is part
* of an mcumgr frame.
*/
if (handle_mcumgr(cmd, byte)) {
continue;
}
#endif /* CONFIG_UART_CONSOLE_MCUMGR */
/* Handle ANSI escape mode */
if (atomic_test_bit(&esc_state, ESC_ANSI)) {
handle_ansi(byte, cmd->line);
@ -389,8 +513,8 @@ void uart_console_isr(struct device *unused)
continue;
}
/* Handle special control characters */
if (!isprint(byte)) {
/* Handle special control characters */
switch (byte) {
case DEL:
if (cur > 0) {
@ -417,8 +541,6 @@ void uart_console_isr(struct device *unused)
default:
break;
}
continue;
}
/* Ignore characters if there's no more buffer space */
@ -456,11 +578,11 @@ void uart_register_input(struct k_fifo *avail, struct k_fifo *lines,
}
#else
#define console_input_init(x) \
do {/* nothing */ \
#define console_input_init(x) \
do { /* nothing */ \
} while ((0))
#define uart_register_input(x) \
do {/* nothing */ \
#define uart_register_input(x) \
do { /* nothing */ \
} while ((0))
#endif
@ -510,10 +632,10 @@ static int uart_console_init(struct device *arg)
/* UART console initializes after the UART device itself */
SYS_INIT(uart_console_init,
#if defined(CONFIG_USB_UART_CONSOLE)
APPLICATION,
APPLICATION,
#elif defined(CONFIG_EARLY_CONSOLE)
PRE_KERNEL_1,
PRE_KERNEL_1,
#else
POST_KERNEL,
POST_KERNEL,
#endif
CONFIG_UART_CONSOLE_INIT_PRIORITY);
CONFIG_UART_CONSOLE_INIT_PRIORITY);

View file

@ -0,0 +1,190 @@
/*
* 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 <assert.h>
#include <string.h>
#include <kernel.h>
#include <uart.h>
#include <mgmt/serial.h>
#include <console/uart_mcumgr.h>
static struct device *uart_mcumgr_dev;
/** 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);
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);
}
/**
* 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);
}
/**
* Processes a single incoming byte.
*/
static struct uart_mcumgr_rx_buf *uart_mcumgr_rx_byte(u8_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;
}
/**
* ISR that is called when UART bytes are received.
*/
static void uart_mcumgr_isr(struct device *unused)
{
struct uart_mcumgr_rx_buf *rx_buf;
u8_t buf[32];
int chunk_len;
int i;
ARG_UNUSED(unused);
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);
}
}
}
}
/**
* Sends raw data over the UART.
*/
static int uart_mcumgr_send_raw(const void *data, int len, void *arg)
{
const u8_t *u8p;
u8p = data;
while (len--) {
uart_poll_out(uart_mcumgr_dev, *u8p++);
}
return 0;
}
int uart_mcumgr_send(const u8_t *data, int len)
{
return mcumgr_serial_tx_pkt(data, len, uart_mcumgr_send_raw, NULL);
}
static void uart_mcumgr_setup(struct device *uart)
{
u8_t c;
uart_irq_rx_disable(uart);
uart_irq_tx_disable(uart);
/* Drain the fifo */
while (uart_fifo_read(uart, &c, 1)) {
continue;
}
uart_irq_callback_set(uart, uart_mcumgr_isr);
uart_irq_rx_enable(uart);
}
void uart_mcumgr_register(uart_mcumgr_recv_fn *cb)
{
uart_mgumgr_recv_cb = cb;
uart_mcumgr_dev = device_get_binding(CONFIG_UART_MCUMGR_ON_DEV_NAME);
if (uart_mcumgr_dev != NULL) {
uart_mcumgr_setup(uart_mcumgr_dev);
}
}

View file

@ -0,0 +1,75 @@
/*
* 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.
*
* @see include/mgmt/serial.h
*/
#ifndef H_UART_MCUMGR_
#define H_UART_MCUMGR_
#include <stdlib.h>
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Contains an mcumgr fragment received over UART.
*/
struct uart_mcumgr_rx_buf {
void *fifo_reserved; /* 1st word reserved for use by fifo */
u8_t data[CONFIG_UART_MCUMGR_RX_BUF_SIZE];
int length;
};
/** @typedef uart_mcumgr_recv_fn
* @brief Function that gets called when an mcumgr packet is received.
*
* Function that gets called when an mcumgr packet is received. This function
* gets called in the interrupt context. Ownership of the specified buffer is
* transferred to the callback when this function gets called.
*
* @param rx_buf A buffer containing the incoming mcumgr packet.
*/
typedef void uart_mcumgr_recv_fn(struct uart_mcumgr_rx_buf *rx_buf);
/**
* @brief Sends an mcumgr packet over UART.
*
* @param data Buffer containing the mcumgr packet to send.
* @param len The length of the buffer, in bytes.
*
* @return 0 on success; negative error code on failure.
*/
int uart_mcumgr_send(const u8_t *data, int len);
/**
* @brief Frees the supplied receive buffer.
*
* @param rx_buf The buffer to free.
*/
void uart_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf *rx_buf);
/**
* @brief Registers an mcumgr UART receive handler.
*
* Configures the mcumgr UART driver to call the specified function when an
* mcumgr request packet is received.
*
* @param cb The callback to execute when an mcumgr request
* packet is received.
*/
void uart_mcumgr_register(uart_mcumgr_recv_fn *cb);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -877,7 +877,8 @@ def main():
"CONFIG_UART_CONSOLE_ON_DEV_NAME": "zephyr,console",
"CONFIG_BT_UART_ON_DEV_NAME": "zephyr,bt-uart",
"CONFIG_UART_PIPE_ON_DEV_NAME": "zephyr,uart-pipe",
"CONFIG_BT_MONITOR_ON_DEV_NAME": "zephyr,bt-mon-uart"
"CONFIG_BT_MONITOR_ON_DEV_NAME": "zephyr,bt-mon-uart",
"CONFIG_UART_MCUMGR_ON_DEV_NAME": "zephyr,uart-mcumgr",
}
for k, v in name_dict.items():

101
subsys/mgmt/smp_uart.c Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright Runtime.io 2018. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief UART transport for the mcumgr SMP protocol.
*/
#include <string.h>
#include <zephyr.h>
#include <init.h>
#include "net/buf.h"
#include "console/uart_mcumgr.h"
#include "mgmt/mgmt.h"
#include <mgmt/serial.h>
#include "mgmt/buf.h"
#include "mgmt/smp.h"
struct device;
static void smp_uart_process_rx_queue(struct k_work *work);
K_FIFO_DEFINE(smp_uart_rx_fifo);
K_WORK_DEFINE(smp_uart_work, smp_uart_process_rx_queue);
static struct mcumgr_serial_rx_ctxt smp_uart_rx_ctxt;
static struct zephyr_smp_transport smp_uart_transport;
/**
* Processes a single line (fragment) coming from the mcumgr UART driver.
*/
static void smp_uart_process_frag(struct uart_mcumgr_rx_buf *rx_buf)
{
struct net_buf *nb;
/* Decode the fragment and write the result to the global receive
* context.
*/
nb = mcumgr_serial_process_frag(&smp_uart_rx_ctxt,
rx_buf->data, rx_buf->length);
/* Release the encoded fragment. */
uart_mcumgr_free_rx_buf(rx_buf);
/* If a complete packet has been received, pass it to SMP for
* processing.
*/
if (nb != NULL) {
zephyr_smp_rx_req(&smp_uart_transport, nb);
}
}
static void smp_uart_process_rx_queue(struct k_work *work)
{
struct uart_mcumgr_rx_buf *rx_buf;
while ((rx_buf = k_fifo_get(&smp_uart_rx_fifo, K_NO_WAIT)) != NULL) {
smp_uart_process_frag(rx_buf);
}
}
/**
* Enqueues a received SMP fragment for later processing. This function
* executes in the interrupt context.
*/
static void smp_uart_rx_frag(struct uart_mcumgr_rx_buf *rx_buf)
{
k_fifo_put(&smp_uart_rx_fifo, rx_buf);
k_work_submit(&smp_uart_work);
}
static u16_t smp_uart_get_mtu(const struct net_buf *nb)
{
return CONFIG_MCUMGR_SMP_UART_MTU;
}
static int smp_uart_tx_pkt(struct zephyr_smp_transport *zst,
struct net_buf *nb)
{
int rc;
rc = uart_mcumgr_send(nb->data, nb->len);
mcumgr_buf_free(nb);
return rc;
}
static int smp_uart_init(struct device *dev)
{
ARG_UNUSED(dev);
zephyr_smp_transport_init(&smp_uart_transport, smp_uart_tx_pkt,
smp_uart_get_mtu);
uart_mcumgr_register(smp_uart_rx_frag);
return 0;
}
SYS_INIT(smp_uart_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);