1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <errno.h>
7 #include <stdbool.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <zephyr/autoconf.h>
14 #include <zephyr/bluetooth/audio/audio.h>
15 #include <zephyr/bluetooth/audio/bap.h>
16 #include <zephyr/bluetooth/audio/bap_lc3_preset.h>
17 #include <zephyr/bluetooth/audio/cap.h>
18 #include <zephyr/bluetooth/audio/csip.h>
19 #include <zephyr/bluetooth/audio/lc3.h>
20 #include <zephyr/bluetooth/audio/pacs.h>
21 #include <zephyr/bluetooth/audio/gmap.h>
22 #include <zephyr/bluetooth/bluetooth.h>
23 #include <zephyr/bluetooth/gap.h>
24 #include <zephyr/bluetooth/uuid.h>
25 #include <zephyr/sys/printk.h>
26 #include <zephyr/sys/util.h>
27 #include <zephyr/sys/util_macro.h>
28
29 #include "bstests.h"
30 #include "common.h"
31 #include "bap_common.h"
32
33 #if defined(CONFIG_BT_CAP_ACCEPTOR)
34 extern enum bst_result_t bst_result;
35
36 #define CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | BT_AUDIO_CONTEXT_TYPE_GAME)
37 #define LOCATION (BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT)
38
39 static uint8_t csis_rank = 1;
40
41 static struct bt_audio_codec_cap codec_cap =
42 BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY,
43 BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2), 30, 240, 2, CONTEXT);
44
45 static const struct bt_bap_qos_cfg_pref unicast_qos_pref =
46 BT_BAP_QOS_CFG_PREF(true, BT_GAP_LE_PHY_2M, 0U, 60U, 10000U, 60000U, 10000U, 60000U);
47
48 #define UNICAST_CHANNEL_COUNT_1 BIT(0)
49
50 static struct bt_cap_stream
51 unicast_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT];
52
53 CREATE_FLAG(flag_unicast_stream_started);
54 CREATE_FLAG(flag_gmap_discovered);
55
unicast_stream_enabled_cb(struct bt_bap_stream * stream)56 static void unicast_stream_enabled_cb(struct bt_bap_stream *stream)
57 {
58 struct bt_bap_ep_info ep_info;
59 int err;
60
61 printk("Enabled: stream %p\n", stream);
62
63 err = bt_bap_ep_get_info(stream->ep, &ep_info);
64 if (err != 0) {
65 FAIL("Failed to get ep info: %d\n", err);
66 return;
67 }
68
69 if (ep_info.dir == BT_AUDIO_DIR_SINK) {
70 /* Automatically do the receiver start ready operation */
71 err = bt_bap_stream_start(stream);
72
73 if (err != 0) {
74 FAIL("Failed to start stream: %d\n", err);
75 return;
76 }
77 }
78 }
79
unicast_stream_started_cb(struct bt_bap_stream * stream)80 static void unicast_stream_started_cb(struct bt_bap_stream *stream)
81 {
82 printk("Started: stream %p\n", stream);
83 SET_FLAG(flag_unicast_stream_started);
84 }
85
86 static struct bt_bap_stream_ops unicast_stream_ops = {
87 .enabled = unicast_stream_enabled_cb,
88 .started = unicast_stream_started_cb,
89 };
90
91 /* TODO: Expand with GMAP service data */
92 static const struct bt_data gmap_acceptor_ad[] = {
93 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
94 BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_CAS_VAL)),
95 };
96
97 static struct bt_csip_set_member_svc_inst *csip_set_member;
98
unicast_stream_alloc(void)99 static struct bt_bap_stream *unicast_stream_alloc(void)
100 {
101 for (size_t i = 0; i < ARRAY_SIZE(unicast_streams); i++) {
102 struct bt_bap_stream *stream = &unicast_streams[i].bap_stream;
103
104 if (!stream->conn) {
105 return stream;
106 }
107 }
108
109 return NULL;
110 }
111
unicast_server_config(struct bt_conn * conn,const struct bt_bap_ep * ep,enum bt_audio_dir dir,const struct bt_audio_codec_cfg * codec_cfg,struct bt_bap_stream ** stream,struct bt_bap_qos_cfg_pref * const pref,struct bt_bap_ascs_rsp * rsp)112 static int unicast_server_config(struct bt_conn *conn, const struct bt_bap_ep *ep,
113 enum bt_audio_dir dir, const struct bt_audio_codec_cfg *codec_cfg,
114 struct bt_bap_stream **stream,
115 struct bt_bap_qos_cfg_pref *const pref,
116 struct bt_bap_ascs_rsp *rsp)
117 {
118 printk("ASE Codec Config: conn %p ep %p dir %u\n", conn, ep, dir);
119
120 print_codec_cfg(codec_cfg);
121
122 *stream = unicast_stream_alloc();
123 if (*stream == NULL) {
124 printk("No streams available\n");
125 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_NO_MEM, BT_BAP_ASCS_REASON_NONE);
126
127 return -ENOMEM;
128 }
129
130 printk("ASE Codec Config stream %p\n", *stream);
131
132 *pref = unicast_qos_pref;
133
134 return 0;
135 }
136
unicast_server_reconfig(struct bt_bap_stream * stream,enum bt_audio_dir dir,const struct bt_audio_codec_cfg * codec_cfg,struct bt_bap_qos_cfg_pref * const pref,struct bt_bap_ascs_rsp * rsp)137 static int unicast_server_reconfig(struct bt_bap_stream *stream, enum bt_audio_dir dir,
138 const struct bt_audio_codec_cfg *codec_cfg,
139 struct bt_bap_qos_cfg_pref *const pref,
140 struct bt_bap_ascs_rsp *rsp)
141 {
142 printk("ASE Codec Reconfig: stream %p\n", stream);
143
144 print_codec_cfg(codec_cfg);
145
146 *pref = unicast_qos_pref;
147
148 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_CONF_UNSUPPORTED, BT_BAP_ASCS_REASON_NONE);
149
150 /* We only support one QoS at the moment, reject changes */
151 return -ENOEXEC;
152 }
153
unicast_server_qos(struct bt_bap_stream * stream,const struct bt_bap_qos_cfg * qos,struct bt_bap_ascs_rsp * rsp)154 static int unicast_server_qos(struct bt_bap_stream *stream, const struct bt_bap_qos_cfg *qos,
155 struct bt_bap_ascs_rsp *rsp)
156 {
157 printk("QoS: stream %p qos %p\n", stream, qos);
158
159 print_qos(qos);
160
161 return 0;
162 }
163
data_func_cb(struct bt_data * data,void * user_data)164 static bool data_func_cb(struct bt_data *data, void *user_data)
165 {
166 struct bt_bap_ascs_rsp *rsp = (struct bt_bap_ascs_rsp *)user_data;
167
168 if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->type)) {
169 printk("Invalid metadata type %u or length %u\n", data->type, data->data_len);
170 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED, data->type);
171
172 return -EINVAL;
173 }
174
175 return true;
176 }
177
unicast_server_enable(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len,struct bt_bap_ascs_rsp * rsp)178 static int unicast_server_enable(struct bt_bap_stream *stream, const uint8_t meta[],
179 size_t meta_len, struct bt_bap_ascs_rsp *rsp)
180 {
181 printk("Enable: stream %p meta_len %zu\n", stream, meta_len);
182
183 return bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
184 }
185
unicast_server_start(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)186 static int unicast_server_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
187 {
188 printk("Start: stream %p\n", stream);
189
190 return 0;
191 }
192
unicast_server_metadata(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len,struct bt_bap_ascs_rsp * rsp)193 static int unicast_server_metadata(struct bt_bap_stream *stream, const uint8_t meta[],
194 size_t meta_len, struct bt_bap_ascs_rsp *rsp)
195 {
196 printk("Metadata: stream %p meta_len %zu\n", stream, meta_len);
197
198 return bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
199 }
200
unicast_server_disable(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)201 static int unicast_server_disable(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
202 {
203 printk("Disable: stream %p\n", stream);
204
205 return 0;
206 }
207
unicast_server_stop(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)208 static int unicast_server_stop(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
209 {
210 printk("Stop: stream %p\n", stream);
211
212 return 0;
213 }
214
unicast_server_release(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)215 static int unicast_server_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
216 {
217 printk("Release: stream %p\n", stream);
218
219 return 0;
220 }
221
222 static struct bt_bap_unicast_server_register_param param = {
223 CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT,
224 CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT
225 };
226
227 static struct bt_bap_unicast_server_cb unicast_server_cbs = {
228 .config = unicast_server_config,
229 .reconfig = unicast_server_reconfig,
230 .qos = unicast_server_qos,
231 .enable = unicast_server_enable,
232 .start = unicast_server_start,
233 .metadata = unicast_server_metadata,
234 .disable = unicast_server_disable,
235 .stop = unicast_server_stop,
236 .release = unicast_server_release,
237 };
238
set_location(void)239 static void set_location(void)
240 {
241 int err;
242
243 if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC)) {
244 err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, LOCATION);
245 if (err != 0) {
246 FAIL("Failed to set sink location (err %d)\n", err);
247 return;
248 }
249 }
250
251 if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC)) {
252 err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, LOCATION);
253 if (err != 0) {
254 FAIL("Failed to set source location (err %d)\n", err);
255 return;
256 }
257 }
258
259 printk("Location successfully set\n");
260 }
261
set_supported_contexts(void)262 static int set_supported_contexts(void)
263 {
264 int err;
265
266 if (IS_ENABLED(CONFIG_BT_PAC_SNK)) {
267 err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, CONTEXT);
268 if (err != 0) {
269 printk("Failed to set sink supported contexts (err %d)\n", err);
270
271 return err;
272 }
273 }
274
275 if (IS_ENABLED(CONFIG_BT_PAC_SRC)) {
276 err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE, CONTEXT);
277 if (err != 0) {
278 printk("Failed to set source supported contexts (err %d)\n", err);
279
280 return err;
281 }
282 }
283
284 printk("Supported contexts successfully set\n");
285
286 return 0;
287 }
288
set_available_contexts(void)289 static void set_available_contexts(void)
290 {
291 int err;
292
293 err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, CONTEXT);
294 if (IS_ENABLED(CONFIG_BT_PAC_SNK) && err != 0) {
295 FAIL("Failed to set sink available contexts (err %d)\n", err);
296 return;
297 }
298
299 err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE, CONTEXT);
300 if (IS_ENABLED(CONFIG_BT_PAC_SRC) && err != 0) {
301 FAIL("Failed to set source available contexts (err %d)\n", err);
302 return;
303 }
304
305 printk("Available contexts successfully set\n");
306 }
307
gmap_discover_cb(struct bt_conn * conn,int err,enum bt_gmap_role role,struct bt_gmap_feat features)308 static void gmap_discover_cb(struct bt_conn *conn, int err, enum bt_gmap_role role,
309 struct bt_gmap_feat features)
310 {
311 enum bt_gmap_ugg_feat ugg_feat;
312
313 if (err != 0) {
314 FAIL("GMAP discovery (err %d)\n", err);
315 return;
316 }
317
318 printk("GMAP discovered for conn %p:\n\trole 0x%02x\n\tugg_feat 0x%02x\n\tugt_feat "
319 "0x%02x\n\tbgs_feat 0x%02x\n\tbgr_feat 0x%02x\n",
320 conn, role, features.ugg_feat, features.ugt_feat, features.bgs_feat,
321 features.bgr_feat);
322
323 if ((role & BT_GMAP_ROLE_UGG) == 0) {
324 FAIL("Remote GMAP device is not a UGG\n");
325 return;
326 }
327
328 ugg_feat = features.ugg_feat;
329 if ((ugg_feat & BT_GMAP_UGG_FEAT_MULTIPLEX) == 0 ||
330 (ugg_feat & BT_GMAP_UGG_FEAT_96KBPS_SOURCE) == 0 ||
331 (ugg_feat & BT_GMAP_UGG_FEAT_MULTISINK) == 0) {
332 FAIL("Remote GMAP device does not have expected UGG features: %d\n", ugg_feat);
333 return;
334 }
335
336 SET_FLAG(flag_gmap_discovered);
337 }
338
339 static const struct bt_gmap_cb gmap_cb = {
340 .discover = gmap_discover_cb,
341 };
342
discover_gmas(struct bt_conn * conn)343 static void discover_gmas(struct bt_conn *conn)
344 {
345 int err;
346
347 UNSET_FLAG(flag_gmap_discovered);
348
349 err = bt_gmap_discover(conn);
350 if (err != 0) {
351 printk("Failed to discover GMAS: %d\n", err);
352 return;
353 }
354
355 WAIT_FOR_FLAG(flag_gmap_discovered);
356 }
357
test_main(void)358 static void test_main(void)
359 {
360 /* TODO: Register all GMAP codec capabilities */
361 const enum bt_gmap_role role = BT_GMAP_ROLE_UGT;
362 const struct bt_gmap_feat features = {
363 .ugt_feat = (BT_GMAP_UGT_FEAT_SOURCE | BT_GMAP_UGT_FEAT_80KBPS_SOURCE |
364 BT_GMAP_UGT_FEAT_SINK | BT_GMAP_UGT_FEAT_64KBPS_SINK |
365 BT_GMAP_UGT_FEAT_MULTIPLEX | BT_GMAP_UGT_FEAT_MULTISINK |
366 BT_GMAP_UGT_FEAT_MULTISOURCE),
367 };
368 static struct bt_pacs_cap unicast_cap = {
369 .codec_cap = &codec_cap,
370 };
371 int err;
372
373 err = bt_enable(NULL);
374 if (err != 0) {
375 FAIL("Bluetooth enable failed (err %d)\n", err);
376 return;
377 }
378
379 printk("Bluetooth initialized\n");
380
381 if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
382 const struct bt_csip_set_member_register_param csip_set_member_param = {
383 .set_size = 2,
384 .rank = csis_rank,
385 .lockable = true,
386 /* Using the CSIP_SET_MEMBER test sample SIRK */
387 .sirk = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
388 0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 },
389 };
390
391 err = bt_cap_acceptor_register(&csip_set_member_param, &csip_set_member);
392 if (err != 0) {
393 FAIL("CAP acceptor failed to register (err %d)\n", err);
394 return;
395 }
396 }
397
398 err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &unicast_cap);
399 if (err != 0) {
400 FAIL("Capability register failed (err %d)\n", err);
401
402 return;
403 }
404
405 err = bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &unicast_cap);
406 if (err != 0) {
407 FAIL("Capability register failed (err %d)\n", err);
408
409 return;
410 }
411
412 err = bt_bap_unicast_server_register(¶m);
413 if (err != 0) {
414 FAIL("Failed to register unicast server (err %d)\n", err);
415
416 return;
417 }
418
419 err = bt_bap_unicast_server_register_cb(&unicast_server_cbs);
420 if (err != 0) {
421 FAIL("Failed to register unicast server callbacks (err %d)\n", err);
422
423 return;
424 }
425
426 for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
427 bt_cap_stream_ops_register(&unicast_streams[i], &unicast_stream_ops);
428 }
429
430 set_supported_contexts();
431 set_available_contexts();
432 set_location();
433
434 err = bt_gmap_register(role, features);
435 if (err != 0) {
436 FAIL("Failed to register GMAS (err %d)\n", err);
437
438 return;
439 }
440
441 err = bt_gmap_cb_register(&gmap_cb);
442 if (err != 0) {
443 FAIL("Failed to register callbacks (err %d)\n", err);
444
445 return;
446 }
447
448 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, gmap_acceptor_ad, ARRAY_SIZE(gmap_acceptor_ad),
449 NULL, 0);
450 if (err != 0) {
451 FAIL("Advertising failed to start (err %d)\n", err);
452 return;
453 }
454
455 WAIT_FOR_FLAG(flag_connected);
456
457 discover_gmas(default_conn);
458 discover_gmas(default_conn); /* test that we can discover twice */
459
460 WAIT_FOR_FLAG(flag_disconnected);
461
462 PASS("GMAP UGT passed\n");
463 }
464
test_args(int argc,char * argv[])465 static void test_args(int argc, char *argv[])
466 {
467 for (size_t argn = 0; argn < argc; argn++) {
468 const char *arg = argv[argn];
469
470 if (strcmp(arg, "rank") == 0) {
471 csis_rank = strtoul(argv[++argn], NULL, 10);
472 } else {
473 FAIL("Invalid arg: %s\n", arg);
474 }
475 }
476 }
477
478 static const struct bst_test_instance test_gmap_ugt[] = {
479 {
480 .test_id = "gmap_ugt",
481 .test_pre_init_f = test_init,
482 .test_tick_f = test_tick,
483 .test_main_f = test_main,
484 .test_args_f = test_args,
485 },
486 BSTEST_END_MARKER,
487 };
488
test_gmap_ugt_install(struct bst_test_list * tests)489 struct bst_test_list *test_gmap_ugt_install(struct bst_test_list *tests)
490 {
491 return bst_add_tests(tests, test_gmap_ugt);
492 }
493
494 #else /* !(CONFIG_BT_CAP_ACCEPTOR) */
495
test_gmap_ugt_install(struct bst_test_list * tests)496 struct bst_test_list *test_gmap_ugt_install(struct bst_test_list *tests)
497 {
498 return tests;
499 }
500
501 #endif /* CONFIG_BT_CAP_ACCEPTOR */
502