/** * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 * */ #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(test_central, LOG_LEVEL_DBG); #include "bs_bt_utils.h" DEFINE_FLAG(flag_discovered); DEFINE_FLAG(flag_subscribed); DEFINE_FLAG(flag_indicated); enum GATT_HANDLES { SC, CCC, NUM_HANDLES, }; static uint16_t gatt_handles[NUM_HANDLES] = {0}; static struct bt_gatt_subscribe_params subscribe_params; static void sc_subscribed(struct bt_conn *conn, uint8_t err, struct bt_gatt_subscribe_params *params) { LOG_DBG("subscribed"); SET_FLAG(flag_subscribed); } static uint8_t sc_indicated(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data, uint16_t length) { LOG_DBG("indication received"); SET_FLAG(flag_indicated); return BT_GATT_ITER_CONTINUE; } static void subscribe(void) { int err; subscribe_params.ccc_handle = gatt_handles[CCC]; subscribe_params.value_handle = gatt_handles[SC]; subscribe_params.value = BT_GATT_CCC_INDICATE; subscribe_params.subscribe = sc_subscribed; subscribe_params.notify = sc_indicated; err = bt_gatt_subscribe(get_g_conn(), &subscribe_params); BSIM_ASSERT(!err, "bt_gatt_subscribe failed (%d)\n", err); WAIT_FOR_FLAG(flag_subscribed); } static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { if (attr == NULL) { for (size_t i = 0U; i < ARRAY_SIZE(gatt_handles); i++) { LOG_DBG("handle[%d] = 0x%x", i, gatt_handles[i]); BSIM_ASSERT(gatt_handles[i] != 0, "did not find all handles\n"); } (void)memset(params, 0, sizeof(*params)); SET_FLAG(flag_discovered); return BT_GATT_ITER_STOP; } if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) { const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; static const struct bt_uuid_16 ccc_uuid = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL); if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) { int err; LOG_DBG("found sc"); gatt_handles[SC] = chrc->value_handle; params->uuid = &ccc_uuid.uuid; params->start_handle = attr->handle + 2; params->type = BT_GATT_DISCOVER_DESCRIPTOR; err = bt_gatt_discover(conn, params); BSIM_ASSERT(!err, "bt_gatt_discover failed (%d)\n", err); return BT_GATT_ITER_STOP; } } else if (params->type == BT_GATT_DISCOVER_DESCRIPTOR && bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC) == 0) { LOG_DBG("found ccc"); gatt_handles[CCC] = attr->handle; SET_FLAG(flag_discovered); return BT_GATT_ITER_STOP; } return BT_GATT_ITER_CONTINUE; } static void gatt_discover(void) { int err; static struct bt_gatt_discover_params discover_params; discover_params.uuid = NULL; discover_params.func = discover_func; discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; err = bt_gatt_discover(get_g_conn(), &discover_params); BSIM_ASSERT(!err, "bt_gatt_discover failed (%d)\n", err); WAIT_FOR_FLAG(flag_discovered); LOG_DBG("sc handle: %d", gatt_handles[SC]); LOG_DBG("ccc handle: %d", gatt_handles[CCC]); } void central(void) { /* * test goal: check that service changed indication is sent on * reconnection when the server's GATT database has been updated since * last connection * * the central will connect, bond with the peripheral and then * disconnect after doing that, the central will try to connect again, * this time it will not elevate the security * * to pass the test, the central will wait to receive the service * changed indication */ int err; struct bt_conn_auth_info_cb bt_conn_auth_info_cb = { .pairing_failed = pairing_failed, .pairing_complete = pairing_complete, }; err = bt_enable(NULL); BSIM_ASSERT(!err, "bt_enable failed (%d)\n", err); err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb); BSIM_ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n"); err = settings_load(); BSIM_ASSERT(!err, "settings_load failed (%d)\n", err); scan_connect_to_first_result(); wait_connected(); set_security(BT_SECURITY_L2); TAKE_FLAG(flag_pairing_complete); TAKE_FLAG(flag_bonded); /* subscribe to the service changed indication */ gatt_discover(); subscribe(); disconnect(); wait_disconnected(); clear_g_conn(); scan_connect_to_first_result(); wait_connected(); /* wait for service change indication */ WAIT_FOR_FLAG(flag_indicated); PASS("PASS\n"); }