1 /*
2 * Copyright (c) 2023 Demant A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/audio/pacs.h>
8
9 #include "common.h"
10
11 extern enum bst_result_t bst_result;
12
13 static struct bt_audio_codec_cap lc3_codec_1 =
14 BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_LC3_FREQ_16KHZ | BT_AUDIO_CODEC_LC3_FREQ_24KHZ,
15 BT_AUDIO_CODEC_LC3_DURATION_10,
16 BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
17 BT_AUDIO_CONTEXT_TYPE_ANY);
18 static struct bt_audio_codec_cap lc3_codec_2 =
19 BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_LC3_FREQ_16KHZ,
20 BT_AUDIO_CODEC_LC3_DURATION_10,
21 BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
22 BT_AUDIO_CONTEXT_TYPE_ANY);
23 static struct bt_pacs_cap caps_1 = {
24 .codec_cap = &lc3_codec_1,
25 };
26 static struct bt_pacs_cap caps_2 = {
27 .codec_cap = &lc3_codec_2,
28 };
29
is_peer_subscribed(struct bt_conn * conn)30 static bool is_peer_subscribed(struct bt_conn *conn)
31 {
32 struct bt_gatt_attr *attr;
33 uint8_t nbr_subscribed = 0;
34
35 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK);
36 if (!attr) {
37 printk("No BT_UUID_PACS_SNK attribute found\n");
38 }
39 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
40 nbr_subscribed++;
41 }
42
43 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK_LOC);
44 if (!attr) {
45 printk("No BT_UUID_PACS_SNK_LOC attribute found\n");
46 }
47 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
48 nbr_subscribed++;
49 }
50
51 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC);
52 if (!attr) {
53 printk("No BT_UUID_PACS_SRC attribute found\n");
54 }
55 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
56 nbr_subscribed++;
57 }
58
59 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC_LOC);
60 if (!attr) {
61 printk("No BT_UUID_PACS_SRC_LOC attribute found\n");
62 }
63 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
64 nbr_subscribed++;
65 }
66
67 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_AVAILABLE_CONTEXT);
68 if (!attr) {
69 printk("No BT_UUID_PACS_AVAILABLE_CONTEXT attribute found\n");
70 }
71 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
72 nbr_subscribed++;
73 }
74
75 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SUPPORTED_CONTEXT);
76 if (!attr) {
77 printk("No BT_UUID_PACS_SUPPORTED_CONTEXT attribute found\n");
78 }
79 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
80 nbr_subscribed++;
81 }
82
83 if (nbr_subscribed != 6) {
84 return false;
85 }
86
87 return true;
88 }
89
trigger_notifications(void)90 static void trigger_notifications(void)
91 {
92 static enum bt_audio_context available = BT_AUDIO_CONTEXT_TYPE_ANY;
93 static enum bt_audio_context supported = BT_AUDIO_CONTEXT_TYPE_ANY;
94 static int i;
95 int err;
96 struct bt_pacs_cap *caps;
97 enum bt_audio_location snk_loc;
98 enum bt_audio_location src_loc;
99
100 printk("Triggering Notifications\n");
101
102 if (i) {
103 caps = &caps_1;
104 snk_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
105 src_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
106 i = 0;
107 } else {
108 caps = &caps_2;
109 snk_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
110 src_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
111 i++;
112 }
113
114 printk("Changing Sink PACs\n");
115 bt_pacs_cap_register(BT_AUDIO_DIR_SINK, caps);
116 bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, caps);
117
118 printk("Changing Sink Location\n");
119 err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, snk_loc);
120 if (err != 0) {
121 printk("Failed to set device sink location\n");
122 }
123
124 printk("Changing Source Location\n");
125 err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, src_loc);
126 if (err != 0) {
127 printk("Failed to set device source location\n");
128 }
129
130 printk("Changing Supported Contexts Location\n");
131 supported = supported ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
132 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, supported);
133
134 printk("Changing Available Contexts\n");
135 available = available ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
136 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, available);
137 }
138
test_main(void)139 static void test_main(void)
140 {
141 int err;
142 const struct bt_data ad[] = {
143 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
144 };
145
146 printk("Enabling Bluetooth\n");
147 err = bt_enable(NULL);
148 if (err != 0) {
149 FAIL("Bluetooth enable failed (err %d)\n", err);
150 return;
151 }
152
153 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
154 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
155 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
156 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
157
158 printk("Registereding PACS\n");
159 bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &caps_1);
160 bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &caps_1);
161
162 err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT);
163 if (err != 0) {
164 printk("Failed to set device sink location\n");
165 return;
166 }
167
168 err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, BT_AUDIO_LOCATION_FRONT_RIGHT);
169 if (err != 0) {
170 printk("Failed to set device source location\n");
171 return;
172 }
173
174 printk("Start Advertising\n");
175 err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
176 if (err != 0) {
177 FAIL("Advertising failed to start (err %d)\n", err);
178 return;
179 }
180
181 printk("Waiting to be connected\n");
182 WAIT_FOR_FLAG(flag_connected);
183 printk("Connected\n");
184 printk("Waiting to be subscribed\n");
185
186 while (!is_peer_subscribed(default_conn)) {
187 (void)k_sleep(K_MSEC(10));
188 }
189 printk("Subscribed\n");
190
191 trigger_notifications();
192
193 /* Now wait for client to disconnect, then stop adv so it does not reconnect */
194 printk("Wait for client disconnect\n");
195 WAIT_FOR_UNSET_FLAG(flag_connected);
196 printk("Client disconnected\n");
197
198 err = bt_le_adv_stop();
199 if (err != 0) {
200 FAIL("Advertising failed to stop (err %d)\n", err);
201 return;
202 }
203
204 /* Trigger changes while device is disconnected */
205 trigger_notifications();
206
207 printk("Start Advertising\n");
208 err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
209 if (err != 0) {
210 FAIL("Advertising failed to start (err %d)\n", err);
211 return;
212 }
213
214 WAIT_FOR_FLAG(flag_connected);
215 WAIT_FOR_UNSET_FLAG(flag_connected);
216
217 PASS("PACS Notify Server passed\n");
218 }
219
220 static const struct bst_test_instance test_pacs_notify_server[] = {
221 {
222 .test_id = "pacs_notify_server",
223 .test_post_init_f = test_init,
224 .test_tick_f = test_tick,
225 .test_main_f = test_main,
226 },
227 BSTEST_END_MARKER,
228 };
229
test_pacs_notify_server_install(struct bst_test_list * tests)230 struct bst_test_list *test_pacs_notify_server_install(struct bst_test_list *tests)
231 {
232 return bst_add_tests(tests, test_pacs_notify_server);
233 }
234