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:
parent
76bf5646d5
commit
14735116d1
|
@ -3,6 +3,7 @@ zephyr_sources_if_kconfig(ram_console.c)
|
||||||
zephyr_sources_if_kconfig(rtt_console.c)
|
zephyr_sources_if_kconfig(rtt_console.c)
|
||||||
zephyr_sources_if_kconfig(ipm_console_receiver.c)
|
zephyr_sources_if_kconfig(ipm_console_receiver.c)
|
||||||
zephyr_sources_if_kconfig(ipm_console_sender.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(uart_pipe.c)
|
||||||
zephyr_sources_if_kconfig(telnet_console.c)
|
zephyr_sources_if_kconfig(telnet_console.c)
|
||||||
zephyr_sources_if_kconfig(xtensa_sim_console.c)
|
zephyr_sources_if_kconfig(xtensa_sim_console.c)
|
||||||
|
|
|
@ -79,6 +79,17 @@ config UART_CONSOLE_DEBUG_SERVER_HOOKS
|
||||||
they are some sort of control characters, or let the regular console
|
they are some sort of control characters, or let the regular console
|
||||||
code handle them if they are of no special significance to it.
|
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
|
config USB_UART_CONSOLE
|
||||||
bool
|
bool
|
||||||
prompt "Use USB port for console outputs"
|
prompt "Use USB port for console outputs"
|
||||||
|
@ -189,6 +200,51 @@ config UART_PIPE_ON_DEV_NAME
|
||||||
for pipe UART.
|
for pipe UART.
|
||||||
endif
|
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
|
config XTENSA_SIM_CONSOLE
|
||||||
bool
|
bool
|
||||||
prompt "Use Xtensa simulator console"
|
prompt "Use Xtensa simulator console"
|
||||||
|
|
|
@ -32,6 +32,9 @@
|
||||||
#include <linker/sections.h>
|
#include <linker/sections.h>
|
||||||
#include <atomic.h>
|
#include <atomic.h>
|
||||||
#include <misc/printk.h>
|
#include <misc/printk.h>
|
||||||
|
#ifdef CONFIG_UART_CONSOLE_MCUMGR
|
||||||
|
#include "mgmt/serial.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct device *uart_console_dev;
|
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;
|
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);
|
ARG_UNUSED(c);
|
||||||
return !UART_CONSOLE_DEBUG_HOOK_HANDLED;
|
return !UART_CONSOLE_DEBUG_HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +103,7 @@ static int console_out(int c)
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
|
#endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
|
||||||
|
|
||||||
if ('\n' == c) {
|
if ('\n' == c) {
|
||||||
uart_poll_out(uart_console_dev, '\r');
|
uart_poll_out(uart_console_dev, '\r');
|
||||||
|
@ -116,16 +118,16 @@ static int console_out(int c)
|
||||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||||
extern void __stdout_hook_install(int (*hook)(int));
|
extern void __stdout_hook_install(int (*hook)(int));
|
||||||
#else
|
#else
|
||||||
#define __stdout_hook_install(x) \
|
#define __stdout_hook_install(x) \
|
||||||
do {/* nothing */ \
|
do { /* nothing */ \
|
||||||
} while ((0))
|
} while ((0))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_PRINTK)
|
#if defined(CONFIG_PRINTK)
|
||||||
extern void __printk_hook_install(int (*fn)(int));
|
extern void __printk_hook_install(int (*fn)(int));
|
||||||
#else
|
#else
|
||||||
#define __printk_hook_install(x) \
|
#define __printk_hook_install(x) \
|
||||||
do {/* nothing */ \
|
do { /* nothing */ \
|
||||||
} while ((0))
|
} while ((0))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -239,7 +241,13 @@ enum {
|
||||||
ESC_ANSI,
|
ESC_ANSI,
|
||||||
ESC_ANSI_FIRST,
|
ESC_ANSI_FIRST,
|
||||||
ESC_ANSI_VAL,
|
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;
|
static atomic_t esc_state;
|
||||||
|
@ -335,6 +343,113 @@ ansi_cmd:
|
||||||
atomic_clear_bit(&esc_state, ESC_ANSI);
|
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)
|
void uart_console_isr(struct device *unused)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(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 */
|
/* Handle ANSI escape mode */
|
||||||
if (atomic_test_bit(&esc_state, ESC_ANSI)) {
|
if (atomic_test_bit(&esc_state, ESC_ANSI)) {
|
||||||
handle_ansi(byte, cmd->line);
|
handle_ansi(byte, cmd->line);
|
||||||
|
@ -389,8 +513,8 @@ void uart_console_isr(struct device *unused)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle special control characters */
|
|
||||||
if (!isprint(byte)) {
|
if (!isprint(byte)) {
|
||||||
|
/* Handle special control characters */
|
||||||
switch (byte) {
|
switch (byte) {
|
||||||
case DEL:
|
case DEL:
|
||||||
if (cur > 0) {
|
if (cur > 0) {
|
||||||
|
@ -417,8 +541,6 @@ void uart_console_isr(struct device *unused)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore characters if there's no more buffer space */
|
/* 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
|
#else
|
||||||
#define console_input_init(x) \
|
#define console_input_init(x) \
|
||||||
do {/* nothing */ \
|
do { /* nothing */ \
|
||||||
} while ((0))
|
} while ((0))
|
||||||
#define uart_register_input(x) \
|
#define uart_register_input(x) \
|
||||||
do {/* nothing */ \
|
do { /* nothing */ \
|
||||||
} while ((0))
|
} while ((0))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -510,10 +632,10 @@ static int uart_console_init(struct device *arg)
|
||||||
/* UART console initializes after the UART device itself */
|
/* UART console initializes after the UART device itself */
|
||||||
SYS_INIT(uart_console_init,
|
SYS_INIT(uart_console_init,
|
||||||
#if defined(CONFIG_USB_UART_CONSOLE)
|
#if defined(CONFIG_USB_UART_CONSOLE)
|
||||||
APPLICATION,
|
APPLICATION,
|
||||||
#elif defined(CONFIG_EARLY_CONSOLE)
|
#elif defined(CONFIG_EARLY_CONSOLE)
|
||||||
PRE_KERNEL_1,
|
PRE_KERNEL_1,
|
||||||
#else
|
#else
|
||||||
POST_KERNEL,
|
POST_KERNEL,
|
||||||
#endif
|
#endif
|
||||||
CONFIG_UART_CONSOLE_INIT_PRIORITY);
|
CONFIG_UART_CONSOLE_INIT_PRIORITY);
|
||||||
|
|
190
drivers/console/uart_mcumgr.c
Normal file
190
drivers/console/uart_mcumgr.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
75
include/drivers/console/uart_mcumgr.h
Normal file
75
include/drivers/console/uart_mcumgr.h
Normal 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
|
|
@ -877,7 +877,8 @@ def main():
|
||||||
"CONFIG_UART_CONSOLE_ON_DEV_NAME": "zephyr,console",
|
"CONFIG_UART_CONSOLE_ON_DEV_NAME": "zephyr,console",
|
||||||
"CONFIG_BT_UART_ON_DEV_NAME": "zephyr,bt-uart",
|
"CONFIG_BT_UART_ON_DEV_NAME": "zephyr,bt-uart",
|
||||||
"CONFIG_UART_PIPE_ON_DEV_NAME": "zephyr,uart-pipe",
|
"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():
|
for k, v in name_dict.items():
|
||||||
|
|
101
subsys/mgmt/smp_uart.c
Normal file
101
subsys/mgmt/smp_uart.c
Normal 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);
|
Loading…
Reference in a new issue