NimBLE Bonding: Add NVS support

- Added NVS support to persist bonding across device reboots.
- Make minimal changes to `ble_store_config`, which makes it co-exist with `ble_store_ram`.
This commit is contained in:
Prasad Alatkar
2024-02-14 12:31:36 +05:30
committed by Abhinav Kudnar
parent 29722059a5
commit 6f39612bea
2 changed files with 454 additions and 1 deletions
@@ -23,7 +23,6 @@
#include "sysinit/sysinit.h"
#include "syscfg/syscfg.h"
#include "host/ble_hs.h"
#include "base64/base64.h"
#include "store/config/ble_store_config.h"
#include "ble_store_config_priv.h"
@@ -0,0 +1,454 @@
/*
* Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
#include <string.h>
#include <esp_system.h>
#include "sysinit/sysinit.h"
#include "host/ble_hs.h"
#include "store/config/ble_store_config.h"
#include "ble_store_config_priv.h"
#include "esp_log.h"
#include "nvs.h"
#define NIMBLE_NVS_STR_NAME_MAX_LEN 16
#define NIMBLE_NVS_PEER_SEC_KEY "peer_sec"
#define NIMBLE_NVS_OUR_SEC_KEY "our_sec"
#define NIMBLE_NVS_CCCD_SEC_KEY "cccd_sec"
#define NIMBLE_NVS_NAMESPACE "nimble_bond"
static const char *TAG = "NIMBLE_NVS";
/*****************************************************************************
* $ MISC *
*****************************************************************************/
static void
get_nvs_key_string(int obj_type, int index, char *key_string)
{
if (obj_type == BLE_STORE_OBJ_TYPE_PEER_SEC) {
sprintf(key_string, "%s_%d", NIMBLE_NVS_PEER_SEC_KEY, index);
} else if (obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC) {
sprintf(key_string, "%s_%d", NIMBLE_NVS_OUR_SEC_KEY, index);
} else {
sprintf(key_string, "%s_%d", NIMBLE_NVS_CCCD_SEC_KEY, index);
}
}
/* compares values at two addresses of size = item_size
* @Returns index if entries match
* -1 if mismatch
*/
static int
get_nvs_matching_index(void *nvs_val, void *db_list, int db_num, size_t
item_size)
{
uint8_t *db_item = (uint8_t *)db_list;
int i;
for (i = 0; i < db_num; i++) {
if (memcmp(nvs_val, db_item, item_size) == 0) {
/* Key matches with the one in RAM database */
return i;
}
db_item += item_size;
}
return -1;
}
static int
get_nvs_max_bonds(int obj_type)
{
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
return MYNEWT_VAL(BLE_STORE_MAX_CCCDS);
} else {
return MYNEWT_VAL(BLE_STORE_MAX_BONDS);
}
}
/*****************************************************************************
* $ NVS *
*****************************************************************************/
static int
get_nvs_db_value(int obj_type, char *key_string, union ble_store_value *val)
{
esp_err_t err;
size_t required_size = 0;
nvs_handle nimble_handle;
err = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS open operation failed");
return BLE_HS_ESTORE_FAIL;
}
err = nvs_get_blob(nimble_handle, key_string, NULL, &required_size);
/* if Address pointer for value is NULL, filling of value not needed */
if (err != ESP_OK || val == NULL) {
goto end;
}
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
err = nvs_get_blob(nimble_handle, key_string, &val->cccd,
&required_size);
} else {
err = nvs_get_blob(nimble_handle, key_string, &val->sec,
&required_size);
}
end:
nvs_close(nimble_handle);
return err;
}
/* Finds empty index or total count or index to be deleted in NVS database
* This function serves 3 different purposes depending upon 'empty' and `value`
* arguments.
* @ returns - empty NVS index, if empty = 1
* - count of NVS database, if empty = 0, value = NULL
* - index that does not match with RAM db, if empty = 0 &
* value has valid database address.
*/
static int
get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
{
union ble_store_value cur = {0};
esp_err_t err;
int i, count = 0, max_limit = 0;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
max_limit = get_nvs_max_bonds(obj_type);
for (i = 1; i <= max_limit; i++) {
get_nvs_key_string(obj_type, i, key_string);
err = get_nvs_db_value(obj_type, key_string, &cur);
/* Check if the user is searching for empty index to write to */
if (err == ESP_ERR_NVS_NOT_FOUND) {
if (empty) {
ESP_LOGI(TAG, "Empty NVS index found = %d", i);
return i;
}
} else if (err == ESP_OK) {
count++;
/* If user has provided value, then the purpose is to find
* non-matching entry from NVS */
if (value) {
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
err = get_nvs_matching_index(&cur.sec, value, num_value,
sizeof(struct ble_store_value_sec));
} else {
err = get_nvs_matching_index(&cur.sec, value, num_value,
sizeof(struct ble_store_value_cccd));
}
/* If found non-matching/odd entry of NVS with entries in the
* internal database, return NVS index so can be deleted */
if (err == -1 && !empty) {
return i;
}
}
} else {
ESP_LOGE(TAG, "NVS read operation failed while fetching size !!");
return -1;
}
}
if (empty == 0) {
return count;
} else {
return (max_limit + 1);
}
}
/* Deletes NVS value at given index
* @Returns 0 on success,
* -1 on NVS memory access failure
*/
static int
ble_nvs_delete_value(int obj_type, int8_t index)
{
esp_err_t err;
nvs_handle nimble_handle;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
if (index > get_nvs_max_bonds(obj_type)) {
ESP_LOGD(TAG, "Invalid index provided to delete");
return BLE_HS_EUNKNOWN;
}
err = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS open operation failed !!");
return BLE_HS_ESTORE_FAIL;
}
get_nvs_key_string(obj_type, index, key_string);
/* Erase the key with given index */
err = nvs_erase_key(nimble_handle, key_string);
if (err != ESP_OK) {
goto error;
}
err = nvs_commit(nimble_handle);
if (err != ESP_OK) {
goto error;
}
nvs_close(nimble_handle);
return 0;
error:
nvs_close(nimble_handle);
return BLE_HS_ESTORE_FAIL;
}
static int
ble_nvs_write_key_value(char *key, const void *value, size_t required_size)
{
nvs_handle nimble_handle;
esp_err_t err;
err = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS open operation failed !!");
return BLE_HS_ESTORE_FAIL;
}
err = nvs_set_blob(nimble_handle, key, value, required_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS write operation failed !!");
goto error;
}
/* NVS commit and close */
err = nvs_commit(nimble_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS commit operation failed !!");
goto error;
}
nvs_close(nimble_handle);
return 0;
error:
nvs_close(nimble_handle);
return BLE_HS_ESTORE_FAIL;
}
/* To write key value in NVS.
* @Returns 0 if success
* BLE_HS_ESTORE_FAIL if failure
* BLE_HS_ESTORE_CAP if no space in NVS
*/
static int
ble_store_nvs_write(int obj_type, const union ble_store_value *val)
{
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
int8_t write_key_index = 0;
write_key_index = get_nvs_db_attribute(obj_type, 1, NULL, 0);
if (write_key_index == -1) {
ESP_LOGE(TAG, "NVS operation failed !!");
return BLE_HS_ESTORE_FAIL;
} else if (write_key_index > get_nvs_max_bonds(obj_type)) {
/* bare-bone config code will take care of capacity overflow event,
* however another check added for consistency */
ESP_LOGD(TAG, "NVS size overflow.");
return BLE_HS_ESTORE_CAP;
}
get_nvs_key_string(obj_type, write_key_index, key_string);
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
return ble_nvs_write_key_value(key_string, &val->cccd, sizeof(struct
ble_store_value_cccd));
} else {
return ble_nvs_write_key_value(key_string, &val->sec, sizeof(struct
ble_store_value_sec));
}
}
static int
populate_db_from_nvs(int obj_type, void *dst, int *db_num)
{
uint8_t *db_item = (uint8_t *)dst;
union ble_store_value cur = {0};
esp_err_t err;
int i;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
for (i = 1; i <= get_nvs_max_bonds(obj_type); i++) {
get_nvs_key_string(obj_type, i, key_string);
err = get_nvs_db_value(obj_type, key_string, &cur);
if (err == ESP_ERR_NVS_NOT_FOUND) {
continue;
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS read operation failed !!");
return -1;
}
/* NVS index has data, fill up the ram db with it */
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i);
memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd));
db_item += sizeof(struct ble_store_value_cccd);
(*db_num)++;
} else {
ESP_LOGD(TAG, "KEY in RAM is filled up from NVS index = %d", i);
memcpy(db_item, &cur.sec, sizeof(struct ble_store_value_sec));
db_item += sizeof(struct ble_store_value_sec);
(*db_num)++;
}
}
return 0;
}
/* Gets the database in RAM filled up with keys stored in NVS. The sequence of
* the keys in database may get lost.
*/
static int
ble_nvs_restore_sec_keys()
{
esp_err_t err;
err = populate_db_from_nvs(BLE_STORE_OBJ_TYPE_OUR_SEC, ble_store_config_our_secs,
&ble_store_config_num_our_secs);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS operation failed for 'our sec'");
return err;
}
ESP_LOGD(TAG, "ble_store_config_our_secs restored %d bonds", ble_store_config_num_our_secs);
err = populate_db_from_nvs(BLE_STORE_OBJ_TYPE_PEER_SEC, ble_store_config_peer_secs,
&ble_store_config_num_peer_secs);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS operation failed for 'peer sec'");
return err;
}
ESP_LOGD(TAG, "ble_store_config_peer_secs restored %d bonds",
ble_store_config_num_peer_secs);
err = populate_db_from_nvs(BLE_STORE_OBJ_TYPE_CCCD, ble_store_config_cccds,
&ble_store_config_num_cccds);
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS operation failed for 'CCCD'");
return err;
}
ESP_LOGD(TAG, "ble_store_config_cccds restored %d bonds",
ble_store_config_num_cccds);
return 0;
}
int ble_store_config_persist_cccds()
{
int nvs_count, nvs_idx;
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0);
if (nvs_count < ble_store_config_num_cccds) {
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting CCCD value in NVS...");
val.cccd = ble_store_config_cccds[ble_store_config_num_cccds - 1];
return ble_store_nvs_write(BLE_STORE_OBJ_TYPE_CCCD, &val);
} else if (nvs_count > ble_store_config_num_cccds) {
/* NVS db count more than RAM count, delete operation */
nvs_idx = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, -1,
ble_store_config_cccds, ble_store_config_num_cccds);
if (nvs_idx == -1) {
ESP_LOGE(TAG, "NVS delete operation failed for CCCD");
return BLE_HS_ESTORE_FAIL;
}
ESP_LOGD(TAG, "Deleting CCCD, nvs idx = %d", nvs_idx);
return ble_nvs_delete_value(BLE_STORE_OBJ_TYPE_CCCD, nvs_idx);
}
return 0;
}
int ble_store_config_persist_peer_secs()
{
int nvs_count, nvs_idx;
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0);
if (nvs_count < ble_store_config_num_peer_secs) {
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting peer sec value in NVS...");
val.sec = ble_store_config_our_secs[ble_store_config_num_peer_secs - 1];
return ble_store_nvs_write(BLE_STORE_OBJ_TYPE_PEER_SEC, &val);
} else if (nvs_count > ble_store_config_num_peer_secs) {
/* NVS db count more than RAM count, delete operation */
nvs_idx = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, -1,
ble_store_config_peer_secs, ble_store_config_num_peer_secs);
if (nvs_idx == -1) {
ESP_LOGE(TAG, "NVS delete operation failed for peer sec");
return BLE_HS_ESTORE_FAIL;
}
ESP_LOGD(TAG, "Deleting peer sec, nvs idx = %d", nvs_idx);
return ble_nvs_delete_value(BLE_STORE_OBJ_TYPE_PEER_SEC, nvs_idx);
}
return 0;
}
int ble_store_config_persist_our_secs()
{
int nvs_count, nvs_idx;
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0);
if (nvs_count < ble_store_config_num_our_secs) {
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting our sec value to NVS...");
val.sec = ble_store_config_our_secs[ble_store_config_num_our_secs - 1];
return ble_store_nvs_write(BLE_STORE_OBJ_TYPE_OUR_SEC, &val);
} else if (nvs_count > ble_store_config_num_our_secs) {
/* NVS db count more than RAM count, delete operation */
nvs_idx = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, -1,
ble_store_config_our_secs, ble_store_config_num_our_secs);
if (nvs_idx == -1) {
ESP_LOGE(TAG, "NVS delete operation failed for our sec");
return BLE_HS_ESTORE_FAIL;
}
ESP_LOGD(TAG, "Deleting our sec, nvs idx = %d", nvs_idx);
return ble_nvs_delete_value(BLE_STORE_OBJ_TYPE_OUR_SEC, nvs_idx);
}
return 0;
}
void ble_store_config_conf_init()
{
esp_err_t err;
err = ble_nvs_restore_sec_keys();
if (err != ESP_OK) {
ESP_LOGE(TAG, "NVS operation failed, can't retrieve the bonding info");
}
}
/***************************************************************************************/
#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */