1 /*
2  * Copyright (c) 2024, Jamie McCrae
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/lorawan/lorawan.h>
10 #include <zephyr/mgmt/mcumgr/smp/smp.h>
11 #include <zephyr/mgmt/mcumgr/transport/smp.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
13 
14 #include <mgmt/mcumgr/transport/smp_internal.h>
15 #include <mgmt/mcumgr/transport/smp_reassembly.h>
16 
17 LOG_MODULE_REGISTER(smp_lorawan, CONFIG_MCUMGR_TRANSPORT_LORAWAN_LOG_LEVEL);
18 
19 static void smp_lorawan_downlink(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr,
20 				 uint8_t len, const uint8_t *hex_data);
21 
22 static int smp_lorawan_uplink(struct net_buf *nb);
23 
24 static uint16_t smp_lorawan_get_mtu(const struct net_buf *nb);
25 
26 static struct lorawan_downlink_cb lorawan_smp_downlink_cb = {
27 	.port = CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT,
28 	.cb = smp_lorawan_downlink,
29 };
30 
31 struct smp_transport smp_lorawan_transport = {
32 	.functions.output = smp_lorawan_uplink,
33 	.functions.get_mtu = smp_lorawan_get_mtu,
34 };
35 
36 #ifdef CONFIG_SMP_CLIENT
37 struct smp_client_transport_entry smp_lorawan_client_transport = {
38 	.smpt = &smp_lorawan_transport,
39 	.smpt_type = SMP_LORAWAN_TRANSPORT,
40 };
41 #endif
42 
43 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
44 static struct k_thread smp_lorawan_thread;
45 K_KERNEL_STACK_MEMBER(smp_lorawan_stack, CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_STACK_SIZE);
46 K_FIFO_DEFINE(smp_lorawan_fifo);
47 
48 struct smp_lorawan_uplink_message_t {
49 	void *fifo_reserved;
50 	struct net_buf *nb;
51 	struct k_sem my_sem;
52 };
53 
54 static struct smp_lorawan_uplink_message_t empty_message = {
55 	.nb = NULL,
56 };
57 
smp_lorawan_uplink_thread(void * p1,void * p2,void * p3)58 static void smp_lorawan_uplink_thread(void *p1, void *p2, void *p3)
59 {
60 	struct smp_lorawan_uplink_message_t *msg;
61 
62 	while (1) {
63 		msg = k_fifo_get(&smp_lorawan_fifo, K_FOREVER);
64 		uint16_t size = 0;
65 		uint16_t pos = 0;
66 
67 		if (msg->nb != NULL) {
68 			size = msg->nb->len;
69 		}
70 
71 		while (pos < size || size == 0) {
72 			uint8_t *data = NULL;
73 			uint8_t data_size;
74 			uint8_t temp;
75 			uint8_t tries = CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_RETRIES;
76 
77 			lorawan_get_payload_sizes(&data_size, &temp);
78 
79 			if (data_size > size) {
80 				data_size = size;
81 			}
82 
83 			if (size > 0) {
84 				if ((data_size + pos) > size) {
85 					data_size = size - pos;
86 				}
87 
88 				data = net_buf_pull_mem(msg->nb, data_size);
89 			}
90 
91 			while (tries > 0) {
92 				int rc;
93 
94 				rc = lorawan_send(CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT,
95 						  data, data_size,
96 #if defined(CONFIG_MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS)
97 						  LORAWAN_MSG_CONFIRMED
98 #else
99 						  LORAWAN_MSG_UNCONFIRMED
100 #endif
101 						 );
102 
103 				if (rc != 0) {
104 					--tries;
105 				} else {
106 					break;
107 				}
108 			}
109 
110 			if (size == 0) {
111 				break;
112 			}
113 
114 			pos += data_size;
115 		}
116 
117 		/* For empty packets, do not trigger semaphore */
118 		if (size != 0) {
119 			k_sem_give(&msg->my_sem);
120 		}
121 	}
122 }
123 #endif
124 
smp_lorawan_downlink(uint8_t port,uint8_t flags,int16_t rssi,int8_t snr,uint8_t len,const uint8_t * hex_data)125 static void smp_lorawan_downlink(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr,
126 				 uint8_t len, const uint8_t *hex_data)
127 {
128 	ARG_UNUSED(flags);
129 	ARG_UNUSED(rssi);
130 	ARG_UNUSED(snr);
131 
132 	if (port == CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT) {
133 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
134 		int rc;
135 
136 		if (len == 0) {
137 			/* Empty packet is used to clear partially queued data */
138 			(void)smp_reassembly_drop(&smp_lorawan_transport);
139 		} else {
140 			rc = smp_reassembly_collect(&smp_lorawan_transport, hex_data, len);
141 
142 			if (rc == 0) {
143 				rc = smp_reassembly_complete(&smp_lorawan_transport, false);
144 
145 				if (rc) {
146 					LOG_ERR("LoRaWAN SMP reassembly complete failed: %d", rc);
147 				}
148 			} else if (rc < 0) {
149 				LOG_ERR("LoRaWAN SMP reassembly collect failed: %d", rc);
150 			} else {
151 				LOG_ERR("LoRaWAN SMP expected data left: %d", rc);
152 
153 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
154 				/* Send empty LoRaWAN packet to receive next packet from server */
155 				k_fifo_put(&smp_lorawan_fifo, &empty_message);
156 #endif
157 			}
158 		}
159 #else
160 		if (len > sizeof(struct smp_hdr)) {
161 			struct net_buf *nb;
162 
163 			nb = smp_packet_alloc();
164 
165 			if (!nb) {
166 				LOG_ERR("LoRaWAN SMP packet allocation failure");
167 				return;
168 			}
169 
170 			net_buf_add_mem(nb, hex_data, len);
171 			smp_rx_req(&smp_lorawan_transport, nb);
172 		} else {
173 			LOG_ERR("Invalid LoRaWAN SMP downlink");
174 		}
175 #endif
176 	} else {
177 		LOG_ERR("Invalid LoRaWAN SMP downlink");
178 	}
179 }
180 
smp_lorawan_uplink(struct net_buf * nb)181 static int smp_lorawan_uplink(struct net_buf *nb)
182 {
183 	int rc = 0;
184 
185 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAGMENTED_UPLINKS
186 	struct smp_lorawan_uplink_message_t tx_data = {
187 		.nb = nb,
188 	};
189 
190 	k_sem_init(&tx_data.my_sem, 0, 1);
191 	k_fifo_put(&smp_lorawan_fifo, &tx_data);
192 	k_sem_take(&tx_data.my_sem, K_FOREVER);
193 #else
194 	uint8_t data_size;
195 	uint8_t temp;
196 
197 	lorawan_get_payload_sizes(&data_size, &temp);
198 
199 	if (nb->len > data_size) {
200 		LOG_ERR("Cannot send LoRaWAN SMP message, too large. Message: %d, maximum: %d",
201 			nb->len, data_size);
202 	} else {
203 		rc = lorawan_send(CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT, nb->data, nb->len,
204 #if defined(CONFIG_MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS)
205 				  LORAWAN_MSG_CONFIRMED
206 #else
207 				  LORAWAN_MSG_UNCONFIRMED
208 #endif
209 				 );
210 
211 		if (rc != 0) {
212 			LOG_ERR("Failed to send LoRaWAN SMP message: %d", rc);
213 		}
214 	}
215 #endif
216 
217 	smp_packet_free(nb);
218 
219 	return rc;
220 }
221 
smp_lorawan_get_mtu(const struct net_buf * nb)222 static uint16_t smp_lorawan_get_mtu(const struct net_buf *nb)
223 {
224 	ARG_UNUSED(nb);
225 
226 	uint8_t max_data_size;
227 	uint8_t temp;
228 
229 	lorawan_get_payload_sizes(&max_data_size, &temp);
230 
231 	return (uint16_t)max_data_size;
232 }
233 
smp_lorawan_start(void)234 static void smp_lorawan_start(void)
235 {
236 	int rc;
237 
238 	rc = smp_transport_init(&smp_lorawan_transport);
239 
240 #ifdef CONFIG_SMP_CLIENT
241 	if (rc == 0) {
242 		smp_client_transport_register(&smp_lorawan_client_transport);
243 	}
244 #endif
245 
246 	if (rc == 0) {
247 		lorawan_register_downlink_callback(&lorawan_smp_downlink_cb);
248 	} else {
249 		LOG_ERR("Failed to init LoRaWAN MCUmgr SMP transport: %d", rc);
250 	}
251 
252 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
253 	smp_reassembly_init(&smp_lorawan_transport);
254 #endif
255 
256 #ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
257 	k_thread_create(&smp_lorawan_thread, smp_lorawan_stack,
258 			K_KERNEL_STACK_SIZEOF(smp_lorawan_stack),
259 			smp_lorawan_uplink_thread, NULL, NULL, NULL,
260 			CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_THREAD_PRIORITY, 0,
261 			K_FOREVER);
262 
263 	k_thread_start(&smp_lorawan_thread);
264 #endif
265 }
266 
267 MCUMGR_HANDLER_DEFINE(smp_lorawan, smp_lorawan_start);
268