@@ -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+ */
503506enum {
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
523527struct 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+
556613static 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
56275701bool 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