1 /*
2  * Copyright Runtime.io 2018. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file
8  * @brief UART transport for the mcumgr SMP protocol.
9  */
10 
11 #include <string.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/init.h>
14 #include <zephyr/net_buf.h>
15 #include <zephyr/drivers/console/uart_mcumgr.h>
16 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
17 #include <zephyr/mgmt/mcumgr/smp/smp.h>
18 #include <zephyr/mgmt/mcumgr/transport/smp.h>
19 #include <zephyr/mgmt/mcumgr/transport/serial.h>
20 
21 #include <mgmt/mcumgr/transport/smp_internal.h>
22 
23 BUILD_ASSERT(CONFIG_MCUMGR_TRANSPORT_UART_MTU != 0, "CONFIG_MCUMGR_TRANSPORT_UART_MTU must be > 0");
24 
25 struct device;
26 
27 static void smp_uart_process_rx_queue(struct k_work *work);
28 
29 K_FIFO_DEFINE(smp_uart_rx_fifo);
30 K_WORK_DEFINE(smp_uart_work, smp_uart_process_rx_queue);
31 
32 static struct mcumgr_serial_rx_ctxt smp_uart_rx_ctxt;
33 static struct smp_transport smp_uart_transport;
34 #ifdef CONFIG_SMP_CLIENT
35 static struct smp_client_transport_entry smp_client_transport;
36 #endif
37 
38 /**
39  * Processes a single line (fragment) coming from the mcumgr UART driver.
40  */
smp_uart_process_frag(struct uart_mcumgr_rx_buf * rx_buf)41 static void smp_uart_process_frag(struct uart_mcumgr_rx_buf *rx_buf)
42 {
43 	struct net_buf *nb;
44 
45 	/* Decode the fragment and write the result to the global receive
46 	 * context.
47 	 */
48 	nb = mcumgr_serial_process_frag(&smp_uart_rx_ctxt,
49 					rx_buf->data, rx_buf->length);
50 
51 	/* Release the encoded fragment. */
52 	uart_mcumgr_free_rx_buf(rx_buf);
53 
54 	/* If a complete packet has been received, pass it to SMP for
55 	 * processing.
56 	 */
57 	if (nb != NULL) {
58 		smp_rx_req(&smp_uart_transport, nb);
59 	}
60 }
61 
smp_uart_process_rx_queue(struct k_work * work)62 static void smp_uart_process_rx_queue(struct k_work *work)
63 {
64 	struct uart_mcumgr_rx_buf *rx_buf;
65 
66 	while ((rx_buf = k_fifo_get(&smp_uart_rx_fifo, K_NO_WAIT)) != NULL) {
67 		smp_uart_process_frag(rx_buf);
68 	}
69 }
70 
71 /**
72  * Enqueues a received SMP fragment for later processing.  This function
73  * executes in the interrupt context.
74  */
smp_uart_rx_frag(struct uart_mcumgr_rx_buf * rx_buf)75 static void smp_uart_rx_frag(struct uart_mcumgr_rx_buf *rx_buf)
76 {
77 	k_fifo_put(&smp_uart_rx_fifo, rx_buf);
78 	k_work_submit(&smp_uart_work);
79 }
80 
smp_uart_get_mtu(const struct net_buf * nb)81 static uint16_t smp_uart_get_mtu(const struct net_buf *nb)
82 {
83 	return CONFIG_MCUMGR_TRANSPORT_UART_MTU;
84 }
85 
smp_uart_tx_pkt(struct net_buf * nb)86 static int smp_uart_tx_pkt(struct net_buf *nb)
87 {
88 	int rc;
89 
90 	rc = uart_mcumgr_send(nb->data, nb->len);
91 	smp_packet_free(nb);
92 
93 	return rc;
94 }
95 
smp_uart_init(void)96 static int smp_uart_init(void)
97 {
98 	int rc;
99 
100 	smp_uart_transport.functions.output = smp_uart_tx_pkt;
101 	smp_uart_transport.functions.get_mtu = smp_uart_get_mtu;
102 
103 	rc = smp_transport_init(&smp_uart_transport);
104 
105 	if (rc == 0) {
106 		uart_mcumgr_register(smp_uart_rx_frag);
107 #ifdef CONFIG_SMP_CLIENT
108 		smp_client_transport.smpt = &smp_uart_transport;
109 		smp_client_transport.smpt_type = SMP_SERIAL_TRANSPORT;
110 		smp_client_transport_register(&smp_client_transport);
111 #endif
112 	}
113 
114 	return rc;
115 }
116 
117 SYS_INIT(smp_uart_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
118