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