Skip to content

Commit 4772e56

Browse files
jori-nordiccarlescufi
authored andcommitted
Bluetooth: host: Store Client Supported Features on write
Store the Client supported features value when it is written to, instead of only on disconnection/identity resolved. Works around the situation where a user bonds, CF is written and the device is abruptly powered off, discarding the CF value, but keeping the bond. Fixes zephyrproject-rtos#54172. Signed-off-by: Jonathan Rico <[email protected]>
1 parent 67236de commit 4772e56

File tree

2 files changed

+158
-56
lines changed

2 files changed

+158
-56
lines changed

subsys/bluetooth/host/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,40 @@ config BT_SETTINGS_CCC_LAZY_LOADING
208208
Disabling this option will increase memory usage as CCC values for all
209209
bonded devices will be loaded when calling settings_load.
210210

211+
config BT_SETTINGS_DELAYED_STORE
212+
# Enables delayed non-volatile storage mechanism
213+
bool
214+
help
215+
Triggers the storage of the CF and CCC right after a write.
216+
This is done in the workqueue context, in order to not block the BT RX
217+
thread for too long.
218+
219+
config BT_SETTINGS_DELAYED_STORE_MS
220+
int
221+
default 1000
222+
help
223+
(Advanced) Governs the timeout after which the settings write will
224+
take effect.
225+
211226
config BT_SETTINGS_CCC_STORE_ON_WRITE
212227
bool "Store CCC value immediately after it has been written"
213228
depends on BT_CONN
229+
select BT_SETTINGS_DELAYED_STORE
214230
default y
215231
help
216232
Store Client Configuration Characteristic value right after it has
217233
been updated. If the option is disabled, the CCC is only stored on
218234
disconnection.
219235

236+
config BT_SETTINGS_CF_STORE_ON_WRITE
237+
bool "Store CF value immediately after it has been written"
238+
depends on BT_CONN && BT_GATT_CACHING
239+
select BT_SETTINGS_DELAYED_STORE
240+
default y
241+
help
242+
Store Client Supported Features value right after it has been updated.
243+
If the option is disabled, the CF is only stored on disconnection.
244+
220245
config BT_SETTINGS_USE_PRINTK
221246
bool "Use snprintk to encode Bluetooth settings key strings"
222247
depends on SETTINGS && PRINTK

subsys/bluetooth/host/gatt.c

Lines changed: 133 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
LOG_MODULE_REGISTER(bt_gatt);
5151

5252
#define SC_TIMEOUT K_MSEC(10)
53-
#define CCC_STORE_DELAY K_SECONDS(1)
54-
5553
#define DB_HASH_TIMEOUT K_MSEC(10)
5654

5755
static uint16_t last_static_handle;
@@ -544,6 +542,16 @@ static void clear_cf_cfg(struct gatt_cf_cfg *cfg)
544542
atomic_set(cfg->flags, 0);
545543
}
546544

545+
enum delayed_store_flags {
546+
DELAYED_STORE_CCC,
547+
DELAYED_STORE_CF,
548+
DELAYED_STORE_NUM_FLAGS
549+
};
550+
551+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
552+
static void gatt_delayed_store_enqueue(struct bt_conn *conn, enum delayed_store_flags flag);
553+
#endif
554+
547555
#if defined(CONFIG_BT_GATT_CACHING)
548556
static struct gatt_cf_cfg *find_cf_cfg(struct bt_conn *conn)
549557
{
@@ -640,6 +648,12 @@ static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
640648
cfg->id = conn->id;
641649
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
642650

651+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
652+
/* Save the new configuration to NVM (in another thread) */
653+
LOG_DBG("trigger CF write");
654+
gatt_delayed_store_enqueue(conn->id, &conn->le.dst, DELAYED_STORE_CF);
655+
#endif
656+
643657
return len;
644658
}
645659

@@ -915,7 +929,10 @@ static ssize_t sf_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
915929
#endif /* CONFIG_BT_EATT */
916930
#endif /* CONFIG_BT_GATT_CACHING */
917931

918-
static int bt_gatt_store_cf(struct bt_conn *conn)
932+
static struct gatt_cf_cfg *find_cf_cfg_by_addr(uint8_t id,
933+
const bt_addr_le_t *addr);
934+
935+
static int bt_gatt_store_cf(uint8_t id, const bt_addr_le_t *peer)
919936
{
920937
#if defined(CONFIG_BT_GATT_CACHING)
921938
struct gatt_cf_cfg *cfg;
@@ -924,7 +941,7 @@ static int bt_gatt_store_cf(struct bt_conn *conn)
924941
size_t len;
925942
int err;
926943

927-
cfg = find_cf_cfg(conn);
944+
cfg = find_cf_cfg_by_addr(id, peer);
928945
if (!cfg) {
929946
/* No cfg found, just clear it */
930947
LOG_DBG("No config for CF");
@@ -934,18 +951,18 @@ static int bt_gatt_store_cf(struct bt_conn *conn)
934951
str = (char *)cfg->data;
935952
len = sizeof(cfg->data);
936953

937-
if (conn->id) {
954+
if (id) {
938955
char id_str[4];
939956

940-
u8_to_dec(id_str, sizeof(id_str), conn->id);
957+
u8_to_dec(id_str, sizeof(id_str), id);
941958
bt_settings_encode_key(key, sizeof(key), "cf",
942-
&conn->le.dst, id_str);
959+
peer, id_str);
943960
}
944961
}
945962

946-
if (!cfg || !conn->id) {
963+
if (!cfg || !id) {
947964
bt_settings_encode_key(key, sizeof(key), "cf",
948-
&conn->le.dst, NULL);
965+
peer, NULL);
949966
}
950967

951968
err = settings_save_one(key, str, len);
@@ -954,7 +971,7 @@ static int bt_gatt_store_cf(struct bt_conn *conn)
954971
return err;
955972
}
956973

957-
LOG_DBG("Stored CF for %s (%s)", bt_addr_le_str(&conn->le.dst), key);
974+
LOG_DBG("Stored CF for %s (%s)", bt_addr_le_str(peer), key);
958975
#endif /* CONFIG_BT_GATT_CACHING */
959976
return 0;
960977

@@ -1008,7 +1025,7 @@ static void bt_gatt_identity_resolved(struct bt_conn *conn, const bt_addr_le_t *
10081025

10091026
/* Store the ccc and cf data */
10101027
bt_gatt_store_ccc(conn->id, &(conn->le.dst));
1011-
bt_gatt_store_cf(conn);
1028+
bt_gatt_store_cf(conn->id, &conn->le.dst);
10121029
}
10131030
#endif /* CONFIG_BT_SETTINGS && CONFIG_BT_SMP && CONFIG_BT_GATT_CLIENT */
10141031

@@ -1211,73 +1228,129 @@ static void clear_ccc_cfg(struct bt_gatt_ccc_cfg *cfg)
12111228
cfg->value = 0U;
12121229
}
12131230

1214-
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
1215-
static struct gatt_ccc_store {
1216-
struct bt_conn *conn_list[CONFIG_BT_MAX_CONN];
1231+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
1232+
struct ds_peer {
1233+
uint8_t id;
1234+
bt_addr_le_t peer;
1235+
1236+
ATOMIC_DEFINE(flags, DELAYED_STORE_NUM_FLAGS);
1237+
};
1238+
1239+
static struct gatt_delayed_store {
1240+
struct ds_peer peer_list[CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN];
12171241
struct k_work_delayable work;
1218-
} gatt_ccc_store;
1242+
} gatt_delayed_store;
12191243

1220-
static bool gatt_ccc_conn_is_queued(struct bt_conn *conn)
1244+
static struct ds_peer *gatt_delayed_store_find(uint8_t id,
1245+
const bt_addr_le_t *peer_addr)
12211246
{
1222-
return (conn == gatt_ccc_store.conn_list[bt_conn_index(conn)]);
1247+
struct ds_peer *el;
1248+
1249+
for (size_t i = 0; i < ARRAY_SIZE(gatt_delayed_store.peer_list); i++) {
1250+
el = &gatt_delayed_store.peer_list[i];
1251+
if (el->id == id &&
1252+
bt_addr_le_eq(peer_addr, &el->peer)) {
1253+
return el;
1254+
}
1255+
}
1256+
1257+
return NULL;
12231258
}
12241259

1225-
static void gatt_ccc_conn_unqueue(struct bt_conn *conn)
1260+
static struct ds_peer *gatt_delayed_store_alloc(uint8_t id,
1261+
const bt_addr_le_t *peer_addr)
12261262
{
1227-
uint8_t index = bt_conn_index(conn);
1263+
struct ds_peer *el;
1264+
1265+
for (size_t i = 0; i < ARRAY_SIZE(gatt_delayed_store.peer_list); i++) {
1266+
el = &gatt_delayed_store.peer_list[i];
12281267

1229-
if (gatt_ccc_store.conn_list[index] != NULL) {
1230-
bt_conn_unref(gatt_ccc_store.conn_list[index]);
1231-
gatt_ccc_store.conn_list[index] = NULL;
1268+
/* Checking for the flags is cheaper than a memcmp for the
1269+
* address, so we use that to signal that a given slot is
1270+
* free.
1271+
*/
1272+
if (atomic_get(el->flags) == 0) {
1273+
bt_addr_le_copy(&el->peer, peer_addr);
1274+
el->id = id;
1275+
1276+
return el;
1277+
}
12321278
}
1279+
1280+
return NULL;
12331281
}
12341282

1235-
static void gatt_ccc_conn_enqueue(struct bt_conn *conn)
1283+
static void gatt_delayed_store_free(struct ds_peer *el)
12361284
{
1237-
if ((!gatt_ccc_conn_is_queued(conn)) &&
1238-
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
1239-
/* Store the connection with the same index it has in
1240-
* the conns array
1241-
*/
1242-
gatt_ccc_store.conn_list[bt_conn_index(conn)] =
1243-
bt_conn_ref(conn);
1285+
if (el) {
1286+
el->id = 0;
1287+
memset(&el->peer, 0, sizeof(el->peer));
1288+
atomic_set(el->flags, 0);
1289+
}
1290+
}
1291+
1292+
static void gatt_delayed_store_enqueue(uint8_t id, const bt_addr_le_t *peer_addr,
1293+
enum delayed_store_flags flag)
1294+
{
1295+
bool bonded = bt_addr_le_is_bonded(id, peer_addr);
1296+
struct ds_peer *el = gatt_delayed_store_find(id, peer_addr);
1297+
1298+
if (bonded) {
1299+
if (el == NULL) {
1300+
el = gatt_delayed_store_alloc(id, peer_addr);
1301+
__ASSERT(el != NULL, "Can't save CF / CCC to flash");
1302+
}
1303+
1304+
atomic_set_bit(el->flags, flag);
12441305

1245-
k_work_reschedule(&gatt_ccc_store.work, CCC_STORE_DELAY);
1306+
k_work_reschedule(&gatt_delayed_store.work,
1307+
K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
12461308
}
12471309
}
12481310

1249-
static bool gatt_ccc_conn_queue_is_empty(void)
1311+
static bool gatt_delayed_work_queue_is_empty(void)
12501312
{
1251-
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
1252-
if (gatt_ccc_store.conn_list[i]) {
1313+
for (size_t i = 0; i < ARRAY_SIZE(gatt_delayed_store.peer_list); i++) {
1314+
/* Checking for the flags is cheaper than a memcmp for the
1315+
* address, so we use that to signal that a given slot is
1316+
* free.
1317+
*/
1318+
if (atomic_get(gatt_delayed_store.peer_list[i].flags) != 0) {
12531319
return false;
12541320
}
12551321
}
12561322

12571323
return true;
12581324
}
12591325

1260-
static void ccc_delayed_store(struct k_work *work)
1326+
static void delayed_store(struct k_work *work)
12611327
{
1328+
struct ds_peer *el;
12621329
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1263-
struct gatt_ccc_store *ccc_store =
1264-
CONTAINER_OF(dwork, struct gatt_ccc_store, work);
1330+
struct gatt_delayed_store *store =
1331+
CONTAINER_OF(dwork, struct gatt_delayed_store, work);
12651332

1266-
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
1267-
struct bt_conn *conn = ccc_store->conn_list[i];
1333+
for (size_t i = 0; i < ARRAY_SIZE(gatt_delayed_store.peer_list); i++) {
1334+
el = &store->peer_list[i];
12681335

1269-
if (!conn) {
1270-
continue;
1271-
}
1336+
if (bt_addr_le_is_bonded(el->id, &el->peer)) {
1337+
if (IS_ENABLED(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) &&
1338+
atomic_test_and_clear_bit(el->flags, DELAYED_STORE_CCC)) {
1339+
bt_gatt_store_ccc(el->id, &el->peer);
1340+
}
12721341

1273-
if (bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
1274-
ccc_store->conn_list[i] = NULL;
1275-
bt_gatt_store_ccc(conn->id, &conn->le.dst);
1276-
bt_conn_unref(conn);
1342+
if (IS_ENABLED(CONFIG_BT_SETTINGS_CF_STORE_ON_WRITE) &&
1343+
atomic_test_and_clear_bit(el->flags, DELAYED_STORE_CF)) {
1344+
bt_gatt_store_cf(el->id, &el->peer);
1345+
}
1346+
1347+
if (atomic_get(el->flags) == 0) {
1348+
gatt_delayed_store_free(el);
1349+
}
12771350
}
12781351
}
12791352
}
1280-
#endif
1353+
#endif /* CONFIG_BT_SETTINGS_DELAYED_STORE */
12811354

12821355
static void bt_gatt_service_init(void)
12831356
{
@@ -1323,8 +1396,8 @@ void bt_gatt_init(void)
13231396
}
13241397
#endif /* defined(CONFIG_BT_GATT_SERVICE_CHANGED) */
13251398

1326-
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
1327-
k_work_init_delayable(&gatt_ccc_store.work, ccc_delayed_store);
1399+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
1400+
k_work_init_delayable(&gatt_delayed_store.work, delayed_store);
13281401
#endif
13291402

13301403
#if defined(CONFIG_BT_GATT_CLIENT) && defined(CONFIG_BT_SETTINGS) && defined(CONFIG_BT_SMP)
@@ -1431,7 +1504,9 @@ static void gatt_unregister_ccc(struct _bt_gatt_ccc *ccc)
14311504
if (conn) {
14321505
if (conn->state == BT_CONN_CONNECTED) {
14331506
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
1434-
gatt_ccc_conn_enqueue(conn);
1507+
gatt_delayed_store_enqueue(conn->id,
1508+
&conn->le.dst,
1509+
DELAYED_STORE_CCC);
14351510
#endif
14361511
store = false;
14371512
}
@@ -1998,7 +2073,7 @@ ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
19982073
if (value_changed) {
19992074
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
20002075
/* Enqueue CCC store if value has changed for the connection */
2001-
gatt_ccc_conn_enqueue(conn);
2076+
gatt_delayed_store_enqueue(conn->id, &conn->le.dst, DELAYED_STORE_CCC);
20022077
#endif
20032078
}
20042079

@@ -6067,18 +6142,20 @@ void bt_gatt_disconnected(struct bt_conn *conn)
60676142
cleanup_notify(conn);
60686143
#endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
60696144

6070-
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
6071-
gatt_ccc_conn_unqueue(conn);
6145+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
6146+
struct ds_peer *el = gatt_delayed_store_find(conn->id, &conn->le.dst);
6147+
6148+
gatt_delayed_store_free(el);
60726149

6073-
if (gatt_ccc_conn_queue_is_empty()) {
6074-
k_work_cancel_delayable(&gatt_ccc_store.work);
6150+
if (gatt_delayed_work_queue_is_empty()) {
6151+
k_work_cancel_delayable(&gatt_delayed_store.work);
60756152
}
60766153
#endif
60776154

60786155
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
60796156
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
60806157
bt_gatt_store_ccc(conn->id, &conn->le.dst);
6081-
bt_gatt_store_cf(conn);
6158+
bt_gatt_store_cf(conn->id, &conn->le.dst);
60826159
}
60836160

60846161
/* Make sure to clear the CCC entry when using lazy loading */

0 commit comments

Comments
 (0)