1 /*
2  * Copyright Runtime.io 2018. All rights reserved.
3  * Copyright (c) 2021-2022 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <assert.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/net/buf.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
13 #include <zephyr/mgmt/mcumgr/smp/smp.h>
14 #include <zephyr/mgmt/mcumgr/transport/smp.h>
15 
16 #include <mgmt/mcumgr/transport/smp_reassembly.h>
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(mcumgr_smp, CONFIG_MCUMGR_TRANSPORT_LOG_LEVEL);
20 
21 /* To be able to unit test some callers some functions need to be
22  * demoted to allow overriding them.
23  */
24 #ifdef CONFIG_ZTEST
25 #define WEAK __weak
26 #else
27 #define WEAK
28 #endif
29 
30 K_THREAD_STACK_DEFINE(smp_work_queue_stack, CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_STACK_SIZE);
31 
32 static struct k_work_q smp_work_queue;
33 
34 #ifdef CONFIG_SMP_CLIENT
35 static sys_slist_t smp_transport_clients;
36 #endif
37 
38 static const struct k_work_queue_config smp_work_queue_config = {
39 	.name = "mcumgr smp"
40 };
41 
42 NET_BUF_POOL_DEFINE(pkt_pool, CONFIG_MCUMGR_TRANSPORT_NETBUF_COUNT,
43 		    CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE,
44 		    CONFIG_MCUMGR_TRANSPORT_NETBUF_USER_DATA_SIZE, NULL);
45 
smp_packet_alloc(void)46 struct net_buf *smp_packet_alloc(void)
47 {
48 	return net_buf_alloc(&pkt_pool, K_NO_WAIT);
49 }
50 
smp_packet_free(struct net_buf * nb)51 void smp_packet_free(struct net_buf *nb)
52 {
53 	net_buf_unref(nb);
54 }
55 
56 /**
57  * @brief Allocates a response buffer.
58  *
59  * If a source buf is provided, its user data is copied into the new buffer.
60  *
61  * @param req		An optional source buffer to copy user data from.
62  * @param arg		The streamer providing the callback.
63  *
64  * @return	Newly-allocated buffer on success
65  *		NULL on failure.
66  */
smp_alloc_rsp(const void * req,void * arg)67 void *smp_alloc_rsp(const void *req, void *arg)
68 {
69 	const struct net_buf *req_nb;
70 	struct net_buf *rsp_nb;
71 	struct smp_transport *smpt = arg;
72 
73 	req_nb = req;
74 
75 	rsp_nb = smp_packet_alloc();
76 	if (rsp_nb == NULL) {
77 		return NULL;
78 	}
79 
80 	if (smpt->functions.ud_copy) {
81 		smpt->functions.ud_copy(rsp_nb, req_nb);
82 	} else {
83 		memcpy(net_buf_user_data(rsp_nb),
84 		       net_buf_user_data((void *)req_nb),
85 		       req_nb->user_data_size);
86 	}
87 
88 	return rsp_nb;
89 }
90 
smp_free_buf(void * buf,void * arg)91 void smp_free_buf(void *buf, void *arg)
92 {
93 	struct smp_transport *smpt = arg;
94 
95 	if (!buf) {
96 		return;
97 	}
98 
99 	if (smpt->functions.ud_free) {
100 		smpt->functions.ud_free(net_buf_user_data((struct net_buf *)buf));
101 	}
102 
103 	smp_packet_free(buf);
104 }
105 
106 /**
107  * Processes a single SMP packet and sends the corresponding response(s).
108  */
109 static int
smp_process_packet(struct smp_transport * smpt,struct net_buf * nb)110 smp_process_packet(struct smp_transport *smpt, struct net_buf *nb)
111 {
112 	struct cbor_nb_reader reader;
113 	struct cbor_nb_writer writer;
114 	struct smp_streamer streamer;
115 	int rc;
116 
117 	streamer = (struct smp_streamer) {
118 		.reader = &reader,
119 		.writer = &writer,
120 		.smpt = smpt,
121 	};
122 
123 	rc = smp_process_request_packet(&streamer, nb);
124 	return rc;
125 }
126 
127 /**
128  * Processes all received SNP request packets.
129  */
130 static void
smp_handle_reqs(struct k_work * work)131 smp_handle_reqs(struct k_work *work)
132 {
133 	struct smp_transport *smpt;
134 	struct net_buf *nb;
135 
136 	smpt = (void *)work;
137 
138 	/* Read and handle received messages */
139 	while ((nb = net_buf_get(&smpt->fifo, K_NO_WAIT)) != NULL) {
140 		smp_process_packet(smpt, nb);
141 	}
142 }
143 
smp_transport_init(struct smp_transport * smpt)144 int smp_transport_init(struct smp_transport *smpt)
145 {
146 	__ASSERT((smpt->functions.output != NULL),
147 		 "Required transport output function pointer cannot be NULL");
148 
149 	if (smpt->functions.output == NULL) {
150 		return -EINVAL;
151 	}
152 
153 #ifdef CONFIG_MCUMGR_TRANSPORT_REASSEMBLY
154 	smp_reassembly_init(smpt);
155 #endif
156 
157 	k_work_init(&smpt->work, smp_handle_reqs);
158 	k_fifo_init(&smpt->fifo);
159 
160 	return 0;
161 }
162 
163 #ifdef CONFIG_SMP_CLIENT
smp_client_transport_get(int smpt_type)164 struct smp_transport *smp_client_transport_get(int smpt_type)
165 {
166 	struct smp_client_transport_entry *entry;
167 
168 	SYS_SLIST_FOR_EACH_CONTAINER(&smp_transport_clients, entry, node) {
169 		if (entry->smpt_type == smpt_type) {
170 			return entry->smpt;
171 		}
172 	}
173 
174 	return NULL;
175 }
176 
smp_client_transport_register(struct smp_client_transport_entry * entry)177 void smp_client_transport_register(struct smp_client_transport_entry *entry)
178 {
179 	if (smp_client_transport_get(entry->smpt_type)) {
180 		/* Already in list */
181 		return;
182 	}
183 
184 	sys_slist_append(&smp_transport_clients, &entry->node);
185 
186 }
187 
188 #endif /* CONFIG_SMP_CLIENT */
189 
190 /**
191  * @brief Enqueues an incoming SMP request packet for processing.
192  *
193  * This function always consumes the supplied net_buf.
194  *
195  * @param smpt                  The transport to use to send the corresponding
196  *                                  response(s).
197  * @param nb                    The request packet to process.
198  */
199 WEAK void
smp_rx_req(struct smp_transport * smpt,struct net_buf * nb)200 smp_rx_req(struct smp_transport *smpt, struct net_buf *nb)
201 {
202 	net_buf_put(&smpt->fifo, nb);
203 	k_work_submit_to_queue(&smp_work_queue, &smpt->work);
204 }
205 
206 #ifdef CONFIG_SMP_CLIENT
smp_tx_req(struct k_work * work)207 void smp_tx_req(struct k_work *work)
208 {
209 	k_work_submit_to_queue(&smp_work_queue, work);
210 }
211 #endif
212 
smp_rx_remove_invalid(struct smp_transport * zst,void * arg)213 void smp_rx_remove_invalid(struct smp_transport *zst, void *arg)
214 {
215 	struct net_buf *nb;
216 	struct k_fifo temp_fifo;
217 
218 	if (zst->functions.query_valid_check == NULL) {
219 		/* No check check function registered, abort check */
220 		return;
221 	}
222 
223 	/* Cancel current work-queue if ongoing */
224 	if (k_work_busy_get(&zst->work) & (K_WORK_RUNNING | K_WORK_QUEUED)) {
225 		k_work_cancel(&zst->work);
226 	}
227 
228 	/* Run callback function and remove all buffers that are no longer needed. Store those
229 	 * that are in a temporary FIFO
230 	 */
231 	k_fifo_init(&temp_fifo);
232 
233 	while ((nb = net_buf_get(&zst->fifo, K_NO_WAIT)) != NULL) {
234 		if (!zst->functions.query_valid_check(nb, arg)) {
235 			smp_free_buf(nb, zst);
236 		} else {
237 			net_buf_put(&temp_fifo, nb);
238 		}
239 	}
240 
241 	/* Re-insert the remaining queued operations into the original FIFO */
242 	while ((nb = net_buf_get(&temp_fifo, K_NO_WAIT)) != NULL) {
243 		net_buf_put(&zst->fifo, nb);
244 	}
245 
246 	/* If at least one entry remains, queue the workqueue for running */
247 	if (!k_fifo_is_empty(&zst->fifo)) {
248 		k_work_submit_to_queue(&smp_work_queue, &zst->work);
249 	}
250 }
251 
smp_rx_clear(struct smp_transport * zst)252 void smp_rx_clear(struct smp_transport *zst)
253 {
254 	struct net_buf *nb;
255 
256 	/* Cancel current work-queue if ongoing */
257 	if (k_work_busy_get(&zst->work) & (K_WORK_RUNNING | K_WORK_QUEUED)) {
258 		k_work_cancel(&zst->work);
259 	}
260 
261 	/* Drain the FIFO of all entries without re-adding any */
262 	while ((nb = net_buf_get(&zst->fifo, K_NO_WAIT)) != NULL) {
263 		smp_free_buf(nb, zst);
264 	}
265 }
266 
smp_init(void)267 static int smp_init(void)
268 {
269 #ifdef CONFIG_SMP_CLIENT
270 	sys_slist_init(&smp_transport_clients);
271 #endif
272 
273 	k_work_queue_init(&smp_work_queue);
274 
275 	k_work_queue_start(&smp_work_queue, smp_work_queue_stack,
276 			   K_THREAD_STACK_SIZEOF(smp_work_queue_stack),
277 			   CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_THREAD_PRIO, &smp_work_queue_config);
278 
279 	return 0;
280 }
281 
282 SYS_INIT(smp_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
283