1 /*
2 * Copyright (c) 2022 Intellinium <giuliano.franchetto@intellinium.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <LoRaMac.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/settings/settings.h>
10 #include "lorawan_nvm.h"
11 #include <zephyr/logging/log.h>
12
13 LOG_MODULE_REGISTER(lorawan_nvm, CONFIG_LORAWAN_LOG_LEVEL);
14
15 #define LORAWAN_SETTINGS_BASE "lorawan/nvm"
16
17 struct lorawan_nvm_setting_descr {
18 const char *name;
19 const char *setting_name;
20 size_t size;
21 off_t offset;
22 uint16_t flag;
23 };
24
25 #define NVM_SETTING_DESCR(_flag, _member) \
26 { \
27 .flag = _flag, \
28 .name = STRINGIFY(_member), \
29 .setting_name = \
30 LORAWAN_SETTINGS_BASE "/" STRINGIFY(_member), \
31 .offset = offsetof(LoRaMacNvmData_t, _member), \
32 .size = sizeof(((LoRaMacNvmData_t *)0)->_member), \
33 }
34
35 static const struct lorawan_nvm_setting_descr nvm_setting_descriptors[] = {
36 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_CRYPTO, Crypto),
37 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1, MacGroup1),
38 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2, MacGroup2),
39 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT, SecureElement),
40 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1, RegionGroup1),
41 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2, RegionGroup2),
42 NVM_SETTING_DESCR(LORAMAC_NVM_NOTIFY_FLAG_CLASS_B, ClassB),
43 };
44
lorawan_nvm_save_settings(uint16_t nvm_notify_flag)45 static void lorawan_nvm_save_settings(uint16_t nvm_notify_flag)
46 {
47 MibRequestConfirm_t mib_req;
48
49 LOG_DBG("Saving LoRaWAN settings");
50
51 /* Retrieve the actual context */
52 mib_req.Type = MIB_NVM_CTXS;
53 if (LoRaMacMibGetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
54 LOG_ERR("Could not get NVM context");
55 return;
56 }
57
58 LoRaMacNvmData_t *nvm = mib_req.Param.Contexts;
59
60 LOG_DBG("Crypto version: %"PRIu32", DevNonce: %d, JoinNonce: %"PRIu32,
61 mib_req.Param.Contexts->Crypto.LrWanVersion.Value,
62 mib_req.Param.Contexts->Crypto.DevNonce,
63 mib_req.Param.Contexts->Crypto.JoinNonce);
64
65 for (uint32_t i = 0; i < ARRAY_SIZE(nvm_setting_descriptors); i++) {
66 const struct lorawan_nvm_setting_descr *descr =
67 &nvm_setting_descriptors[i];
68
69 if ((nvm_notify_flag & descr->flag) == descr->flag) {
70 LOG_DBG("Saving configuration %s", descr->setting_name);
71 int err = settings_save_one(descr->setting_name,
72 (char *)nvm + descr->offset,
73 descr->size);
74 if (err) {
75 LOG_ERR("Could not save settings %s, error %d",
76 descr->name, err);
77 }
78 }
79 }
80 }
81
lorawan_nvm_data_mgmt_event(uint16_t flags)82 void lorawan_nvm_data_mgmt_event(uint16_t flags)
83 {
84 if (flags != LORAMAC_NVM_NOTIFY_FLAG_NONE) {
85 lorawan_nvm_save_settings(flags);
86 }
87 }
88
load_setting(void * tgt,size_t tgt_size,const char * key,size_t len,settings_read_cb read_cb,void * cb_arg)89 static int load_setting(void *tgt, size_t tgt_size,
90 const char *key, size_t len,
91 settings_read_cb read_cb, void *cb_arg)
92 {
93 if (len != tgt_size) {
94 LOG_ERR("Can't load '%s' state, size mismatch.",
95 key);
96 return -EINVAL;
97 }
98
99 if (!tgt) {
100 LOG_ERR("Can't load '%s' state, no target.",
101 key);
102 return -EINVAL;
103 }
104
105 if (read_cb(cb_arg, tgt, len) != len) {
106 LOG_ERR("Can't load '%s' state, short read.",
107 key);
108 return -EINVAL;
109 }
110
111 return 0;
112 }
113
on_setting_loaded(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)114 static int on_setting_loaded(const char *key, size_t len,
115 settings_read_cb read_cb,
116 void *cb_arg, void *param)
117 {
118 int err;
119 LoRaMacNvmData_t *nvm = param;
120
121 LOG_DBG("Key: %s", key);
122
123 for (uint32_t i = 0; i < ARRAY_SIZE(nvm_setting_descriptors); i++) {
124 const struct lorawan_nvm_setting_descr *descr =
125 &nvm_setting_descriptors[i];
126
127 if (strcmp(descr->name, key) == 0) {
128 err = load_setting((char *)nvm + descr->offset,
129 descr->size, key, len, read_cb, cb_arg);
130 if (err) {
131 LOG_ERR("Could not read setting %s", descr->name);
132 }
133 return err;
134 }
135 }
136
137 LOG_WRN("Unknown LoRaWAN setting: %s", key);
138 return 0;
139 }
140
lorawan_nvm_data_restore(void)141 int lorawan_nvm_data_restore(void)
142 {
143 int err;
144 LoRaMacStatus_t status;
145 MibRequestConfirm_t mib_req;
146
147 LOG_DBG("Restoring LoRaWAN settings");
148
149 /* Retrieve the actual context */
150 mib_req.Type = MIB_NVM_CTXS;
151 if (LoRaMacMibGetRequestConfirm(&mib_req) != LORAMAC_STATUS_OK) {
152 LOG_ERR("Could not get NVM context");
153 return -EINVAL;
154 }
155
156 err = settings_load_subtree_direct(LORAWAN_SETTINGS_BASE,
157 on_setting_loaded,
158 mib_req.Param.Contexts);
159 if (err) {
160 LOG_ERR("Could not load LoRaWAN settings, error %d", err);
161 return err;
162 }
163
164 LOG_DBG("Crypto version: %"PRIu32", DevNonce: %d, JoinNonce: %"PRIu32,
165 mib_req.Param.Contexts->Crypto.LrWanVersion.Value,
166 mib_req.Param.Contexts->Crypto.DevNonce,
167 mib_req.Param.Contexts->Crypto.JoinNonce);
168
169 mib_req.Type = MIB_NVM_CTXS;
170 status = LoRaMacMibSetRequestConfirm(&mib_req);
171 if (status != LORAMAC_STATUS_OK) {
172 LOG_ERR("Could not set the NVM context, error %d", status);
173 return -EINVAL;
174 }
175
176 LOG_DBG("LoRaWAN context restored");
177
178 return 0;
179 }
180
lorawan_nvm_init(void)181 int lorawan_nvm_init(void)
182 {
183 return settings_subsys_init();
184 }
185