tests: bsim: gatt: ccc_store: Reproduce the subscription lost issue

Zephyr by default renews the characteristic value subscription on every
reconnection. If the ACL is disconnected in the middle of this
procedure, all the subscriptions waiting to be renewed are removed, even
if the device was successfully subscribed already.

Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-12-05 11:26:41 +01:00 committed by Carles Cufí
commit 3b4ce7f8e9
5 changed files with 85 additions and 22 deletions

View file

@ -29,6 +29,7 @@
CREATE_FLAG(connected_flag); CREATE_FLAG(connected_flag);
CREATE_FLAG(disconnected_flag); CREATE_FLAG(disconnected_flag);
CREATE_FLAG(security_updated_flag); CREATE_FLAG(security_updated_flag);
CREATE_FLAG(notification_received_flag);
#define BT_UUID_DUMMY_SERVICE BT_UUID_DECLARE_128(DUMMY_SERVICE_TYPE) #define BT_UUID_DUMMY_SERVICE BT_UUID_DECLARE_128(DUMMY_SERVICE_TYPE)
#define BT_UUID_DUMMY_SERVICE_NOTIFY BT_UUID_DECLARE_128(DUMMY_SERVICE_NOTIFY_TYPE) #define BT_UUID_DUMMY_SERVICE_NOTIFY BT_UUID_DECLARE_128(DUMMY_SERVICE_NOTIFY_TYPE)
@ -37,11 +38,28 @@ static struct bt_conn *default_conn;
static struct bt_conn_cb central_cb; static struct bt_conn_cb central_cb;
CREATE_FLAG(gatt_subscribe_flag); CREATE_FLAG(gatt_subscribed_flag);
static uint8_t notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, static uint8_t notify_cb(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length) const void *data, uint16_t length)
{ {
uint8_t value;
if (conn == NULL || data == NULL) {
/* Peer unpaired or subscription was removed */
UNSET_FLAG(gatt_subscribed_flag);
return BT_GATT_ITER_STOP;
}
__ASSERT_NO_MSG(length == sizeof(value));
value = *(uint8_t *)data;
LOG_DBG("#%d notification received", value);
SET_FLAG(notification_received_flag);
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
} }
@ -51,7 +69,7 @@ static void subscribe_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_subsc
return; return;
} }
SET_FLAG(gatt_subscribe_flag); SET_FLAG(gatt_subscribed_flag);
} }
static struct bt_gatt_subscribe_params subscribe_params; static struct bt_gatt_subscribe_params subscribe_params;
@ -60,7 +78,7 @@ static void ccc_subscribe(void)
{ {
int err; int err;
UNSET_FLAG(gatt_subscribe_flag); UNSET_FLAG(gatt_subscribed_flag);
subscribe_params.notify = notify_cb; subscribe_params.notify = notify_cb;
subscribe_params.subscribe = subscribe_cb; subscribe_params.subscribe = subscribe_cb;
@ -73,7 +91,7 @@ static void ccc_subscribe(void)
FAIL("Failed to subscribe (att err %d)", err); FAIL("Failed to subscribe (att err %d)", err);
} }
WAIT_FOR_FLAG(gatt_subscribe_flag); WAIT_FOR_FLAG(gatt_subscribed_flag);
} }
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
@ -200,6 +218,9 @@ static void connect_pair_subscribe(void)
backchannel_sync_send(SERVER_CHAN, SERVER_ID); backchannel_sync_send(SERVER_CHAN, SERVER_ID);
/* wait for server to check that the subscribtion is well registered */ /* wait for server to check that the subscribtion is well registered */
backchannel_sync_wait(SERVER_CHAN, SERVER_ID); backchannel_sync_wait(SERVER_CHAN, SERVER_ID);
WAIT_FOR_FLAG(notification_received_flag);
UNSET_FLAG(notification_received_flag);
} }
static void connect_restore_sec(void) static void connect_restore_sec(void)
@ -219,10 +240,18 @@ static void connect_restore_sec(void)
WAIT_FOR_FLAG(security_updated_flag); WAIT_FOR_FLAG(security_updated_flag);
UNSET_FLAG(security_updated_flag); UNSET_FLAG(security_updated_flag);
/* check local subscription state */
if (GET_FLAG(gatt_subscribed_flag) == false) {
FAIL("Not subscribed\n");
}
/* notify the end of security update to server */ /* notify the end of security update to server */
backchannel_sync_send(SERVER_CHAN, SERVER_ID); backchannel_sync_send(SERVER_CHAN, SERVER_ID);
/* wait for server to check that the subscribtion has been restored */ /* wait for server to check that the subscribtion has been restored */
backchannel_sync_wait(SERVER_CHAN, SERVER_ID); backchannel_sync_wait(SERVER_CHAN, SERVER_ID);
WAIT_FOR_FLAG(notification_received_flag);
UNSET_FLAG(notification_received_flag);
} }
/* Util functions */ /* Util functions */
@ -256,7 +285,7 @@ static void set_public_addr(void)
/* Main functions */ /* Main functions */
void run_central(void) void run_central(int times)
{ {
int err; int err;
@ -289,8 +318,10 @@ void run_central(void)
connect_pair_subscribe(); connect_pair_subscribe();
disconnect(); disconnect();
connect_restore_sec(); for (int i = 0; i < times; i++) {
disconnect(); connect_restore_sec();
disconnect();
}
PASS("Central test passed\n"); PASS("Central test passed\n");
} }

View file

@ -30,17 +30,19 @@ extern enum bst_result_t bst_result;
#define WAIT_TIME_S 60 #define WAIT_TIME_S 60
#define WAIT_TIME (WAIT_TIME_S * 1e6) #define WAIT_TIME (WAIT_TIME_S * 1e6)
extern void run_peripheral(void); static int n_times;
extern void run_central(void);
extern void run_peripheral(int times);
extern void run_central(int times);
static void central_main(void) static void central_main(void)
{ {
run_central(); run_central(n_times);
} }
static void peripheral_main(void) static void peripheral_main(void)
{ {
run_peripheral(); run_peripheral(n_times);
} }
void test_tick(bs_time_t HW_device_time) void test_tick(bs_time_t HW_device_time)
@ -52,6 +54,13 @@ void test_tick(bs_time_t HW_device_time)
} }
} }
static void test_args(int argc, char **argv)
{
__ASSERT(argc == 1, "Please specify only 1 test argument\n");
n_times = atol(argv[0]);
}
static void test_ccc_store_init(void) static void test_ccc_store_init(void)
{ {
bst_ticker_set_next_tick_absolute(WAIT_TIME); bst_ticker_set_next_tick_absolute(WAIT_TIME);
@ -64,6 +73,7 @@ static const struct bst_test_instance test_def[] = {
.test_post_init_f = test_ccc_store_init, .test_post_init_f = test_ccc_store_init,
.test_tick_f = test_tick, .test_tick_f = test_tick,
.test_main_f = central_main, .test_main_f = central_main,
.test_args_f = test_args,
}, },
{ {
.test_id = "peripheral", .test_id = "peripheral",
@ -71,6 +81,7 @@ static const struct bst_test_instance test_def[] = {
.test_post_init_f = test_ccc_store_init, .test_post_init_f = test_ccc_store_init,
.test_tick_f = test_tick, .test_tick_f = test_tick,
.test_main_f = peripheral_main, .test_main_f = peripheral_main,
.test_args_f = test_args,
}, },
BSTEST_END_MARKER}; BSTEST_END_MARKER};

View file

@ -161,6 +161,21 @@ static bool is_peer_subscribed(struct bt_conn *conn)
/* Test steps */ /* Test steps */
static void send_value_notification(void)
{
const struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0,
&notify_characteristic_uuid.uuid);
static uint8_t value;
int err;
err = bt_gatt_notify(default_conn, attr, &value, sizeof(value));
if (err != 0) {
FAIL("Failed to send notification (err %d)\n", err);
}
value++;
}
static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv) static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
{ {
start_adv(adv); start_adv(adv);
@ -182,6 +197,8 @@ static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
/* confirm to client that the subscribtion has been well registered */ /* confirm to client that the subscribtion has been well registered */
backchannel_sync_send(CLIENT_CHAN, CLIENT_ID); backchannel_sync_send(CLIENT_CHAN, CLIENT_ID);
send_value_notification();
} }
static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv) static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
@ -205,6 +222,8 @@ static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
/* confirm to good client that the subscribtion has been well restored */ /* confirm to good client that the subscribtion has been well restored */
backchannel_sync_send(CLIENT_CHAN, CLIENT_ID); backchannel_sync_send(CLIENT_CHAN, CLIENT_ID);
send_value_notification();
} }
/* Util functions */ /* Util functions */
@ -251,7 +270,7 @@ static void check_ccc_handle(void)
/* Main function */ /* Main function */
void run_peripheral(void) void run_peripheral(int times)
{ {
int err; int err;
struct bt_le_ext_adv *adv = NULL; struct bt_le_ext_adv *adv = NULL;
@ -288,8 +307,10 @@ void run_peripheral(void)
connect_pair_check_subscribtion(adv); connect_pair_check_subscribtion(adv);
WAIT_FOR_FLAG(disconnected_flag); WAIT_FOR_FLAG(disconnected_flag);
connect_restore_sec_check_subscribtion(adv); for (int i = 0; i < times; i++) {
WAIT_FOR_FLAG(disconnected_flag); connect_restore_sec_check_subscribtion(adv);
WAIT_FOR_FLAG(disconnected_flag);
}
PASS("Peripheral test passed\n"); PASS("Peripheral test passed\n");
} }

View file

@ -15,13 +15,13 @@ cd ${BSIM_OUT_PATH}/bin
if [ "${1}" != 'debug0' ]; then if [ "${1}" != 'debug0' ]; then
Execute "./${test_exe}" \ Execute "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \ -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \
-flash="${simulation_id}_client.log.bin" -flash_rm -flash="${simulation_id}_client.log.bin" -flash_rm -argstest 10
fi fi
if [ "${1}" != 'debug1' ]; then if [ "${1}" != 'debug1' ]; then
Execute "./${test_exe}" \ Execute "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \ -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \
-flash="${simulation_id}_server.log.bin" -flash_rm -flash="${simulation_id}_server.log.bin" -flash_rm -argstest 10
fi fi
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
@ -30,13 +30,13 @@ Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
if [ "${1}" == 'debug0' ]; then if [ "${1}" == 'debug0' ]; then
gdb --args "./${test_exe}" \ gdb --args "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \ -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \
-flash="${simulation_id}_client.log.bin" -flash_rm -flash="${simulation_id}_client.log.bin" -flash_rm -argstest 10
fi fi
if [ "${1}" == 'debug1' ]; then if [ "${1}" == 'debug1' ]; then
gdb --args "./${test_exe}" \ gdb --args "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \ -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \
-flash="${simulation_id}_server.log.bin" -flash_rm -flash="${simulation_id}_server.log.bin" -flash_rm -argstest 10
fi fi
wait_for_background_jobs wait_for_background_jobs

View file

@ -15,13 +15,13 @@ cd ${BSIM_OUT_PATH}/bin
if [ "${1}" != 'debug0' ]; then if [ "${1}" != 'debug0' ]; then
Execute "./${test_exe}" \ Execute "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \ -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \
-flash="${simulation_id}_client.log.bin" -flash_rm -flash="${simulation_id}_client.log.bin" -flash_rm -argstest 10
fi fi
if [ "${1}" != 'debug1' ]; then if [ "${1}" != 'debug1' ]; then
Execute "./${test_exe}" \ Execute "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \ -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \
-flash="${simulation_id}_server.log.bin" -flash_rm -flash="${simulation_id}_server.log.bin" -flash_rm -argstest 10
fi fi
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
@ -30,13 +30,13 @@ Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
if [ "${1}" == 'debug0' ]; then if [ "${1}" == 'debug0' ]; then
gdb --args "./${test_exe}" \ gdb --args "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \ -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central \
-flash="${simulation_id}_client.log.bin" -flash_rm -flash="${simulation_id}_client.log.bin" -flash_rm -argstest 10
fi fi
if [ "${1}" == 'debug1' ]; then if [ "${1}" == 'debug1' ]; then
gdb --args "./${test_exe}" \ gdb --args "./${test_exe}" \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \ -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral \
-flash="${simulation_id}_server.log.bin" -flash_rm -flash="${simulation_id}_server.log.bin" -flash_rm -argstest 10
fi fi
wait_for_background_jobs wait_for_background_jobs