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