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 #ifdef CONFIG_SMP_CLIENT
43 static struct smp_client_transport_entry smp_client_transport;
44 #endif
45
46 /** SMP mcumgr frame fragments. */
47 enum smp_shell_esc_mcumgr {
48 ESC_MCUMGR_PKT_1,
49 ESC_MCUMGR_PKT_2,
50 ESC_MCUMGR_FRAG_1,
51 ESC_MCUMGR_FRAG_2,
52 };
53
54 /** These states indicate whether an mcumgr frame is being received. */
55 enum smp_shell_mcumgr_state {
56 SMP_SHELL_MCUMGR_STATE_NONE,
57 SMP_SHELL_MCUMGR_STATE_HEADER,
58 SMP_SHELL_MCUMGR_STATE_PAYLOAD
59 };
60
61 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
smp_shell_input_timeout_handler(struct k_timer * timer)62 static void smp_shell_input_timeout_handler(struct k_timer *timer)
63 {
64 ARG_UNUSED(timer);
65 struct smp_shell_data *const data = shell_uart_smp_shell_data_get_ptr();
66
67 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
68 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
69 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
70 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
71
72 if (data->buf) {
73 net_buf_reset(data->buf);
74 net_buf_unref(data->buf);
75 data->buf = NULL;
76 }
77 }
78
79 K_TIMER_DEFINE(smp_shell_input_timer, smp_shell_input_timeout_handler, NULL);
80 #endif
81
read_mcumgr_byte(struct smp_shell_data * data,uint8_t byte)82 static int read_mcumgr_byte(struct smp_shell_data *data, uint8_t byte)
83 {
84 bool frag_1;
85 bool frag_2;
86 bool pkt_1;
87 bool pkt_2;
88
89 pkt_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
90 pkt_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
91 frag_1 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
92 frag_2 = atomic_test_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
93
94 if (pkt_2 || frag_2) {
95 /* Already fully framed. */
96 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
97 }
98
99 if (pkt_1) {
100 if (byte == MCUMGR_SERIAL_HDR_PKT_2) {
101 /* Final framing byte received. */
102 atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
103 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
104 k_timer_start(&smp_shell_input_timer,
105 K_MSEC(CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME),
106 K_NO_WAIT);
107 #endif
108 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
109 }
110 } else if (frag_1) {
111 if (byte == MCUMGR_SERIAL_HDR_FRAG_2) {
112 /* Final framing byte received. */
113 atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
114 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
115 k_timer_start(&smp_shell_input_timer,
116 K_MSEC(CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT_TIME),
117 K_NO_WAIT);
118 #endif
119 return SMP_SHELL_MCUMGR_STATE_PAYLOAD;
120 }
121 } else {
122 if (byte == MCUMGR_SERIAL_HDR_PKT_1) {
123 /* First framing byte received. */
124 atomic_set_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
125 return SMP_SHELL_MCUMGR_STATE_HEADER;
126 } else if (byte == MCUMGR_SERIAL_HDR_FRAG_1) {
127 /* First framing byte received. */
128 atomic_set_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
129 return SMP_SHELL_MCUMGR_STATE_HEADER;
130 }
131 }
132
133 /* Non-mcumgr byte received. */
134 return SMP_SHELL_MCUMGR_STATE_NONE;
135 }
136
smp_shell_rx_bytes(struct smp_shell_data * data,const uint8_t * bytes,size_t size)137 size_t smp_shell_rx_bytes(struct smp_shell_data *data, const uint8_t *bytes,
138 size_t size)
139 {
140 size_t consumed = 0; /* Number of bytes consumed by SMP */
141
142 /* Process all bytes that are accepted as SMP commands. */
143 while (size != consumed) {
144 uint8_t byte = bytes[consumed];
145 int mcumgr_state = read_mcumgr_byte(data, byte);
146
147 if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_NONE) {
148 break;
149 } else if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_HEADER &&
150 !data->buf) {
151 data->buf = net_buf_alloc(data->buf_pool, K_NO_WAIT);
152 if (!data->buf) {
153 LOG_WRN("Failed to alloc SMP buf");
154 }
155 }
156
157 if (data->buf && net_buf_tailroom(data->buf) > 0) {
158 net_buf_add_u8(data->buf, byte);
159 }
160
161 /* Newline in payload means complete frame */
162 if (mcumgr_state == SMP_SHELL_MCUMGR_STATE_PAYLOAD &&
163 byte == '\n') {
164 if (data->buf) {
165 net_buf_put(&data->buf_ready, data->buf);
166 data->buf = NULL;
167 }
168 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_1);
169 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_PKT_2);
170 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_1);
171 atomic_clear_bit(&data->esc_state, ESC_MCUMGR_FRAG_2);
172
173 #ifdef CONFIG_MCUMGR_TRANSPORT_SHELL_INPUT_TIMEOUT
174 k_timer_stop(&smp_shell_input_timer);
175 #endif
176 }
177
178 ++consumed;
179 }
180
181 return consumed;
182 }
183
smp_shell_process(struct smp_shell_data * data)184 void smp_shell_process(struct smp_shell_data *data)
185 {
186 struct net_buf *buf;
187 struct net_buf *nb;
188
189 while (true) {
190 buf = net_buf_get(&data->buf_ready, K_NO_WAIT);
191 if (!buf) {
192 break;
193 }
194
195 nb = mcumgr_serial_process_frag(&smp_shell_rx_ctxt,
196 buf->data,
197 buf->len);
198 if (nb != NULL) {
199 smp_rx_req(&smp_shell_transport, nb);
200 }
201
202 net_buf_unref(buf);
203 }
204 }
205
smp_shell_get_mtu(const struct net_buf * nb)206 static uint16_t smp_shell_get_mtu(const struct net_buf *nb)
207 {
208 return CONFIG_MCUMGR_TRANSPORT_SHELL_MTU;
209 }
210
smp_shell_tx_raw(const void * data,int len)211 static int smp_shell_tx_raw(const void *data, int len)
212 {
213 static const struct device *const sh_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
214 const uint8_t *out = data;
215
216 while ((out != NULL) && (len != 0)) {
217 uart_poll_out(sh_dev, *out);
218 ++out;
219 --len;
220 }
221
222 return 0;
223 }
224
smp_shell_tx_pkt(struct net_buf * nb)225 static int smp_shell_tx_pkt(struct net_buf *nb)
226 {
227 int rc;
228
229 rc = mcumgr_serial_tx_pkt(nb->data, nb->len, smp_shell_tx_raw);
230 smp_packet_free(nb);
231
232 return rc;
233 }
234
smp_shell_init(void)235 int smp_shell_init(void)
236 {
237 int rc;
238
239 smp_shell_transport.functions.output = smp_shell_tx_pkt;
240 smp_shell_transport.functions.get_mtu = smp_shell_get_mtu;
241
242 rc = smp_transport_init(&smp_shell_transport);
243 #ifdef CONFIG_SMP_CLIENT
244 if (rc == 0) {
245 smp_client_transport.smpt = &smp_shell_transport;
246 smp_client_transport.smpt_type = SMP_SHELL_TRANSPORT;
247 smp_client_transport_register(&smp_client_transport);
248 }
249 #endif
250
251 return rc;
252 }
253