1 /*
2  * Copyright Runtime.io 2018. All rights reserved.
3  * Copyright Laird Connectivity 2021-2022.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /** @file
9  * @brief Dummy transport for the mcumgr SMP protocol for unit testing.
10  */
11 
12 /* Define required for uart_mcumgr.h functionality reuse */
13 #define CONFIG_UART_MCUMGR_RX_BUF_SIZE CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE
14 #define MCUMGR_DUMMY_MAX_FRAME CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE
15 
16 #include <zephyr/kernel.h>
17 #include <zephyr/init.h>
18 #include <zephyr/sys/crc.h>
19 #include <zephyr/sys/byteorder.h>
20 #include <zephyr/net/buf.h>
21 #include <zephyr/sys/base64.h>
22 #include <zephyr/drivers/console/uart_mcumgr.h>
23 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
24 #include <zephyr/mgmt/mcumgr/smp/smp.h>
25 #include <zephyr/mgmt/mcumgr/transport/smp.h>
26 #include <zephyr/mgmt/mcumgr/transport/smp_dummy.h>
27 #include <zephyr/mgmt/mcumgr/transport/serial.h>
28 #include <string.h>
29 
30 #include <mgmt/mcumgr/transport/smp_internal.h>
31 
32 BUILD_ASSERT(CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE != 0,
33 	     "CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE must be > 0");
34 
35 struct device;
36 static struct mcumgr_serial_rx_ctxt smp_dummy_rx_ctxt;
37 static struct mcumgr_serial_rx_ctxt smp_dummy_tx_ctxt;
38 static struct smp_transport smp_dummy_transport;
39 static bool enable_dummy_smp;
40 static struct k_sem smp_data_ready_sem;
41 static uint8_t smp_send_buffer[CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE];
42 static uint16_t smp_send_pos;
43 static uint8_t smp_receive_buffer[CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE];
44 static uint16_t smp_receive_pos;
45 
46 /** Callback to execute when a valid fragment has been received. */
47 static uart_mcumgr_recv_fn *dummy_mgumgr_recv_cb;
48 
49 /** Contains the fragment currently being received. */
50 static struct uart_mcumgr_rx_buf *dummy_mcumgr_cur_buf;
51 
52 /**
53  * Whether the line currently being read should be ignored.  This is true if
54  * the line is too long or if there is no buffer available to hold it.
55  */
56 static bool dummy_mcumgr_ignoring;
57 
58 static void smp_dummy_process_rx_queue(struct k_work *work);
59 static void dummy_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf *rx_buf);
60 
61 
62 static struct net_buf *mcumgr_dummy_process_frag(
63 	struct mcumgr_serial_rx_ctxt *rx_ctxt,
64 	const uint8_t *frag, int frag_len);
65 
66 static struct net_buf *mcumgr_dummy_process_frag_outgoing(
67 	struct mcumgr_serial_rx_ctxt *tx_ctxt,
68 	const uint8_t *frag, uint16_t frag_len);
69 
70 static int mcumgr_dummy_tx_pkt(const uint8_t *data, int len,
71 			       mcumgr_serial_tx_cb cb);
72 
73 K_FIFO_DEFINE(smp_dummy_rx_fifo);
74 K_WORK_DEFINE(smp_dummy_work, smp_dummy_process_rx_queue);
75 K_MEM_SLAB_DEFINE(dummy_mcumgr_slab, sizeof(struct uart_mcumgr_rx_buf), 1, 1);
76 
smp_dummy_clear_state(void)77 void smp_dummy_clear_state(void)
78 {
79 	k_sem_reset(&smp_data_ready_sem);
80 
81 	memset(smp_receive_buffer, 0, sizeof(smp_receive_buffer));
82 	smp_receive_pos = 0;
83 	memset(smp_send_buffer, 0, sizeof(smp_send_buffer));
84 	smp_send_pos = 0;
85 }
86 
87 /**
88  * Processes a single line (fragment) coming from the mcumgr UART driver.
89  */
smp_dummy_process_frag(struct uart_mcumgr_rx_buf * rx_buf)90 static void smp_dummy_process_frag(struct uart_mcumgr_rx_buf *rx_buf)
91 {
92 	struct net_buf *nb;
93 
94 	/* Decode the fragment and write the result to the global receive
95 	 * context.
96 	 */
97 	nb = mcumgr_dummy_process_frag(&smp_dummy_rx_ctxt,
98 					rx_buf->data, rx_buf->length);
99 
100 	/* Release the encoded fragment. */
101 	dummy_mcumgr_free_rx_buf(rx_buf);
102 
103 	/* If a complete packet has been received, pass it to SMP for
104 	 * processing.
105 	 */
106 	if (nb != NULL) {
107 		smp_rx_req(&smp_dummy_transport, nb);
108 	}
109 }
110 
111 /**
112  * Processes a single line (fragment) coming from the mcumgr response to be
113  * used in tests
114  */
smp_dummy_process_frag_outgoing(uint8_t * buffer,uint16_t buffer_size)115 static struct net_buf *smp_dummy_process_frag_outgoing(uint8_t *buffer,
116 						       uint16_t buffer_size)
117 {
118 	struct net_buf *nb;
119 
120 	/* Decode the fragment and write the result to the global receive
121 	 * context.
122 	 */
123 	nb = mcumgr_dummy_process_frag_outgoing(&smp_dummy_tx_ctxt,
124 					buffer, buffer_size);
125 
126 	return nb;
127 }
128 
smp_dummy_process_rx_queue(struct k_work * work)129 static void smp_dummy_process_rx_queue(struct k_work *work)
130 {
131 	struct uart_mcumgr_rx_buf *rx_buf;
132 
133 	while ((rx_buf = k_fifo_get(&smp_dummy_rx_fifo, K_NO_WAIT)) != NULL) {
134 		smp_dummy_process_frag(rx_buf);
135 	}
136 }
137 
smp_dummy_get_outgoing(void)138 struct net_buf *smp_dummy_get_outgoing(void)
139 {
140 	return smp_dummy_process_frag_outgoing(smp_send_buffer, smp_send_pos);
141 }
142 
143 /**
144  * Enqueues a received SMP fragment for later processing.  This function
145  * executes in the interrupt context.
146  */
smp_dummy_rx_frag(struct uart_mcumgr_rx_buf * rx_buf)147 static void smp_dummy_rx_frag(struct uart_mcumgr_rx_buf *rx_buf)
148 {
149 	k_fifo_put(&smp_dummy_rx_fifo, rx_buf);
150 	k_work_submit(&smp_dummy_work);
151 }
152 
smp_dummy_get_mtu(const struct net_buf * nb)153 static uint16_t smp_dummy_get_mtu(const struct net_buf *nb)
154 {
155 	return CONFIG_MCUMGR_TRANSPORT_DUMMY_RX_BUF_SIZE;
156 }
157 
dummy_mcumgr_send_raw(const void * data,int len)158 int dummy_mcumgr_send_raw(const void *data, int len)
159 {
160 	uint16_t data_size =
161 	MIN(len, (sizeof(smp_send_buffer) - smp_send_pos - 1));
162 
163 	if (enable_dummy_smp == true) {
164 		memcpy(&smp_send_buffer[smp_send_pos], data, data_size);
165 		smp_send_pos += data_size;
166 
167 		if (smp_send_buffer[(smp_send_pos - 1)] == 0x0a) {
168 			/* End character of SMP over console message has been
169 			 * received
170 			 */
171 			k_sem_give(&smp_data_ready_sem);
172 		}
173 	}
174 
175 	return 0;
176 }
177 
smp_dummy_tx_pkt_int(struct net_buf * nb)178 static int smp_dummy_tx_pkt_int(struct net_buf *nb)
179 {
180 	int rc;
181 
182 	rc = mcumgr_dummy_tx_pkt(nb->data, nb->len, dummy_mcumgr_send_raw);
183 	smp_packet_free(nb);
184 
185 	return rc;
186 }
187 
smp_dummy_init(void)188 static int smp_dummy_init(void)
189 {
190 	int rc;
191 
192 	k_sem_init(&smp_data_ready_sem, 0, 1);
193 
194 	smp_dummy_transport.functions.output = smp_dummy_tx_pkt_int;
195 	smp_dummy_transport.functions.get_mtu = smp_dummy_get_mtu;
196 
197 	rc = smp_transport_init(&smp_dummy_transport);
198 
199 	if (rc != 0) {
200 		return rc;
201 	}
202 
203 	dummy_mgumgr_recv_cb = smp_dummy_rx_frag;
204 
205 	return 0;
206 }
207 
dummy_mcumgr_alloc_rx_buf(void)208 static struct uart_mcumgr_rx_buf *dummy_mcumgr_alloc_rx_buf(void)
209 {
210 	struct uart_mcumgr_rx_buf *rx_buf;
211 	void *block;
212 	int rc;
213 
214 	rc = k_mem_slab_alloc(&dummy_mcumgr_slab, &block, K_NO_WAIT);
215 	if (rc != 0) {
216 		return NULL;
217 	}
218 
219 	rx_buf = block;
220 	rx_buf->length = 0;
221 	return rx_buf;
222 }
223 
dummy_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf * rx_buf)224 static void dummy_mcumgr_free_rx_buf(struct uart_mcumgr_rx_buf *rx_buf)
225 {
226 	void *block;
227 
228 	block = rx_buf;
229 	k_mem_slab_free(&dummy_mcumgr_slab, block);
230 }
231 
232 /**
233  * Processes a single incoming byte.
234  */
dummy_mcumgr_rx_byte(uint8_t byte)235 static struct uart_mcumgr_rx_buf *dummy_mcumgr_rx_byte(uint8_t byte)
236 {
237 	struct uart_mcumgr_rx_buf *rx_buf;
238 
239 	if (!dummy_mcumgr_ignoring) {
240 		if (dummy_mcumgr_cur_buf == NULL) {
241 			dummy_mcumgr_cur_buf = dummy_mcumgr_alloc_rx_buf();
242 			if (dummy_mcumgr_cur_buf == NULL) {
243 				/* Insufficient buffers; drop this fragment. */
244 				dummy_mcumgr_ignoring = true;
245 			}
246 		}
247 	}
248 
249 	rx_buf = dummy_mcumgr_cur_buf;
250 	if (!dummy_mcumgr_ignoring) {
251 		if (rx_buf->length >= sizeof(rx_buf->data)) {
252 			/* Line too long; drop this fragment. */
253 			dummy_mcumgr_free_rx_buf(dummy_mcumgr_cur_buf);
254 			dummy_mcumgr_cur_buf = NULL;
255 			dummy_mcumgr_ignoring = true;
256 		} else {
257 			rx_buf->data[rx_buf->length++] = byte;
258 		}
259 	}
260 
261 	if (byte == '\n') {
262 		/* Fragment complete. */
263 		if (dummy_mcumgr_ignoring) {
264 			dummy_mcumgr_ignoring = false;
265 		} else {
266 			dummy_mcumgr_cur_buf = NULL;
267 			return rx_buf;
268 		}
269 	}
270 
271 	return NULL;
272 }
273 
dummy_mcumgr_add_data(uint8_t * data,uint16_t data_size)274 void dummy_mcumgr_add_data(uint8_t *data, uint16_t data_size)
275 {
276 	struct uart_mcumgr_rx_buf *rx_buf;
277 	int i;
278 
279 	for (i = 0; i < data_size; i++) {
280 		rx_buf = dummy_mcumgr_rx_byte(data[i]);
281 		if (rx_buf != NULL) {
282 			dummy_mgumgr_recv_cb(rx_buf);
283 		}
284 	}
285 }
286 
mcumgr_dummy_free_rx_ctxt(struct mcumgr_serial_rx_ctxt * rx_ctxt)287 static void mcumgr_dummy_free_rx_ctxt(struct mcumgr_serial_rx_ctxt *rx_ctxt)
288 {
289 	if (rx_ctxt->nb != NULL) {
290 		smp_packet_free(rx_ctxt->nb);
291 		rx_ctxt->nb = NULL;
292 	}
293 }
294 
mcumgr_dummy_calc_crc(const uint8_t * data,int len)295 static uint16_t mcumgr_dummy_calc_crc(const uint8_t *data, int len)
296 {
297 	return crc16_itu_t(0x0000, data, len);
298 }
299 
mcumgr_dummy_parse_op(const uint8_t * buf,int len)300 static int mcumgr_dummy_parse_op(const uint8_t *buf, int len)
301 {
302 	uint16_t op;
303 
304 	if (len < sizeof(op)) {
305 		return -EINVAL;
306 	}
307 
308 	memcpy(&op, buf, sizeof(op));
309 	op = sys_be16_to_cpu(op);
310 
311 	if (op != MCUMGR_SERIAL_HDR_PKT && op != MCUMGR_SERIAL_HDR_FRAG) {
312 		return -EINVAL;
313 	}
314 
315 	return op;
316 }
317 
mcumgr_dummy_extract_len(struct mcumgr_serial_rx_ctxt * rx_ctxt)318 static int mcumgr_dummy_extract_len(struct mcumgr_serial_rx_ctxt *rx_ctxt)
319 {
320 	if (rx_ctxt->nb->len < 2) {
321 		return -EINVAL;
322 	}
323 
324 	rx_ctxt->pkt_len = net_buf_pull_be16(rx_ctxt->nb);
325 	return 0;
326 }
327 
mcumgr_dummy_decode_frag(struct mcumgr_serial_rx_ctxt * rx_ctxt,const uint8_t * frag,int frag_len)328 static int mcumgr_dummy_decode_frag(struct mcumgr_serial_rx_ctxt *rx_ctxt,
329 				     const uint8_t *frag, int frag_len)
330 {
331 	size_t dec_len;
332 	int rc;
333 
334 	rc = base64_decode(rx_ctxt->nb->data + rx_ctxt->nb->len,
335 				   net_buf_tailroom(rx_ctxt->nb), &dec_len,
336 				   frag, frag_len);
337 	if (rc != 0) {
338 		return -EINVAL;
339 	}
340 
341 	rx_ctxt->nb->len += dec_len;
342 
343 	return 0;
344 }
345 
346 /**
347  * Processes a received mcumgr fragment
348  *
349  * @param rx_ctxt    The receive context
350  * @param frag       The fragment buffer
351  * @param frag_len   The size of the fragment
352  *
353  * @return           The net buffer if a complete packet was received, NULL if
354  *                   the frame is invalid or if additional fragments are
355  *                   expected
356  */
mcumgr_dummy_process_frag(struct mcumgr_serial_rx_ctxt * rx_ctxt,const uint8_t * frag,int frag_len)357 static struct net_buf *mcumgr_dummy_process_frag(
358 	struct mcumgr_serial_rx_ctxt *rx_ctxt,
359 	const uint8_t *frag, int frag_len)
360 {
361 	struct net_buf *nb;
362 	uint16_t crc;
363 	uint16_t op;
364 	int rc;
365 
366 	if (rx_ctxt->nb == NULL) {
367 		rx_ctxt->nb = smp_packet_alloc();
368 		if (rx_ctxt->nb == NULL) {
369 			return NULL;
370 		}
371 	}
372 
373 	op = mcumgr_dummy_parse_op(frag, frag_len);
374 	switch (op) {
375 	case MCUMGR_SERIAL_HDR_PKT:
376 		net_buf_reset(rx_ctxt->nb);
377 		break;
378 
379 	case MCUMGR_SERIAL_HDR_FRAG:
380 		if (rx_ctxt->nb->len == 0U) {
381 			mcumgr_dummy_free_rx_ctxt(rx_ctxt);
382 			return NULL;
383 		}
384 		break;
385 
386 	default:
387 		return NULL;
388 	}
389 
390 	rc = mcumgr_dummy_decode_frag(rx_ctxt,
391 				       frag + sizeof(op),
392 				       frag_len - sizeof(op));
393 	if (rc != 0) {
394 		mcumgr_dummy_free_rx_ctxt(rx_ctxt);
395 		return NULL;
396 	}
397 
398 	if (op == MCUMGR_SERIAL_HDR_PKT) {
399 		rc = mcumgr_dummy_extract_len(rx_ctxt);
400 		if (rc < 0) {
401 			mcumgr_dummy_free_rx_ctxt(rx_ctxt);
402 			return NULL;
403 		}
404 	}
405 
406 	if (rx_ctxt->nb->len < rx_ctxt->pkt_len) {
407 		/* More fragments expected. */
408 		return NULL;
409 	}
410 
411 	if (rx_ctxt->nb->len > rx_ctxt->pkt_len) {
412 		/* Payload longer than indicated in header. */
413 		mcumgr_dummy_free_rx_ctxt(rx_ctxt);
414 		return NULL;
415 	}
416 
417 	crc = mcumgr_dummy_calc_crc(rx_ctxt->nb->data, rx_ctxt->nb->len);
418 	if (crc != 0U) {
419 		mcumgr_dummy_free_rx_ctxt(rx_ctxt);
420 		return NULL;
421 	}
422 
423 	/* Packet is complete; strip the CRC. */
424 	rx_ctxt->nb->len -= 2U;
425 
426 	nb = rx_ctxt->nb;
427 	rx_ctxt->nb = NULL;
428 	return nb;
429 }
430 
431 /**
432  * Processes a mcumgr response fragment
433  *
434  * @param tx_ctxt    The transmission context
435  * @param frag       The fragment buffer
436  * @param frag_len   The size of the fragment
437  *
438  * @return           The net buffer if a complete packet was received, NULL if
439  *                   the frame is invalid or if additional fragments are
440  *                   expected
441  */
mcumgr_dummy_process_frag_outgoing(struct mcumgr_serial_rx_ctxt * tx_ctxt,const uint8_t * frag,uint16_t frag_len)442 static struct net_buf *mcumgr_dummy_process_frag_outgoing(
443 	struct mcumgr_serial_rx_ctxt *tx_ctxt,
444 	const uint8_t *frag, uint16_t frag_len)
445 {
446 	struct net_buf *nb;
447 	uint16_t crc;
448 	uint16_t op;
449 	int rc;
450 
451 	if (tx_ctxt->nb == NULL) {
452 		tx_ctxt->nb = smp_packet_alloc();
453 		if (tx_ctxt->nb == NULL) {
454 			return NULL;
455 		}
456 	}
457 
458 	op = mcumgr_dummy_parse_op(frag, frag_len);
459 	switch (op) {
460 	case MCUMGR_SERIAL_HDR_PKT:
461 		net_buf_reset(tx_ctxt->nb);
462 		break;
463 
464 	case MCUMGR_SERIAL_HDR_FRAG:
465 		if (tx_ctxt->nb->len == 0U) {
466 			mcumgr_dummy_free_rx_ctxt(tx_ctxt);
467 			return NULL;
468 		}
469 		break;
470 
471 	default:
472 		return NULL;
473 	}
474 
475 	rc = mcumgr_dummy_decode_frag(tx_ctxt,
476 				       frag + sizeof(op),
477 				       frag_len - sizeof(op));
478 	if (rc != 0) {
479 		mcumgr_dummy_free_rx_ctxt(tx_ctxt);
480 		return NULL;
481 	}
482 
483 	if (op == MCUMGR_SERIAL_HDR_PKT) {
484 		rc = mcumgr_dummy_extract_len(tx_ctxt);
485 		if (rc < 0) {
486 			mcumgr_dummy_free_rx_ctxt(tx_ctxt);
487 			return NULL;
488 		}
489 	}
490 
491 	if (tx_ctxt->nb->len < tx_ctxt->pkt_len) {
492 		/* More fragments expected. */
493 		return NULL;
494 	}
495 
496 	if (tx_ctxt->nb->len > tx_ctxt->pkt_len) {
497 		/* Payload longer than indicated in header. */
498 		mcumgr_dummy_free_rx_ctxt(tx_ctxt);
499 		return NULL;
500 	}
501 
502 	crc = mcumgr_dummy_calc_crc(tx_ctxt->nb->data, tx_ctxt->nb->len);
503 	if (crc != 0U) {
504 		mcumgr_dummy_free_rx_ctxt(tx_ctxt);
505 		return NULL;
506 	}
507 
508 	/* Packet is complete; strip the CRC. */
509 	tx_ctxt->nb->len -= 2U;
510 
511 	nb = tx_ctxt->nb;
512 	tx_ctxt->nb = NULL;
513 	return nb;
514 }
515 
516 /**
517  * Base64-encodes a small chunk of data and transmits it.  The data must be no
518  * larger than three bytes.
519  */
mcumgr_dummy_tx_small(const void * data,int len,mcumgr_serial_tx_cb cb)520 static int mcumgr_dummy_tx_small(const void *data, int len,
521 				  mcumgr_serial_tx_cb cb)
522 {
523 	uint8_t b64[4 + 1]; /* +1 required for null terminator. */
524 	size_t dst_len;
525 	int rc;
526 
527 	rc = base64_encode(b64, sizeof(b64), &dst_len, data, len);
528 	__ASSERT_NO_MSG(rc == 0);
529 	__ASSERT_NO_MSG(dst_len == 4);
530 
531 	return cb(b64, 4);
532 }
533 
534 /**
535  * @brief Transmits a single mcumgr frame over serial.
536  *
537  * @param data                  The frame payload to transmit.  This does not
538  *                                  include a header or CRC.
539  * @param first                 Whether this is the first frame in the packet.
540  * @param len                   The number of untransmitted data bytes in the
541  *                                  packet.
542  * @param crc                   The 16-bit CRC of the entire packet.
543  * @param cb                    A callback used for transmitting raw data.
544  * @param out_data_bytes_txed   On success, the number of data bytes
545  *                                  transmitted gets written here.
546  *
547  * @return                      0 on success; negative error code on failure.
548  */
mcumgr_dummy_tx_frame(const uint8_t * data,bool first,int len,uint16_t crc,mcumgr_serial_tx_cb cb,int * out_data_bytes_txed)549 int mcumgr_dummy_tx_frame(const uint8_t *data, bool first, int len,
550 			   uint16_t crc, mcumgr_serial_tx_cb cb,
551 			   int *out_data_bytes_txed)
552 {
553 	uint8_t raw[3];
554 	uint16_t u16;
555 	int dst_off;
556 	int src_off;
557 	int rem;
558 	int rc;
559 
560 	src_off = 0;
561 	dst_off = 0;
562 
563 	if (first) {
564 		u16 = sys_cpu_to_be16(MCUMGR_SERIAL_HDR_PKT);
565 	} else {
566 		u16 = sys_cpu_to_be16(MCUMGR_SERIAL_HDR_FRAG);
567 	}
568 
569 	rc = cb(&u16, sizeof(u16));
570 	if (rc != 0) {
571 		return rc;
572 	}
573 	dst_off += 2;
574 
575 	/* Only the first fragment contains the packet length. */
576 	if (first) {
577 		u16 = sys_cpu_to_be16(len + 2); /* Bug fix to account for CRC */
578 		memcpy(raw, &u16, sizeof(u16));
579 		raw[2] = data[0];
580 
581 		rc = mcumgr_dummy_tx_small(raw, 3, cb);
582 		if (rc != 0) {
583 			return rc;
584 		}
585 
586 		src_off++;
587 		dst_off += 4;
588 	}
589 
590 	while (1) {
591 		if (dst_off >= MCUMGR_DUMMY_MAX_FRAME - 4) {
592 			/* Can't fit any more data in this frame. */
593 			break;
594 		}
595 
596 		/* If we have reached the end of the packet, we need to encode
597 		 * and send the CRC.
598 		 */
599 		rem = len - src_off;
600 		if (rem == 0) {
601 			raw[0] = (crc & 0xff00) >> 8;
602 			raw[1] = crc & 0x00ff;
603 			rc = mcumgr_dummy_tx_small(raw, 2, cb);
604 			if (rc != 0) {
605 				return rc;
606 			}
607 			break;
608 		}
609 
610 		if (rem == 1) {
611 			raw[0] = data[src_off];
612 			src_off++;
613 
614 			raw[1] = (crc & 0xff00) >> 8;
615 			raw[2] = crc & 0x00ff;
616 			rc = mcumgr_dummy_tx_small(raw, 3, cb);
617 			if (rc != 0) {
618 				return rc;
619 			}
620 			break;
621 		}
622 
623 		if (rem == 2) {
624 			raw[0] = data[src_off];
625 			raw[1] = data[src_off + 1];
626 			src_off += 2;
627 
628 			raw[2] = (crc & 0xff00) >> 8;
629 			rc = mcumgr_dummy_tx_small(raw, 3, cb);
630 			if (rc != 0) {
631 				return rc;
632 			}
633 
634 			raw[0] = crc & 0x00ff;
635 			rc = mcumgr_dummy_tx_small(raw, 1, cb);
636 			if (rc != 0) {
637 				return rc;
638 			}
639 			break;
640 		}
641 
642 		/* Otherwise, just encode payload data. */
643 		memcpy(raw, data + src_off, 3);
644 		rc = mcumgr_dummy_tx_small(raw, 3, cb);
645 		if (rc != 0) {
646 			return rc;
647 		}
648 		src_off += 3;
649 		dst_off += 4;
650 	}
651 
652 	rc = cb("\n", 1);
653 	if (rc != 0) {
654 		return rc;
655 	}
656 
657 	*out_data_bytes_txed = src_off;
658 	return 0;
659 }
660 
mcumgr_dummy_tx_pkt(const uint8_t * data,int len,mcumgr_serial_tx_cb cb)661 static int mcumgr_dummy_tx_pkt(const uint8_t *data, int len, mcumgr_serial_tx_cb cb)
662 {
663 	uint16_t crc;
664 	int data_bytes_txed;
665 	int src_off;
666 	int rc;
667 
668 	/* Calculate CRC of entire packet. */
669 	crc = mcumgr_dummy_calc_crc(data, len);
670 
671 	/* Transmit packet as a sequence of frames. */
672 	src_off = 0;
673 	while (src_off < len) {
674 		rc = mcumgr_dummy_tx_frame(data + src_off,
675 					   src_off == 0,
676 					   len - src_off,
677 					   crc, cb,
678 					   &data_bytes_txed);
679 		if (rc != 0) {
680 			return rc;
681 		}
682 
683 		src_off += data_bytes_txed;
684 	}
685 
686 	return 0;
687 }
688 
smp_receive(const void * data,int len)689 static int smp_receive(const void *data, int len)
690 {
691 	uint16_t data_size =
692 		MIN(len, (sizeof(smp_receive_buffer) - smp_receive_pos - 1));
693 
694 	if (enable_dummy_smp == true) {
695 		memcpy(&smp_receive_buffer[smp_receive_pos], data, data_size);
696 		smp_receive_pos += data_size;
697 	}
698 
699 	return 0;
700 }
701 
smp_dummy_wait_for_data(uint32_t wait_time_s)702 bool smp_dummy_wait_for_data(uint32_t wait_time_s)
703 {
704 	return k_sem_take(&smp_data_ready_sem, K_SECONDS(wait_time_s)) == 0 ? true : false;
705 }
706 
smp_dummy_add_data(void)707 void smp_dummy_add_data(void)
708 {
709 	dummy_mcumgr_add_data(smp_receive_buffer, smp_receive_pos);
710 }
711 
smp_dummy_get_send_pos(void)712 uint16_t smp_dummy_get_send_pos(void)
713 {
714 	return smp_send_pos;
715 }
716 
smp_dummy_get_receive_pos(void)717 uint16_t smp_dummy_get_receive_pos(void)
718 {
719 	return smp_receive_pos;
720 }
721 
smp_dummy_tx_pkt(const uint8_t * data,int len)722 int smp_dummy_tx_pkt(const uint8_t *data, int len)
723 {
724 	return mcumgr_dummy_tx_pkt(data, len, smp_receive);
725 }
726 
smp_dummy_enable(void)727 void smp_dummy_enable(void)
728 {
729 	enable_dummy_smp = true;
730 }
731 
smp_dummy_disable(void)732 void smp_dummy_disable(void)
733 {
734 	enable_dummy_smp = false;
735 }
736 
smp_dummy_get_status(void)737 bool smp_dummy_get_status(void)
738 {
739 	return enable_dummy_smp;
740 }
741 
742 SYS_INIT(smp_dummy_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
743