Skip to content

Commit 7c290b0

Browse files
Qbiczjhedberg
authored andcommitted
Bluetooth: settings: store CCC on write
By default, CCC value is only stored to persistent memory during BT disconnection. This commit adds an optional storing of CCC right after it has been updated. This results in better robustness of peripheral but increases system workqueue stack usage. Signed-off-by: Filip Kubicz <[email protected]>
1 parent 47f5298 commit 7c290b0

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

subsys/bluetooth/host/Kconfig

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ config BT_RX_STACK_SIZE
101101
depends on BT_HCI_HOST || BT_RECV_IS_RX_THREAD
102102
default 512 if BT_HCI_RAW
103103
default 2048 if BT_MESH
104-
default 2048 if BT_SETTINGS
104+
default 2200 if BT_SETTINGS
105105
default 1024
106106
range 512 65536 if BT_HCI_RAW
107107
range 1100 65536 if BT_MESH
@@ -153,6 +153,20 @@ config BT_SETTINGS
153153
which case it's more efficient to load all settings in one go,
154154
instead of each subsystem doing it independently.
155155

156+
if BT_SETTINGS
157+
config BT_SETTINGS_CCC_STORE_ON_WRITE
158+
bool "Store CCC value immediately after it has been written"
159+
default n
160+
help
161+
Store Client Configuration Characteristic value right after it has
162+
been updated.
163+
164+
By default, CCC is only stored on disconnection.
165+
Choosing this option is safer for battery-powered devices or devices
166+
that expect to be reset suddenly. However, it requires additional
167+
workqueue stack space.
168+
endif # BT_SETTINGS
169+
156170
if BT_CONN
157171

158172
if BT_HCI_ACL_FLOW_CONTROL

subsys/bluetooth/host/gatt.c

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
#include "settings.h"
3737
#include "gatt_internal.h"
3838

39-
#define SC_TIMEOUT K_MSEC(10)
39+
#define SC_TIMEOUT K_MSEC(10)
40+
#define CCC_STORE_DELAY K_SECONDS(1)
4041

4142
/* Persistent storage format for GATT CCC */
4243
struct ccc_store {
@@ -275,6 +276,59 @@ static void sc_process(struct k_work *work)
275276
atomic_set_bit(sc->flags, SC_INDICATE_PENDING);
276277
}
277278

279+
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
280+
static struct gatt_ccc_store {
281+
struct bt_conn *conn_list[CONFIG_BT_MAX_CONN];
282+
struct k_delayed_work work;
283+
} gatt_ccc_store;
284+
285+
static bool gatt_ccc_conn_is_queued(struct bt_conn *conn)
286+
{
287+
return (conn == gatt_ccc_store.conn_list[bt_conn_get_id(conn)]);
288+
}
289+
290+
static void gatt_ccc_conn_unqueue(struct bt_conn *conn)
291+
{
292+
u8_t index = bt_conn_get_id(conn);
293+
294+
if (gatt_ccc_store.conn_list[index] != NULL) {
295+
bt_conn_unref(gatt_ccc_store.conn_list[index]);
296+
gatt_ccc_store.conn_list[index] = NULL;
297+
}
298+
}
299+
300+
static bool gatt_ccc_conn_queue_is_empty(void)
301+
{
302+
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
303+
if (gatt_ccc_store.conn_list[i]) {
304+
return false;
305+
}
306+
}
307+
308+
return true;
309+
}
310+
311+
static void ccc_delayed_store(struct k_work *work)
312+
{
313+
struct gatt_ccc_store *ccc_store =
314+
CONTAINER_OF(work, struct gatt_ccc_store, work);
315+
316+
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
317+
struct bt_conn *conn = ccc_store->conn_list[i];
318+
319+
if (!conn) {
320+
continue;
321+
}
322+
323+
if (bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
324+
bt_gatt_store_ccc(conn->id, &conn->le.dst);
325+
bt_conn_unref(conn);
326+
ccc_store->conn_list[i] = NULL;
327+
}
328+
}
329+
}
330+
#endif
331+
278332
void bt_gatt_init(void)
279333
{
280334
if (!atomic_cas(&init, 0, 1)) {
@@ -286,6 +340,9 @@ void bt_gatt_init(void)
286340
gatt_register(&gatt_svc);
287341

288342
k_delayed_work_init(&gatt_sc.work, sc_process);
343+
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
344+
k_delayed_work_init(&gatt_ccc_store.work, ccc_delayed_store);
345+
#endif
289346
}
290347

291348
static bool update_range(u16_t *start, u16_t *end, u16_t new_start,
@@ -658,6 +715,19 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
658715
/* Update cfg if don't match */
659716
if (ccc->cfg[i].value != ccc->value) {
660717
gatt_ccc_changed(attr, ccc);
718+
719+
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
720+
if ((!gatt_ccc_conn_is_queued(conn)) &&
721+
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
722+
/* Store the connection with the same index it has in
723+
* the conns array
724+
*/
725+
gatt_ccc_store.conn_list[bt_conn_get_id(conn)] =
726+
bt_conn_ref(conn);
727+
k_delayed_work_submit(&gatt_ccc_store.work,
728+
CCC_STORE_DELAY);
729+
}
730+
#endif
661731
}
662732

663733
/* Disabled CCC is the same as no configured CCC, so clear the entry */
@@ -2365,6 +2435,14 @@ void bt_gatt_disconnected(struct bt_conn *conn)
23652435
BT_DBG("conn %p", conn);
23662436
bt_gatt_foreach_attr(0x0001, 0xffff, disconnected_cb, conn);
23672437

2438+
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
2439+
gatt_ccc_conn_unqueue(conn);
2440+
2441+
if (gatt_ccc_conn_queue_is_empty()) {
2442+
k_delayed_work_cancel(&gatt_ccc_store.work);
2443+
}
2444+
#endif
2445+
23682446
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
23692447
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
23702448
bt_gatt_store_ccc(conn->id, &conn->le.dst);

0 commit comments

Comments
 (0)