| /* |
| * 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); |