Skip to content

Commit

Permalink
mgmt: mcumgr: transport: Add LoRaWAN MCUmgr SMP transport
Browse files Browse the repository at this point in the history
Adds a transport that uses LoRaWAN for receiving and responding
to messages

Signed-off-by: Jamie McCrae <[email protected]>
  • Loading branch information
thedjnK authored and aescolar committed Oct 25, 2024
1 parent 62d706e commit 973ba91
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 0 deletions.
2 changes: 2 additions & 0 deletions include/zephyr/mgmt/mcumgr/transport/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ enum smp_transport_type {
SMP_UDP_IPV4_TRANSPORT,
/** SMP UDP IPv6 */
SMP_UDP_IPV6_TRANSPORT,
/** SMP LoRaWAN */
SMP_LORAWAN_TRANSPORT,
/** SMP user defined type */
SMP_USER_DEFINED_TRANSPORT
};
Expand Down
3 changes: 3 additions & 0 deletions subsys/mgmt/mcumgr/transport/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_UART
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_UDP
src/smp_udp.c
)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_LORAWAN
src/smp_lorawan.c
)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_DUMMY
src/smp_dummy.c
)
Expand Down
2 changes: 2 additions & 0 deletions subsys/mgmt/mcumgr/transport/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ rsource "Kconfig.dummy"

rsource "Kconfig.bluetooth"

rsource "Kconfig.lorawan"

rsource "Kconfig.shell"

rsource "Kconfig.uart"
Expand Down
93 changes: 93 additions & 0 deletions subsys/mgmt/mcumgr/transport/Kconfig.lorawan
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#
# Copyright (c) 2024, Jamie McCrae
#
# SPDX-License-Identifier: Apache-2.0
#

# The Kconfig file is dedicated to the LoRaWAN transport of MCUmgr
# subsystem and provides Kconfig options to control aspects of
# the transport.
#
# Options defined in this file should be prefixed:
# MCUMGR_TRANSPORT_LORAWAN_

menuconfig MCUMGR_TRANSPORT_LORAWAN
bool "LoRaWAN MCUmgr SMP transport"
depends on LORAWAN
help
Enables handling of SMP commands received over LoRaWAN.

if MCUMGR_TRANSPORT_LORAWAN

config MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT
int "LoRaWAN SMP frame port"
range 1 223
default 2
help
LoRaWAN download and uplink frame port used for communication. All messages received on
this port will be treated as SMP packets.

config MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS
bool "Use confirmed packets for uplinks"
default y
help
Will use confirmed uplink packets for responses if enabled, otherwise will use
unconfirmed packets.

config MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
bool "Reassemble LoRaWAN SMP messages"
select MCUMGR_TRANSPORT_REASSEMBLY
default y
help
Will reassemble downlink LoRaWAN messages together to allow for messages larger than a
single message to be received, otherwise will support messages up to a single packet in
size.

menuconfig MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
bool "Send empty packet if partial packet received"
depends on MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
default y
help
Will send an empty packet if a partial (fragmented) message has been received from the
server, this will allow the next packet to be received without waiting for next
transmission window.

Note: this requires a dedicated thread in order to prevent blocking the system workqueue.

if MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA

config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_STACK_SIZE
int "Poll thread stack size"
default 1800
help
Stack size of the thread that will poll for empty additional packets when a partial
frame is received.

config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_THREAD_PRIORITY
int "Poll thread priority"
default 3
help
Priority of the thread for polling for empty additional packets when a partial frame
is received.

config MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_RETRIES
int "Poll thread retries"
default 3
help
Number of LoRaWAN message send retries if sending fails for the thread for polling for
empty additional packets when a partial frame is received.

endif # MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA

config MCUMGR_TRANSPORT_LORAWAN_FRAGMENTED_UPLINKS
bool "Fragment uplink messages"
default y
help
Will fragment messages into multiple uplink messages if they are too big to fit into a
single uplink message. If disabled then uplinks that are too large will not be sent.

module = MCUMGR_TRANSPORT_LORAWAN
module-str = LoRaWAN MCUmgr SMP transport
source "subsys/logging/Kconfig.template.log_config"

endif # MCUMGR_TRANSPORT_LORAWAN
267 changes: 267 additions & 0 deletions subsys/mgmt/mcumgr/transport/src/smp_lorawan.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
/*
* Copyright (c) 2024, Jamie McCrae
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/lorawan/lorawan.h>
#include <zephyr/mgmt/mcumgr/smp/smp.h>
#include <zephyr/mgmt/mcumgr/transport/smp.h>
#include <zephyr/mgmt/mcumgr/mgmt/handlers.h>

#include <mgmt/mcumgr/transport/smp_internal.h>
#include <mgmt/mcumgr/transport/smp_reassembly.h>

LOG_MODULE_REGISTER(smp_lorawan, CONFIG_MCUMGR_TRANSPORT_LORAWAN_LOG_LEVEL);

static void smp_lorawan_downlink(uint8_t port, bool data_pending, int16_t rssi, int8_t snr,
uint8_t len, const uint8_t *hex_data);

static int smp_lorawan_uplink(struct net_buf *nb);

static uint16_t smp_lorawan_get_mtu(const struct net_buf *nb);

static struct lorawan_downlink_cb lorawan_smp_downlink_cb = {
.port = CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT,
.cb = smp_lorawan_downlink,
};

struct smp_transport smp_lorawan_transport = {
.functions.output = smp_lorawan_uplink,
.functions.get_mtu = smp_lorawan_get_mtu,
};

#ifdef CONFIG_SMP_CLIENT
struct smp_client_transport_entry smp_lorawan_client_transport = {
.smpt = &smp_lorawan_transport,
.smpt_type = SMP_LORAWAN_TRANSPORT,
};
#endif

#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
static struct k_thread smp_lorawan_thread;
K_KERNEL_STACK_MEMBER(smp_lorawan_stack, CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_STACK_SIZE);
K_FIFO_DEFINE(smp_lorawan_fifo);

struct smp_lorawan_uplink_message_t {
void *fifo_reserved;
struct net_buf *nb;
struct k_sem my_sem;
};

static struct smp_lorawan_uplink_message_t empty_message = {
.nb = NULL,
};

static void smp_lorawan_uplink_thread(void *p1, void *p2, void *p3)
{
struct smp_lorawan_uplink_message_t *msg;

while (1) {
msg = k_fifo_get(&smp_lorawan_fifo, K_FOREVER);
uint16_t size = 0;
uint16_t pos = 0;

if (msg->nb != NULL) {
size = msg->nb->len;
}

while (pos < size || size == 0) {
uint8_t *data = NULL;
uint8_t data_size;
uint8_t temp;
uint8_t tries = CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_RETRIES;

lorawan_get_payload_sizes(&data_size, &temp);

if (data_size > size) {
data_size = size;
}

if (size > 0) {
if ((data_size + pos) > size) {
data_size = size - pos;
}

data = net_buf_pull_mem(msg->nb, data_size);
}

while (tries > 0) {
int rc;

rc = lorawan_send(CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT,
data, data_size,
#if defined(CONFIG_MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS)
LORAWAN_MSG_CONFIRMED
#else
LORAWAN_MSG_UNCONFIRMED
#endif
);

if (rc != 0) {
--tries;
} else {
break;
}
}

if (size == 0) {
break;
}

pos += data_size;
}

/* For empty packets, do not trigger semaphore */
if (size != 0) {
k_sem_give(&msg->my_sem);
}
}
}
#endif

static void smp_lorawan_downlink(uint8_t port, bool data_pending, int16_t rssi, int8_t snr,
uint8_t len, const uint8_t *hex_data)
{
ARG_UNUSED(data_pending);
ARG_UNUSED(rssi);
ARG_UNUSED(snr);

if (port == CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT) {
#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
int rc;

if (len == 0) {
/* Empty packet is used to clear partially queued data */
(void)smp_reassembly_drop(&smp_lorawan_transport);
} else {
rc = smp_reassembly_collect(&smp_lorawan_transport, hex_data, len);

if (rc == 0) {
rc = smp_reassembly_complete(&smp_lorawan_transport, false);

if (rc) {
LOG_ERR("LoRaWAN SMP reassembly complete failed: %d", rc);
}
} else if (rc < 0) {
LOG_ERR("LoRaWAN SMP reassembly collect failed: %d", rc);
} else {
LOG_ERR("LoRaWAN SMP expected data left: %d", rc);

#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
/* Send empty LoRaWAN packet to receive next packet from server */
k_fifo_put(&smp_lorawan_fifo, &empty_message);
#endif
}
}
#else
if (len > sizeof(struct smp_hdr)) {
struct net_buf *nb;

nb = smp_packet_alloc();

if (!nb) {
LOG_ERR("LoRaWAN SMP packet allocation failure");
return;
}

net_buf_add_mem(nb, hex_data, len);
smp_rx_req(&smp_lorawan_transport, nb);
} else {
LOG_ERR("Invalid LoRaWAN SMP downlink");
}
#endif
} else {
LOG_ERR("Invalid LoRaWAN SMP downlink");
}
}

static int smp_lorawan_uplink(struct net_buf *nb)
{
int rc = 0;

#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAGMENTED_UPLINKS
struct smp_lorawan_uplink_message_t tx_data = {
.nb = nb,
};

k_sem_init(&tx_data.my_sem, 0, 1);
k_fifo_put(&smp_lorawan_fifo, &tx_data);
k_sem_take(&tx_data.my_sem, K_FOREVER);
#else
uint8_t data_size;
uint8_t temp;

lorawan_get_payload_sizes(&data_size, &temp);

if (nb->len > data_size) {
LOG_ERR("Cannot send LoRaWAN SMP message, too large. Message: %d, maximum: %d",
nb->len, data_size);
} else {
rc = lorawan_send(CONFIG_MCUMGR_TRANSPORT_LORAWAN_FRAME_PORT, nb->data, nb->len,
#if defined(CONFIG_MCUMGR_TRANSPORT_LORAWAN_CONFIRMED_UPLINKS)
LORAWAN_MSG_CONFIRMED
#else
LORAWAN_MSG_UNCONFIRMED
#endif
);

if (rc != 0) {
LOG_ERR("Failed to send LoRaWAN SMP message: %d", rc);
}
}
#endif

smp_packet_free(nb);

return rc;
}

static uint16_t smp_lorawan_get_mtu(const struct net_buf *nb)
{
ARG_UNUSED(nb);

uint8_t max_data_size;
uint8_t temp;

lorawan_get_payload_sizes(&max_data_size, &temp);

return (uint16_t)max_data_size;
}

static void smp_lorawan_start(void)
{
int rc;

rc = smp_transport_init(&smp_lorawan_transport);

#ifdef CONFIG_SMP_CLIENT
if (rc == 0) {
smp_client_transport_register(&smp_lorawan_client_transport);
}
#endif

if (rc == 0) {
lorawan_register_downlink_callback(&lorawan_smp_downlink_cb);
} else {
LOG_ERR("Failed to init LoRaWAN MCUmgr SMP transport: %d", rc);
}

#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_REASSEMBLY
smp_reassembly_init(&smp_lorawan_transport);
#endif

#ifdef CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA
k_thread_create(&smp_lorawan_thread, smp_lorawan_stack,
K_KERNEL_STACK_SIZEOF(smp_lorawan_stack),
smp_lorawan_uplink_thread, NULL, NULL, NULL,
CONFIG_MCUMGR_TRANSPORT_LORAWAN_POLL_FOR_DATA_THREAD_PRIORITY, 0,
K_FOREVER);

k_thread_start(&smp_lorawan_thread);
#endif
}

MCUMGR_HANDLER_DEFINE(smp_lorawan, smp_lorawan_start);

0 comments on commit 973ba91

Please sign in to comment.