/* * Copyright Runtime.io 2018. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** @file * @brief Shell transport for the mcumgr SMP protocol. */ #include #include #include #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);