1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <stdlib.h>
10 #include <zephyr/settings/settings.h>
11
12 #include "wifi_credentials_internal.h"
13
14 LOG_MODULE_REGISTER(wifi_credentials_backend, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
15
16 BUILD_ASSERT(ENTRY_MAX_LEN <= SETTINGS_MAX_VAL_LEN);
17
18 #define WIFI_CREDENTIALS_SBE_BASE_KEY "wifi_cred"
19 #define WIFI_CREDENTIALS_SBE_KEY_SIZE \
20 sizeof(WIFI_CREDENTIALS_SBE_BASE_KEY "/" STRINGIFY(CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES))
21 #define WIFI_CREDENTIALS_SBE_KEY_FMT WIFI_CREDENTIALS_SBE_BASE_KEY "/%d"
22
23 /* Type of the callback argument used in the function below. */
24 struct zephyr_settings_backend_load_cb_arg {
25 uint8_t *buf;
26 size_t buf_len;
27 size_t idx;
28 bool found;
29 };
30
31 /* This callback function is used to retrieve credentials on demand. */
zephyr_settings_backend_load_val_cb(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)32 static int zephyr_settings_backend_load_val_cb(const char *key, size_t len,
33 settings_read_cb read_cb, void *cb_arg, void *param)
34 {
35 struct zephyr_settings_backend_load_cb_arg *arg = param;
36 int idx = atoi(key);
37
38 if (arg->idx != idx) {
39 LOG_DBG("Skipping index [%s]", key);
40 return 0;
41 }
42
43 if (len != arg->buf_len) {
44 LOG_ERR("Settings error: invalid settings length");
45 return -EINVAL;
46 }
47
48 size_t length_read = read_cb(cb_arg, arg->buf, arg->buf_len);
49
50 /* value validation */
51 if (length_read < len) {
52 LOG_ERR("Settings error: entry incomplete");
53 return -ENODATA;
54 }
55
56 arg->found = true;
57
58 return 0;
59 }
60
61 /* This callback function is used to initialize the SSID cache. */
zephyr_settings_backend_load_key_cb(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)62 static int zephyr_settings_backend_load_key_cb(const char *key, size_t len,
63 settings_read_cb read_cb, void *cb_arg, void *param)
64 {
65 ARG_UNUSED(param);
66
67 /* key validation */
68 if (!key) {
69 LOG_ERR("Settings error: no key");
70 return -EINVAL;
71 }
72
73 int idx = atoi(key);
74
75 if ((idx == 0 && strcmp(key, "0") != 0) || idx >= CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES) {
76 LOG_ERR("Settings error: index too large");
77 return -EINVAL;
78 }
79
80 if (len < sizeof(struct wifi_credentials_header)) {
81 LOG_ERR("Settings error: invalid settings length");
82 return -EINVAL;
83 }
84
85 uint8_t buf[ENTRY_MAX_LEN];
86 size_t length_read = read_cb(cb_arg, buf, ARRAY_SIZE(buf));
87
88 /* value validation */
89 if (length_read < len) {
90 LOG_ERR("Settings error: entry incomplete");
91 return -ENODATA;
92 }
93
94 wifi_credentials_cache_ssid(idx, (struct wifi_credentials_header *)buf);
95 return 0;
96 }
97
wifi_credentials_backend_init(void)98 int wifi_credentials_backend_init(void)
99 {
100 int ret = settings_subsys_init();
101
102 if (ret) {
103 LOG_ERR("Initializing settings subsystem failed: %d", ret);
104 return ret;
105 }
106
107 ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
108 zephyr_settings_backend_load_key_cb, NULL);
109
110 return ret;
111 }
112
wifi_credentials_store_entry(size_t idx,const void * buf,size_t buf_len)113 int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
114 {
115 char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
116
117 int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
118 WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
119
120 if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
121 LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
122 return -EFAULT;
123 }
124
125 return settings_save_one(settings_name_buf, buf, buf_len);
126 }
127
wifi_credentials_delete_entry(size_t idx)128 int wifi_credentials_delete_entry(size_t idx)
129 {
130 char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
131
132 int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
133 WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
134
135 if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
136 LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
137 return -EFAULT;
138 }
139
140 return settings_delete(settings_name_buf);
141 }
142
wifi_credentials_load_entry(size_t idx,void * buf,size_t buf_len)143 int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
144 {
145 struct zephyr_settings_backend_load_cb_arg arg = {
146 .buf = buf,
147 .buf_len = buf_len,
148 .idx = idx,
149 .found = false,
150 };
151 int ret;
152
153 /* Browse through the settings entries with custom callback to load the whole entry. */
154 ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
155 zephyr_settings_backend_load_val_cb, &arg);
156
157 if (ret) {
158 return ret;
159 }
160
161 if (!arg.found) {
162 return -ENOENT;
163 }
164
165 return 0;
166 }
167