Skip to content

Commit e8f3ef2

Browse files
jori-nordiccarlescufi
authored andcommitted
Bluetooth: host: store GATT change-aware status in settings
The spec says we have to persist the change-aware status of bonded peers between resets. This stores it at the end of the CF storage that we currently have. Fixes zephyrproject-rtos#54173 Signed-off-by: Jonathan Rico <[email protected]>
1 parent 4772e56 commit e8f3ef2

File tree

1 file changed

+118
-25
lines changed

1 file changed

+118
-25
lines changed

subsys/bluetooth/host/gatt.c

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ static struct _bt_gatt_ccc sc_ccc = BT_GATT_CCC_INITIALIZER(NULL,
500500
sc_ccc_cfg_write,
501501
NULL);
502502

503+
/* Do not shuffle the values in this enum, they are used as bit offsets when
504+
* saving the CF flags to NVS (i.e. NVS persists between FW upgrades).
505+
*/
503506
enum {
504507
CF_CHANGE_AWARE, /* Client is changed aware */
505508
CF_DB_HASH_READ, /* The client has read the database hash */
@@ -513,15 +516,16 @@ enum {
513516
#define CF_BIT_NOTIFY_MULTI 2
514517
#define CF_BIT_LAST CF_BIT_NOTIFY_MULTI
515518

516-
#define CF_NUM_BITS (CF_BIT_LAST + 1)
517-
#define CF_NUM_BYTES ((CF_BIT_LAST / 8) + 1)
519+
#define CF_NUM_BITS (CF_BIT_LAST + 1)
520+
#define CF_NUM_BYTES ((CF_BIT_LAST / 8) + 1)
521+
#define CF_FLAGS_STORE_LEN 1
518522

519523
#define CF_ROBUST_CACHING(_cfg) (_cfg->data[0] & BIT(CF_BIT_ROBUST_CACHING))
520524
#define CF_EATT(_cfg) (_cfg->data[0] & BIT(CF_BIT_EATT))
521525
#define CF_NOTIFY_MULTI(_cfg) (_cfg->data[0] & BIT(CF_BIT_NOTIFY_MULTI))
522526

523527
struct gatt_cf_cfg {
524-
uint8_t id;
528+
uint8_t id;
525529
bt_addr_le_t peer;
526530
uint8_t data[CF_NUM_BYTES];
527531
ATOMIC_DEFINE(flags, CF_NUM_FLAGS);
@@ -549,10 +553,63 @@ enum delayed_store_flags {
549553
};
550554

551555
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
552-
static void gatt_delayed_store_enqueue(struct bt_conn *conn, enum delayed_store_flags flag);
556+
static void gatt_delayed_store_enqueue(uint8_t id, const bt_addr_le_t *peer_addr,
557+
enum delayed_store_flags flag);
553558
#endif
554559

555560
#if defined(CONFIG_BT_GATT_CACHING)
561+
static bool set_change_aware_no_store(struct gatt_cf_cfg *cfg, bool aware)
562+
{
563+
bool changed;
564+
565+
if (aware) {
566+
changed = !atomic_test_and_set_bit(cfg->flags, CF_CHANGE_AWARE);
567+
} else {
568+
changed = atomic_test_and_clear_bit(cfg->flags, CF_CHANGE_AWARE);
569+
}
570+
571+
LOG_DBG("peer is now change-%saware", aware ? "" : "un");
572+
573+
return changed;
574+
}
575+
576+
static void set_change_aware(struct gatt_cf_cfg *cfg, bool aware)
577+
{
578+
bool changed = set_change_aware_no_store(cfg, aware);
579+
580+
#if defined(CONFIG_BT_SETTINGS_DELAYED_STORE)
581+
if (changed) {
582+
gatt_delayed_store_enqueue(cfg->id, &cfg->peer, DELAYED_STORE_CF);
583+
}
584+
#else
585+
(void)changed;
586+
#endif
587+
}
588+
589+
static int bt_gatt_store_cf(uint8_t id, const bt_addr_le_t *peer);
590+
591+
static void set_all_change_unaware(void)
592+
{
593+
#if defined(CONFIG_BT_SETTINGS)
594+
/* Mark all bonded peers as change-unaware.
595+
* - Can be called when not in a connection with said peers
596+
* - Doesn't have any effect when no bonds are in memory. This is the
597+
* case when the device has just booted and `settings_load` hasn't yet
598+
* been called.
599+
* - Expensive to call, as it will write the new status to settings
600+
* right away.
601+
*/
602+
for (size_t i = 0; i < ARRAY_SIZE(cf_cfg); i++) {
603+
struct gatt_cf_cfg *cfg = &cf_cfg[i];
604+
605+
if (!bt_addr_le_eq(&cfg->peer, BT_ADDR_LE_ANY)) {
606+
set_change_aware_no_store(cfg, false);
607+
bt_gatt_store_cf(cfg->id, &cfg->peer);
608+
}
609+
}
610+
#endif /* CONFIG_BT_SETTINGS */
611+
}
612+
556613
static struct gatt_cf_cfg *find_cf_cfg(struct bt_conn *conn)
557614
{
558615
int i;
@@ -646,13 +703,7 @@ static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
646703

647704
bt_addr_le_copy(&cfg->peer, &conn->le.dst);
648705
cfg->id = conn->id;
649-
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
650-
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
706+
set_change_aware(cfg, true);
656707

657708
return len;
658709
}
@@ -814,6 +865,7 @@ static void db_hash_gen(bool store)
814865
LOG_HEXDUMP_DBG(db_hash.hash, sizeof(db_hash.hash), "Hash: ");
815866

816867
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
868+
set_all_change_unaware();
817869
db_hash_store();
818870
}
819871

@@ -852,7 +904,11 @@ static void db_hash_process(struct k_work *work)
852904
*/
853905
sc_indicate(0x0001, 0xffff);
854906

855-
/* Hash did not match, overwrite with current hash */
907+
/* Hash did not match, overwrite with current hash.
908+
* Also immediately set all peers (in settings) as
909+
* change-unaware.
910+
*/
911+
set_all_change_unaware();
856912
db_hash_store();
857913
return;
858914
}
@@ -937,6 +993,7 @@ static int bt_gatt_store_cf(uint8_t id, const bt_addr_le_t *peer)
937993
#if defined(CONFIG_BT_GATT_CACHING)
938994
struct gatt_cf_cfg *cfg;
939995
char key[BT_SETTINGS_KEY_MAX];
996+
char dst[CF_NUM_BYTES + CF_FLAGS_STORE_LEN];
940997
char *str;
941998
size_t len;
942999
int err;
@@ -958,6 +1015,18 @@ static int bt_gatt_store_cf(uint8_t id, const bt_addr_le_t *peer)
9581015
bt_settings_encode_key(key, sizeof(key), "cf",
9591016
peer, id_str);
9601017
}
1018+
1019+
/* add the CF data to a temp array */
1020+
memcpy(dst, str, len);
1021+
1022+
/* add the change-aware flag */
1023+
bool is_change_aware = atomic_test_bit(cfg->flags, CF_CHANGE_AWARE);
1024+
1025+
dst[len] = 0;
1026+
WRITE_BIT(dst[len], CF_CHANGE_AWARE, is_change_aware);
1027+
len += CF_FLAGS_STORE_LEN;
1028+
1029+
str = dst;
9611030
}
9621031

9631032
if (!cfg || !id) {
@@ -972,6 +1041,7 @@ static int bt_gatt_store_cf(uint8_t id, const bt_addr_le_t *peer)
9721041
}
9731042

9741043
LOG_DBG("Stored CF for %s (%s)", bt_addr_le_str(peer), key);
1044+
LOG_HEXDUMP_DBG(str, len, "Saved data");
9751045
#endif /* CONFIG_BT_GATT_CACHING */
9761046
return 0;
9771047

@@ -1179,8 +1249,7 @@ static void sc_indicate_rsp(struct bt_conn *conn,
11791249
if (bt_att_fixed_chan_only(conn)) {
11801250
cfg = find_cf_cfg(conn);
11811251
if (cfg && CF_ROBUST_CACHING(cfg)) {
1182-
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
1183-
LOG_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
1252+
set_change_aware(cfg, true);
11841253
}
11851254
}
11861255
#endif /* CONFIG_BT_GATT_CACHING */
@@ -1480,10 +1549,7 @@ static void db_changed(void)
14801549
}
14811550

14821551
atomic_clear_bit(cfg->flags, CF_DB_HASH_READ);
1483-
if (atomic_test_and_clear_bit(cfg->flags,
1484-
CF_CHANGE_AWARE)) {
1485-
LOG_DBG("%s change-unaware", bt_addr_le_str(&cfg->peer));
1486-
}
1552+
set_change_aware(cfg, false);
14871553
}
14881554
}
14891555
#endif
@@ -3026,8 +3092,7 @@ static void sc_restore_rsp(struct bt_conn *conn,
30263092
if (bt_att_fixed_chan_only(conn)) {
30273093
cfg = find_cf_cfg(conn);
30283094
if (cfg && CF_ROBUST_CACHING(cfg)) {
3029-
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
3030-
LOG_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
3095+
set_change_aware(cfg, true);
30313096
}
30323097
}
30333098
#endif /* CONFIG_BT_GATT_CACHING */
@@ -5622,6 +5687,15 @@ void bt_gatt_encrypt_change(struct bt_conn *conn)
56225687
#endif /* CONFIG_BT_GATT_AUTO_RESUBSCRIBE */
56235688

56245689
bt_gatt_foreach_attr(0x0001, 0xffff, update_ccc, &data);
5690+
5691+
#if defined(CONFIG_BT_SETTINGS)
5692+
if (!bt_gatt_change_aware(conn, false)) {
5693+
/* Send a Service Changed indication if the current peer is
5694+
* marked as change-unaware.
5695+
*/
5696+
sc_indicate(0x0001, 0xffff);
5697+
}
5698+
#endif /* CONFIG_BT_SETTINGS */
56255699
}
56265700

56275701
bool bt_gatt_change_aware(struct bt_conn *conn, bool req)
@@ -5654,8 +5728,7 @@ bool bt_gatt_change_aware(struct bt_conn *conn, bool req)
56545728
*/
56555729
if (atomic_test_and_clear_bit(cfg->flags, CF_DB_HASH_READ)) {
56565730
bt_att_clear_out_of_sync_sent(conn);
5657-
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
5658-
LOG_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
5731+
set_change_aware(cfg, true);
56595732
return true;
56605733
}
56615734

@@ -5670,8 +5743,7 @@ bool bt_gatt_change_aware(struct bt_conn *conn, bool req)
56705743
if (bt_att_fixed_chan_only(conn) && bt_att_out_of_sync_sent_on_fixed(conn)) {
56715744
atomic_clear_bit(cfg->flags, CF_DB_HASH_READ);
56725745
bt_att_clear_out_of_sync_sent(conn);
5673-
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
5674-
LOG_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
5746+
set_change_aware(cfg, true);
56755747
return true;
56765748
}
56775749

@@ -5921,13 +5993,34 @@ static int cf_set(const char *name, size_t len_rd, settings_read_cb read_cb,
59215993
}
59225994

59235995
if (len_rd) {
5924-
len = read_cb(cb_arg, cfg->data, sizeof(cfg->data));
5996+
char dst[CF_NUM_BYTES + CF_FLAGS_STORE_LEN];
5997+
5998+
len = read_cb(cb_arg, dst, sizeof(dst));
59255999
if (len < 0) {
59266000
LOG_ERR("Failed to decode value (err %zd)", len);
59276001
return len;
59286002
}
59296003

6004+
memcpy(cfg->data, dst, sizeof(cfg->data));
59306005
LOG_DBG("Read CF: len %zd", len);
6006+
6007+
if (len != sizeof(dst)) {
6008+
LOG_WRN("Change-aware status not found in settings, "
6009+
"defaulting peer status to change-unaware");
6010+
set_change_aware(cfg, false);
6011+
} else {
6012+
/* change-aware byte is present in NVS */
6013+
uint8_t change_aware = dst[sizeof(cfg->data)];
6014+
6015+
if (change_aware & ~BIT(CF_CHANGE_AWARE)) {
6016+
LOG_WRN("Read back bad change-aware value: 0x%x, "
6017+
"defaulting peer status to change-unaware",
6018+
change_aware);
6019+
set_change_aware(cfg, false);
6020+
} else {
6021+
set_change_aware_no_store(cfg, change_aware);
6022+
}
6023+
}
59316024
} else {
59326025
clear_cf_cfg(cfg);
59336026
}

0 commit comments

Comments
 (0)