mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-13 05:27:56 +00:00
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:
committed by
Abhinav Kudnar
parent
29722059a5
commit
6f39612bea
@@ -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) */
|
||||
Reference in New Issue
Block a user