1 /**
2 * @file
3 * @brief Shell APIs for Bluetooth CAP initiator
4 *
5 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16
17 #include <zephyr/autoconf.h>
18 #include <zephyr/bluetooth/audio/audio.h>
19 #include <zephyr/bluetooth/audio/bap.h>
20 #include <zephyr/bluetooth/audio/csip.h>
21 #include <zephyr/bluetooth/gap.h>
22 #include <zephyr/bluetooth/iso.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/net_buf.h>
25 #include <zephyr/shell/shell_string_conv.h>
26 #include <zephyr/sys/byteorder.h>
27 #include <zephyr/sys/printk.h>
28 #include <zephyr/sys/util.h>
29 #include <zephyr/types.h>
30 #include <zephyr/shell/shell.h>
31 #include <zephyr/bluetooth/conn.h>
32 #include <zephyr/bluetooth/gatt.h>
33 #include <zephyr/bluetooth/bluetooth.h>
34 #include <zephyr/bluetooth/audio/cap.h>
35
36 #include "host/shell/bt.h"
37 #include "audio.h"
38
39 #if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
40 #define UNICAST_SINK_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0)
41 #define UNICAST_SRC_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0)
42
43 #define CAP_UNICAST_CLIENT_STREAM_COUNT ARRAY_SIZE(unicast_streams)
44
cap_discover_cb(struct bt_conn * conn,int err,const struct bt_csip_set_coordinator_set_member * member,const struct bt_csip_set_coordinator_csis_inst * csis_inst)45 static void cap_discover_cb(struct bt_conn *conn, int err,
46 const struct bt_csip_set_coordinator_set_member *member,
47 const struct bt_csip_set_coordinator_csis_inst *csis_inst)
48 {
49 if (err != 0) {
50 shell_error(ctx_shell, "discover failed (%d)", err);
51 return;
52 }
53
54 shell_print(ctx_shell, "discovery completed%s",
55 csis_inst == NULL ? "" : " with CSIS");
56 }
57
cap_unicast_start_complete_cb(int err,struct bt_conn * conn)58 static void cap_unicast_start_complete_cb(int err, struct bt_conn *conn)
59 {
60 if (err == -ECANCELED) {
61 shell_print(ctx_shell, "Unicast start was cancelled for conn %p", conn);
62 } else if (err != 0) {
63 shell_error(ctx_shell, "Unicast start failed for conn %p (%d)", conn, err);
64 } else {
65 shell_print(ctx_shell, "Unicast start completed");
66 }
67 }
68
unicast_update_complete_cb(int err,struct bt_conn * conn)69 static void unicast_update_complete_cb(int err, struct bt_conn *conn)
70 {
71 if (err == -ECANCELED) {
72 shell_print(ctx_shell, "Unicast update was cancelled for conn %p", conn);
73 } else if (err != 0) {
74 shell_error(ctx_shell, "Unicast update failed for conn %p (%d)",
75 conn, err);
76 } else {
77 shell_print(ctx_shell, "Unicast updated completed");
78 }
79 }
80
unicast_stop_complete_cb(int err,struct bt_conn * conn)81 static void unicast_stop_complete_cb(int err, struct bt_conn *conn)
82 {
83 if (err == -ECANCELED) {
84 shell_print(ctx_shell, "Unicast stop was cancelled for conn %p", conn);
85 } else if (err != 0) {
86 shell_error(ctx_shell, "Unicast stop failed for conn %p (%d)", conn, err);
87 } else {
88 shell_print(ctx_shell, "Unicast stop completed");
89
90 if (default_unicast_group != NULL) {
91 err = bt_bap_unicast_group_delete(default_unicast_group);
92 if (err != 0) {
93 shell_error(ctx_shell, "Failed to delete unicast group %p: %d",
94 default_unicast_group, err);
95 } else {
96 default_unicast_group = NULL;
97 }
98 }
99 }
100 }
101
102 static struct bt_cap_initiator_cb cbs = {
103 .unicast_discovery_complete = cap_discover_cb,
104 .unicast_start_complete = cap_unicast_start_complete_cb,
105 .unicast_update_complete = unicast_update_complete_cb,
106 .unicast_stop_complete = unicast_stop_complete_cb,
107 };
108
cmd_cap_initiator_discover(const struct shell * sh,size_t argc,char * argv[])109 static int cmd_cap_initiator_discover(const struct shell *sh, size_t argc,
110 char *argv[])
111 {
112 static bool cbs_registered;
113 int err;
114
115 if (default_conn == NULL) {
116 shell_error(sh, "Not connected");
117 return -ENOEXEC;
118 }
119
120 if (ctx_shell == NULL) {
121 ctx_shell = sh;
122 }
123
124 if (!cbs_registered) {
125 bt_cap_initiator_register_cb(&cbs);
126 cbs_registered = true;
127 }
128
129 err = bt_cap_initiator_unicast_discover(default_conn);
130 if (err != 0) {
131 shell_error(sh, "Fail: %d", err);
132 }
133
134 return err;
135 }
136
populate_connected_conns(struct bt_conn * conn,void * data)137 static void populate_connected_conns(struct bt_conn *conn, void *data)
138 {
139 struct bt_conn **connected_conns = (struct bt_conn **)data;
140
141 for (int i = 0; i < CONFIG_BT_MAX_CONN; i++) {
142 if (connected_conns[i] == NULL) {
143 connected_conns[i] = conn;
144 return;
145 }
146 }
147 }
148
cmd_cap_initiator_unicast_start(const struct shell * sh,size_t argc,char * argv[])149 static int cmd_cap_initiator_unicast_start(const struct shell *sh, size_t argc,
150 char *argv[])
151 {
152 struct bt_bap_unicast_group_stream_param
153 group_stream_params[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0};
154 struct bt_bap_unicast_group_stream_pair_param
155 pair_params[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0};
156 struct bt_cap_unicast_audio_start_stream_param
157 stream_param[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0};
158 struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN] = {0};
159 struct bt_cap_unicast_audio_start_param start_param = {0};
160 struct bt_bap_unicast_group_param group_param = {0};
161 size_t source_cnt = 1U;
162 ssize_t conn_cnt = 1U;
163 size_t sink_cnt = 1U;
164 size_t pair_cnt = 0U;
165 int err = 0;
166
167 if (default_conn == NULL) {
168 shell_error(sh, "Not connected");
169 return -ENOEXEC;
170 }
171
172 start_param.type = BT_CAP_SET_TYPE_AD_HOC;
173
174 for (size_t argn = 1; argn < argc; argn++) {
175 const char *arg = argv[argn];
176
177 if (strcmp(arg, "csip") == 0) {
178 start_param.type = BT_CAP_SET_TYPE_CSIP;
179 } else if (strcmp(arg, "sinks") == 0) {
180 if (++argn == argc) {
181 shell_help(sh);
182
183 return SHELL_CMD_HELP_PRINTED;
184 }
185
186 sink_cnt = shell_strtoul(argv[argn], 10, &err);
187 } else if (strcmp(arg, "sources") == 0) {
188 if (++argn == argc) {
189 shell_help(sh);
190
191 return SHELL_CMD_HELP_PRINTED;
192 }
193
194 source_cnt = shell_strtoul(argv[argn], 10, &err);
195 } else if (strcmp(arg, "conns") == 0) {
196 if (++argn == argc) {
197 shell_help(sh);
198
199 return SHELL_CMD_HELP_PRINTED;
200 }
201
202 if (strcmp(argv[argn], "all") == 0) {
203 conn_cnt = CONFIG_BT_MAX_CONN;
204 } else {
205 conn_cnt = shell_strtoul(argv[argn], 10, &err);
206
207 /* Ensure that we do not iterate over more conns
208 * than the array supports
209 */
210 conn_cnt = MIN(conn_cnt, ARRAY_SIZE(connected_conns));
211 }
212 } else {
213 shell_help(sh);
214
215 return SHELL_CMD_HELP_PRINTED;
216 }
217
218 if (err != 0) {
219 shell_error(sh, "Failed to parse argument: %s: %s (%d)",
220 arg, argv[argn], err);
221
222 return err;
223 }
224 }
225
226 shell_print(sh, "Setting up %u sinks and %u sources on each (%u) conn", sink_cnt,
227 source_cnt, conn_cnt);
228
229 /* Populate the array of connected connections */
230 (void)memset(connected_conns, 0, sizeof(connected_conns));
231 bt_conn_foreach(BT_CONN_TYPE_LE, populate_connected_conns,
232 (void *)connected_conns);
233
234 start_param.count = 0U;
235 start_param.stream_params = stream_param;
236 for (size_t i = 0; i < conn_cnt; i++) {
237 struct bt_conn *conn = connected_conns[i];
238 size_t conn_src_cnt = 0U;
239 size_t conn_snk_cnt = 0U;
240
241 if (conn == NULL) {
242 break;
243 }
244
245 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0
246 conn_snk_cnt = sink_cnt;
247 for (size_t j = 0U; j < sink_cnt; j++) {
248 struct bt_cap_stream *stream =
249 &unicast_streams[start_param.count].stream;
250 struct shell_stream *uni_stream =
251 CONTAINER_OF(stream, struct shell_stream, stream);
252 struct bt_bap_ep *snk_ep = snks[bt_conn_index(conn)][j];
253
254 if (snk_ep == NULL) {
255 shell_info(sh, "Could only setup %zu/%zu sink endpoints",
256 j, sink_cnt);
257 conn_snk_cnt = j;
258 break;
259 }
260
261 stream_param[start_param.count].member.member = conn;
262 stream_param[start_param.count].stream = stream;
263 stream_param[start_param.count].ep = snk_ep;
264 copy_unicast_stream_preset(uni_stream, &default_sink_preset);
265 stream_param[start_param.count].codec_cfg = &uni_stream->codec_cfg;
266
267 group_stream_params[start_param.count].stream =
268 &stream_param[start_param.count].stream->bap_stream;
269 group_stream_params[start_param.count].qos = &uni_stream->qos;
270 pair_params[pair_cnt + j].tx_param =
271 &group_stream_params[start_param.count];
272
273 start_param.count++;
274 }
275 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */
276
277 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0
278 conn_src_cnt = source_cnt;
279 for (size_t j = 0U; j < source_cnt; j++) {
280 struct bt_cap_stream *stream =
281 &unicast_streams[start_param.count].stream;
282 struct shell_stream *uni_stream =
283 CONTAINER_OF(stream, struct shell_stream, stream);
284 struct bt_bap_ep *src_ep = srcs[bt_conn_index(conn)][j];
285
286 if (src_ep == NULL) {
287 shell_info(sh, "Could only setup %zu/%zu source endpoints",
288 j, source_cnt);
289 conn_src_cnt = j;
290 break;
291 }
292
293 stream_param[start_param.count].member.member = conn;
294 stream_param[start_param.count].stream = stream;
295 stream_param[start_param.count].ep = src_ep;
296 copy_unicast_stream_preset(uni_stream, &default_source_preset);
297 stream_param[start_param.count].codec_cfg = &uni_stream->codec_cfg;
298 group_stream_params[start_param.count].stream =
299 &stream_param[start_param.count].stream->bap_stream;
300 group_stream_params[start_param.count].qos = &uni_stream->qos;
301 pair_params[pair_cnt + j].rx_param =
302 &group_stream_params[start_param.count];
303
304 start_param.count++;
305 }
306 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */
307
308 /* Increment pair count with the max of sink and source for this connection, as
309 * we cannot have pairs across connections
310 */
311 pair_cnt += MAX(conn_snk_cnt, conn_src_cnt);
312 }
313
314 if (pair_cnt == 0U) {
315 shell_error(sh, "No streams to setup");
316
317 return -ENOEXEC;
318 }
319
320 group_param.packing = BT_ISO_PACKING_SEQUENTIAL;
321 group_param.params_count = pair_cnt;
322 group_param.params = pair_params;
323
324 if (default_unicast_group == NULL) {
325 err = bt_bap_unicast_group_create(&group_param, &default_unicast_group);
326 if (err != 0) {
327 shell_print(sh, "Failed to create group: %d", err);
328
329 return -ENOEXEC;
330 }
331 }
332
333 shell_print(sh, "Starting %zu streams", start_param.count);
334
335 err = bt_cap_initiator_unicast_audio_start(&start_param);
336 if (err != 0) {
337 shell_print(sh, "Failed to start unicast audio: %d", err);
338
339 return -ENOEXEC;
340 }
341
342 return 0;
343 }
344
cmd_cap_initiator_unicast_list(const struct shell * sh,size_t argc,char * argv[])345 static int cmd_cap_initiator_unicast_list(const struct shell *sh, size_t argc,
346 char *argv[])
347 {
348 for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
349 if (unicast_streams[i].stream.bap_stream.conn == NULL) {
350 break;
351 }
352
353 shell_print(sh, "Stream #%zu: %p", i, &unicast_streams[i].stream);
354 }
355 return 0;
356 }
357
cmd_cap_initiator_unicast_update(const struct shell * sh,size_t argc,char * argv[])358 static int cmd_cap_initiator_unicast_update(const struct shell *sh, size_t argc,
359 char *argv[])
360 {
361 struct bt_cap_unicast_audio_update_stream_param
362 stream_params[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0};
363 struct bt_cap_unicast_audio_update_param param = {0};
364 int err = 0;
365
366 if (default_conn == NULL) {
367 shell_error(sh, "Not connected");
368 return -ENOEXEC;
369 }
370
371 if (argc == 2 && strcmp(argv[1], "all") == 0) {
372 for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
373 struct bt_cap_stream *stream = &unicast_streams[i].stream;
374 struct shell_stream *uni_stream =
375 CONTAINER_OF(stream, struct shell_stream, stream);
376 struct bt_bap_ep_info ep_info;
377
378 if (stream->bap_stream.conn == NULL) {
379 break;
380 }
381
382 err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
383 if (err != 0) {
384 shell_error(sh, "Failed to get endpoint info: %d", err);
385
386 return -ENOEXEC;
387 }
388
389 stream_params[param.count].stream = stream;
390
391 if (ep_info.dir == BT_AUDIO_DIR_SINK) {
392 copy_unicast_stream_preset(uni_stream, &default_sink_preset);
393 } else {
394 copy_unicast_stream_preset(uni_stream, &default_source_preset);
395 }
396
397 stream_params[param.count].meta = uni_stream->codec_cfg.meta;
398 stream_params[param.count].meta_len = uni_stream->codec_cfg.meta_len;
399
400 param.count++;
401 }
402
403 } else {
404 for (size_t i = 1U; i < argc; i++) {
405 struct bt_cap_stream *stream = (void *)shell_strtoul(argv[i], 16, &err);
406 struct shell_stream *uni_stream =
407 CONTAINER_OF(stream, struct shell_stream, stream);
408 struct bt_bap_ep_info ep_info;
409
410 if (err != 0) {
411 shell_error(sh, "Failed to parse stream argument %s: %d",
412 argv[i], err);
413
414 return err;
415 }
416
417 if (!PART_OF_ARRAY(unicast_streams, stream)) {
418 shell_error(sh, "Pointer %p is not a CAP stream pointer",
419 stream);
420
421 return -ENOEXEC;
422 }
423
424 err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
425 if (err != 0) {
426 shell_error(sh, "Failed to get endpoint info: %d", err);
427
428 return -ENOEXEC;
429 }
430
431 stream_params[param.count].stream = stream;
432
433 if (ep_info.dir == BT_AUDIO_DIR_SINK) {
434 copy_unicast_stream_preset(uni_stream, &default_sink_preset);
435 } else {
436 copy_unicast_stream_preset(uni_stream, &default_source_preset);
437 }
438
439 stream_params[param.count].meta = uni_stream->codec_cfg.meta;
440 stream_params[param.count].meta_len = uni_stream->codec_cfg.meta_len;
441
442 param.count++;
443 }
444 }
445
446 if (param.count == 0) {
447 shell_error(sh, "No streams to update");
448
449 return -ENOEXEC;
450 }
451
452 param.stream_params = stream_params;
453 param.type = BT_CAP_SET_TYPE_AD_HOC;
454
455 shell_print(sh, "Updating %zu streams", param.count);
456
457 err = bt_cap_initiator_unicast_audio_update(¶m);
458 if (err != 0) {
459 shell_print(sh, "Failed to update unicast audio: %d", err);
460 }
461
462 return err;
463 }
464
cmd_cap_initiator_unicast_stop(const struct shell * sh,size_t argc,char * argv[])465 static int cmd_cap_initiator_unicast_stop(const struct shell *sh, size_t argc,
466 char *argv[])
467 {
468 struct bt_cap_stream *streams[CAP_UNICAST_CLIENT_STREAM_COUNT];
469 struct bt_cap_unicast_audio_stop_param param = {0};
470 int err = 0;
471
472 if (default_conn == NULL) {
473 shell_error(sh, "Not connected");
474 return -ENOEXEC;
475 }
476
477 if (argc == 1) {
478 for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
479 struct bt_cap_stream *stream = &unicast_streams[i].stream;
480 struct bt_bap_ep_info ep_info;
481
482 if (stream->bap_stream.conn == NULL) {
483 break;
484 }
485
486 err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
487 if (err != 0) {
488 shell_error(sh, "Failed to get endpoint info: %d", err);
489
490 return -ENOEXEC;
491 }
492
493 streams[param.count] = stream;
494 param.count++;
495 }
496
497 } else {
498 for (size_t i = 1U; i < argc; i++) {
499 struct bt_cap_stream *stream = (void *)shell_strtoul(argv[i], 16, &err);
500 struct bt_bap_ep_info ep_info;
501
502 if (err != 0) {
503 shell_error(sh, "Failed to parse stream argument %s: %d", argv[i],
504 err);
505
506 return err;
507 }
508
509 if (!PART_OF_ARRAY(unicast_streams, stream)) {
510 shell_error(sh, "Pointer %p is not a CAP stream pointer", stream);
511
512 return -ENOEXEC;
513 }
514
515 err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
516 if (err != 0) {
517 shell_error(sh, "Failed to get endpoint info: %d", err);
518
519 return -ENOEXEC;
520 }
521
522 streams[param.count] = stream;
523 param.count++;
524 }
525 }
526
527 if (param.count == 0) {
528 shell_error(sh, "No streams to update");
529
530 return -ENOEXEC;
531 }
532
533 param.streams = streams;
534 param.type = BT_CAP_SET_TYPE_AD_HOC;
535 param.release = true;
536
537 err = bt_cap_initiator_unicast_audio_stop(¶m);
538 if (err != 0) {
539 shell_print(sh, "Failed to update unicast audio: %d", err);
540 }
541
542 return err;
543 }
544
cmd_cap_initiator_unicast_cancel(const struct shell * sh,size_t argc,char * argv[])545 static int cmd_cap_initiator_unicast_cancel(const struct shell *sh, size_t argc, char *argv[])
546 {
547 int err = 0;
548
549 err = bt_cap_initiator_unicast_audio_cancel();
550 if (err != 0) {
551 shell_print(sh, "Failed to cancel unicast audio procedure: %d", err);
552 return -ENOEXEC;
553 }
554
555 return 0;
556 }
557
cap_ac_unicast_start(const struct bap_unicast_ac_param * param,struct bt_conn * connected_conns[],struct shell_stream * snk_uni_streams[],size_t snk_cnt,struct shell_stream * src_uni_streams[],size_t src_cnt)558 static int cap_ac_unicast_start(const struct bap_unicast_ac_param *param,
559 struct bt_conn *connected_conns[],
560 struct shell_stream *snk_uni_streams[], size_t snk_cnt,
561 struct shell_stream *src_uni_streams[], size_t src_cnt)
562 {
563 struct bt_cap_unicast_audio_start_stream_param stream_params[BAP_UNICAST_AC_MAX_STREAM] = {
564 0};
565 struct bt_audio_codec_cfg *snk_codec_cfgs[BAP_UNICAST_AC_MAX_SNK] = {0};
566 struct bt_audio_codec_cfg *src_codec_cfgs[BAP_UNICAST_AC_MAX_SRC] = {0};
567 struct bt_cap_stream *snk_cap_streams[BAP_UNICAST_AC_MAX_SNK] = {0};
568 struct bt_cap_stream *src_cap_streams[BAP_UNICAST_AC_MAX_SRC] = {0};
569 struct bt_cap_unicast_audio_start_param start_param = {0};
570 struct bt_bap_ep *snk_eps[BAP_UNICAST_AC_MAX_SNK] = {0};
571 struct bt_bap_ep *src_eps[BAP_UNICAST_AC_MAX_SRC] = {0};
572 size_t snk_stream_cnt = 0U;
573 size_t src_stream_cnt = 0U;
574 size_t stream_cnt = 0U;
575 size_t snk_ep_cnt = 0U;
576 size_t src_ep_cnt = 0U;
577
578 for (size_t i = 0U; i < param->conn_cnt; i++) {
579 #if UNICAST_SINK_SUPPORTED
580 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
581 snk_eps[snk_ep_cnt] = snks[bt_conn_index(connected_conns[i])][j];
582 if (snk_eps[snk_ep_cnt] == NULL) {
583 shell_error(ctx_shell, "No sink[%zu][%zu] endpoint available", i,
584 j);
585
586 return -ENOEXEC;
587 }
588 snk_ep_cnt++;
589 }
590 #endif /* UNICAST_SINK_SUPPORTED */
591
592 #if UNICAST_SRC_SUPPORTED
593 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
594 src_eps[src_ep_cnt] = srcs[bt_conn_index(connected_conns[i])][j];
595 if (src_eps[src_ep_cnt] == NULL) {
596 shell_error(ctx_shell, "No source[%zu][%zu] endpoint available", i,
597 j);
598
599 return -ENOEXEC;
600 }
601 src_ep_cnt++;
602 }
603 #endif /* UNICAST_SRC_SUPPORTED */
604 }
605
606 if (snk_ep_cnt != snk_cnt) {
607 shell_error(ctx_shell, "Sink endpoint and stream count mismatch: %zu != %zu",
608 snk_ep_cnt, snk_cnt);
609
610 return -ENOEXEC;
611 }
612
613 if (src_ep_cnt != src_cnt) {
614 shell_error(ctx_shell, "Source endpoint and stream count mismatch: %zu != %zu",
615 src_ep_cnt, src_cnt);
616
617 return -ENOEXEC;
618 }
619
620 /* Setup arrays of parameters based on the preset for easier access. This also copies the
621 * preset so that we can modify them (e.g. update the metadata)
622 */
623 for (size_t i = 0U; i < snk_cnt; i++) {
624 snk_cap_streams[i] = &snk_uni_streams[i]->stream;
625 snk_codec_cfgs[i] = &snk_uni_streams[i]->codec_cfg;
626 }
627
628 for (size_t i = 0U; i < src_cnt; i++) {
629 src_cap_streams[i] = &src_uni_streams[i]->stream;
630 src_codec_cfgs[i] = &src_uni_streams[i]->codec_cfg;
631 }
632
633 /* CAP Start */
634 for (size_t i = 0U; i < param->conn_cnt; i++) {
635 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
636 struct bt_cap_unicast_audio_start_stream_param *stream_param =
637 &stream_params[stream_cnt];
638
639 stream_param->member.member = connected_conns[i];
640 stream_param->codec_cfg = snk_codec_cfgs[snk_stream_cnt];
641 stream_param->ep = snk_eps[snk_stream_cnt];
642 stream_param->stream = snk_cap_streams[snk_stream_cnt];
643
644 snk_stream_cnt++;
645 stream_cnt++;
646 }
647
648 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
649 struct bt_cap_unicast_audio_start_stream_param *stream_param =
650 &stream_params[stream_cnt];
651
652 stream_param->member.member = connected_conns[i];
653 stream_param->codec_cfg = src_codec_cfgs[src_stream_cnt];
654 stream_param->ep = src_eps[src_stream_cnt];
655 stream_param->stream = src_cap_streams[src_stream_cnt];
656
657 src_stream_cnt++;
658 stream_cnt++;
659 }
660 }
661
662 start_param.stream_params = stream_params;
663 start_param.count = stream_cnt;
664 start_param.type = BT_CAP_SET_TYPE_AD_HOC;
665
666 return bt_cap_initiator_unicast_audio_start(&start_param);
667 }
668
set_codec_config(const struct shell * sh,struct shell_stream * sh_stream,struct named_lc3_preset * preset,size_t conn_cnt,size_t ep_cnt,size_t chan_cnt,size_t conn_index,size_t ep_index)669 static int set_codec_config(const struct shell *sh, struct shell_stream *sh_stream,
670 struct named_lc3_preset *preset, size_t conn_cnt, size_t ep_cnt,
671 size_t chan_cnt, size_t conn_index, size_t ep_index)
672 {
673 enum bt_audio_location new_chan_alloc;
674 enum bt_audio_location chan_alloc;
675 int err;
676
677 copy_unicast_stream_preset(sh_stream, preset);
678
679 if (chan_cnt == 1U) {
680 /* - When we have a single channel on a single connection then we make it mono
681 * - When we have a single channel on a multiple connections then we make it left on
682 * the first connection and right on the second connection
683 * - When we have multiple channels streams for a connection, we make them either
684 * left or right, regardless of the connection count
685 */
686 if (ep_cnt == 1) {
687 if (conn_cnt == 1) {
688 new_chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO;
689 } else if (conn_cnt == 2) {
690 if (conn_index == 0) {
691 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT;
692 } else if (conn_index == 1) {
693 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT;
694 } else {
695 return 0;
696 }
697 } else {
698 return 0;
699 }
700 } else if (ep_cnt == 2) {
701 if (ep_index == 0) {
702 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT;
703 } else if (ep_index == 1) {
704 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT;
705 } else {
706 return 0;
707 }
708 } else {
709 return 0;
710 }
711 } else if (chan_cnt == 2U) {
712 /* Some audio configuration requires multiple sink channels,
713 * so multiply the SDU based on the channel count
714 */
715 sh_stream->qos.sdu *= chan_cnt;
716
717 /* If a stream has 2 channels, we make it stereo */
718 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT;
719
720 } else {
721 return 0;
722 }
723
724 err = bt_audio_codec_cfg_get_chan_allocation(&sh_stream->codec_cfg, &chan_alloc, false);
725 if (err != 0) {
726 if (err == -ENODATA) {
727 chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO;
728 }
729 }
730
731 if (chan_alloc != new_chan_alloc) {
732 shell_info(sh,
733 "[%zu][%zu]: Overwriting existing channel allocation 0x%08X with 0x%08X",
734 conn_index, ep_index, chan_alloc, new_chan_alloc);
735
736 err = bt_audio_codec_cfg_set_chan_allocation(&sh_stream->codec_cfg, new_chan_alloc);
737 if (err < 0) {
738 return err;
739 }
740 }
741
742 return 0;
743 }
744
cap_ac_unicast(const struct shell * sh,const struct bap_unicast_ac_param * param)745 int cap_ac_unicast(const struct shell *sh, const struct bap_unicast_ac_param *param)
746 {
747 /* Allocate params large enough for any params, but only use what is required */
748 struct bt_conn *connected_conns[BAP_UNICAST_AC_MAX_CONN] = {0};
749 struct shell_stream *snk_uni_streams[BAP_UNICAST_AC_MAX_SNK];
750 struct shell_stream *src_uni_streams[BAP_UNICAST_AC_MAX_SRC];
751 size_t conn_avail_cnt;
752 size_t snk_cnt = 0;
753 size_t src_cnt = 0;
754 int err;
755
756 if (default_unicast_group != NULL) {
757 shell_error(sh, "Unicast Group already exist, please delete first");
758 return -ENOEXEC;
759 }
760
761 if (param->conn_cnt > BAP_UNICAST_AC_MAX_CONN) {
762 shell_error(sh, "Invalid conn_cnt: %zu", param->conn_cnt);
763 return -ENOEXEC;
764 }
765
766 for (size_t i = 0; i < param->conn_cnt; i++) {
767 /* Verify conn values */
768 if (param->snk_cnt[i] > BAP_UNICAST_AC_MAX_SNK) {
769 shell_error(sh, "Invalid conn_snk_cnt[%zu]: %zu", i, param->snk_cnt[i]);
770 return -ENOEXEC;
771 }
772
773 if (param->src_cnt[i] > BAP_UNICAST_AC_MAX_SRC) {
774 shell_error(sh, "Invalid conn_src_cnt[%zu]: %zu", i, param->src_cnt[i]);
775 return -ENOEXEC;
776 }
777 }
778
779 /* Populate the array of connected connections */
780 bt_conn_foreach(BT_CONN_TYPE_LE, populate_connected_conns, (void *)connected_conns);
781 for (conn_avail_cnt = 0; conn_avail_cnt < ARRAY_SIZE(connected_conns); conn_avail_cnt++) {
782 if (connected_conns[conn_avail_cnt] == NULL) {
783 break;
784 }
785 }
786
787 if (conn_avail_cnt < param->conn_cnt) {
788 shell_error(sh,
789 "Only %zu/%u connected devices, please connect additional devices for "
790 "this audio configuration",
791 conn_avail_cnt, param->conn_cnt);
792 return -ENOEXEC;
793 }
794
795 /* Set all endpoints from multiple connections in a single array, and verify that the known
796 * endpoints matches the audio configuration
797 */
798 for (size_t i = 0U; i < param->conn_cnt; i++) {
799 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
800 struct shell_stream *snk_uni_stream;
801
802 snk_uni_stream = snk_uni_streams[snk_cnt] = &unicast_streams[snk_cnt];
803
804 err = set_codec_config(sh, snk_uni_stream, &default_sink_preset,
805 param->conn_cnt, param->snk_cnt[i],
806 param->snk_chan_cnt, i, j);
807 if (err != 0) {
808 shell_error(sh, "Failed to set codec configuration: %d", err);
809
810 return -ENOEXEC;
811 }
812
813 snk_cnt++;
814 }
815
816 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
817 struct shell_stream *src_uni_stream;
818
819 src_uni_stream = snk_uni_streams[src_cnt] = &unicast_streams[src_cnt];
820
821 err = set_codec_config(sh, src_uni_stream, &default_source_preset,
822 param->conn_cnt, param->src_cnt[i],
823 param->src_chan_cnt, i, j);
824 if (err != 0) {
825 shell_error(sh, "Failed to set codec configuration: %d", err);
826
827 return -ENOEXEC;
828 }
829
830 src_cnt++;
831 }
832 }
833
834 if (!ctx_shell) {
835 ctx_shell = sh;
836 }
837
838 err = bap_ac_create_unicast_group(param, snk_uni_streams, snk_cnt, src_uni_streams,
839 src_cnt);
840 if (err != 0) {
841 shell_error(sh, "Failed to create group: %d", err);
842
843 return -ENOEXEC;
844 }
845
846 shell_print(sh, "Starting %zu streams for %s", snk_cnt + src_cnt, param->name);
847 err = cap_ac_unicast_start(param, connected_conns, snk_uni_streams, snk_cnt,
848 src_uni_streams, src_cnt);
849 if (err != 0) {
850 shell_error(sh, "Failed to start unicast audio: %d", err);
851
852 err = bt_bap_unicast_group_delete(default_unicast_group);
853 if (err != 0) {
854 shell_error(sh, "Failed to delete group: %d", err);
855 } else {
856 default_unicast_group = NULL;
857 }
858
859 return -ENOEXEC;
860 }
861
862 return 0;
863 }
864
865 #if UNICAST_SINK_SUPPORTED
cmd_cap_ac_1(const struct shell * sh,size_t argc,char ** argv)866 static int cmd_cap_ac_1(const struct shell *sh, size_t argc, char **argv)
867 {
868 const struct bap_unicast_ac_param param = {
869 .name = "AC_1",
870 .conn_cnt = 1U,
871 .snk_cnt = {1U},
872 .src_cnt = {0U},
873 .snk_chan_cnt = 1U,
874 .src_chan_cnt = 0U,
875 };
876
877 return cap_ac_unicast(sh, ¶m);
878 }
879 #endif /* UNICAST_SINK_SUPPORTED */
880
881 #if UNICAST_SRC_SUPPORTED
cmd_cap_ac_2(const struct shell * sh,size_t argc,char ** argv)882 static int cmd_cap_ac_2(const struct shell *sh, size_t argc, char **argv)
883 {
884 const struct bap_unicast_ac_param param = {
885 .name = "AC_2",
886 .conn_cnt = 1U,
887 .snk_cnt = {0U},
888 .src_cnt = {1U},
889 .snk_chan_cnt = 0U,
890 .src_chan_cnt = 1U,
891 };
892
893 return cap_ac_unicast(sh, ¶m);
894 }
895 #endif /* UNICAST_SRC_SUPPORTED */
896
897 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_3(const struct shell * sh,size_t argc,char ** argv)898 static int cmd_cap_ac_3(const struct shell *sh, size_t argc, char **argv)
899 {
900 const struct bap_unicast_ac_param param = {
901 .name = "AC_3",
902 .conn_cnt = 1U,
903 .snk_cnt = {1U},
904 .src_cnt = {1U},
905 .snk_chan_cnt = 1U,
906 .src_chan_cnt = 1U,
907 };
908
909 return cap_ac_unicast(sh, ¶m);
910 }
911 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
912
913 #if UNICAST_SINK_SUPPORTED
cmd_cap_ac_4(const struct shell * sh,size_t argc,char ** argv)914 static int cmd_cap_ac_4(const struct shell *sh, size_t argc, char **argv)
915 {
916 const struct bap_unicast_ac_param param = {
917 .name = "AC_4",
918 .conn_cnt = 1,
919 .snk_cnt = {1U},
920 .src_cnt = {0U},
921 .snk_chan_cnt = 2U,
922 .src_chan_cnt = 0U,
923 };
924
925 return cap_ac_unicast(sh, ¶m);
926 }
927 #endif /* UNICAST_SINK_SUPPORTED */
928
929 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_5(const struct shell * sh,size_t argc,char ** argv)930 static int cmd_cap_ac_5(const struct shell *sh, size_t argc, char **argv)
931 {
932 const struct bap_unicast_ac_param param = {
933 .name = "AC_5",
934 .conn_cnt = 1U,
935 .snk_cnt = {1U},
936 .src_cnt = {1U},
937 .snk_chan_cnt = 2U,
938 .src_chan_cnt = 1U,
939 };
940
941 return cap_ac_unicast(sh, ¶m);
942 }
943 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
944
945 #if UNICAST_SINK_SUPPORTED
946 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
cmd_cap_ac_6_i(const struct shell * sh,size_t argc,char ** argv)947 static int cmd_cap_ac_6_i(const struct shell *sh, size_t argc, char **argv)
948 {
949 const struct bap_unicast_ac_param param = {
950 .name = "AC_6_I",
951 .conn_cnt = 1U,
952 .snk_cnt = {2U},
953 .src_cnt = {0U},
954 .snk_chan_cnt = 1U,
955 .src_chan_cnt = 0U,
956 };
957
958 return cap_ac_unicast(sh, ¶m);
959 }
960 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
961
962 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_6_ii(const struct shell * sh,size_t argc,char ** argv)963 static int cmd_cap_ac_6_ii(const struct shell *sh, size_t argc, char **argv)
964 {
965 const struct bap_unicast_ac_param param = {
966 .name = "AC_6_II",
967 .conn_cnt = 2U,
968 .snk_cnt = {1U, 1U},
969 .src_cnt = {0U, 0U},
970 .snk_chan_cnt = 1U,
971 .src_chan_cnt = 0U,
972 };
973
974 return cap_ac_unicast(sh, ¶m);
975 }
976 #endif /* CONFIG_BT_MAX_CONN >= 2 */
977 #endif /* UNICAST_SINK_SUPPORTED */
978
979 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_7_i(const struct shell * sh,size_t argc,char ** argv)980 static int cmd_cap_ac_7_i(const struct shell *sh, size_t argc, char **argv)
981 {
982 const struct bap_unicast_ac_param param = {
983 .name = "AC_7_I",
984 .conn_cnt = 1U,
985 .snk_cnt = {1U}, /* TODO: These should be separate CIS */
986 .src_cnt = {1U}, /* TODO: These should be separate CIS */
987 .snk_chan_cnt = 1U,
988 .src_chan_cnt = 1U,
989 };
990
991 return cap_ac_unicast(sh, ¶m);
992 }
993
994 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_7_ii(const struct shell * sh,size_t argc,char ** argv)995 static int cmd_cap_ac_7_ii(const struct shell *sh, size_t argc, char **argv)
996 {
997 const struct bap_unicast_ac_param param = {
998 .name = "AC_7_II",
999 .conn_cnt = 2U,
1000 .snk_cnt = {1U, 0U},
1001 .src_cnt = {0U, 1U},
1002 .snk_chan_cnt = 1U,
1003 .src_chan_cnt = 1U,
1004 };
1005
1006 return cap_ac_unicast(sh, ¶m);
1007 }
1008 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1009
1010 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
cmd_cap_ac_8_i(const struct shell * sh,size_t argc,char ** argv)1011 static int cmd_cap_ac_8_i(const struct shell *sh, size_t argc, char **argv)
1012 {
1013 const struct bap_unicast_ac_param param = {
1014 .name = "AC_8_I",
1015 .conn_cnt = 1U,
1016 .snk_cnt = {2U},
1017 .src_cnt = {1U},
1018 .snk_chan_cnt = 1U,
1019 .src_chan_cnt = 1U,
1020 };
1021
1022 return cap_ac_unicast(sh, ¶m);
1023 }
1024 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1025
1026 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_8_ii(const struct shell * sh,size_t argc,char ** argv)1027 static int cmd_cap_ac_8_ii(const struct shell *sh, size_t argc, char **argv)
1028 {
1029 const struct bap_unicast_ac_param param = {
1030 .name = "AC_8_II",
1031 .conn_cnt = 2U,
1032 .snk_cnt = {1U, 1U},
1033 .src_cnt = {1U, 0U},
1034 .snk_chan_cnt = 1U,
1035 .src_chan_cnt = 1U,
1036 };
1037
1038 return cap_ac_unicast(sh, ¶m);
1039 }
1040 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1041
1042 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_9_i(const struct shell * sh,size_t argc,char ** argv)1043 static int cmd_cap_ac_9_i(const struct shell *sh, size_t argc, char **argv)
1044 {
1045 const struct bap_unicast_ac_param param = {
1046 .name = "AC_9_I",
1047 .conn_cnt = 1U,
1048 .snk_cnt = {0U},
1049 .src_cnt = {2U},
1050 .snk_chan_cnt = 0U,
1051 .src_chan_cnt = 1U,
1052 };
1053
1054 return cap_ac_unicast(sh, ¶m);
1055 }
1056 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1057
1058 #if CONFIG_BT_MAX_CONN >= 2 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_9_ii(const struct shell * sh,size_t argc,char ** argv)1059 static int cmd_cap_ac_9_ii(const struct shell *sh, size_t argc, char **argv)
1060 {
1061 const struct bap_unicast_ac_param param = {
1062 .name = "AC_9_II",
1063 .conn_cnt = 2U,
1064 .snk_cnt = {0U, 0U},
1065 .src_cnt = {1U, 1U},
1066 .snk_chan_cnt = 0U,
1067 .src_chan_cnt = 1U,
1068 };
1069
1070 return cap_ac_unicast(sh, ¶m);
1071 }
1072 #endif /* CONFIG_BT_MAX_CONN >= 2 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1073
1074 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_10(const struct shell * sh,size_t argc,char ** argv)1075 static int cmd_cap_ac_10(const struct shell *sh, size_t argc, char **argv)
1076 {
1077 const struct bap_unicast_ac_param param = {
1078 .name = "AC_10",
1079 .conn_cnt = 1U,
1080 .snk_cnt = {0U},
1081 .src_cnt = {1U},
1082 .snk_chan_cnt = 0U,
1083 .src_chan_cnt = 2U,
1084 };
1085
1086 return cap_ac_unicast(sh, ¶m);
1087 }
1088 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1089
1090 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_11_i(const struct shell * sh,size_t argc,char ** argv)1091 static int cmd_cap_ac_11_i(const struct shell *sh, size_t argc, char **argv)
1092 {
1093 const struct bap_unicast_ac_param param = {
1094 .name = "AC_11_I",
1095 .conn_cnt = 1U,
1096 .snk_cnt = {2U},
1097 .src_cnt = {2U},
1098 .snk_chan_cnt = 1U,
1099 .src_chan_cnt = 1U,
1100 };
1101
1102 return cap_ac_unicast(sh, ¶m);
1103 }
1104 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && \
1105 * CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 \
1106 */
1107
1108 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_11_ii(const struct shell * sh,size_t argc,char ** argv)1109 static int cmd_cap_ac_11_ii(const struct shell *sh, size_t argc, char **argv)
1110 {
1111 const struct bap_unicast_ac_param param = {
1112 .name = "AC_11_II",
1113 .conn_cnt = 2U,
1114 .snk_cnt = {1U, 1U},
1115 .src_cnt = {1U, 1U},
1116 .snk_chan_cnt = 1U,
1117 .src_chan_cnt = 1U,
1118 };
1119
1120 return cap_ac_unicast(sh, ¶m);
1121 }
1122 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1123 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1124 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
1125
1126 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
cmd_broadcast_start(const struct shell * sh,size_t argc,char * argv[])1127 static int cmd_broadcast_start(const struct shell *sh, size_t argc, char *argv[])
1128 {
1129 struct bt_le_ext_adv *adv = adv_sets[selected_adv];
1130 int err;
1131
1132 if (adv == NULL) {
1133 shell_info(sh, "Extended advertising set is NULL");
1134
1135 return -ENOEXEC;
1136 }
1137
1138 if (default_source.cap_source == NULL || !default_source.is_cap) {
1139 shell_info(sh, "CAP Broadcast source not created");
1140
1141 return -ENOEXEC;
1142 }
1143
1144 err = bt_cap_initiator_broadcast_audio_start(default_source.cap_source,
1145 adv_sets[selected_adv]);
1146 if (err != 0) {
1147 shell_error(sh, "Unable to start broadcast source: %d", err);
1148
1149 return -ENOEXEC;
1150 }
1151
1152 return 0;
1153 }
1154
cmd_broadcast_update(const struct shell * sh,size_t argc,char * argv[])1155 static int cmd_broadcast_update(const struct shell *sh, size_t argc, char *argv[])
1156 {
1157 uint8_t meta[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
1158 size_t len;
1159 int err;
1160
1161 if (default_source.cap_source == NULL || !default_source.is_cap) {
1162 shell_info(sh, "CAP Broadcast source not created");
1163
1164 return -ENOEXEC;
1165 }
1166
1167 len = hex2bin(argv[1], strlen(argv[1]), meta, sizeof(meta));
1168 if (len == 0) {
1169 shell_print(sh, "Unable to parse metadata (len was %zu, max len is %d)",
1170 strlen(argv[1]) / 2U + strlen(argv[1]) % 2U,
1171 CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE);
1172
1173 return -ENOEXEC;
1174 }
1175
1176 err = bt_cap_initiator_broadcast_audio_update(default_source.cap_source, meta, len);
1177 if (err != 0) {
1178 shell_error(sh, "Unable to update broadcast source: %d", err);
1179
1180 return -ENOEXEC;
1181 }
1182
1183 shell_print(sh, "CAP Broadcast source updated with new metadata. Update the advertised "
1184 "base via `bt per-adv-data`");
1185
1186 return 0;
1187 }
1188
cmd_broadcast_stop(const struct shell * sh,size_t argc,char * argv[])1189 static int cmd_broadcast_stop(const struct shell *sh, size_t argc, char *argv[])
1190 {
1191 int err;
1192
1193 if (default_source.cap_source == NULL || !default_source.is_cap) {
1194 shell_info(sh, "CAP Broadcast source not created");
1195
1196 return -ENOEXEC;
1197 }
1198
1199 err = bt_cap_initiator_broadcast_audio_stop(default_source.cap_source);
1200 if (err != 0) {
1201 shell_error(sh, "Unable to stop broadcast source: %d", err);
1202
1203 return -ENOEXEC;
1204 }
1205
1206 return 0;
1207 }
1208
cmd_broadcast_delete(const struct shell * sh,size_t argc,char * argv[])1209 static int cmd_broadcast_delete(const struct shell *sh, size_t argc, char *argv[])
1210 {
1211 int err;
1212
1213 if (default_source.cap_source == NULL || !default_source.is_cap) {
1214 shell_info(sh, "CAP Broadcast source not created");
1215
1216 return -ENOEXEC;
1217 }
1218
1219 err = bt_cap_initiator_broadcast_audio_delete(default_source.cap_source);
1220 if (err != 0) {
1221 shell_error(sh, "Unable to stop broadcast source: %d", err);
1222
1223 return -ENOEXEC;
1224 }
1225
1226 default_source.cap_source = NULL;
1227 default_source.is_cap = false;
1228
1229 return 0;
1230 }
1231
cap_ac_broadcast(const struct shell * sh,size_t argc,char ** argv,const struct bap_broadcast_ac_param * param)1232 int cap_ac_broadcast(const struct shell *sh, size_t argc, char **argv,
1233 const struct bap_broadcast_ac_param *param)
1234 {
1235 /* TODO: Use CAP API when the CAP shell has broadcast support */
1236 struct bt_cap_initiator_broadcast_stream_param stream_params[BAP_UNICAST_AC_MAX_SRC] = {0};
1237 uint8_t stereo_data[] = {
1238 BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1239 BT_AUDIO_LOCATION_FRONT_RIGHT | BT_AUDIO_LOCATION_FRONT_LEFT)};
1240 uint8_t right_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1241 BT_AUDIO_LOCATION_FRONT_RIGHT)};
1242 uint8_t left_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1243 BT_AUDIO_LOCATION_FRONT_LEFT)};
1244 struct bt_cap_initiator_broadcast_subgroup_param subgroup_param = {0};
1245 struct bt_cap_initiator_broadcast_create_param create_param = {0};
1246 struct bt_le_ext_adv *adv;
1247 int err;
1248
1249 if (default_source.cap_source != NULL) {
1250 shell_error(sh, "Broadcast Source already created, please delete first");
1251 return -ENOEXEC;
1252 }
1253
1254 adv = adv_sets[selected_adv];
1255 if (adv == NULL) {
1256 shell_error(sh, "Extended advertising set is NULL");
1257 return -ENOEXEC;
1258 }
1259
1260 copy_broadcast_source_preset(&default_source, &default_broadcast_source_preset);
1261 default_source.qos.sdu *= param->chan_cnt;
1262
1263 for (size_t i = 0U; i < param->stream_cnt; i++) {
1264 stream_params[i].stream = &broadcast_source_streams[i].stream;
1265
1266 if (param->stream_cnt == 1U) {
1267 stream_params[i].data_len = ARRAY_SIZE(stereo_data);
1268 stream_params[i].data = stereo_data;
1269 } else if (i == 0U) {
1270 stream_params[i].data_len = ARRAY_SIZE(left_data);
1271 stream_params[i].data = left_data;
1272 } else if (i == 1U) {
1273 stream_params[i].data_len = ARRAY_SIZE(right_data);
1274 stream_params[i].data = right_data;
1275 }
1276 }
1277
1278 subgroup_param.stream_count = param->stream_cnt;
1279 subgroup_param.stream_params = stream_params;
1280 subgroup_param.codec_cfg = &default_source.codec_cfg;
1281 create_param.subgroup_count = 1U;
1282 create_param.subgroup_params = &subgroup_param;
1283 create_param.qos = &default_source.qos;
1284
1285 err = bt_cap_initiator_broadcast_audio_create(&create_param, &default_source.cap_source);
1286 if (err != 0) {
1287 shell_error(sh, "Failed to create broadcast source: %d", err);
1288 return -ENOEXEC;
1289 }
1290
1291 /* We don't start the broadcast source here, because in order to populate the BASE in the
1292 * periodic advertising data, the broadcast source needs to be created but not started.
1293 */
1294 shell_print(sh,
1295 "CAP Broadcast source for %s created. "
1296 "Start via `cap_initiator broadcast_start`, "
1297 "and update / set the base via `bt per-adv data`",
1298 param->name);
1299 default_source.is_cap = true;
1300
1301 return 0;
1302 }
1303
cmd_cap_ac_12(const struct shell * sh,size_t argc,char ** argv)1304 static int cmd_cap_ac_12(const struct shell *sh, size_t argc, char **argv)
1305 {
1306 const struct bap_broadcast_ac_param param = {
1307 .name = "AC_12",
1308 .stream_cnt = 1U,
1309 .chan_cnt = 1U,
1310 };
1311
1312 return cap_ac_broadcast(sh, argc, argv, ¶m);
1313 }
1314
1315 #if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1
cmd_cap_ac_13(const struct shell * sh,size_t argc,char ** argv)1316 static int cmd_cap_ac_13(const struct shell *sh, size_t argc, char **argv)
1317 {
1318 const struct bap_broadcast_ac_param param = {
1319 .name = "AC_13",
1320 .stream_cnt = 2U,
1321 .chan_cnt = 1U,
1322 };
1323
1324 return cap_ac_broadcast(sh, argc, argv, ¶m);
1325 }
1326 #endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1 */
1327
cmd_cap_ac_14(const struct shell * sh,size_t argc,char ** argv)1328 static int cmd_cap_ac_14(const struct shell *sh, size_t argc, char **argv)
1329 {
1330 const struct bap_broadcast_ac_param param = {
1331 .name = "AC_14",
1332 .stream_cnt = 2U,
1333 .chan_cnt = 2U,
1334 };
1335
1336 return cap_ac_broadcast(sh, argc, argv, ¶m);
1337 }
1338 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1339
cmd_cap_initiator(const struct shell * sh,size_t argc,char ** argv)1340 static int cmd_cap_initiator(const struct shell *sh, size_t argc, char **argv)
1341 {
1342 if (argc > 1) {
1343 shell_error(sh, "%s unknown parameter: %s",
1344 argv[0], argv[1]);
1345 } else {
1346 shell_error(sh, "%s Missing subcommand", argv[0]);
1347 }
1348
1349 return -ENOEXEC;
1350 }
1351
1352 SHELL_STATIC_SUBCMD_SET_CREATE(
1353 cap_initiator_cmds,
1354 #if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
1355 SHELL_CMD_ARG(discover, NULL, "Discover CAS", cmd_cap_initiator_discover, 1, 0),
1356 SHELL_CMD_ARG(unicast_start, NULL,
1357 "Unicast Start [csip] [sinks <cnt> (default 1)] "
1358 "[sources <cnt> (default 1)] "
1359 "[conns (<cnt> | all) (default 1)]",
1360 cmd_cap_initiator_unicast_start, 1, 7),
1361 SHELL_CMD_ARG(unicast_list, NULL, "Unicast list streams", cmd_cap_initiator_unicast_list, 1,
1362 0),
1363 SHELL_CMD_ARG(unicast_update, NULL, "Unicast Update <all | stream [stream [stream...]]>",
1364 cmd_cap_initiator_unicast_update, 2, CAP_UNICAST_CLIENT_STREAM_COUNT),
1365 SHELL_CMD_ARG(unicast_stop, NULL,
1366 "Unicast stop streams [stream [stream [stream...]]] (all by default)",
1367 cmd_cap_initiator_unicast_stop, 1, CAP_UNICAST_CLIENT_STREAM_COUNT),
1368 SHELL_CMD_ARG(unicast_cancel, NULL, "Unicast cancel current procedure",
1369 cmd_cap_initiator_unicast_cancel, 1, 0),
1370 #if UNICAST_SINK_SUPPORTED
1371 SHELL_CMD_ARG(ac_1, NULL, "Unicast audio configuration 1", cmd_cap_ac_1, 1, 0),
1372 #endif /* UNICAST_SINK_SUPPORTED */
1373 #if UNICAST_SRC_SUPPORTED
1374 SHELL_CMD_ARG(ac_2, NULL, "Unicast audio configuration 2", cmd_cap_ac_2, 1, 0),
1375 #endif /* UNICAST_SRC_SUPPORTED */
1376 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1377 SHELL_CMD_ARG(ac_3, NULL, "Unicast audio configuration 3", cmd_cap_ac_3, 1, 0),
1378 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1379 #if UNICAST_SINK_SUPPORTED
1380 SHELL_CMD_ARG(ac_4, NULL, "Unicast audio configuration 4", cmd_cap_ac_4, 1, 0),
1381 #endif /* UNICAST_SINK_SUPPORTED */
1382 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1383 SHELL_CMD_ARG(ac_5, NULL, "Unicast audio configuration 5", cmd_cap_ac_5, 1, 0),
1384 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1385 #if UNICAST_SINK_SUPPORTED
1386 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
1387 SHELL_CMD_ARG(ac_6_i, NULL, "Unicast audio configuration 6(i)", cmd_cap_ac_6_i, 1, 0),
1388 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1389 #if CONFIG_BT_MAX_CONN >= 2
1390 SHELL_CMD_ARG(ac_6_ii, NULL, "Unicast audio configuration 6(ii)", cmd_cap_ac_6_ii, 1, 0),
1391 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1392 #endif /* UNICAST_SINK_SUPPORTED */
1393 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1394 SHELL_CMD_ARG(ac_7_i, NULL, "Unicast audio configuration 7(i)", cmd_cap_ac_7_i, 1, 0),
1395 #if CONFIG_BT_MAX_CONN >= 2
1396 SHELL_CMD_ARG(ac_7_ii, NULL, "Unicast audio configuration 7(ii)", cmd_cap_ac_7_ii, 1, 0),
1397 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1398 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
1399 SHELL_CMD_ARG(ac_8_i, NULL, "Unicast audio configuration 8(i)", cmd_cap_ac_8_i, 1, 0),
1400 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1401 #if CONFIG_BT_MAX_CONN >= 2
1402 SHELL_CMD_ARG(ac_8_ii, NULL, "Unicast audio configuration 8(ii)", cmd_cap_ac_8_ii, 1, 0),
1403 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1404 #if UNICAST_SRC_SUPPORTED
1405 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
1406 SHELL_CMD_ARG(ac_9_i, NULL, "Unicast audio configuration 9(i)", cmd_cap_ac_9_i, 1, 0),
1407 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1408 #if CONFIG_BT_MAX_CONN >= 2
1409 SHELL_CMD_ARG(ac_9_ii, NULL, "Unicast audio configuration 9(ii)", cmd_cap_ac_9_ii, 1, 0),
1410 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1411 SHELL_CMD_ARG(ac_10, NULL, "Unicast audio configuration 10", cmd_cap_ac_10, 1, 0),
1412 #endif /* UNICAST_SRC_SUPPORTED */
1413 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
1414 SHELL_CMD_ARG(ac_11_i, NULL, "Unicast audio configuration 11(i)", cmd_cap_ac_11_i, 1, 0),
1415 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && \
1416 * CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 \
1417 */
1418 #if CONFIG_BT_MAX_CONN >= 2
1419 SHELL_CMD_ARG(ac_11_ii, NULL, "Unicast audio configuration 11(ii)", cmd_cap_ac_11_ii, 1, 0),
1420 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1421 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1422 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
1423 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1424 SHELL_CMD_ARG(broadcast_start, NULL, "", cmd_broadcast_start, 1, 0),
1425 SHELL_CMD_ARG(broadcast_update, NULL, "<meta>", cmd_broadcast_update, 2, 0),
1426 SHELL_CMD_ARG(broadcast_stop, NULL, "", cmd_broadcast_stop, 1, 0),
1427 SHELL_CMD_ARG(broadcast_delete, NULL, "", cmd_broadcast_delete, 1, 0),
1428 SHELL_CMD_ARG(ac_12, NULL, "Broadcast audio configuration 12", cmd_cap_ac_12, 1, 0),
1429 #if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1
1430 SHELL_CMD_ARG(ac_13, NULL, "Broadcast audio configuration 13", cmd_cap_ac_13, 1, 0),
1431 #endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1 */
1432 SHELL_CMD_ARG(ac_14, NULL, "Broadcast audio configuration 14", cmd_cap_ac_14, 1, 0),
1433 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1434 SHELL_SUBCMD_SET_END);
1435
1436 SHELL_CMD_ARG_REGISTER(cap_initiator, &cap_initiator_cmds,
1437 "Bluetooth CAP initiator shell commands",
1438 cmd_cap_initiator, 1, 1);
1439
nonconnectable_ad_data_add(struct bt_data * data_array,const size_t data_array_size)1440 static ssize_t nonconnectable_ad_data_add(struct bt_data *data_array, const size_t data_array_size)
1441 {
1442 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1443 if (default_source.cap_source != NULL && default_source.is_cap) {
1444 static uint8_t ad_cap_broadcast_announcement[5] = {
1445 BT_UUID_16_ENCODE(BT_UUID_BROADCAST_AUDIO_VAL),
1446 };
1447 uint32_t broadcast_id;
1448 int err;
1449
1450 err = bt_rand(&broadcast_id, BT_AUDIO_BROADCAST_ID_SIZE);
1451 if (err) {
1452 printk("Unable to generate broadcast ID: %d\n", err);
1453
1454 return -1;
1455 }
1456
1457 sys_put_le24(broadcast_id, &ad_cap_broadcast_announcement[2]);
1458 data_array[0].type = BT_DATA_SVC_DATA16;
1459 data_array[0].data_len = ARRAY_SIZE(ad_cap_broadcast_announcement);
1460 data_array[0].data = ad_cap_broadcast_announcement;
1461
1462 return 1;
1463 }
1464 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1465
1466 return 0;
1467 }
1468
cap_initiator_ad_data_add(struct bt_data * data_array,const size_t data_array_size,const bool discoverable,const bool connectable)1469 ssize_t cap_initiator_ad_data_add(struct bt_data *data_array, const size_t data_array_size,
1470 const bool discoverable, const bool connectable)
1471 {
1472 if (!discoverable) {
1473 return 0;
1474 }
1475
1476 if (!connectable) {
1477 return nonconnectable_ad_data_add(data_array, data_array_size);
1478 }
1479
1480 return 0;
1481 }
1482
cap_initiator_pa_data_add(struct bt_data * data_array,const size_t data_array_size)1483 ssize_t cap_initiator_pa_data_add(struct bt_data *data_array, const size_t data_array_size)
1484 {
1485 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1486 if (default_source.cap_source != NULL && default_source.is_cap) {
1487 /* Required size of the buffer depends on what has been
1488 * configured. We just use the maximum size possible.
1489 */
1490 NET_BUF_SIMPLE_DEFINE_STATIC(base_buf, UINT8_MAX);
1491 int err;
1492
1493 net_buf_simple_reset(&base_buf);
1494
1495 err = bt_cap_initiator_broadcast_get_base(default_source.cap_source, &base_buf);
1496 if (err != 0) {
1497 printk("Unable to get BASE: %d\n", err);
1498
1499 return -1;
1500 }
1501
1502 data_array[0].type = BT_DATA_SVC_DATA16;
1503 data_array[0].data_len = base_buf.len;
1504 data_array[0].data = base_buf.data;
1505
1506 return 1;
1507 }
1508 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1509
1510 return 0;
1511 }
1512