From b997a283f7ec3d13728b7ca9c92258cd3bcac3e0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 26 Apr 2018 22:05:26 +0300 Subject: [PATCH] Bluetooth: Introduce skeleton for settings-based storage Introduce a basic skeleton for peristent storage based on the settings subsystem. Also enable support for this to the peripheral sample application, so the new code gets exersized by CI. For now, the implementation provides the same level support as the bt_storage API ever did, i.e. for the identity address and the IRK. Signed-off-by: Johan Hedberg --- samples/bluetooth/peripheral/prj.conf | 11 ++ samples/bluetooth/peripheral/src/main.c | 6 + subsys/bluetooth/host/CMakeLists.txt | 1 + subsys/bluetooth/host/Kconfig | 24 ++++ subsys/bluetooth/host/hci_core.c | 16 ++- subsys/bluetooth/host/hci_core.h | 2 + subsys/bluetooth/host/settings.c | 152 ++++++++++++++++++++++++ subsys/bluetooth/host/settings.h | 7 ++ 8 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 subsys/bluetooth/host/settings.c create mode 100644 subsys/bluetooth/host/settings.h diff --git a/samples/bluetooth/peripheral/prj.conf b/samples/bluetooth/peripheral/prj.conf index 7973a614458..dd695046076 100644 --- a/samples/bluetooth/peripheral/prj.conf +++ b/samples/bluetooth/peripheral/prj.conf @@ -1,3 +1,6 @@ +# Incresed stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + CONFIG_BT=y CONFIG_BT_DEBUG_LOG=y CONFIG_BT_SMP=y @@ -7,3 +10,11 @@ CONFIG_BT_ATT_PREPARE_COUNT=2 CONFIG_BT_PRIVACY=y CONFIG_BT_DEVICE_NAME="Test peripheral" CONFIG_BT_DEVICE_APPEARANCE=833 + +CONFIG_BT_SETTINGS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_FCB=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y diff --git a/samples/bluetooth/peripheral/src/main.c b/samples/bluetooth/peripheral/src/main.c index 365c0312154..bf8d3c5187a 100644 --- a/samples/bluetooth/peripheral/src/main.c +++ b/samples/bluetooth/peripheral/src/main.c @@ -14,6 +14,8 @@ #include #include +#include + #include #include #include @@ -242,6 +244,10 @@ static void bt_ready(int err) dis_init(CONFIG_SOC, "Manufacturer"); bt_gatt_service_register(&vnd_svc); + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (err) { diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index 856cb1585f5..b560e2c0610 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -13,6 +13,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_A2DP a2dp.c) zephyr_library_sources_ifdef(CONFIG_BT_AVDTP avdtp.c) zephyr_library_sources_ifdef(CONFIG_BT_RFCOMM rfcomm.c) zephyr_library_sources_ifdef(CONFIG_BT_TESTING testing.c) +zephyr_library_sources_ifdef(CONFIG_BT_SETTINGS settings.c) zephyr_library_sources_ifdef( CONFIG_BT_BREDR diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index c69f9c810f2..5a594188f67 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -95,6 +95,7 @@ config BT_RX_STACK_SIZE int "Size of the receiving thread stack" depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD default 1024 + default 2048 if BT_SETTINGS default 2048 if BT_MESH default 512 if BT_HCI_RAW range 512 65536 if BT_HCI_RAW @@ -130,6 +131,23 @@ config BT_HOST_CRYPTO select TINYCRYPT_SHA256_HMAC select TINYCRYPT_SHA256_HMAC_PRNG +config BT_SETTINGS + bool "Store Bluetooth state and configuration persistently" + depends on SETTINGS + select MPU_ALLOW_FLASH_WRITE if ARM_MPU + help + When selected, the Bluetooth stack will take care of storing + (and restoring) the Bluetooth state (e.g. pairing keys) and + configuration persistently in flash. + + When this option has been enabled, it's important that the + application makes a call to settings_load() after having done + all necessary initialization (e.g. calling bt_enable). The + reason settings_load() is handled externally to the stack, is + that there may be other subsystems using the settings API, in + which case it's more efficient to load all settings in one go, + instead of each subsystem doing it independently. + config BT_INTERNAL_STORAGE bool "Use an internal persistent storage handler" depends on FILE_SYSTEM @@ -350,6 +368,12 @@ config BT_TINYCRYPT_ECC Connections so that Hosts can make use of those. if BT_DEBUG +config BT_DEBUG_SETTINGS + bool "Bluetooth storage debug" + depends on BT_SETTINGS + help + This option enables debug support for Bluetooth storage. + config BT_DEBUG_HCI_CORE bool "Bluetooth HCI core debug" help diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 1faad4b10c2..d5af97f21fb 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -41,6 +41,7 @@ #include "l2cap_internal.h" #include "smp.h" #include "crypto.h" +#include "settings.h" /* Peripheral timeout to initialize Connection Parameter Update procedure */ #define CONN_UPDATE_TIMEOUT K_SECONDS(5) @@ -4143,7 +4144,7 @@ static const char *ver_str(u8_t ver) return "unknown"; } -static void show_dev_info(void) +void bt_dev_show_info(void) { BT_INFO("Identity: %s", bt_addr_le_str(&bt_dev.id_addr)); BT_INFO("HCI: version %s (0x%02x) revision 0x%04x, manufacturer 0x%04x", @@ -4154,7 +4155,7 @@ static void show_dev_info(void) bt_dev.lmp_subversion); } #else -static inline void show_dev_info(void) +void bt_dev_show_info(void) { } #endif /* CONFIG_BT_DEBUG */ @@ -4320,7 +4321,9 @@ static int hci_init(void) } } - show_dev_info(); + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_dev_show_info(); + } return 0; } @@ -4549,6 +4552,13 @@ int bt_enable(bt_ready_cb_t cb) return -EALREADY; } + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + err = bt_settings_init(); + if (err) { + return err; + } + } + ready_cb = cb; /* TX thread */ diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 06f08f17443..91544a751bb 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -190,3 +190,5 @@ u16_t bt_hci_get_cmd_opcode(struct net_buf *buf); struct bt_keys; int bt_id_add(struct bt_keys *keys); int bt_id_del(struct bt_keys *keys); + +void bt_dev_show_info(void); diff --git a/subsys/bluetooth/host/settings.c b/subsys/bluetooth/host/settings.c new file mode 100644 index 00000000000..64f7c6164fe --- /dev/null +++ b/subsys/bluetooth/host/settings.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SETTINGS) +#include "common/log.h" + +#include "hci_core.h" +#include "settings.h" + +static int set(int argc, char **argv, char *val) +{ + int len; + + BT_DBG("argc %d argv[0] %s val %s", argc, argv[0], val); + + if (argc != 1) { + return -ENOENT; + } + + if (!strcmp(argv[0], "id")) { + len = sizeof(bt_dev.id_addr); + settings_bytes_from_str(val, &bt_dev.id_addr, &len); + BT_DBG("ID Addr set to %s", bt_addr_le_str(&bt_dev.id_addr)); + return 0; + } + +#if defined(CONFIG_BT_PRIVACY) + if (!strcmp(argv[0], "irk")) { + len = sizeof(bt_dev.irk); + settings_bytes_from_str(val, bt_dev.irk, &len); + BT_DBG("IRK set to %s", bt_hex(bt_dev.irk, 16)); + return 0; + } +#endif /* CONFIG_BT_PRIVACY */ + + return 0; +} + +static void generate_static_addr(void) +{ + char buf[13]; + char *str; + + BT_DBG("Generating new static random address"); + + if (bt_addr_le_create_static(&bt_dev.id_addr)) { + BT_ERR("Failed to generate static addr"); + return; + } + + atomic_set_bit(bt_dev.flags, BT_DEV_ID_STATIC_RANDOM); + + BT_DBG("New ID Addr: %s", bt_addr_le_str(&bt_dev.id_addr)); + + str = settings_str_from_bytes(&bt_dev.id_addr, sizeof(bt_dev.id_addr), + buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode ID Addr as value"); + return; + } + + BT_DBG("Saving ID addr as value %s", str); + settings_save_one("bt/id", str); +} + +#if defined(CONFIG_BT_PRIVACY) +static void generate_irk(void) +{ + char buf[25]; + char *str; + + BT_DBG("Generating new IRK"); + + if (bt_rand(bt_dev.irk, sizeof(bt_dev.irk))) { + BT_ERR("Failed to generate IRK"); + return; + } + + BT_DBG("New local IRK: %s", bt_hex(bt_dev.irk, 16)); + + str = settings_str_from_bytes(bt_dev.irk, 16, buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode IRK as value"); + return; + } + + BT_DBG("Saving IRK as value %s", str); + settings_save_one("bt/irk", str); +} +#endif /* CONFIG_BT_PRIVACY */ + +static int commit(void) +{ + BT_DBG(""); + + if (!bt_addr_le_cmp(&bt_dev.id_addr, BT_ADDR_LE_ANY) || + !bt_addr_le_cmp(&bt_dev.id_addr, BT_ADDR_LE_NONE)) { + generate_static_addr(); + } + +#if defined(CONFIG_BT_PRIVACY) + { + u8_t zero[16] = { 0 }; + + if (!memcmp(bt_dev.irk, zero, 16)) { + generate_irk(); + } + } +#endif /* CONFIG_BT_PRIVACY */ + + bt_dev_show_info(); + + return 0; +} + +static struct settings_handler bt_settings = { + .name = "bt", + .h_set = set, + .h_commit = commit, +}; + +int bt_settings_init(void) +{ + int err; + + BT_DBG(""); + + err = settings_subsys_init(); + if (err) { + BT_ERR("settings_subsys_init failed (err %d)", err); + return err; + } + + err = settings_register(&bt_settings); + if (err) { + BT_ERR("settings_register failed (err %d)", err); + return err; + } + + return 0; +} diff --git a/subsys/bluetooth/host/settings.h b/subsys/bluetooth/host/settings.h new file mode 100644 index 00000000000..6f14f32a1ab --- /dev/null +++ b/subsys/bluetooth/host/settings.h @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int bt_settings_init(void);