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