Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | /*
* 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/mcumgr/serial.h"
#include "mgmt/mcumgr/buf.h"
#include "mgmt/mcumgr/smp.h"
#include "mgmt/mcumgr/smp_shell.h"
#include "drivers/uart.h"
#include "syscalls/uart.h"
#include "shell/shell.h"
#include "shell/shell_uart.h"
#include <logging/log.h>
LOG_MODULE_REGISTER(smp_shell);
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, uint8_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;
}
size_t smp_shell_rx_bytes(struct smp_shell_data *data, const uint8_t *bytes,
size_t size)
{
size_t consumed = 0; /* Number of bytes consumed by SMP */
/* Process all bytes that are accepted as SMP commands. */
while (size != consumed) {
uint8_t byte = bytes[consumed];
int mcumgr_state = read_mcumgr_byte(data, byte);
if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_NONE) {
break;
} else if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_HEADER &&
!data->buf) {
data->buf = net_buf_alloc(data->buf_pool, K_NO_WAIT);
if (!data->buf) {
LOG_WRN("Failed to alloc SMP buf");
}
}
if (data->buf && net_buf_tailroom(data->buf) > 0) {
net_buf_add_u8(data->buf, byte);
}
/* Newline in payload means complete frame */
if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_PAYLOAD &&
byte == '\n') {
if (data->buf) {
net_buf_put(&data->buf_ready, data->buf);
data->buf = NULL;
}
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);
}
++consumed;
}
return consumed;
}
void smp_shell_process(struct smp_shell_data *data)
{
struct net_buf *buf;
struct net_buf *nb;
while (true) {
buf = net_buf_get(&data->buf_ready, K_NO_WAIT);
if (!buf) {
break;
}
nb = mcumgr_serial_process_frag(&smp_shell_rx_ctxt,
buf->data,
buf->len);
if (nb != NULL) {
zephyr_smp_rx_req(&smp_shell_transport, nb);
}
net_buf_unref(buf);
}
}
static uint16_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)
{
const struct shell *const sh = shell_backend_uart_get_ptr();
const struct shell_uart *const su = sh->iface->ctx;
const struct shell_uart_ctrl_blk *const scb = su->ctrl_blk;
const uint8_t *out = data;
while ((out != NULL) && (len != 0)) {
uart_poll_out(scb->dev, *out);
++out;
--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;
}
int smp_shell_init(void)
{
zephyr_smp_transport_init(&smp_shell_transport, smp_shell_tx_pkt,
smp_shell_get_mtu, NULL, NULL);
return 0;
}
|