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