From 0f711963d7e5a3436ab83d2384d55d5f599b7fac Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Thu, 19 Jan 2017 17:11:01 +0800 Subject: [PATCH] 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 --- components/bt/bluedroid/btif/btif_config.c | 453 ++++++++++++++ components/bt/bluedroid/btif/btif_core.c | 2 +- components/bt/bluedroid/btif/btif_dm.c | 97 ++- components/bt/bluedroid/btif/btif_storage.c | 191 ++++++ .../bt/bluedroid/btif/include/btif_config.h | 58 ++ .../bt/bluedroid/btif/include/btif_storage.h | 66 ++ components/bt/bluedroid/btif/stack_manager.c | 6 +- components/bt/bluedroid/osi/config.c | 586 ++++++++++++++++++ components/bt/bluedroid/osi/include/config.h | 127 ++++ .../bt/bluedroid/stack/gatt/gatt_attr.c | 2 +- components/bt/lib | 2 +- 11 files changed, 1584 insertions(+), 6 deletions(-) create mode 100644 components/bt/bluedroid/btif/btif_config.c create mode 100644 components/bt/bluedroid/btif/btif_storage.c create mode 100755 components/bt/bluedroid/btif/include/btif_config.h create mode 100644 components/bt/bluedroid/btif/include/btif_storage.h create mode 100644 components/bt/bluedroid/osi/config.c create mode 100755 components/bt/bluedroid/osi/include/config.h mode change 100644 => 100755 components/bt/bluedroid/stack/gatt/gatt_attr.c diff --git a/components/bt/bluedroid/btif/btif_config.c b/components/bt/bluedroid/btif/btif_config.c new file mode 100644 index 0000000000..b080b2a8e2 --- /dev/null +++ b/components/bt/bluedroid/btif/btif_config.c @@ -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 +#include +// #include +#include +#include + +#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); +} diff --git a/components/bt/bluedroid/btif/btif_core.c b/components/bt/bluedroid/btif/btif_core.c index f9d3a05739..f35caba310 100644 --- a/components/bt/bluedroid/btif/btif_core.c +++ b/components/bt/bluedroid/btif/btif_core.c @@ -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; diff --git a/components/bt/bluedroid/btif/btif_dm.c b/components/bt/bluedroid/btif/btif_dm.c index 9d6ad3a3be..f4ba2a0a8b 100644 --- a/components/bt/bluedroid/btif/btif_dm.c +++ b/components/bt/bluedroid/btif/btif_dm.c @@ -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: diff --git a/components/bt/bluedroid/btif/btif_storage.c b/components/bt/bluedroid/btif/btif_storage.c new file mode 100644 index 0000000000..19eeaf1f23 --- /dev/null +++ b/components/bt/bluedroid/btif/btif_storage.c @@ -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; +} diff --git a/components/bt/bluedroid/btif/include/btif_config.h b/components/bt/bluedroid/btif/include/btif_config.h new file mode 100755 index 0000000000..9172c05831 --- /dev/null +++ b/components/bt/bluedroid/btif/include/btif_config.h @@ -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 +#include + +#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); + diff --git a/components/bt/bluedroid/btif/include/btif_storage.h b/components/bt/bluedroid/btif/include/btif_storage.h new file mode 100644 index 0000000000..bf31ba04c0 --- /dev/null +++ b/components/bt/bluedroid/btif/include/btif_storage.h @@ -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 +#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 */ diff --git a/components/bt/bluedroid/btif/stack_manager.c b/components/bt/bluedroid/btif/stack_manager.c index d3df7ea8bd..ecf526b4bb 100644 --- a/components/bt/bluedroid/btif/stack_manager.c +++ b/components/bt/bluedroid/btif/stack_manager.c @@ -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; diff --git a/components/bt/bluedroid/osi/config.c b/components/bt/bluedroid/osi/config.c new file mode 100644 index 0000000000..fadb75bc74 --- /dev/null +++ b/components/bt/bluedroid/osi/config.c @@ -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 +#include +#include +#include +#include +#include +// #include + +#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; +} diff --git a/components/bt/bluedroid/osi/include/config.h b/components/bt/bluedroid/osi/include/config.h new file mode 100755 index 0000000000..ab3096d4e9 --- /dev/null +++ b/components/bt/bluedroid/osi/include/config.h @@ -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 + +// 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); diff --git a/components/bt/bluedroid/stack/gatt/gatt_attr.c b/components/bt/bluedroid/stack/gatt/gatt_attr.c old mode 100644 new mode 100755 index 421b17cab7..f50a6491f2 --- a/components/bt/bluedroid/stack/gatt/gatt_attr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_attr.c @@ -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 */ diff --git a/components/bt/lib b/components/bt/lib index 566acfd8c6..9c1eea6bb0 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 566acfd8c61a4ba0fb6b9026c89488b01af0fff0 +Subproject commit 9c1eea6bb03adc3b3847fff79c3f017652840a46 -- 2.40.0