lorawan: adding settings NVM for LoRaWAN
Adding a reference implementation of the Non-Volatile Memory module needed to join any LoRaWAN network. This NVM is based on the SETTINGS subsys to store all the required key to join and communicate on a LoRaWAN network. Without proper NVM, one may experience errors when using OTAA to join the network, as the device may violate anti-replay protection (depending on the version of LoRaWAN). Signed-off-by: Giuliano Franchetto <giuliano.franchetto@intellinium.com>
This commit is contained in:
parent
83c79d1051
commit
6630f7fc07
7 changed files with 305 additions and 22 deletions
|
@ -22,3 +22,4 @@ zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_RU864 REGION_RU864)
|
|||
|
||||
zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LORAWAN lw_priv.c)
|
||||
add_subdirectory(nvm)
|
||||
|
|
|
@ -66,4 +66,6 @@ config LORAMAC_REGION_RU864
|
|||
|
||||
endchoice
|
||||
|
||||
rsource "nvm/Kconfig"
|
||||
|
||||
endif # LORAWAN
|
||||
|
|
|
@ -7,38 +7,38 @@
|
|||
#include <init.h>
|
||||
#include <errno.h>
|
||||
#include <lorawan/lorawan.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include "lw_priv.h"
|
||||
|
||||
#include <LoRaMac.h>
|
||||
#include <Region.h>
|
||||
#include "nvm/lorawan_nvm.h"
|
||||
|
||||
BUILD_ASSERT(!IS_ENABLED(CONFIG_LORAMAC_REGION_UNKNOWN),
|
||||
"Unknown region specified for LoRaWAN in Kconfig");
|
||||
|
||||
#ifdef CONFIG_LORAMAC_REGION_AS923
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AS923
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AS923
|
||||
#elif CONFIG_LORAMAC_REGION_AU915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AU915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AU915
|
||||
#elif CONFIG_LORAMAC_REGION_CN470
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN470
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN470
|
||||
#elif CONFIG_LORAMAC_REGION_CN779
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN779
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN779
|
||||
#elif CONFIG_LORAMAC_REGION_EU433
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU433
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU433
|
||||
#elif CONFIG_LORAMAC_REGION_EU868
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU868
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU868
|
||||
#elif CONFIG_LORAMAC_REGION_KR920
|
||||
#define LORAWAN_REGION LORAMAC_REGION_KR920
|
||||
#define LORAWAN_REGION LORAMAC_REGION_KR920
|
||||
#elif CONFIG_LORAMAC_REGION_IN865
|
||||
#define LORAWAN_REGION LORAMAC_REGION_IN865
|
||||
#define LORAWAN_REGION LORAMAC_REGION_IN865
|
||||
#elif CONFIG_LORAMAC_REGION_US915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_US915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_US915
|
||||
#elif CONFIG_LORAMAC_REGION_RU864
|
||||
#define LORAWAN_REGION LORAMAC_REGION_RU864
|
||||
#define LORAWAN_REGION LORAMAC_REGION_RU864
|
||||
#else
|
||||
#error "At least one LoRaWAN region should be selected"
|
||||
#error "At least one LoRaWAN region should be selected"
|
||||
#endif
|
||||
|
||||
/* Use version 1.0.3.0 for ABP */
|
||||
|
@ -212,13 +212,17 @@ static LoRaMacStatus_t lorawan_join_otaa(
|
|||
mlme_req.Req.Join.Datarate = default_datarate;
|
||||
mlme_req.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA;
|
||||
|
||||
/* Retrieve the NVM context to store device nonce */
|
||||
mib_req.Type = MIB_NVM_CTXS;
|
||||
if (LoRaMacMibGetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Could not get NVM context");
|
||||
return -EINVAL;
|
||||
if (IS_ENABLED(CONFIG_LORAWAN_NVM_NONE)) {
|
||||
/* Retrieve the NVM context to store device nonce */
|
||||
mib_req.Type = MIB_NVM_CTXS;
|
||||
if (LoRaMacMibGetRequestConfirm(&mib_req) !=
|
||||
LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Could not get NVM context");
|
||||
return -EINVAL;
|
||||
}
|
||||
mib_req.Param.Contexts->Crypto.DevNonce =
|
||||
join_cfg->otaa.dev_nonce;
|
||||
}
|
||||
mib_req.Param.Contexts->Crypto.DevNonce = join_cfg->otaa.dev_nonce;
|
||||
|
||||
mib_req.Type = MIB_DEV_EUI;
|
||||
mib_req.Param.DevEui = join_cfg->dev_eui;
|
||||
|
@ -414,7 +418,7 @@ void lorawan_get_payload_sizes(uint8_t *max_next_payload_size,
|
|||
LoRaMacTxInfo_t txInfo;
|
||||
|
||||
/* QueryTxPossible cannot fail */
|
||||
(void)LoRaMacQueryTxPossible(0, &txInfo);
|
||||
(void) LoRaMacQueryTxPossible(0, &txInfo);
|
||||
|
||||
*max_next_payload_size = txInfo.MaxPossibleApplicationDataSize;
|
||||
*max_payload_size = txInfo.CurrentPossiblePayloadSize;
|
||||
|
@ -456,7 +460,8 @@ int lorawan_set_conf_msg_tries(uint8_t tries)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int lorawan_send(uint8_t port, uint8_t *data, uint8_t len, enum lorawan_message_type type)
|
||||
int lorawan_send(uint8_t port, uint8_t *data, uint8_t len,
|
||||
enum lorawan_message_type type)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
McpsReq_t mcpsReq;
|
||||
|
@ -584,6 +589,8 @@ int lorawan_start(void)
|
|||
|
||||
static int lorawan_init(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
LoRaMacStatus_t status;
|
||||
|
||||
sys_slist_init(&dl_callbacks);
|
||||
|
@ -594,7 +601,13 @@ static int lorawan_init(const struct device *dev)
|
|||
macPrimitives.MacMlmeIndication = MlmeIndication;
|
||||
macCallbacks.GetBatteryLevel = getBatteryLevelLocal;
|
||||
macCallbacks.GetTemperatureLevel = NULL;
|
||||
macCallbacks.NvmDataChange = NULL;
|
||||
|
||||
if (IS_ENABLED(CONFIG_LORAWAN_NVM_NONE)) {
|
||||
macCallbacks.NvmDataChange = NULL;
|
||||
} else {
|
||||
macCallbacks.NvmDataChange = lorawan_nvm_data_mgmt_event;
|
||||
}
|
||||
|
||||
macCallbacks.MacProcessNotify = OnMacProcessNotify;
|
||||
|
||||
status = LoRaMacInitialization(&macPrimitives, &macCallbacks,
|
||||
|
@ -605,9 +618,13 @@ static int lorawan_init(const struct device *dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_LORAWAN_NVM_NONE)) {
|
||||
lorawan_nvm_init();
|
||||
lorawan_nvm_data_restore();
|
||||
}
|
||||
|
||||
LOG_DBG("LoRaMAC Initialized");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
||||
|
|
3
subsys/lorawan/nvm/CMakeLists.txt
Normal file
3
subsys/lorawan/nvm/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_LORAWAN_NVM_SETTINGS lorawan_nvm_settings.c)
|
23
subsys/lorawan/nvm/Kconfig
Normal file
23
subsys/lorawan/nvm/Kconfig
Normal file
|
@ -0,0 +1,23 @@
|
|||
# LoRaWAN Non Volatile Memory configuration options
|
||||
|
||||
# Copyright (c) 2022 Intellinium <giuliano.franchetto@intellinium.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
choice LORAWAN_NVM
|
||||
bool "LoRaWAN NVM backend"
|
||||
default LORAWAN_NVM_SETTINGS if SETTINGS
|
||||
default LORAWAN_NVM_NONE
|
||||
|
||||
config LORAWAN_NVM_NONE
|
||||
bool "No NVM backend for LoRaWAN"
|
||||
help
|
||||
If this configuration is used, it is the responsibility of the
|
||||
application to store and restore the DevNonce value each time
|
||||
a OTAA join request is sent. This value should be used in the
|
||||
join configuration directly.
|
||||
|
||||
config LORAWAN_NVM_SETTINGS
|
||||
bool "Settings based NVM"
|
||||
depends on SETTINGS
|
||||
|
||||
endchoice
|
51
subsys/lorawan/nvm/lorawan_nvm.h
Normal file
51
subsys/lorawan/nvm/lorawan_nvm.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Intellinium <giuliano.franchetto@intellinium.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_SUBSYS_LORAWAN_NVM_H_
|
||||
#define ZEPHYR_SUBSYS_LORAWAN_NVM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Hook function called when an interrupt related to NVM
|
||||
* is received from the LoRaWAN stack.
|
||||
*
|
||||
* @note This function should not be called directly by the application
|
||||
*
|
||||
* @param flags OR'ed flags received with the interrupt
|
||||
*
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_NONE
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_CRYPTO
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2
|
||||
* @see LORAMAC_NVM_NOTIFY_FLAG_CLASS_B
|
||||
*/
|
||||
void lorawan_nvm_data_mgmt_event(uint16_t flags);
|
||||
|
||||
/**
|
||||
* @brief Restores all the relevant LoRaWAN data from the Non-Volatile Memory.
|
||||
*
|
||||
* @note This function should only be called if a NVM has been enabled, and
|
||||
* not directly by the application.
|
||||
*
|
||||
* @return 0 on success, or a negative error code otherwise
|
||||
*/
|
||||
int lorawan_nvm_data_restore(void);
|
||||
|
||||
/**
|
||||
* @brief Initializes the NVM backend
|
||||
*
|
||||
* @note This function shall be called before any other
|
||||
* NVM back functions.
|
||||
*
|
||||
* @return 0 on success, or a negative error code otherwise
|
||||
*/
|
||||
int lorawan_nvm_init(void);
|
||||
|
||||
#endif /* ZEPHYR_SUBSYS_LORAWAN_NVM_H_ */
|
186
subsys/lorawan/nvm/lorawan_nvm_settings.c
Normal file
186
subsys/lorawan/nvm/lorawan_nvm_settings.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Intellinium <giuliano.franchetto@intellinium.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <LoRaMac.h>
|
||||
#include <kernel.h>
|
||||
#include <settings/settings.h>
|
||||
#include "lorawan_nvm.h"
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(lorawan_nvm, CONFIG_LORAWAN_LOG_LEVEL);
|
||||
|
||||
#define LORAWAN_SETTINGS_BASE "lorawan/nvm"
|
||||
|
||||
struct lorawan_nvm_setting_descr {
|
||||
const char *name;
|
||||
const char *setting_name;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
uint16_t flag;
|
||||
};
|
||||
|
||||
#define NVM_SETTING_DESCR(_flag, _member) \
|
||||
{ \
|
||||
.flag = _flag, \
|
||||
.name = STRINGIFY(_member), \
|
||||
.setting_name = \
|
||||
LORAWAN_SETTINGS_BASE "/" STRINGIFY(_member), \
|
||||
.offset = offsetof(LoRaMacNvmData_t, _member), \
|
||||
.size = sizeof(((LoRaMacNvmData_t *)0)->_member), \
|
||||
}
|
||||
|
||||
static const struct lorawan_nvm_setting_descr nvm_setting_descriptors[] = {
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_CRYPTO, Crypto),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1, MacGroup1),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2, MacGroup2),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT, SecureElement),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1, RegionGroup1),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2, RegionGroup2),
|
||||
NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_CLASS_B, ClassB),
|
||||
};
|
||||
|
||||
static void lorawan_nvm_save_settings(uint16_t nvm_notify_flag)
|
||||
{
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
LOG_DBG("Saving LoRaWAN settings");
|
||||
|
||||
/* Retrieve the actual context */
|
||||
mib_req.Type = MIB_NVM_CTXS;
|
||||
if (LoRaMacMibGetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Could not get NVM context");
|
||||
return;
|
||||
}
|
||||
|
||||
LoRaMacNvmData_t *nvm = mib_req.Param.Contexts;
|
||||
|
||||
LOG_DBG("Crypto version: %"PRIu32", DevNonce: %d, JoinNonce: %"PRIu32,
|
||||
mib_req.Param.Contexts->Crypto.LrWanVersion.Value,
|
||||
mib_req.Param.Contexts->Crypto.DevNonce,
|
||||
mib_req.Param.Contexts->Crypto.JoinNonce);
|
||||
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(nvm_setting_descriptors); i++) {
|
||||
const struct lorawan_nvm_setting_descr *descr =
|
||||
&nvm_setting_descriptors[i];
|
||||
|
||||
if ((nvm_notify_flag & descr->flag) == descr->flag) {
|
||||
LOG_DBG("Saving configuration %s", descr->setting_name);
|
||||
int err = settings_save_one(descr->setting_name,
|
||||
(char *)nvm + descr->offset,
|
||||
descr->size);
|
||||
if (err) {
|
||||
LOG_ERR("Could not save settings %s, error %d",
|
||||
descr->name, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
settings_save();
|
||||
}
|
||||
|
||||
void lorawan_nvm_data_mgmt_event(uint16_t flags)
|
||||
{
|
||||
if (flags != LORAMAC_NVM_NOTIFY_FLAG_NONE) {
|
||||
lorawan_nvm_save_settings(flags);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_setting(void *tgt, size_t tgt_size,
|
||||
const char *key, size_t len,
|
||||
settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
if (len != tgt_size) {
|
||||
LOG_ERR("Can't load '%s' state, size mismatch.",
|
||||
log_strdup(key));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!tgt) {
|
||||
LOG_ERR("Can't load '%s' state, no target.",
|
||||
log_strdup(key));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (read_cb(cb_arg, tgt, len) != len) {
|
||||
LOG_ERR("Can't load '%s' state, short read.",
|
||||
log_strdup(key));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_setting_loaded(const char *key, size_t len,
|
||||
settings_read_cb read_cb,
|
||||
void *cb_arg, void *param)
|
||||
{
|
||||
int err;
|
||||
LoRaMacNvmData_t *nvm = param;
|
||||
|
||||
LOG_DBG("Key: %s", log_strdup(key));
|
||||
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(nvm_setting_descriptors); i++) {
|
||||
const struct lorawan_nvm_setting_descr *descr =
|
||||
&nvm_setting_descriptors[i];
|
||||
|
||||
if (strcmp(descr->name, key) == 0) {
|
||||
err = load_setting((char *)nvm + descr->offset,
|
||||
descr->size, key, len, read_cb, cb_arg);
|
||||
if (err) {
|
||||
LOG_ERR("Could not read setting %s", descr->name);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_WRN("Unknown LoRaWAN setting: %s", log_strdup(key));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lorawan_nvm_data_restore(void)
|
||||
{
|
||||
int err;
|
||||
LoRaMacStatus_t status;
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
LOG_DBG("Restoring LoRaWAN settings");
|
||||
|
||||
/* Retrieve the actual context */
|
||||
mib_req.Type = MIB_NVM_CTXS;
|
||||
if (LoRaMacMibGetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Could not get NVM context");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = settings_load_subtree_direct(LORAWAN_SETTINGS_BASE,
|
||||
on_setting_loaded,
|
||||
mib_req.Param.Contexts);
|
||||
if (err) {
|
||||
LOG_ERR("Could not load LoRaWAN settings, error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("Crypto version: %"PRIu32", DevNonce: %d, JoinNonce: %"PRIu32,
|
||||
mib_req.Param.Contexts->Crypto.LrWanVersion.Value,
|
||||
mib_req.Param.Contexts->Crypto.DevNonce,
|
||||
mib_req.Param.Contexts->Crypto.JoinNonce);
|
||||
|
||||
mib_req.Type = MIB_NVM_CTXS;
|
||||
status = LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Could not set the NVM context, error %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_DBG("LoRaWAN context restored");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lorawan_nvm_init(void)
|
||||
{
|
||||
return settings_subsys_init();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue