1 /*
2 * Copyright (c) 2023 Demant A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdbool.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <zephyr/bluetooth/audio/audio.h>
12 #include <zephyr/bluetooth/audio/lc3.h>
13 #include <zephyr/bluetooth/audio/pacs.h>
14 #include <zephyr/bluetooth/bluetooth.h>
15 #include <zephyr/bluetooth/gap.h>
16 #include <zephyr/bluetooth/gatt.h>
17 #include <zephyr/bluetooth/uuid.h>
18 #include <zephyr/kernel.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/logging/log_core.h>
21 #include <zephyr/sys/__assert.h>
22 #include <zephyr/sys/util.h>
23
24 #include "bstests.h"
25 #include "common.h"
26
27 LOG_MODULE_REGISTER(pacs_notify_server_test, LOG_LEVEL_DBG);
28
29 extern enum bst_result_t bst_result;
30
31 static struct bt_audio_codec_cap lc3_codec_1 =
32 BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_16KHZ | BT_AUDIO_CODEC_CAP_FREQ_24KHZ,
33 BT_AUDIO_CODEC_CAP_DURATION_10,
34 BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
35 BT_AUDIO_CONTEXT_TYPE_ANY);
36 static struct bt_audio_codec_cap lc3_codec_2 =
37 BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_16KHZ,
38 BT_AUDIO_CODEC_CAP_DURATION_10,
39 BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
40 BT_AUDIO_CONTEXT_TYPE_ANY);
41 static struct bt_pacs_cap caps_1 = {
42 .codec_cap = &lc3_codec_1,
43 };
44 static struct bt_pacs_cap caps_2 = {
45 .codec_cap = &lc3_codec_2,
46 };
47
is_peer_subscribed(struct bt_conn * conn)48 static bool is_peer_subscribed(struct bt_conn *conn)
49 {
50 struct bt_gatt_attr *attr;
51 uint8_t nbr_subscribed = 0;
52
53 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK);
54 if (!attr) {
55 LOG_DBG("No BT_UUID_PACS_SNK attribute found");
56 }
57 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
58 nbr_subscribed++;
59 }
60
61 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK_LOC);
62 if (!attr) {
63 LOG_DBG("No BT_UUID_PACS_SNK_LOC attribute found");
64 }
65 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
66 nbr_subscribed++;
67 }
68
69 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC);
70 if (!attr) {
71 LOG_DBG("No BT_UUID_PACS_SRC attribute found");
72 }
73 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
74 nbr_subscribed++;
75 }
76
77 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC_LOC);
78 if (!attr) {
79 LOG_DBG("No BT_UUID_PACS_SRC_LOC attribute found");
80 }
81 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
82 nbr_subscribed++;
83 }
84
85 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_AVAILABLE_CONTEXT);
86 if (!attr) {
87 LOG_DBG("No BT_UUID_PACS_AVAILABLE_CONTEXT attribute found");
88 }
89 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
90 nbr_subscribed++;
91 }
92
93 attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SUPPORTED_CONTEXT);
94 if (!attr) {
95 LOG_DBG("No BT_UUID_PACS_SUPPORTED_CONTEXT attribute found");
96 }
97 if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
98 nbr_subscribed++;
99 }
100
101 if (nbr_subscribed != 6) {
102 return false;
103 }
104
105 return true;
106 }
107
trigger_notifications(void)108 static void trigger_notifications(void)
109 {
110 static enum bt_audio_context available = BT_AUDIO_CONTEXT_TYPE_ANY;
111 static enum bt_audio_context supported = BT_AUDIO_CONTEXT_TYPE_ANY;
112 static int i;
113 int err;
114 struct bt_pacs_cap *caps;
115 enum bt_audio_location snk_loc;
116 enum bt_audio_location src_loc;
117
118 LOG_DBG("Triggering Notifications");
119
120 if (i) {
121 caps = &caps_1;
122 snk_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
123 src_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
124 i = 0;
125 } else {
126 caps = &caps_2;
127 snk_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
128 src_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
129 i++;
130 }
131
132 LOG_DBG("Changing Sink PACs");
133 bt_pacs_cap_register(BT_AUDIO_DIR_SINK, caps);
134 bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, caps);
135
136 LOG_DBG("Changing Sink Location");
137 err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, snk_loc);
138 if (err != 0) {
139 LOG_DBG("Failed to set device sink location");
140 }
141
142 LOG_DBG("Changing Source Location");
143 err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, src_loc);
144 if (err != 0) {
145 LOG_DBG("Failed to set device source location");
146 }
147
148 LOG_DBG("Changing Supported Contexts Location");
149 supported = supported ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
150 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, supported);
151
152 LOG_DBG("Changing Available Contexts");
153 available = available ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
154 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, available);
155 }
156
test_main(void)157 static void test_main(void)
158 {
159 int err;
160 enum bt_audio_context available, available_for_conn;
161 struct bt_le_ext_adv *ext_adv;
162 const struct bt_pacs_register_param pacs_param = {
163 .snk_pac = true,
164 .snk_loc = true,
165 .src_pac = true,
166 .src_loc = true,
167 };
168
169 LOG_DBG("Enabling Bluetooth");
170 err = bt_enable(NULL);
171 if (err != 0) {
172 FAIL("Bluetooth enable failed (err %d)", err);
173 return;
174 }
175
176 err = bt_pacs_register(&pacs_param);
177 if (err) {
178 FAIL("Could not register PACS (err %d)\n", err);
179 return;
180 }
181
182 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
183 bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
184 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
185 bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
186
187 LOG_DBG("Registereding PACS");
188 bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &caps_1);
189 bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &caps_1);
190
191 err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT);
192 if (err != 0) {
193 LOG_DBG("Failed to set device sink location");
194 return;
195 }
196
197 err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, BT_AUDIO_LOCATION_FRONT_RIGHT);
198 if (err != 0) {
199 LOG_DBG("Failed to set device source location");
200 return;
201 }
202
203 LOG_DBG("Start Advertising");
204 setup_connectable_adv(&ext_adv);
205
206 LOG_DBG("Waiting to be connected");
207 WAIT_FOR_FLAG(flag_connected);
208 LOG_DBG("Connected");
209 LOG_DBG("Waiting to be subscribed");
210
211 while (!is_peer_subscribed(default_conn)) {
212 (void)k_sleep(K_MSEC(10));
213 }
214 LOG_DBG("Subscribed");
215
216 LOG_INF("Trigger changes while device is connected");
217 trigger_notifications();
218
219 /* Now wait for client to disconnect */
220 LOG_DBG("Wait for client disconnect");
221 WAIT_FOR_UNSET_FLAG(flag_connected);
222 LOG_DBG("Client disconnected");
223
224 LOG_INF("Trigger changes while device is disconnected");
225 trigger_notifications();
226
227 LOG_DBG("Start Advertising");
228 err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
229 if (err != 0) {
230 FAIL("Failed to start advertising set (err %d)\n", err);
231
232 bt_le_ext_adv_delete(ext_adv);
233
234 return;
235 }
236
237 WAIT_FOR_FLAG(flag_connected);
238 WAIT_FOR_UNSET_FLAG(flag_connected);
239 LOG_DBG("Client disconnected");
240
241 LOG_DBG("Start Advertising");
242 err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
243 if (err != 0) {
244 FAIL("Failed to start advertising set (err %d)\n", err);
245
246 bt_le_ext_adv_delete(ext_adv);
247
248 return;
249 }
250
251 WAIT_FOR_FLAG(flag_connected);
252 LOG_DBG("Connected");
253
254 available = bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK);
255 __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) ==
256 available);
257
258 available_for_conn = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
259
260 LOG_INF("Override available contexts");
261 err = bt_pacs_conn_set_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK,
262 &available_for_conn);
263 __ASSERT_NO_MSG(err == 0);
264
265 __ASSERT_NO_MSG(bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK) == available);
266 __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) ==
267 available_for_conn);
268
269 WAIT_FOR_UNSET_FLAG(flag_connected);
270 LOG_DBG("Client disconnected");
271
272 LOG_DBG("Start Advertising");
273 err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
274 if (err != 0) {
275 FAIL("Failed to start advertising set (err %d)\n", err);
276
277 bt_le_ext_adv_delete(ext_adv);
278
279 return;
280 }
281
282 WAIT_FOR_FLAG(flag_connected);
283 LOG_DBG("Connected");
284
285 __ASSERT_NO_MSG(bt_pacs_get_available_contexts(BT_AUDIO_DIR_SINK) == available);
286 __ASSERT_NO_MSG(bt_pacs_get_available_contexts_for_conn(default_conn, BT_AUDIO_DIR_SINK) ==
287 available);
288
289 WAIT_FOR_UNSET_FLAG(flag_connected);
290
291 PASS("PACS Notify Server passed\n");
292 }
293
294 static const struct bst_test_instance test_pacs_notify_server[] = {
295 {
296 .test_id = "pacs_notify_server",
297 .test_pre_init_f = test_init,
298 .test_tick_f = test_tick,
299 .test_main_f = test_main,
300 },
301 BSTEST_END_MARKER,
302 };
303
test_pacs_notify_server_install(struct bst_test_list * tests)304 struct bst_test_list *test_pacs_notify_server_install(struct bst_test_list *tests)
305 {
306 return bst_add_tests(tests, test_pacs_notify_server);
307 }
308