forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
Showing
5 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |