1 /*
2 * Copyright Runtime.io 2018. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief Shell transport for the mcumgr SMP protocol.
9 */
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/init.h>
13 #include <zephyr/net_buf.h>
14 #include <zephyr/drivers/uart.h>
15 #include <zephyr/shell/shell.h>
16 #include <zephyr/shell/shell_uart.h>
17 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
18 #include <zephyr/mgmt/mcumgr/smp/smp.h>
19 #include <zephyr/mgmt/mcumgr/transport/smp.h>
20 #include <zephyr/mgmt/mcumgr/transport/serial.h>
21 #include <zephyr/mgmt/mcumgr/transport/smp_shell.h>
22 #include <zephyr/syscalls/uart.h>
23 #include <string.h>
24
25 #include <mgmt/mcumgr/transport/smp_internal.h>
26
27 #include <zephyr/logging/log.h>
28 LOG_MODULE_REGISTER(smp_shell);
29
30 BUILD_ASSERT(CONFIG_MCUMGR_TRANSPORT_SHELL_MTU != 0,
31 "CONFIG_MCUMGR_TRANSPORT_SHELL_MTU must be > 0");
32
33 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
34 BUILD_ASSERT(CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME != 0,
35 "CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME must be > 0");
36 #endif
37
38 static struct smp_transport smp_shell_transport;
39
40 static struct mcumgr_serial_rx_ctxt smp_shell_rx_ctxt;
41
42 static const struct shell_uart_common *shell_uart;
43
44 #ifdef CONFIG_SMP_CLIENT
45 static struct smp_client_transport_entry smp_client_transport;
46 #endif
47
48 /** SMP mcumgr frame fragments. */
49 enum smp_shell_esc_mcumgr {
50 ESC_MCUMGR_PKT_1,
51 ESC_MCUMGR_PKT_2,
52 ESC_MCUMGR_FRAG_1,
53 ESC_MCUMGR_FRAG_2,
54 };
55
56 /** These states indicate whether an mcumgr frame is being received. */
57 enum smp_shell_mcumgr_state {
58 SMP_SHELL_MCUMGR_STATE_NONE,
59 SMP_SHELL_MCUMGR_STATE_HEADER,
60 SMP_SHELL_MCUMGR_STATE_PAYLOAD
61 };
62
63 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
smp_shell_input_timeout_handler(struct k_timer * timer)64 static void smp_shell_input_timeout_handler(struct k_timer *timer)
65 {
66 ARG_UNUSED(timer);
67 struct smp_shell_data *const data = shell_uart_smp_shell_data_get_ptr();
68
69 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
70 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
71 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
72 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
73
74 if (data->buf) {
75 net_buf_reset(data->buf);
76 net_buf_unref(data->buf);
77 data->buf = NULL;
78 }
79 }
80
81 K_TIMER_DEFINE(smp_shell_input_timer, smp_shell_input_timeout_handler, NULL);
82 #endif
83
read_mcumgr_byte(struct smp_shell_data * data,uint8_t byte)84 static int read_mcumgr_byte(struct smp_shell_data *data, uint8_t byte)
85 {
86 bool frag_1;
87 bool frag_2;
88 bool pkt_1;
89 bool pkt_2;
90
91 pkt_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
92 pkt_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
93 frag_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
94 frag_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
95
96 if (pkt_2 || frag_2) {
97 /* Already fully framed. */
98 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
99 }
100
101 if (pkt_1) {
102 if (byte == MCUMGR_SERIAL_HDR_PKT_2) {
103 /* Final framing byte received. */
104 atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
105 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
106 k_timer_start(&smp_shell_input_timer,
107 K_MSEC(CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME),
108 K_NO_WAIT);
109 #endif
110 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
111 }
112 } else if (frag_1) {
113 if (byte == MCUMGR_SERIAL_HDR_FRAG_2) {
114 /* Final framing byte received. */
115 atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
116 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
117 k_timer_start(&smp_shell_input_timer,
118 K_MSEC(CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME),
119 K_NO_WAIT);
120 #endif
121 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
122 }
123 } else {
124 if (byte == MCUMGR_SERIAL_HDR_PKT_1) {
125 /* First framing byte received. */
126 atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
127 return SMP_SHELL_MCUMGR_STATE_HEADER;
128 } else if (byte == MCUMGR_SERIAL_HDR_FRAG_1) {
129 /* First framing byte received. */
130 atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
131 return SMP_SHELL_MCUMGR_STATE_HEADER;
132 }
133 }
134
135 /* Non-mcumgr byte received. */
136 return SMP_SHELL_MCUMGR_STATE_NONE;
137 }
138
smp_shell_rx_bytes(struct smp_shell_data * data,const uint8_t * bytes,size_t size)139 size_t smp_shell_rx_bytes(struct smp_shell_data *data, const uint8_t *bytes,
140 size_t size)
141 {
142 size_t consumed = 0; /* Number of bytes consumed by SMP */
143
144 /* Process all bytes that are accepted as SMP commands. */
145 while (size != consumed) {
146 uint8_t byte = bytes[consumed];
147 int mcumgr_state = read_mcumgr_byte(data, byte);
148
149 if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_NONE) {
150 break;
151 } else if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_HEADER &&
152 !data->buf) {
153 data->buf = net_buf_alloc(data->buf_pool, K_NO_WAIT);
154 if (!data->buf) {
155 LOG_WRN("Failed to alloc SMP buf");
156 }
157 }
158
159 if (data->buf && net_buf_tailroom(data->buf) > 0) {
160 net_buf_add_u8(data->buf, byte);
161 }
162
163 /* Newline in payload means complete frame */
164 if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_PAYLOAD &&
165 byte == '\n') {
166 if (data->buf) {
167 k_fifo_put(&data->buf_ready, data->buf);
168 data->buf = NULL;
169 }
170 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
171 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
172 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
173 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
174
175 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
176 k_timer_stop(&smp_shell_input_timer);
177 #endif
178 }
179
180 ++consumed;
181 }
182
183 return consumed;
184 }
185
smp_shell_process(struct smp_shell_data * data)186 void smp_shell_process(struct smp_shell_data *data)
187 {
188 struct net_buf *buf;
189 struct net_buf *nb;
190
191 while (true) {
192 buf = k_fifo_get(&data->buf_ready, K_NO_WAIT);
193 if (!buf) {
194 break;
195 }
196
197 nb = mcumgr_serial_process_frag(&smp_shell_rx_ctxt,
198 buf->data,
199 buf->len);
200 if (nb != NULL) {
201 smp_rx_req(&smp_shell_transport, nb);
202 }
203
204 net_buf_unref(buf);
205 }
206 }
207
smp_shell_get_mtu(const struct net_buf * nb)208 static uint16_t smp_shell_get_mtu(const struct net_buf *nb)
209 {
210 return CONFIG_MCUMGR_TRANSPORT_SHELL_MTU;
211 }
212
smp_shell_tx_raw(const void * data,int len)213 static int smp_shell_tx_raw(const void *data, int len)
214 {
215 const uint8_t *out = data;
216
217 while ((out != NULL) && (len != 0)) {
218 uart_poll_out(shell_uart->dev, *out);
219 ++out;
220 --len;
221 }
222
223 return 0;
224 }
225
smp_shell_tx_pkt(struct net_buf * nb)226 static int smp_shell_tx_pkt(struct net_buf *nb)
227 {
228 int rc;
229
230 shell_uart = (struct shell_uart_common *)shell_backend_uart_get_ptr()->iface->ctx;
231 rc = mcumgr_serial_tx_pkt(nb->data, nb->len, smp_shell_tx_raw);
232 smp_packet_free(nb);
233
234 return rc;
235 }
236
smp_shell_init(void)237 int smp_shell_init(void)
238 {
239 int rc;
240
241 smp_shell_transport.functions.output = smp_shell_tx_pkt;
242 smp_shell_transport.functions.get_mtu = smp_shell_get_mtu;
243
244 rc = smp_transport_init(&smp_shell_transport);
245 #ifdef CONFIG_SMP_CLIENT
246 if (rc == 0) {
247 smp_client_transport.smpt = &smp_shell_transport;
248 smp_client_transport.smpt_type = SMP_SHELL_TRANSPORT;
249 smp_client_transport_register(&smp_client_transport);
250 }
251 #endif
252
253 return rc;
254 }
255