Skip to content

Commit 714894e

Browse files
committed
mgmt: mcumgr: transport: Add LoRaWAN MCUmgr SMP transport
Adds a transport that uses LoRaWAN for receiving and responding to messages Signed-off-by: Jamie McCrae <[email protected]>
1 parent 6f64f77 commit 714894e

File tree

5 files changed

+367
-0
lines changed

5 files changed

+367
-0
lines changed

include/zephyr/mgmt/mcumgr/transport/smp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ enum smp_transport_type {
148148
SMP_UDP_IPV4_TRANSPORT,
149149
/** SMP UDP IPv6 */
150150
SMP_UDP_IPV6_TRANSPORT,
151+
/** SMP LoRaWAN */
152+
SMP_LORAWAN_TRANSPORT,
151153
/** SMP user defined type */
152154
SMP_USER_DEFINED_TRANSPORT
153155
};

subsys/mgmt/mcumgr/transport/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_UART
2525
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_UDP
2626
src/smp_udp.c
2727
)
28+
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_LORAWAN
29+
src/smp_lorawan.c
30+
)
2831
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_DUMMY
2932
src/smp_dummy.c
3033
)

subsys/mgmt/mcumgr/transport/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ rsource "Kconfig.dummy"
7777

7878
rsource "Kconfig.bluetooth"
7979

80+
rsource "Kconfig.lorawan"
81+
8082
rsource "Kconfig.shell"
8183

8284
rsource "Kconfig.uart"
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#
2+
# Copyright (c) 2024, Jamie McCrae
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
# The Kconfig file is dedicated to the LoRaWAN transport of MCUmgr
8+
# subsystem and provides Kconfig options to control aspects of
9+
# the transport.
10+
#
11+
# Options defined in this file should be prefixed:
12+
# MCUMGR_TRANSPORT_LORAWAN_
13+
14+
menuconfig MCUMGR_TRANSPORT_LORAWAN
15+
bool "LoRaWAN MCUmgr SMP transport"
16+
depends on LORAWAN
17+
help
18+
Enables handling of SMP commands received over LoRaWAN.
19+
20+
if MCUMGR_TRANSPORT_LORAWAN
21+
22+
config MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT
23+
int "LoRaWAN SMP frame port"
24+
range 1 223
25+
default 2
26+
help
27+
LoRaWAN download and uplink frame port used for communication. All messages received on
28+
this port will be treated as SMP packets.
29+
30+
config MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS
31+
bool "Use confirmed packets for uplinks"
32+
default y
33+
help
34+
Will use confirmed uplink packets for responses if enabled, otherwise will use
35+
unconfirmed packets.
36+
37+
config MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
38+
bool "Reassemble LoRaWAN SMP messages"
39+
select MCUMGR_TRANSPORT_REASSEMBLY
40+
default y
41+
help
42+
Will reassemble downlink LoRaWAN messages together to allow for messages larger than a
43+
single message to be received, otherwise will support messages up to a single packet in
44+
size.
45+
46+
menuconfig MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
47+
bool "Send empty packet if partial packet received"
48+
depends on MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
49+
default y
50+
help
51+
Will send an empty packet if a partial (fragmented) message has been received from the
52+
server, this will allow the next packet to be received without waiting for next
53+
transmission window.
54+
55+
Note: this requires a dedicated thread in order to prevent blocking the system workqueue.
56+
57+
if MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
58+
59+
config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_STACK_SIZE
60+
int "Poll thread stack size"
61+
default 1800
62+
help
63+
Stack size of the thread that will poll for empty additional packets when a partial
64+
frame is received.
65+
66+
config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_THREAD_PRIORITY
67+
int "Poll thread priority"
68+
default 3
69+
help
70+
Priority of the thread for polling for empty additional packets when a partial frame
71+
is received.
72+
73+
config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_RETRIES
74+
int "Poll thread retries"
75+
default 3
76+
help
77+
Number of LoRaWAN message send retries if sending fails for the thread for polling for
78+
empty additional packets when a partial frame is received.
79+
80+
endif # MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
81+
82+
config MCUMGR_TRANSPORT_LORAWAN_FRAGMENTED_UPLINKS
83+
bool "Fragment uplink messages"
84+
default y
85+
help
86+
Will fragment messages into multiple uplink messages if they are too big to fit into a
87+
single uplink message. If disabled then uplinks that are too large will not be sent.
88+
89+
module = MCUMGR_TRANSPORT_LORAWAN
90+
module-str = LoRaWAN MCUmgr SMP transport
91+
source "subsys/logging/Kconfig.template.log_config"
92+
93+
endif # MCUMGR_TRANSPORT_LORAWAN
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
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, bool data_pending, 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+
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+
125+
static void smp_lorawan_downlink(uint8_t port, bool data_pending, int16_t rssi, int8_t snr,
126+
uint8_t len, const uint8_t *hex_data)
127+
{
128+
ARG_UNUSED(data_pending);
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+
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+
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+
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);

0 commit comments

Comments
 (0)