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 <spam@helper3000.net>
This commit is contained in:
Jamie McCrae 2024-10-20 10:22:50 +01:00 committed by Alberto Escolar
parent 62d706e3b3
commit 973ba91487
5 changed files with 367 additions and 0 deletions

View File

@ -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
};

View File

@ -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
)

View File

@ -77,6 +77,8 @@ rsource "Kconfig.dummy"
rsource "Kconfig.bluetooth"
rsource "Kconfig.lorawan"
rsource "Kconfig.shell"
rsource "Kconfig.uart"

View File

@ -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

View File

@ -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);