blob: 3b2b3fd380814338601966277e11106ece3da682 [file] [log] [blame]
/*
* Copyright Runtime.io 2018. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief Shell transport for the mcumgr SMP protocol.
*/
#include <string.h>
#include <zephyr.h>
#include <init.h>
#include "net/buf.h"
#include "mgmt/mgmt.h"
#include "mgmt/serial.h"
#include "mgmt/buf.h"
#include "mgmt/smp.h"
#include "mgmt/smp_shell.h"
static struct zephyr_smp_transport smp_shell_transport;
static struct mcumgr_serial_rx_ctxt smp_shell_rx_ctxt;
/** SMP mcumgr frame fragments. */
enum smp_shell_esc_mcumgr {
ESC_MCUMGR_PKT_1,
ESC_MCUMGR_PKT_2,
ESC_MCUMGR_FRAG_1,
ESC_MCUMGR_FRAG_2,
};
/** These states indicate whether an mcumgr frame is being received. */
enum smp_shell_mcumgr_state {
SMP_SHELL_MCUMGR_STATE_NONE,
SMP_SHELL_MCUMGR_STATE_HEADER,
SMP_SHELL_MCUMGR_STATE_PAYLOAD
};
static int read_mcumgr_byte(struct smp_shell_data *data, u8_t byte)
{
bool frag_1;
bool frag_2;
bool pkt_1;
bool pkt_2;
pkt_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
pkt_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
frag_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
frag_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
if (pkt_2 || frag_2) {
/* Already fully framed. */
return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
}
if (pkt_1) {
if (byte == MCUMGR_SERIAL_HDR_PKT_2) {
/* Final framing byte received. */
atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
}
} else if (frag_1) {
if (byte == MCUMGR_SERIAL_HDR_FRAG_2) {
/* Final framing byte received. */
atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
}
} else {
if (byte == MCUMGR_SERIAL_HDR_PKT_1) {
/* First framing byte received. */
atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
return SMP_SHELL_MCUMGR_STATE_HEADER;
} else if (byte == MCUMGR_SERIAL_HDR_FRAG_1) {
/* First framing byte received. */
atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
return SMP_SHELL_MCUMGR_STATE_HEADER;
}
}
/* Non-mcumgr byte received. */
return SMP_SHELL_MCUMGR_STATE_NONE;
}
bool smp_shell_rx_byte(struct smp_shell_data *data, uint8_t byte)
{
int mcumgr_state;
mcumgr_state = read_mcumgr_byte(data, byte);
if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_NONE) {
/* Not an mcumgr command; let the shell process the byte. */
return false;
}
/*
* The received byte is part of an mcumgr command. Process the byte
* and return true to indicate that shell should ignore it.
*/
if (data->cur + data->end < sizeof(data->mcumgr_buff) - 1) {
data->mcumgr_buff[data->cur++] = byte;
}
if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_PAYLOAD && byte == '\n') {
data->mcumgr_buff[data->cur + data->end] = '\0';
data->cmd_rdy = true;
atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
data->cur = 0U;
data->end = 0U;
}
return true;
}
void smp_shell_process(struct smp_shell_data *data)
{
if (data->cmd_rdy) {
data->cmd_rdy = false;
struct net_buf *nb;
int line_len;
/* Strip the trailing newline. */
line_len = strlen(data->mcumgr_buff) - 1;
nb = mcumgr_serial_process_frag(&smp_shell_rx_ctxt,
data->mcumgr_buff,
line_len);
if (nb != NULL) {
zephyr_smp_rx_req(&smp_shell_transport, nb);
}
}
}
static u16_t smp_shell_get_mtu(const struct net_buf *nb)
{
return CONFIG_MCUMGR_SMP_SHELL_MTU;
}
static int smp_shell_tx_raw(const void *data, int len, void *arg)
{
/* Cast away const. */
k_str_out((void *)data, len);
return 0;
}
static int smp_shell_tx_pkt(struct zephyr_smp_transport *zst,
struct net_buf *nb)
{
int rc;
rc = mcumgr_serial_tx_pkt(nb->data, nb->len, smp_shell_tx_raw, NULL);
mcumgr_buf_free(nb);
return rc;
}
static int smp_shell_init(struct device *dev)
{
ARG_UNUSED(dev);
zephyr_smp_transport_init(&smp_shell_transport, smp_shell_tx_pkt,
smp_shell_get_mtu, NULL, NULL);
return 0;
}
SYS_INIT(smp_shell_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);