| /* |
| * Copyright (c) 2023 Demant A/S |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/bluetooth/audio/pacs.h> |
| |
| #include "common.h" |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(pacs_notify_server_test, LOG_LEVEL_DBG); |
| |
| extern enum bst_result_t bst_result; |
| |
| static struct bt_audio_codec_cap lc3_codec_1 = |
| BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_16KHZ | BT_AUDIO_CODEC_CAP_FREQ_24KHZ, |
| BT_AUDIO_CODEC_CAP_DURATION_10, |
| BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u, |
| BT_AUDIO_CONTEXT_TYPE_ANY); |
| static struct bt_audio_codec_cap lc3_codec_2 = |
| BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_16KHZ, |
| BT_AUDIO_CODEC_CAP_DURATION_10, |
| BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u, |
| BT_AUDIO_CONTEXT_TYPE_ANY); |
| static struct bt_pacs_cap caps_1 = { |
| .codec_cap = &lc3_codec_1, |
| }; |
| static struct bt_pacs_cap caps_2 = { |
| .codec_cap = &lc3_codec_2, |
| }; |
| |
| static bool is_peer_subscribed(struct bt_conn *conn) |
| { |
| struct bt_gatt_attr *attr; |
| uint8_t nbr_subscribed = 0; |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_SNK attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK_LOC); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_SNK_LOC attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_SRC attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC_LOC); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_SRC_LOC attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_AVAILABLE_CONTEXT); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_AVAILABLE_CONTEXT attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SUPPORTED_CONTEXT); |
| if (!attr) { |
| LOG_DBG("No BT_UUID_PACS_SUPPORTED_CONTEXT attribute found"); |
| } |
| if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) { |
| nbr_subscribed++; |
| } |
| |
| if (nbr_subscribed != 6) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void trigger_notifications(void) |
| { |
| static enum bt_audio_context available = BT_AUDIO_CONTEXT_TYPE_ANY; |
| static enum bt_audio_context supported = BT_AUDIO_CONTEXT_TYPE_ANY; |
| static int i; |
| int err; |
| struct bt_pacs_cap *caps; |
| enum bt_audio_location snk_loc; |
| enum bt_audio_location src_loc; |
| |
| LOG_DBG("Triggering Notifications"); |
| |
| if (i) { |
| caps = &caps_1; |
| snk_loc = BT_AUDIO_LOCATION_FRONT_LEFT; |
| src_loc = BT_AUDIO_LOCATION_FRONT_RIGHT; |
| i = 0; |
| } else { |
| caps = &caps_2; |
| snk_loc = BT_AUDIO_LOCATION_FRONT_RIGHT; |
| src_loc = BT_AUDIO_LOCATION_FRONT_LEFT; |
| i++; |
| } |
| |
| LOG_DBG("Changing Sink PACs"); |
| bt_pacs_cap_register(BT_AUDIO_DIR_SINK, caps); |
| bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, caps); |
| |
| LOG_DBG("Changing Sink Location"); |
| err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, snk_loc); |
| if (err != 0) { |
| LOG_DBG("Failed to set device sink location"); |
| } |
| |
| LOG_DBG("Changing Source Location"); |
| err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, src_loc); |
| if (err != 0) { |
| LOG_DBG("Failed to set device source location"); |
| } |
| |
| LOG_DBG("Changing Supported Contexts Location"); |
| supported = supported ^ BT_AUDIO_CONTEXT_TYPE_MEDIA; |
| bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, supported); |
| |
| LOG_DBG("Changing Available Contexts"); |
| available = available ^ BT_AUDIO_CONTEXT_TYPE_MEDIA; |
| bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, available); |
| } |
| |
| static void test_main(void) |
| { |
| int err; |
| enum bt_audio_context available, available_for_conn; |
| const struct bt_data ad[] = { |
| BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), |
| }; |
| |
| LOG_DBG("Enabling Bluetooth"); |
| err = bt_enable(NULL); |
| if (err != 0) { |
| FAIL("Bluetooth enable failed (err %d)", err); |
| return; |
| } |
| |
| bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY); |
| bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY); |
| bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY); |
| bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY); |
| |
| LOG_DBG("Registereding PACS"); |
| bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &caps_1); |
| bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &caps_1); |
| |
| err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT); |
| if (err != 0) { |
| LOG_DBG("Failed to set device sink location"); |
| return; |
| } |
| |
| err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, BT_AUDIO_LOCATION_FRONT_RIGHT); |
| if (err != 0) { |
| LOG_DBG("Failed to set device source location"); |
| return; |
| } |
| |
| LOG_DBG("Start Advertising"); |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (err != 0) { |
| FAIL("Advertising failed to start (err %d)", err); |
| return; |
| } |
| |
| LOG_DBG("Waiting to be connected"); |
| WAIT_FOR_FLAG(flag_connected); |
| LOG_DBG("Connected"); |
| LOG_DBG("Waiting to be subscribed"); |
| |
| while (!is_peer_subscribed(default_conn)) { |
| (void)k_sleep(K_MSEC(10)); |
| } |
| LOG_DBG("Subscribed"); |
| |
| LOG_INF("Trigger changes while device is connected"); |
| trigger_notifications(); |
| |
| /* Now wait for client to disconnect, then stop adv so it does not reconnect */ |
| LOG_DBG("Wait for client disconnect"); |
| WAIT_FOR_UNSET_FLAG(flag_connected); |
| LOG_DBG("Client disconnected"); |
| |
| err = bt_le_adv_stop(); |
| if (err != 0) { |
| FAIL("Advertising failed to stop (err %d)", err); |
| return; |
| } |
| |
| LOG_INF("Trigger changes while device is disconnected"); |
| trigger_notifications(); |
| |
| LOG_DBG("Start Advertising"); |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (err != 0) { |
| FAIL("Advertising failed to start (err %d)", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(flag_connected); |
| WAIT_FOR_UNSET_FLAG(flag_connected); |
| LOG_DBG("Client disconnected"); |
| |
| err = bt_le_adv_stop(); |
| if (err != 0) { |
| FAIL("Advertising failed to stop (err %d)", err); |
| return; |
| } |
| |
| LOG_DBG("Start Advertising"); |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (err != 0) { |
| FAIL("Advertising failed to start (err %d)", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(flag_connected); |
| LOG_DBG("Connected"); |
| |
| available = bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK); |
| __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) == |
| available); |
| |
| available_for_conn = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED; |
| |
| LOG_INF("Override available contexts"); |
| err = bt_pacs_conn_set_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK, |
| &available_for_conn); |
| __ASSERT_NO_MSG(err == 0); |
| |
| __ASSERT_NO_MSG(bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK) == available); |
| __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) == |
| available_for_conn); |
| |
| WAIT_FOR_UNSET_FLAG(flag_connected); |
| LOG_DBG("Client disconnected"); |
| |
| err = bt_le_adv_stop(); |
| if (err != 0) { |
| FAIL("Advertising failed to stop (err %d)", err); |
| return; |
| } |
| |
| LOG_DBG("Start Advertising"); |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); |
| if (err != 0) { |
| FAIL("Advertising failed to start (err %d)", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(flag_connected); |
| LOG_DBG("Connected"); |
| |
| __ASSERT_NO_MSG(bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK) == available); |
| __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) == |
| available); |
| |
| WAIT_FOR_UNSET_FLAG(flag_connected); |
| |
| PASS("PACS Notify Server passed\n"); |
| } |
| |
| static const struct bst_test_instance test_pacs_notify_server[] = { |
| { |
| .test_id = "pacs_notify_server", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_main, |
| }, |
| BSTEST_END_MARKER, |
| }; |
| |
| struct bst_test_list *test_pacs_notify_server_install(struct bst_test_list *tests) |
| { |
| return bst_add_tests(tests, test_pacs_notify_server); |
| } |