component/bt: implement non-volatile memory access module for link key storage
1. btif_storage module is ported 2. update controller library that moves functions called in ISRs to IRAM
This commit is contained in:
parent
36a74daa0a
commit
0f711963d7
453
components/bt/bluedroid/btif/btif_config.c
Normal file
453
components/bt/bluedroid/btif/btif_config.c
Normal file
|
@ -0,0 +1,453 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#define LOG_TAG "bt_btif_config"
|
||||
|
||||
// #include <assert.h>
|
||||
#include <ctype.h>
|
||||
// #include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bt_defs.h"
|
||||
#include "bt_trace.h"
|
||||
#include "alarm.h"
|
||||
#include "allocator.h"
|
||||
#include "bdaddr.h"
|
||||
#include "btif_config.h"
|
||||
#include "btif_util.h"
|
||||
// #include "osi/include/compat.h"
|
||||
#include "config.h"
|
||||
#include "osi.h"
|
||||
|
||||
#include "bt_types.h"
|
||||
|
||||
static const char *CONFIG_FILE_PATH = "bt_config.conf";
|
||||
static const period_ms_t CONFIG_SETTLE_PERIOD_MS = 3000;
|
||||
|
||||
static void timer_config_save(void *data);
|
||||
|
||||
// TODO(zachoverflow): Move these two functions out, because they are too specific for this file
|
||||
// {grumpy-cat/no, monty-python/you-make-me-sad}
|
||||
bool btif_get_device_type(const BD_ADDR bd_addr, int *p_device_type)
|
||||
{
|
||||
if (p_device_type == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bt_bdaddr_t bda;
|
||||
bdcpy(bda.address, bd_addr);
|
||||
|
||||
bdstr_t bd_addr_str;
|
||||
bdaddr_to_string(&bda, bd_addr_str, sizeof(bd_addr_str));
|
||||
|
||||
if (!btif_config_get_int(bd_addr_str, "DevType", p_device_type)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s: Device [%s] type %d", __FUNCTION__, bd_addr_str, *p_device_type);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool btif_get_address_type(const BD_ADDR bd_addr, int *p_addr_type)
|
||||
{
|
||||
if (p_addr_type == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bt_bdaddr_t bda;
|
||||
bdcpy(bda.address, bd_addr);
|
||||
|
||||
bdstr_t bd_addr_str;
|
||||
bdaddr_to_string(&bda, bd_addr_str, sizeof(bd_addr_str));
|
||||
|
||||
if (!btif_config_get_int(bd_addr_str, "AddrType", p_addr_type)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s: Device [%s] address type %d", __FUNCTION__, bd_addr_str, *p_addr_type);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static pthread_mutex_t lock; // protects operations on |config|.
|
||||
static config_t *config;
|
||||
static osi_alarm_t *alarm_timer;
|
||||
|
||||
// Module lifecycle functions
|
||||
|
||||
bool btif_config_init(void)
|
||||
{
|
||||
// karl LOG_ERROR("btif config_init\n");
|
||||
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
config = config_new(CONFIG_FILE_PATH);
|
||||
if (!config) {
|
||||
LOG_WARN("%s unable to load config file; starting unconfigured.\n", __func__);
|
||||
config = config_new_empty();
|
||||
if (!config) {
|
||||
LOG_ERROR("%s unable to allocate a config object.", __func__);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_save(config, CONFIG_FILE_PATH)) {
|
||||
// unlink(LEGACY_CONFIG_FILE_PATH);
|
||||
}
|
||||
|
||||
// TODO(sharvil): use a non-wake alarm for this once we have
|
||||
// API support for it. There's no need to wake the system to
|
||||
// write back to disk.
|
||||
alarm_timer = osi_alarm_new("btif_config", timer_config_save, NULL, CONFIG_SETTLE_PERIOD_MS, false);
|
||||
if (!alarm_timer) {
|
||||
LOG_ERROR("%s unable to create alarm.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// LOG_ERROR("btif config_init end ok\n");
|
||||
return true;
|
||||
|
||||
error:;
|
||||
osi_alarm_free(alarm_timer);
|
||||
config_free(config);
|
||||
pthread_mutex_destroy(&lock);
|
||||
alarm_timer = NULL;
|
||||
config = NULL;
|
||||
LOG_ERROR("btif config_init end failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool btif_config_shut_down(void)
|
||||
{
|
||||
btif_config_flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btif_config_clean_up(void)
|
||||
{
|
||||
btif_config_flush();
|
||||
|
||||
osi_alarm_free(alarm_timer);
|
||||
config_free(config);
|
||||
pthread_mutex_destroy(&lock);
|
||||
alarm_timer = NULL;
|
||||
config = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btif_config_has_section(const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
bool ret = config_has_section(config, section);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool btif_config_exist(const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
bool ret = config_has_key(config, section, key);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool btif_config_get_int(const char *section, const char *key, int *value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(value != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
bool ret = config_has_key(config, section, key);
|
||||
if (ret) {
|
||||
*value = config_get_int(config, section, key, *value);
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool btif_config_set_int(const char *section, const char *key, int value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
config_set_int(config, section, key, value);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btif_config_get_str(const char *section, const char *key, char *value, int *size_bytes)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(value != NULL);
|
||||
assert(size_bytes != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
const char *stored_value = config_get_string(config, section, key, NULL);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
if (!stored_value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(value, stored_value, *size_bytes);
|
||||
*size_bytes = strlen(value) + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btif_config_set_str(const char *section, const char *key, const char *value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(value != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
config_set_string(config, section, key, value);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btif_config_get_bin(const char *section, const char *key, uint8_t *value, size_t *length)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(value != NULL);
|
||||
assert(length != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
const char *value_str = config_get_string(config, section, key, NULL);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
if (!value_str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t value_len = strlen(value_str);
|
||||
if ((value_len % 2) != 0 || *length < (value_len / 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < value_len; ++i)
|
||||
if (!isxdigit((unsigned char)value_str[i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (*length = 0; *value_str; value_str += 2, *length += 1) {
|
||||
unsigned int val;
|
||||
sscanf(value_str, "%02x", &val);
|
||||
value[*length] = (uint8_t)(val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t btif_config_get_bin_length(const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
const char *value_str = config_get_string(config, section, key, NULL);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
if (!value_str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t value_len = strlen(value_str);
|
||||
return ((value_len % 2) != 0) ? 0 : (value_len / 2);
|
||||
}
|
||||
|
||||
bool btif_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length)
|
||||
{
|
||||
const char *lookup = "0123456789abcdef";
|
||||
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
if (length > 0) {
|
||||
assert(value != NULL);
|
||||
}
|
||||
|
||||
char *str = (char *)osi_calloc(length * 2 + 1);
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
str[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F];
|
||||
str[(i * 2) + 1] = lookup[value[i] & 0x0F];
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
config_set_string(config, section, key, str);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
osi_free(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
const btif_config_section_iter_t *btif_config_section_begin(void)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const btif_config_section_iter_t *)config_section_begin(config);
|
||||
}
|
||||
|
||||
const btif_config_section_iter_t *btif_config_section_end(void)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const btif_config_section_iter_t *)config_section_end(config);
|
||||
}
|
||||
|
||||
const btif_config_section_iter_t *btif_config_section_next(const btif_config_section_iter_t *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
return (const btif_config_section_iter_t *)config_section_next((const config_section_node_t *)section);
|
||||
}
|
||||
|
||||
const char *btif_config_section_name(const btif_config_section_iter_t *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
return config_section_name((const config_section_node_t *)section);
|
||||
}
|
||||
|
||||
bool btif_config_remove(const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
bool ret = config_remove_key(config, section, key);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btif_config_save(void)
|
||||
{
|
||||
assert(alarm_timer != NULL);
|
||||
assert(config != NULL);
|
||||
|
||||
osi_alarm_set(alarm_timer, CONFIG_SETTLE_PERIOD_MS);
|
||||
}
|
||||
|
||||
void btif_config_flush(void)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(alarm_timer != NULL);
|
||||
LOG_ERROR("flush bgn\n"); // karl
|
||||
// osi_alarm_cancel(alarm_timer);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
config_save(config, CONFIG_FILE_PATH);
|
||||
pthread_mutex_unlock(&lock);
|
||||
LOG_ERROR("flush end\n"); // karl
|
||||
}
|
||||
|
||||
int btif_config_clear(void)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(alarm_timer != NULL);
|
||||
|
||||
osi_alarm_cancel(alarm_timer);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
config_free(config);
|
||||
|
||||
config = config_new_empty();
|
||||
if (config == NULL) {
|
||||
pthread_mutex_unlock(&lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = config_save(config, CONFIG_FILE_PATH);
|
||||
pthread_mutex_unlock(&lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void timer_config_save(UNUSED_ATTR void *data)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(alarm_timer != NULL);
|
||||
|
||||
// Garbage collection process: the config file accumulates
|
||||
// cached information about remote devices during regular
|
||||
// inquiry scans. We remove some of these junk entries
|
||||
// so the file doesn't grow indefinitely. We have to take care
|
||||
// to make sure we don't remove information about bonded
|
||||
// devices (hence the check for link keys).
|
||||
static const size_t CACHE_MAX = 256;
|
||||
const char *keys[CACHE_MAX];
|
||||
size_t num_keys = 0;
|
||||
size_t total_candidates = 0;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
for (const config_section_node_t *snode = config_section_begin(config); snode != config_section_end(config); snode = config_section_next(snode)) {
|
||||
const char *section = config_section_name(snode);
|
||||
if (!string_is_bdaddr(section)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config_has_key(config, section, "LinkKey") ||
|
||||
config_has_key(config, section, "LE_KEY_PENC") ||
|
||||
config_has_key(config, section, "LE_KEY_PID") ||
|
||||
config_has_key(config, section, "LE_KEY_PCSRK") ||
|
||||
config_has_key(config, section, "LE_KEY_LENC") ||
|
||||
config_has_key(config, section, "LE_KEY_LCSRK")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_keys < CACHE_MAX) {
|
||||
keys[num_keys++] = section;
|
||||
}
|
||||
|
||||
++total_candidates;
|
||||
}
|
||||
|
||||
if (total_candidates > CACHE_MAX * 2)
|
||||
while (num_keys > 0) {
|
||||
config_remove_section(config, keys[--num_keys]);
|
||||
}
|
||||
|
||||
config_save(config, CONFIG_FILE_PATH);
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
|
@ -314,7 +314,7 @@ bt_status_t btif_init_bluetooth(void)
|
|||
goto error_exit;
|
||||
}
|
||||
xBtifQueue = xQueueCreate(60, sizeof(void *));
|
||||
xTaskCreatePinnedToCore(btif_task_thread_handler, "BtifT", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle, 0);
|
||||
xTaskCreatePinnedToCore(btif_task_thread_handler, "BtifT", 2048+1024, NULL, configMAX_PRIORITIES - 1, &xBtifTaskHandle, 0);
|
||||
fixed_queue_register_dequeue(btif_msg_queue, bt_jni_msg_ready);
|
||||
|
||||
return BT_STATUS_SUCCESS;
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#include "btif_api.h"
|
||||
#include "btif_util.h"
|
||||
#include "btif_dm.h"
|
||||
// #include "btif_storage.h"
|
||||
#include "btif_storage.h"
|
||||
// #include "btif_hh.h"
|
||||
// #include "btif_config.h"
|
||||
// #include "btif_sdp.h"
|
||||
|
@ -136,6 +136,97 @@ void btif_dm_execute_service_request(UINT16 event, char *p_param)
|
|||
btif_in_execute_service_request(*((tBTA_SERVICE_ID *)p_param), b_enable);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_dm_auth_cmpl_evt
|
||||
**
|
||||
** Description Executes authentication complete event in btif context
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void btif_dm_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl)
|
||||
{
|
||||
/* Save link key, if not temporary */
|
||||
bt_bdaddr_t bd_addr;
|
||||
bt_status_t status;
|
||||
BTIF_TRACE_EVENT("%s: bond state success %d, present %d, type%d\n", __func__, p_auth_cmpl->success,
|
||||
p_auth_cmpl->key_present, p_auth_cmpl->key_type);
|
||||
|
||||
bdcpy(bd_addr.address, p_auth_cmpl->bd_addr);
|
||||
if ( (p_auth_cmpl->success == TRUE) && (p_auth_cmpl->key_present) )
|
||||
{
|
||||
#if 0
|
||||
if ((p_auth_cmpl->key_type < HCI_LKEY_TYPE_DEBUG_COMB) ||
|
||||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB) ||
|
||||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_CHANGED_COMB) ||
|
||||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB_P_256)
|
||||
)
|
||||
#endif
|
||||
if (1)
|
||||
{
|
||||
bt_status_t ret;
|
||||
BTIF_TRACE_WARNING("%s: Storing link key. key_type=0x%x",
|
||||
__FUNCTION__, p_auth_cmpl->key_type);
|
||||
ret = btif_storage_add_bonded_device(&bd_addr,
|
||||
p_auth_cmpl->key, p_auth_cmpl->key_type,
|
||||
16);
|
||||
ASSERTC(ret == BT_STATUS_SUCCESS, "storing link key failed", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
BTIF_TRACE_EVENT("%s: Temporary key. Not storing. key_type=0x%x",
|
||||
__FUNCTION__, p_auth_cmpl->key_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip SDP for certain HID Devices
|
||||
if (p_auth_cmpl->success)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// Map the HCI fail reason to bt status
|
||||
switch(p_auth_cmpl->fail_reason)
|
||||
{
|
||||
case HCI_ERR_PAGE_TIMEOUT:
|
||||
BTIF_TRACE_WARNING("%s() - Pairing timeout; retrying () ...", __FUNCTION__);
|
||||
return;
|
||||
/* Fall-through */
|
||||
case HCI_ERR_CONNECTION_TOUT:
|
||||
status = BT_STATUS_RMT_DEV_DOWN;
|
||||
break;
|
||||
|
||||
case HCI_ERR_PAIRING_NOT_ALLOWED:
|
||||
status = BT_STATUS_AUTH_REJECTED;
|
||||
break;
|
||||
|
||||
case HCI_ERR_LMP_RESPONSE_TIMEOUT:
|
||||
status = BT_STATUS_AUTH_FAILURE;
|
||||
break;
|
||||
|
||||
/* map the auth failure codes, so we can retry pairing if necessary */
|
||||
case HCI_ERR_AUTH_FAILURE:
|
||||
case HCI_ERR_KEY_MISSING:
|
||||
btif_storage_remove_bonded_device(&bd_addr);
|
||||
case HCI_ERR_HOST_REJECT_SECURITY:
|
||||
case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE:
|
||||
case HCI_ERR_UNIT_KEY_USED:
|
||||
case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED:
|
||||
case HCI_ERR_INSUFFCIENT_SECURITY:
|
||||
case HCI_ERR_PEER_USER:
|
||||
case HCI_ERR_UNSPECIFIED:
|
||||
BTIF_TRACE_DEBUG(" %s() Authentication fail reason %d",
|
||||
__FUNCTION__, p_auth_cmpl->fail_reason);
|
||||
/* if autopair attempts are more than 1, or not attempted */
|
||||
status = BT_STATUS_AUTH_FAILURE;
|
||||
break;
|
||||
default:
|
||||
status = BT_STATUS_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_dm_upstreams_cback
|
||||
|
@ -164,6 +255,7 @@ static void btif_dm_upstreams_evt(UINT16 event, char *p_param)
|
|||
}
|
||||
}
|
||||
btif_enable_bluetooth_evt(p_data->enable.status);
|
||||
btif_storage_load_bonded_devices();
|
||||
break;
|
||||
case BTA_DM_DISABLE_EVT:
|
||||
/* for each of the enabled services in the mask, trigger the profile
|
||||
|
@ -178,7 +270,10 @@ static void btif_dm_upstreams_evt(UINT16 event, char *p_param)
|
|||
btif_disable_bluetooth_evt();
|
||||
break;
|
||||
case BTA_DM_PIN_REQ_EVT:
|
||||
break;
|
||||
case BTA_DM_AUTH_CMPL_EVT:
|
||||
btif_dm_auth_cmpl_evt(&p_data->auth_cmpl);
|
||||
break;
|
||||
case BTA_DM_BOND_CANCEL_CMPL_EVT:
|
||||
case BTA_DM_SP_CFM_REQ_EVT:
|
||||
case BTA_DM_SP_KEY_NOTIF_EVT:
|
||||
|
|
191
components/bt/bluedroid/btif/btif_storage.c
Normal file
191
components/bt/bluedroid/btif/btif_storage.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
#include "btif_storage.h"
|
||||
#include "btif_util.h"
|
||||
#include "osi.h"
|
||||
#include "bt_trace.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "bta_api.h"
|
||||
#include "bdaddr.h"
|
||||
#include "btif_config.h"
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_add_bonded_device
|
||||
**
|
||||
** Description BTIF storage API - Adds the newly bonded device to NVRAM
|
||||
** along with the link-key, Key type and Pin key length
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if the store was successful,
|
||||
** BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
||||
bt_status_t btif_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr,
|
||||
LINK_KEY link_key,
|
||||
uint8_t key_type,
|
||||
uint8_t pin_length)
|
||||
{
|
||||
bdstr_t bdstr;
|
||||
|
||||
bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr));
|
||||
BTIF_TRACE_WARNING("add to storage: Remote device:%s", bdstr);
|
||||
|
||||
int ret = btif_config_set_int(bdstr, "LinkKeyType", (int)key_type);
|
||||
BTIF_TRACE_WARNING("p1\n");
|
||||
ret &= btif_config_set_int(bdstr, "PinLength", (int)pin_length);
|
||||
BTIF_TRACE_WARNING("p2\n");
|
||||
ret &= btif_config_set_bin(bdstr, "LinkKey", link_key, sizeof(LINK_KEY));
|
||||
BTIF_TRACE_WARNING("p3\n");
|
||||
/* write bonded info immediately */
|
||||
// karl
|
||||
btif_config_flush();
|
||||
BTIF_TRACE_WARNING("Storage add rslt %d\n", ret);
|
||||
return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_in_fetch_bonded_devices
|
||||
**
|
||||
** Description Internal helper function to fetch the bonded devices
|
||||
** from NVRAM
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
static bt_status_t btif_in_fetch_bonded_devices(int add)
|
||||
{
|
||||
BOOLEAN bt_linkkey_file_found = FALSE;
|
||||
int device_type;
|
||||
|
||||
for (const btif_config_section_iter_t *iter = btif_config_section_begin(); iter != btif_config_section_end(); iter = btif_config_section_next(iter)) {
|
||||
const char *name = btif_config_section_name(iter);
|
||||
if (!string_is_bdaddr(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BTIF_TRACE_WARNING("Remote device:%s", name);
|
||||
LINK_KEY link_key;
|
||||
size_t size = sizeof(link_key);
|
||||
if (btif_config_get_bin(name, "LinkKey", link_key, &size)) {
|
||||
int linkkey_type;
|
||||
if (btif_config_get_int(name, "LinkKeyType", &linkkey_type)) {
|
||||
//int pin_len;
|
||||
//btif_config_get_int(name, "PinLength", &pin_len))
|
||||
bt_bdaddr_t bd_addr;
|
||||
string_to_bdaddr(name, &bd_addr);
|
||||
if (add) {
|
||||
DEV_CLASS dev_class = {0, 0, 0};
|
||||
int cod;
|
||||
int pin_length = 0;
|
||||
if (btif_config_get_int(name, "DevClass", &cod)) {
|
||||
uint2devclass((UINT32)cod, dev_class);
|
||||
}
|
||||
btif_config_get_int(name, "PinLength", &pin_length);
|
||||
BTA_DmAddDevice(bd_addr.address, dev_class, link_key, 0, 0,
|
||||
(UINT8)linkkey_type, 0, pin_length);
|
||||
}
|
||||
bt_linkkey_file_found = TRUE;
|
||||
} else {
|
||||
BTIF_TRACE_ERROR("bounded device:%s, LinkKeyType or PinLength is invalid", name);
|
||||
}
|
||||
}
|
||||
if (!bt_linkkey_file_found) {
|
||||
BTIF_TRACE_EVENT("Remote device:%s, no link key", name);
|
||||
}
|
||||
}
|
||||
return BT_STATUS_SUCCESS;
|
||||
#if 0
|
||||
int device_type;
|
||||
|
||||
BTIF_TRACE_WARNING("fetch from storage");
|
||||
do {
|
||||
const char *name = "bt_host";
|
||||
|
||||
bt_bdaddr_t bd_addr;
|
||||
size_t size = sizeof(bt_bdaddr_t);
|
||||
if (!btif_config_get_bin(name, "BdAddr", bd_addr.address, &size)) {
|
||||
continue;
|
||||
} else {
|
||||
bdstr_t bdstr;
|
||||
bdaddr_to_string(&bd_addr, bdstr, sizeof(bdstr));
|
||||
BTIF_TRACE_WARNING("fetch from storage: Remote device:%s\n", bdstr);
|
||||
}
|
||||
|
||||
LINK_KEY link_key;
|
||||
size = sizeof(link_key);
|
||||
if (btif_config_get_bin(name, "LinkKey", link_key, &size)) {
|
||||
int linkkey_type;
|
||||
if (btif_config_get_int(name, "LinkKeyType", &linkkey_type)) {
|
||||
if (add) {
|
||||
DEV_CLASS dev_class = {0, 0, 0};
|
||||
int cod;
|
||||
int pin_length = 0;
|
||||
if (btif_config_get_int(name, "DevClass", &cod)) {
|
||||
uint2devclass((UINT32)cod, dev_class);
|
||||
}
|
||||
btif_config_get_int(name, "PinLength", &pin_length);
|
||||
BTA_DmAddDevice(bd_addr.address, dev_class, link_key, 0, 0,
|
||||
(UINT8)linkkey_type, 0, pin_length);
|
||||
|
||||
}
|
||||
} else {
|
||||
BTIF_TRACE_ERROR("bounded device:%s, LinkKeyType or PinLength is invalid\n", name);
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
return BT_STATUS_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_load_bonded_devices
|
||||
**
|
||||
** Description BTIF storage API - Loads all the bonded devices from NVRAM
|
||||
** and adds to the BTA.
|
||||
** Additionally, this API also invokes the adaper_properties_cb
|
||||
** and remote_device_properties_cb for each of the bonded devices.
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
bt_status_t btif_storage_load_bonded_devices(void)
|
||||
{
|
||||
bt_status_t status;
|
||||
status = btif_in_fetch_bonded_devices(1);
|
||||
BTIF_TRACE_WARNING("Storage load rslt %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_remove_bonded_device
|
||||
**
|
||||
** Description BTIF storage API - Deletes the bonded device from NVRAM
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if the deletion was successful,
|
||||
** BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
bt_status_t btif_storage_remove_bonded_device(bt_bdaddr_t *remote_bd_addr)
|
||||
{
|
||||
bdstr_t bdstr;
|
||||
bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr));
|
||||
BTIF_TRACE_EVENT("add to storage: Remote device:%s\n", bdstr);
|
||||
|
||||
int ret = 1;
|
||||
if (btif_config_exist(bdstr, "LinkKeyType")) {
|
||||
ret &= btif_config_remove(bdstr, "LinkKeyType");
|
||||
}
|
||||
if (btif_config_exist(bdstr, "PinLength")) {
|
||||
ret &= btif_config_remove(bdstr, "PinLength");
|
||||
}
|
||||
if (btif_config_exist(bdstr, "LinkKey")) {
|
||||
ret &= btif_config_remove(bdstr, "LinkKey");
|
||||
}
|
||||
/* write bonded info immediately */
|
||||
btif_config_flush();
|
||||
return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
|
||||
}
|
58
components/bt/bluedroid/btif/include/btif_config.h
Executable file
58
components/bt/bluedroid/btif/include/btif_config.h
Executable file
|
@ -0,0 +1,58 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "bt_types.h"
|
||||
|
||||
static const char BTIF_CONFIG_MODULE[] = "btif_config_module";
|
||||
|
||||
typedef struct btif_config_section_iter_t btif_config_section_iter_t;
|
||||
|
||||
bool btif_config_init(void);
|
||||
bool btif_config_shut_down(void);
|
||||
bool btif_config_clean_up(void);
|
||||
|
||||
bool btif_config_has_section(const char *section);
|
||||
bool btif_config_exist(const char *section, const char *key);
|
||||
bool btif_config_get_int(const char *section, const char *key, int *value);
|
||||
bool btif_config_set_int(const char *section, const char *key, int value);
|
||||
bool btif_config_get_str(const char *section, const char *key, char *value, int *size_bytes);
|
||||
bool btif_config_set_str(const char *section, const char *key, const char *value);
|
||||
bool btif_config_get_bin(const char *section, const char *key, uint8_t *value, size_t *length);
|
||||
bool btif_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length);
|
||||
bool btif_config_remove(const char *section, const char *key);
|
||||
|
||||
size_t btif_config_get_bin_length(const char *section, const char *key);
|
||||
|
||||
const btif_config_section_iter_t *btif_config_section_begin(void);
|
||||
const btif_config_section_iter_t *btif_config_section_end(void);
|
||||
const btif_config_section_iter_t *btif_config_section_next(const btif_config_section_iter_t *section);
|
||||
const char *btif_config_section_name(const btif_config_section_iter_t *section);
|
||||
|
||||
void btif_config_save(void);
|
||||
void btif_config_flush(void);
|
||||
int btif_config_clear(void);
|
||||
|
||||
// TODO(zachoverflow): Eww...we need to move these out. These are peer specific, not config general.
|
||||
bool btif_get_address_type(const BD_ADDR bd_addr, int *p_addr_type);
|
||||
bool btif_get_device_type(const BD_ADDR bd_addr, int *p_device_type);
|
||||
|
66
components/bt/bluedroid/btif/include/btif_storage.h
Normal file
66
components/bt/bluedroid/btif/include/btif_storage.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef __BTIF_STORAGE_H__
|
||||
#define __BTIF_STORAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bt_defs.h"
|
||||
#include "bt_types.h"
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_add_bonded_device
|
||||
**
|
||||
** Description BTIF storage API - Adds the newly bonded device to NVRAM
|
||||
** along with the link-key, Key type and Pin key length
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if the store was successful,
|
||||
** BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
bt_status_t btif_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr,
|
||||
LINK_KEY link_key,
|
||||
uint8_t key_type,
|
||||
uint8_t pin_length);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_remove_bonded_device
|
||||
**
|
||||
** Description BTIF storage API - Deletes the bonded device from NVRAM
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if the deletion was successful,
|
||||
** BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
bt_status_t btif_storage_remove_bonded_device(bt_bdaddr_t *remote_bd_addr);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btif_storage_remove_bonded_device
|
||||
**
|
||||
** Description BTIF storage API - Deletes the bonded device from NVRAM
|
||||
**
|
||||
** Returns BT_STATUS_SUCCESS if the deletion was successful,
|
||||
** BT_STATUS_FAIL otherwise
|
||||
**
|
||||
*******************************************************************************/
|
||||
bt_status_t btif_storage_load_bonded_devices(void);
|
||||
|
||||
|
||||
|
||||
#endif /* BTIF_STORAGE_H */
|
|
@ -8,6 +8,7 @@
|
|||
#include "btif_common.h"
|
||||
#include "btif_api.h"
|
||||
#include "btif_dm.h"
|
||||
#include "btif_config.h"
|
||||
|
||||
/************************************************************************************
|
||||
** Constants & Macros
|
||||
|
@ -32,6 +33,7 @@ static bt_status_t event_init_stack(void)
|
|||
bt_status_t ret;
|
||||
if (!stack_is_initialized) {
|
||||
hack_future = future_new();
|
||||
btif_config_init();
|
||||
ret = btif_init_bluetooth();
|
||||
if (future_await(hack_future) != FUTURE_SUCCESS) {
|
||||
return BT_STATUS_FAIL;
|
||||
|
@ -90,7 +92,7 @@ static bt_status_t event_shut_down_stack(void)
|
|||
stack_is_running = false;
|
||||
|
||||
btif_disable_bluetooth();
|
||||
|
||||
btif_config_shut_down();
|
||||
future_await(hack_future);
|
||||
|
||||
LOG_DEBUG("%s finished.\n", __func__);
|
||||
|
@ -111,7 +113,7 @@ static bt_status_t event_clean_up_stack(void)
|
|||
LOG_DEBUG("%s is cleaning up the stack.\n", __func__);
|
||||
|
||||
stack_is_initialized = false;
|
||||
|
||||
btif_config_clean_up();
|
||||
btif_shutdown_bluetooth();
|
||||
|
||||
return BT_STATUS_SUCCESS;
|
||||
|
|
586
components/bt/bluedroid/osi/config.c
Normal file
586
components/bt/bluedroid/osi/config.c
Normal file
|
@ -0,0 +1,586 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#define LOG_TAG "bt_osi_config"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
// #include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
// #include <sys/stat.h>
|
||||
|
||||
#include "allocator.h"
|
||||
#include "config.h"
|
||||
#include "list.h"
|
||||
#include "bt_trace.h"
|
||||
|
||||
#define CONFIG_FILE_MAX_SIZE (4096)
|
||||
#define CONFIG_KEY "cfg_key8"
|
||||
typedef struct {
|
||||
char *key;
|
||||
char *value;
|
||||
} entry_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
list_t *entries;
|
||||
} section_t;
|
||||
|
||||
struct config_t {
|
||||
list_t *sections;
|
||||
};
|
||||
|
||||
// Empty definition; this type is aliased to list_node_t.
|
||||
struct config_section_iter_t {};
|
||||
|
||||
static void config_parse(nvs_handle fp, config_t *config);
|
||||
|
||||
static section_t *section_new(const char *name);
|
||||
static void section_free(void *ptr);
|
||||
static section_t *section_find(const config_t *config, const char *section);
|
||||
|
||||
static entry_t *entry_new(const char *key, const char *value);
|
||||
static void entry_free(void *ptr);
|
||||
static entry_t *entry_find(const config_t *config, const char *section, const char *key);
|
||||
|
||||
static void my_nvs_close(nvs_handle fp)
|
||||
{
|
||||
// LOG_ERROR("nvs close %d\n", (int)fp);
|
||||
nvs_close(fp);
|
||||
}
|
||||
|
||||
|
||||
config_t *config_new_empty(void)
|
||||
{
|
||||
config_t *config = osi_calloc(sizeof(config_t));
|
||||
if (!config) {
|
||||
LOG_ERROR("%s unable to allocate memory for config_t.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
config->sections = list_new(section_free);
|
||||
if (!config->sections) {
|
||||
LOG_ERROR("%s unable to allocate list for sections.", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
error:;
|
||||
config_free(config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config_t *config_new(const char *filename)
|
||||
{
|
||||
assert(filename != NULL);
|
||||
|
||||
// LOG_ERROR("config new bgn\n");
|
||||
config_t *config = config_new_empty();
|
||||
if (!config) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
nvs_handle fp;
|
||||
err = nvs_open(filename, NVS_READWRITE, &fp);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("%s unable to open file '%s'", __func__, filename);
|
||||
config_free(config);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// LOG_ERROR("config parse bgn\n");
|
||||
config_parse(fp, config);
|
||||
// LOG_ERROR("config parse end\n");
|
||||
my_nvs_close(fp);
|
||||
return config;
|
||||
}
|
||||
|
||||
void config_free(config_t *config)
|
||||
{
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_free(config->sections);
|
||||
osi_free(config);
|
||||
}
|
||||
|
||||
bool config_has_section(const config_t *config, const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
return (section_find(config, section) != NULL);
|
||||
}
|
||||
|
||||
bool config_has_key(const config_t *config, const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
return (entry_find(config, section, key) != NULL);
|
||||
}
|
||||
|
||||
int config_get_int(const config_t *config, const char *section, const char *key, int def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
int ret = strtol(entry->value, &endptr, 0);
|
||||
return (*endptr == '\0') ? ret : def_value;
|
||||
}
|
||||
|
||||
bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
if (!strcmp(entry->value, "true")) {
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(entry->value, "false")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return def_value;
|
||||
}
|
||||
|
||||
const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!entry) {
|
||||
return def_value;
|
||||
}
|
||||
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
void config_set_int(config_t *config, const char *section, const char *key, int value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
char value_str[32] = { 0 };
|
||||
sprintf(value_str, "%d", value);
|
||||
config_set_string(config, section, key, value_str);
|
||||
}
|
||||
|
||||
void config_set_bool(config_t *config, const char *section, const char *key, bool value)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
config_set_string(config, section, key, value ? "true" : "false");
|
||||
}
|
||||
|
||||
void config_set_string(config_t *config, const char *section, const char *key, const char *value)
|
||||
{
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
sec = section_new(section);
|
||||
list_append(config->sections, sec);
|
||||
}
|
||||
|
||||
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
|
||||
entry_t *entry = list_node(node);
|
||||
if (!strcmp(entry->key, key)) {
|
||||
osi_free(entry->value);
|
||||
entry->value = osi_strdup(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entry_t *entry = entry_new(key, value);
|
||||
list_append(sec->entries, entry);
|
||||
}
|
||||
|
||||
bool config_remove_section(config_t *config, const char *section)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return list_remove(config->sections, sec);
|
||||
}
|
||||
|
||||
bool config_remove_key(config_t *config, const char *section, const char *key)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
section_t *sec = section_find(config, section);
|
||||
entry_t *entry = entry_find(config, section, key);
|
||||
if (!sec || !entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return list_remove(sec->entries, entry);
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_begin(const config_t *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const config_section_node_t *)list_begin(config->sections);
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_end(const config_t *config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
return (const config_section_node_t *)list_end(config->sections);
|
||||
}
|
||||
|
||||
const config_section_node_t *config_section_next(const config_section_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return (const config_section_node_t *)list_next((const list_node_t *)node);
|
||||
}
|
||||
|
||||
const char *config_section_name(const config_section_node_t *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
const list_node_t *lnode = (const list_node_t *)node;
|
||||
const section_t *section = (const section_t *)list_node(lnode);
|
||||
return section->name;
|
||||
}
|
||||
|
||||
bool config_save(const config_t *config, const char *filename)
|
||||
{
|
||||
assert(config != NULL);
|
||||
assert(filename != NULL);
|
||||
assert(*filename != '\0');
|
||||
|
||||
esp_err_t err;
|
||||
nvs_handle fp;
|
||||
char *line = osi_calloc(1024);
|
||||
char *buf = osi_calloc(CONFIG_FILE_MAX_SIZE);
|
||||
if (!line || !buf) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = nvs_open(filename, NVS_READWRITE, &fp);
|
||||
// LOG_ERROR("nvs open: %d\n", (int)fp);
|
||||
if (err != ESP_OK) {
|
||||
// LOG_ERROR("%s unable to write file '%s'\n", __func__, filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// LOG_ERROR("m1, %s\n", filename);
|
||||
int w_cnt, w_cnt_total = 0;
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
const section_t *section = (const section_t *)list_node(node);
|
||||
// LOG_ERROR("m11\n");
|
||||
// LOG_ERROR("m12, %s\n", section->name);
|
||||
w_cnt = snprintf(line, 1024, "[%s]\n", section->name);
|
||||
// LOG_ERROR("m2 : %s\n", section->name);
|
||||
if (w_cnt + w_cnt_total < CONFIG_FILE_MAX_SIZE) {
|
||||
memcpy(buf + w_cnt_total, line, w_cnt);
|
||||
w_cnt_total += w_cnt;
|
||||
// LOG_ERROR("m21\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// LOG_ERROR("m22\n");
|
||||
for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
|
||||
const entry_t *entry = (const entry_t *)list_node(enode);
|
||||
// LOG_ERROR("m30: %s, %s\n", entry->key, entry->value);
|
||||
w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value);
|
||||
// LOG_ERROR("m3 : %s, %s\n", entry->key, entry->value);
|
||||
if (w_cnt + w_cnt_total < CONFIG_FILE_MAX_SIZE) {
|
||||
memcpy(buf + w_cnt_total, line, w_cnt);
|
||||
w_cnt_total += w_cnt;
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only add a separating newline if there are more sections.
|
||||
if (list_next(node) != list_end(config->sections)) {
|
||||
if (1 + w_cnt_total < CONFIG_FILE_MAX_SIZE) {
|
||||
buf[w_cnt_total] = '\n';
|
||||
w_cnt_total += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("m4 : %s, %d\n", buf, w_cnt_total);
|
||||
{
|
||||
// LOG_ERROR("m4x\n");
|
||||
size_t tmp_len = 4096;
|
||||
char *buf1 = osi_calloc(tmp_len);
|
||||
err = nvs_get_str(fp, CONFIG_KEY, buf1, &tmp_len);
|
||||
if (err == ESP_OK) {
|
||||
LOG_ERROR("rd %d\n%s", tmp_len, buf1);
|
||||
}
|
||||
// err = nvs_erase_key(fp, CONFIG_KEY);
|
||||
// LOG_ERROR("m4y\n");
|
||||
osi_free(buf1);
|
||||
}
|
||||
buf[w_cnt_total] = '\0';
|
||||
|
||||
// LOG_ERROR("set str bgn %d, %s, %d %d\n", (int)fp, CONFIG_KEY, w_cnt_total, strlen(buf));
|
||||
err = nvs_set_blob(fp, CONFIG_KEY, buf, w_cnt_total);
|
||||
// err = nvs_set_str(fp, CONFIG_KEY, "abc");
|
||||
|
||||
// LOG_ERROR("set str end\n");
|
||||
if (err != ESP_OK) {
|
||||
// LOG_ERROR("m40\n");
|
||||
my_nvs_close(fp);
|
||||
LOG_ERROR("m41\n");
|
||||
goto error;
|
||||
}
|
||||
// LOG_ERROR("m5\n");
|
||||
err = nvs_commit(fp);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("m50\n");
|
||||
my_nvs_close(fp);
|
||||
LOG_ERROR("m51\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// LOG_ERROR("m6\n");
|
||||
my_nvs_close(fp);
|
||||
osi_free(line);
|
||||
osi_free(buf);
|
||||
return true;
|
||||
|
||||
error:
|
||||
LOG_ERROR("m7\n");
|
||||
if (buf) {
|
||||
osi_free(buf);
|
||||
}
|
||||
if (line) {
|
||||
osi_free(line);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *trim(char *str)
|
||||
{
|
||||
while (isspace((unsigned char)(*str))) {
|
||||
++str;
|
||||
}
|
||||
|
||||
if (!*str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
char *end_str = str + strlen(str) - 1;
|
||||
while (end_str > str && isspace((unsigned char)(*end_str))) {
|
||||
--end_str;
|
||||
}
|
||||
|
||||
end_str[1] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static void config_parse(nvs_handle fp, config_t *config)
|
||||
{
|
||||
assert(fp != 0);
|
||||
assert(config != NULL);
|
||||
|
||||
LOG_ERROR("cfg parse\n");
|
||||
int line_num = 0;
|
||||
char *line = osi_calloc(1024);
|
||||
char *section = osi_calloc(1024);
|
||||
char *buf = osi_calloc(CONFIG_FILE_MAX_SIZE);
|
||||
if (!line || !section || !buf) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// LOG_ERROR("p1\n");
|
||||
esp_err_t err;
|
||||
size_t length = CONFIG_FILE_MAX_SIZE;
|
||||
err = nvs_get_blob(fp, CONFIG_KEY, buf, &length);
|
||||
if (err != ESP_OK) {
|
||||
// LOG_ERROR("p2\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
LOG_ERROR("p3 %d\n%s\n", length, buf);
|
||||
char *p_line_end;
|
||||
char *p_line_bgn = buf;
|
||||
strcpy(section, CONFIG_DEFAULT_SECTION);
|
||||
// LOG_ERROR("p4\n");
|
||||
while ( (p_line_bgn < buf + length - 1) && (p_line_end = strchr(p_line_bgn, '\n'))) {
|
||||
|
||||
// get one line
|
||||
int line_len = p_line_end - p_line_bgn;
|
||||
// LOG_ERROR("pii, %d, %x, %x, %x\n", line_len, p_line_bgn, p_line_end, buf);
|
||||
if (line_len > 1023) {
|
||||
break;
|
||||
}
|
||||
memcpy(line, p_line_bgn, line_len);
|
||||
line[line_len] = '\0';
|
||||
p_line_bgn = p_line_end + 1;
|
||||
// LOG_ERROR("pi0\n");
|
||||
char *line_ptr = trim(line);
|
||||
++line_num;
|
||||
|
||||
// Skip blank and comment lines.
|
||||
if (*line_ptr == '\0' || *line_ptr == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// LOG_ERROR("pi1\n");
|
||||
if (*line_ptr == '[') {
|
||||
size_t len = strlen(line_ptr);
|
||||
if (line_ptr[len - 1] != ']') {
|
||||
LOG_WARN("%s unterminated section name on line %d.", __func__, line_num);
|
||||
continue;
|
||||
}
|
||||
strncpy(section, line_ptr + 1, len - 2);
|
||||
section[len - 2] = '\0';
|
||||
// LOG_ERROR("pi2\n");
|
||||
} else {
|
||||
// LOG_ERROR("pi3\n");
|
||||
char *split = strchr(line_ptr, '=');
|
||||
if (!split) {
|
||||
LOG_DEBUG("%s no key/value separator found on line %d.", __func__, line_num);
|
||||
continue;
|
||||
}
|
||||
// LOG_ERROR("pi4\n");
|
||||
*split = '\0';
|
||||
config_set_string(config, section, trim(line_ptr), trim(split + 1));
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
// LOG_ERROR("p5\n");
|
||||
if (buf) {
|
||||
osi_free(buf);
|
||||
}
|
||||
if (line) {
|
||||
osi_free(line);
|
||||
}
|
||||
if (section) {
|
||||
osi_free(section);
|
||||
}
|
||||
// LOG_ERROR("p6\n");
|
||||
}
|
||||
|
||||
static section_t *section_new(const char *name)
|
||||
{
|
||||
section_t *section = osi_calloc(sizeof(section_t));
|
||||
if (!section) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
section->name = osi_strdup(name);
|
||||
section->entries = list_new(entry_free);
|
||||
return section;
|
||||
}
|
||||
|
||||
static void section_free(void *ptr)
|
||||
{
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
section_t *section = ptr;
|
||||
osi_free(section->name);
|
||||
list_free(section->entries);
|
||||
osi_free(section);
|
||||
}
|
||||
|
||||
static section_t *section_find(const config_t *config, const char *section)
|
||||
{
|
||||
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
|
||||
section_t *sec = list_node(node);
|
||||
if (!strcmp(sec->name, section)) {
|
||||
return sec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static entry_t *entry_new(const char *key, const char *value)
|
||||
{
|
||||
entry_t *entry = osi_calloc(sizeof(entry_t));
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->key = osi_strdup(key);
|
||||
entry->value = osi_strdup(value);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void entry_free(void *ptr)
|
||||
{
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry_t *entry = ptr;
|
||||
osi_free(entry->key);
|
||||
osi_free(entry->value);
|
||||
osi_free(entry);
|
||||
}
|
||||
|
||||
static entry_t *entry_find(const config_t *config, const char *section, const char *key)
|
||||
{
|
||||
section_t *sec = section_find(config, section);
|
||||
if (!sec) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
|
||||
entry_t *entry = list_node(node);
|
||||
if (!strcmp(entry->key, key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
127
components/bt/bluedroid/osi/include/config.h
Executable file
127
components/bt/bluedroid/osi/include/config.h
Executable file
|
@ -0,0 +1,127 @@
|
|||
#pragma once
|
||||
|
||||
// This module implements a configuration parser. Clients can query the
|
||||
// contents of a configuration file through the interface provided here.
|
||||
// The current implementation is read-only; mutations are only kept in
|
||||
// memory. This parser supports the INI file format.
|
||||
|
||||
// Implementation notes:
|
||||
// - Key/value pairs that are not within a section are assumed to be under
|
||||
// the |CONFIG_DEFAULT_SECTION| section.
|
||||
// - Multiple sections with the same name will be merged as if they were in
|
||||
// a single section.
|
||||
// - Empty sections with no key/value pairs will be treated as if they do
|
||||
// not exist. In other words, |config_has_section| will return false for
|
||||
// empty sections.
|
||||
// - Duplicate keys in a section will overwrite previous values.
|
||||
// - All strings are case sensitive.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// The default section name to use if a key/value pair is not defined within
|
||||
// a section.
|
||||
#define CONFIG_DEFAULT_SECTION "Global"
|
||||
|
||||
typedef struct config_t config_t;
|
||||
typedef struct config_section_node_t config_section_node_t;
|
||||
|
||||
// Creates a new config object with no entries (i.e. not backed by a file).
|
||||
// This function returns a config object or NULL on error. Clients must call
|
||||
// |config_free| on the returned handle when it is no longer required.
|
||||
config_t *config_new_empty(void);
|
||||
|
||||
// Loads the specified file and returns a handle to the config file. If there
|
||||
// was a problem loading the file or allocating memory, this function returns
|
||||
// NULL. Clients must call |config_free| on the returned handle when it is no
|
||||
// longer required. |filename| must not be NULL and must point to a readable
|
||||
// file on the filesystem.
|
||||
config_t *config_new(const char *filename);
|
||||
|
||||
// Frees resources associated with the config file. No further operations may
|
||||
// be performed on the |config| object after calling this function. |config|
|
||||
// may be NULL.
|
||||
void config_free(config_t *config);
|
||||
|
||||
// Returns true if the config file contains a section named |section|. If
|
||||
// the section has no key/value pairs in it, this function will return false.
|
||||
// |config| and |section| must not be NULL.
|
||||
bool config_has_section(const config_t *config, const char *section);
|
||||
|
||||
// Returns true if the config file has a key named |key| under |section|.
|
||||
// Returns false otherwise. |config|, |section|, and |key| must not be NULL.
|
||||
bool config_has_key(const config_t *config, const char *section, const char *key);
|
||||
|
||||
// Returns the integral value for a given |key| in |section|. If |section|
|
||||
// or |key| do not exist, or the value cannot be fully converted to an integer,
|
||||
// this function returns |def_value|. |config|, |section|, and |key| must not
|
||||
// be NULL.
|
||||
int config_get_int(const config_t *config, const char *section, const char *key, int def_value);
|
||||
|
||||
// Returns the boolean value for a given |key| in |section|. If |section|
|
||||
// or |key| do not exist, or the value cannot be converted to a boolean, this
|
||||
// function returns |def_value|. |config|, |section|, and |key| must not be NULL.
|
||||
bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value);
|
||||
|
||||
// Returns the string value for a given |key| in |section|. If |section| or
|
||||
// |key| do not exist, this function returns |def_value|. The returned string
|
||||
// is owned by the config module and must not be freed. |config|, |section|,
|
||||
// and |key| must not be NULL. |def_value| may be NULL.
|
||||
const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value);
|
||||
|
||||
// Sets an integral value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, and |key|
|
||||
// must not be NULL.
|
||||
void config_set_int(config_t *config, const char *section, const char *key, int value);
|
||||
|
||||
// Sets a boolean value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, and |key|
|
||||
// must not be NULL.
|
||||
void config_set_bool(config_t *config, const char *section, const char *key, bool value);
|
||||
|
||||
// Sets a string value for the |key| in |section|. If |key| or |section| do
|
||||
// not already exist, this function creates them. |config|, |section|, |key|, and
|
||||
// |value| must not be NULL.
|
||||
void config_set_string(config_t *config, const char *section, const char *key, const char *value);
|
||||
|
||||
// Removes |section| from the |config| (and, as a result, all keys in the section).
|
||||
// Returns true if |section| was found and removed from |config|, false otherwise.
|
||||
// Neither |config| nor |section| may be NULL.
|
||||
bool config_remove_section(config_t *config, const char *section);
|
||||
|
||||
// Removes one specific |key| residing in |section| of the |config|. Returns true
|
||||
// if the section and key were found and the key was removed, false otherwise.
|
||||
// None of |config|, |section|, or |key| may be NULL.
|
||||
bool config_remove_key(config_t *config, const char *section, const char *key);
|
||||
|
||||
// Returns an iterator to the first section in the config file. If there are no
|
||||
// sections, the iterator will equal the return value of |config_section_end|.
|
||||
// The returned pointer must be treated as an opaque handle and must not be freed.
|
||||
// The iterator is invalidated on any config mutating operation. |config| may not
|
||||
// be NULL.
|
||||
const config_section_node_t *config_section_begin(const config_t *config);
|
||||
|
||||
// Returns an iterator to one past the last section in the config file. It does not
|
||||
// represent a valid section, but can be used to determine if all sections have been
|
||||
// iterated over. The returned pointer must be treated as an opaque handle and must
|
||||
// not be freed and must not be iterated on (must not call |config_section_next| on
|
||||
// it). |config| may not be NULL.
|
||||
const config_section_node_t *config_section_end(const config_t *config);
|
||||
|
||||
// Moves |iter| to the next section. If there are no more sections, |iter| will
|
||||
// equal the value of |config_section_end|. |iter| may not be NULL and must be
|
||||
// a pointer returned by either |config_section_begin| or |config_section_next|.
|
||||
const config_section_node_t *config_section_next(const config_section_node_t *iter);
|
||||
|
||||
// Returns the name of the section referred to by |iter|. The returned pointer is
|
||||
// owned by the config module and must not be freed by the caller. The pointer will
|
||||
// remain valid until |config_free| is called. |iter| may not be NULL and must not
|
||||
// equal the value returned by |config_section_end|.
|
||||
const char *config_section_name(const config_section_node_t *iter);
|
||||
|
||||
// Saves |config| to a file given by |filename|. Note that this could be a destructive
|
||||
// operation: if |filename| already exists, it will be overwritten. The config
|
||||
// module does not preserve comments or formatting so if a config file was opened
|
||||
// with |config_new| and subsequently overwritten with |config_save|, all comments
|
||||
// and special formatting in the original file will be lost. Neither |config| nor
|
||||
// |filename| may be NULL.
|
||||
bool config_save(const config_t *config, const char *filename);
|
2
components/bt/bluedroid/stack/gatt/gatt_attr.c
Normal file → Executable file
2
components/bt/bluedroid/stack/gatt/gatt_attr.c
Normal file → Executable file
|
@ -279,7 +279,7 @@ void gatt_profile_db_init (void)
|
|||
GATT_StartIf(gatt_cb.gatt_if);
|
||||
|
||||
service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE);
|
||||
GATT_TRACE_ERROR ("GATTS_CreateService: handle of service handle%x", service_handle);
|
||||
GATT_TRACE_DEBUG ("GATTS_CreateService: handle of service handle%x", service_handle);
|
||||
|
||||
/* add Service Changed characteristic
|
||||
*/
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 566acfd8c61a4ba0fb6b9026c89488b01af0fff0
|
||||
Subproject commit 9c1eea6bb03adc3b3847fff79c3f017652840a46
|
Loading…
Reference in a new issue