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 "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
536 err = bt_cap_initiator_unicast_audio_stop(¶m);
537 if (err != 0) {
538 shell_print(sh, "Failed to update unicast audio: %d", err);
539 }
540
541 return err;
542 }
543
cmd_cap_initiator_unicast_cancel(const struct shell * sh,size_t argc,char * argv[])544 static int cmd_cap_initiator_unicast_cancel(const struct shell *sh, size_t argc, char *argv[])
545 {
546 int err = 0;
547
548 err = bt_cap_initiator_unicast_audio_cancel();
549 if (err != 0) {
550 shell_print(sh, "Failed to cancel unicast audio procedure: %d", err);
551 return -ENOEXEC;
552 }
553
554 return 0;
555 }
556
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)557 static int cap_ac_unicast_start(const struct bap_unicast_ac_param *param,
558 struct bt_conn *connected_conns[],
559 struct shell_stream *snk_uni_streams[], size_t snk_cnt,
560 struct shell_stream *src_uni_streams[], size_t src_cnt)
561 {
562 struct bt_cap_unicast_audio_start_stream_param stream_params[BAP_UNICAST_AC_MAX_STREAM] = {
563 0};
564 struct bt_audio_codec_cfg *snk_codec_cfgs[BAP_UNICAST_AC_MAX_SNK] = {0};
565 struct bt_audio_codec_cfg *src_codec_cfgs[BAP_UNICAST_AC_MAX_SRC] = {0};
566 struct bt_cap_stream *snk_cap_streams[BAP_UNICAST_AC_MAX_SNK] = {0};
567 struct bt_cap_stream *src_cap_streams[BAP_UNICAST_AC_MAX_SRC] = {0};
568 struct bt_cap_unicast_audio_start_param start_param = {0};
569 struct bt_bap_ep *snk_eps[BAP_UNICAST_AC_MAX_SNK] = {0};
570 struct bt_bap_ep *src_eps[BAP_UNICAST_AC_MAX_SRC] = {0};
571 size_t snk_stream_cnt = 0U;
572 size_t src_stream_cnt = 0U;
573 size_t stream_cnt = 0U;
574 size_t snk_ep_cnt = 0U;
575 size_t src_ep_cnt = 0U;
576
577 for (size_t i = 0U; i < param->conn_cnt; i++) {
578 #if UNICAST_SINK_SUPPORTED
579 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
580 snk_eps[snk_ep_cnt] = snks[bt_conn_index(connected_conns[i])][j];
581 if (snk_eps[snk_ep_cnt] == NULL) {
582 shell_error(ctx_shell, "No sink[%zu][%zu] endpoint available", i,
583 j);
584
585 return -ENOEXEC;
586 }
587 snk_ep_cnt++;
588 }
589 #endif /* UNICAST_SINK_SUPPORTED */
590
591 #if UNICAST_SRC_SUPPORTED
592 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
593 src_eps[src_ep_cnt] = srcs[bt_conn_index(connected_conns[i])][j];
594 if (src_eps[src_ep_cnt] == NULL) {
595 shell_error(ctx_shell, "No source[%zu][%zu] endpoint available", i,
596 j);
597
598 return -ENOEXEC;
599 }
600 src_ep_cnt++;
601 }
602 #endif /* UNICAST_SRC_SUPPORTED */
603 }
604
605 if (snk_ep_cnt != snk_cnt) {
606 shell_error(ctx_shell, "Sink endpoint and stream count mismatch: %zu != %zu",
607 snk_ep_cnt, snk_cnt);
608
609 return -ENOEXEC;
610 }
611
612 if (src_ep_cnt != src_cnt) {
613 shell_error(ctx_shell, "Source endpoint and stream count mismatch: %zu != %zu",
614 src_ep_cnt, src_cnt);
615
616 return -ENOEXEC;
617 }
618
619 /* Setup arrays of parameters based on the preset for easier access. This also copies the
620 * preset so that we can modify them (e.g. update the metadata)
621 */
622 for (size_t i = 0U; i < snk_cnt; i++) {
623 snk_cap_streams[i] = &snk_uni_streams[i]->stream;
624 snk_codec_cfgs[i] = &snk_uni_streams[i]->codec_cfg;
625 }
626
627 for (size_t i = 0U; i < src_cnt; i++) {
628 src_cap_streams[i] = &src_uni_streams[i]->stream;
629 src_codec_cfgs[i] = &src_uni_streams[i]->codec_cfg;
630 }
631
632 /* CAP Start */
633 for (size_t i = 0U; i < param->conn_cnt; i++) {
634 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
635 struct bt_cap_unicast_audio_start_stream_param *stream_param =
636 &stream_params[stream_cnt];
637
638 stream_param->member.member = connected_conns[i];
639 stream_param->codec_cfg = snk_codec_cfgs[snk_stream_cnt];
640 stream_param->ep = snk_eps[snk_stream_cnt];
641 stream_param->stream = snk_cap_streams[snk_stream_cnt];
642
643 snk_stream_cnt++;
644 stream_cnt++;
645 }
646
647 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
648 struct bt_cap_unicast_audio_start_stream_param *stream_param =
649 &stream_params[stream_cnt];
650
651 stream_param->member.member = connected_conns[i];
652 stream_param->codec_cfg = src_codec_cfgs[src_stream_cnt];
653 stream_param->ep = src_eps[src_stream_cnt];
654 stream_param->stream = src_cap_streams[src_stream_cnt];
655
656 src_stream_cnt++;
657 stream_cnt++;
658 }
659 }
660
661 start_param.stream_params = stream_params;
662 start_param.count = stream_cnt;
663 start_param.type = BT_CAP_SET_TYPE_AD_HOC;
664
665 return bt_cap_initiator_unicast_audio_start(&start_param);
666 }
667
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)668 static int set_codec_config(const struct shell *sh, struct shell_stream *sh_stream,
669 struct named_lc3_preset *preset, size_t conn_cnt, size_t ep_cnt,
670 size_t chan_cnt, size_t conn_index, size_t ep_index)
671 {
672 enum bt_audio_location new_chan_alloc;
673 enum bt_audio_location chan_alloc;
674 int err;
675
676 copy_unicast_stream_preset(sh_stream, preset);
677
678 if (chan_cnt == 1U) {
679 /* - When we have a single channel on a single connection then we make it mono
680 * - When we have a single channel on a multiple connections then we make it left on
681 * the first connection and right on the second connection
682 * - When we have multiple channels streams for a connection, we make them either
683 * left or right, regardless of the connection count
684 */
685 if (ep_cnt == 1) {
686 if (conn_cnt == 1) {
687 new_chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO;
688 } else if (conn_cnt == 2) {
689 if (conn_index == 0) {
690 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT;
691 } else if (conn_index == 1) {
692 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT;
693 } else {
694 return 0;
695 }
696 } else {
697 return 0;
698 }
699 } else if (ep_cnt == 2) {
700 if (ep_index == 0) {
701 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT;
702 } else if (ep_index == 1) {
703 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT;
704 } else {
705 return 0;
706 }
707 } else {
708 return 0;
709 }
710 } else if (chan_cnt == 2U) {
711 /* Some audio configuration requires multiple sink channels,
712 * so multiply the SDU based on the channel count
713 */
714 sh_stream->qos.sdu *= chan_cnt;
715
716 /* If a stream has 2 channels, we make it stereo */
717 new_chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT;
718
719 } else {
720 return 0;
721 }
722
723 err = bt_audio_codec_cfg_get_chan_allocation(&sh_stream->codec_cfg, &chan_alloc, false);
724 if (err != 0) {
725 if (err == -ENODATA) {
726 chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO;
727 }
728 }
729
730 if (chan_alloc != new_chan_alloc) {
731 shell_info(sh,
732 "[%zu][%zu]: Overwriting existing channel allocation 0x%08X with 0x%08X",
733 conn_index, ep_index, chan_alloc, new_chan_alloc);
734
735 err = bt_audio_codec_cfg_set_chan_allocation(&sh_stream->codec_cfg, new_chan_alloc);
736 if (err < 0) {
737 return err;
738 }
739 }
740
741 return 0;
742 }
743
cap_ac_unicast(const struct shell * sh,const struct bap_unicast_ac_param * param)744 int cap_ac_unicast(const struct shell *sh, const struct bap_unicast_ac_param *param)
745 {
746 /* Allocate params large enough for any params, but only use what is required */
747 struct bt_conn *connected_conns[BAP_UNICAST_AC_MAX_CONN] = {0};
748 struct shell_stream *snk_uni_streams[BAP_UNICAST_AC_MAX_SNK];
749 struct shell_stream *src_uni_streams[BAP_UNICAST_AC_MAX_SRC];
750 size_t conn_avail_cnt;
751 size_t snk_cnt = 0;
752 size_t src_cnt = 0;
753 int err;
754
755 if (default_unicast_group != NULL) {
756 shell_error(sh, "Unicast Group already exist, please delete first");
757 return -ENOEXEC;
758 }
759
760 if (param->conn_cnt > BAP_UNICAST_AC_MAX_CONN) {
761 shell_error(sh, "Invalid conn_cnt: %zu", param->conn_cnt);
762 return -ENOEXEC;
763 }
764
765 for (size_t i = 0; i < param->conn_cnt; i++) {
766 /* Verify conn values */
767 if (param->snk_cnt[i] > BAP_UNICAST_AC_MAX_SNK) {
768 shell_error(sh, "Invalid conn_snk_cnt[%zu]: %zu", i, param->snk_cnt[i]);
769 return -ENOEXEC;
770 }
771
772 if (param->src_cnt[i] > BAP_UNICAST_AC_MAX_SRC) {
773 shell_error(sh, "Invalid conn_src_cnt[%zu]: %zu", i, param->src_cnt[i]);
774 return -ENOEXEC;
775 }
776 }
777
778 /* Populate the array of connected connections */
779 bt_conn_foreach(BT_CONN_TYPE_LE, populate_connected_conns, (void *)connected_conns);
780 for (conn_avail_cnt = 0; conn_avail_cnt < ARRAY_SIZE(connected_conns); conn_avail_cnt++) {
781 if (connected_conns[conn_avail_cnt] == NULL) {
782 break;
783 }
784 }
785
786 if (conn_avail_cnt < param->conn_cnt) {
787 shell_error(sh,
788 "Only %zu/%u connected devices, please connect additional devices for "
789 "this audio configuration",
790 conn_avail_cnt, param->conn_cnt);
791 return -ENOEXEC;
792 }
793
794 /* Set all endpoints from multiple connections in a single array, and verify that the known
795 * endpoints matches the audio configuration
796 */
797 for (size_t i = 0U; i < param->conn_cnt; i++) {
798 for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
799 struct shell_stream *snk_uni_stream;
800
801 snk_uni_stream = snk_uni_streams[snk_cnt] = &unicast_streams[snk_cnt];
802
803 err = set_codec_config(sh, snk_uni_stream, &default_sink_preset,
804 param->conn_cnt, param->snk_cnt[i],
805 param->snk_chan_cnt, i, j);
806 if (err != 0) {
807 shell_error(sh, "Failed to set codec configuration: %d", err);
808
809 return -ENOEXEC;
810 }
811
812 snk_cnt++;
813 }
814
815 for (size_t j = 0U; j < param->src_cnt[i]; j++) {
816 struct shell_stream *src_uni_stream;
817
818 src_uni_stream = snk_uni_streams[src_cnt] = &unicast_streams[src_cnt];
819
820 err = set_codec_config(sh, src_uni_stream, &default_source_preset,
821 param->conn_cnt, param->src_cnt[i],
822 param->src_chan_cnt, i, j);
823 if (err != 0) {
824 shell_error(sh, "Failed to set codec configuration: %d", err);
825
826 return -ENOEXEC;
827 }
828
829 src_cnt++;
830 }
831 }
832
833 if (!ctx_shell) {
834 ctx_shell = sh;
835 }
836
837 err = bap_ac_create_unicast_group(param, snk_uni_streams, snk_cnt, src_uni_streams,
838 src_cnt);
839 if (err != 0) {
840 shell_error(sh, "Failed to create group: %d", err);
841
842 return -ENOEXEC;
843 }
844
845 shell_print(sh, "Starting %zu streams for %s", snk_cnt + src_cnt, param->name);
846 err = cap_ac_unicast_start(param, connected_conns, snk_uni_streams, snk_cnt,
847 src_uni_streams, src_cnt);
848 if (err != 0) {
849 shell_error(sh, "Failed to start unicast audio: %d", err);
850
851 err = bt_bap_unicast_group_delete(default_unicast_group);
852 if (err != 0) {
853 shell_error(sh, "Failed to delete group: %d", err);
854 } else {
855 default_unicast_group = NULL;
856 }
857
858 return -ENOEXEC;
859 }
860
861 return 0;
862 }
863
864 #if UNICAST_SINK_SUPPORTED
cmd_cap_ac_1(const struct shell * sh,size_t argc,char ** argv)865 static int cmd_cap_ac_1(const struct shell *sh, size_t argc, char **argv)
866 {
867 const struct bap_unicast_ac_param param = {
868 .name = "AC_1",
869 .conn_cnt = 1U,
870 .snk_cnt = {1U},
871 .src_cnt = {0U},
872 .snk_chan_cnt = 1U,
873 .src_chan_cnt = 0U,
874 };
875
876 return cap_ac_unicast(sh, ¶m);
877 }
878 #endif /* UNICAST_SINK_SUPPORTED */
879
880 #if UNICAST_SRC_SUPPORTED
cmd_cap_ac_2(const struct shell * sh,size_t argc,char ** argv)881 static int cmd_cap_ac_2(const struct shell *sh, size_t argc, char **argv)
882 {
883 const struct bap_unicast_ac_param param = {
884 .name = "AC_2",
885 .conn_cnt = 1U,
886 .snk_cnt = {0U},
887 .src_cnt = {1U},
888 .snk_chan_cnt = 0U,
889 .src_chan_cnt = 1U,
890 };
891
892 return cap_ac_unicast(sh, ¶m);
893 }
894 #endif /* UNICAST_SRC_SUPPORTED */
895
896 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_3(const struct shell * sh,size_t argc,char ** argv)897 static int cmd_cap_ac_3(const struct shell *sh, size_t argc, char **argv)
898 {
899 const struct bap_unicast_ac_param param = {
900 .name = "AC_3",
901 .conn_cnt = 1U,
902 .snk_cnt = {1U},
903 .src_cnt = {1U},
904 .snk_chan_cnt = 1U,
905 .src_chan_cnt = 1U,
906 };
907
908 return cap_ac_unicast(sh, ¶m);
909 }
910 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
911
912 #if UNICAST_SINK_SUPPORTED
cmd_cap_ac_4(const struct shell * sh,size_t argc,char ** argv)913 static int cmd_cap_ac_4(const struct shell *sh, size_t argc, char **argv)
914 {
915 const struct bap_unicast_ac_param param = {
916 .name = "AC_4",
917 .conn_cnt = 1,
918 .snk_cnt = {1U},
919 .src_cnt = {0U},
920 .snk_chan_cnt = 2U,
921 .src_chan_cnt = 0U,
922 };
923
924 return cap_ac_unicast(sh, ¶m);
925 }
926 #endif /* UNICAST_SINK_SUPPORTED */
927
928 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_5(const struct shell * sh,size_t argc,char ** argv)929 static int cmd_cap_ac_5(const struct shell *sh, size_t argc, char **argv)
930 {
931 const struct bap_unicast_ac_param param = {
932 .name = "AC_5",
933 .conn_cnt = 1U,
934 .snk_cnt = {1U},
935 .src_cnt = {1U},
936 .snk_chan_cnt = 2U,
937 .src_chan_cnt = 1U,
938 };
939
940 return cap_ac_unicast(sh, ¶m);
941 }
942 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
943
944 #if UNICAST_SINK_SUPPORTED
945 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
cmd_cap_ac_6_i(const struct shell * sh,size_t argc,char ** argv)946 static int cmd_cap_ac_6_i(const struct shell *sh, size_t argc, char **argv)
947 {
948 const struct bap_unicast_ac_param param = {
949 .name = "AC_6_I",
950 .conn_cnt = 1U,
951 .snk_cnt = {2U},
952 .src_cnt = {0U},
953 .snk_chan_cnt = 1U,
954 .src_chan_cnt = 0U,
955 };
956
957 return cap_ac_unicast(sh, ¶m);
958 }
959 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
960
961 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_6_ii(const struct shell * sh,size_t argc,char ** argv)962 static int cmd_cap_ac_6_ii(const struct shell *sh, size_t argc, char **argv)
963 {
964 const struct bap_unicast_ac_param param = {
965 .name = "AC_6_II",
966 .conn_cnt = 2U,
967 .snk_cnt = {1U, 1U},
968 .src_cnt = {0U, 0U},
969 .snk_chan_cnt = 1U,
970 .src_chan_cnt = 0U,
971 };
972
973 return cap_ac_unicast(sh, ¶m);
974 }
975 #endif /* CONFIG_BT_MAX_CONN >= 2 */
976 #endif /* UNICAST_SINK_SUPPORTED */
977
978 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
cmd_cap_ac_7_i(const struct shell * sh,size_t argc,char ** argv)979 static int cmd_cap_ac_7_i(const struct shell *sh, size_t argc, char **argv)
980 {
981 const struct bap_unicast_ac_param param = {
982 .name = "AC_7_I",
983 .conn_cnt = 1U,
984 .snk_cnt = {1U}, /* TODO: These should be separate CIS */
985 .src_cnt = {1U}, /* TODO: These should be separate CIS */
986 .snk_chan_cnt = 1U,
987 .src_chan_cnt = 1U,
988 };
989
990 return cap_ac_unicast(sh, ¶m);
991 }
992
993 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_7_ii(const struct shell * sh,size_t argc,char ** argv)994 static int cmd_cap_ac_7_ii(const struct shell *sh, size_t argc, char **argv)
995 {
996 const struct bap_unicast_ac_param param = {
997 .name = "AC_7_II",
998 .conn_cnt = 2U,
999 .snk_cnt = {1U, 0U},
1000 .src_cnt = {0U, 1U},
1001 .snk_chan_cnt = 1U,
1002 .src_chan_cnt = 1U,
1003 };
1004
1005 return cap_ac_unicast(sh, ¶m);
1006 }
1007 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1008
1009 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
cmd_cap_ac_8_i(const struct shell * sh,size_t argc,char ** argv)1010 static int cmd_cap_ac_8_i(const struct shell *sh, size_t argc, char **argv)
1011 {
1012 const struct bap_unicast_ac_param param = {
1013 .name = "AC_8_I",
1014 .conn_cnt = 1U,
1015 .snk_cnt = {2U},
1016 .src_cnt = {1U},
1017 .snk_chan_cnt = 1U,
1018 .src_chan_cnt = 1U,
1019 };
1020
1021 return cap_ac_unicast(sh, ¶m);
1022 }
1023 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1024
1025 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_8_ii(const struct shell * sh,size_t argc,char ** argv)1026 static int cmd_cap_ac_8_ii(const struct shell *sh, size_t argc, char **argv)
1027 {
1028 const struct bap_unicast_ac_param param = {
1029 .name = "AC_8_II",
1030 .conn_cnt = 2U,
1031 .snk_cnt = {1U, 1U},
1032 .src_cnt = {1U, 0U},
1033 .snk_chan_cnt = 1U,
1034 .src_chan_cnt = 1U,
1035 };
1036
1037 return cap_ac_unicast(sh, ¶m);
1038 }
1039 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1040
1041 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_9_i(const struct shell * sh,size_t argc,char ** argv)1042 static int cmd_cap_ac_9_i(const struct shell *sh, size_t argc, char **argv)
1043 {
1044 const struct bap_unicast_ac_param param = {
1045 .name = "AC_9_I",
1046 .conn_cnt = 1U,
1047 .snk_cnt = {0U},
1048 .src_cnt = {2U},
1049 .snk_chan_cnt = 0U,
1050 .src_chan_cnt = 1U,
1051 };
1052
1053 return cap_ac_unicast(sh, ¶m);
1054 }
1055 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1056
1057 #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)1058 static int cmd_cap_ac_9_ii(const struct shell *sh, size_t argc, char **argv)
1059 {
1060 const struct bap_unicast_ac_param param = {
1061 .name = "AC_9_II",
1062 .conn_cnt = 2U,
1063 .snk_cnt = {0U, 0U},
1064 .src_cnt = {1U, 1U},
1065 .snk_chan_cnt = 0U,
1066 .src_chan_cnt = 1U,
1067 };
1068
1069 return cap_ac_unicast(sh, ¶m);
1070 }
1071 #endif /* CONFIG_BT_MAX_CONN >= 2 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1072
1073 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
cmd_cap_ac_10(const struct shell * sh,size_t argc,char ** argv)1074 static int cmd_cap_ac_10(const struct shell *sh, size_t argc, char **argv)
1075 {
1076 const struct bap_unicast_ac_param param = {
1077 .name = "AC_10",
1078 .conn_cnt = 1U,
1079 .snk_cnt = {0U},
1080 .src_cnt = {1U},
1081 .snk_chan_cnt = 0U,
1082 .src_chan_cnt = 2U,
1083 };
1084
1085 return cap_ac_unicast(sh, ¶m);
1086 }
1087 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1088
1089 #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)1090 static int cmd_cap_ac_11_i(const struct shell *sh, size_t argc, char **argv)
1091 {
1092 const struct bap_unicast_ac_param param = {
1093 .name = "AC_11_I",
1094 .conn_cnt = 1U,
1095 .snk_cnt = {2U},
1096 .src_cnt = {2U},
1097 .snk_chan_cnt = 1U,
1098 .src_chan_cnt = 1U,
1099 };
1100
1101 return cap_ac_unicast(sh, ¶m);
1102 }
1103 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && \
1104 * CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 \
1105 */
1106
1107 #if CONFIG_BT_MAX_CONN >= 2
cmd_cap_ac_11_ii(const struct shell * sh,size_t argc,char ** argv)1108 static int cmd_cap_ac_11_ii(const struct shell *sh, size_t argc, char **argv)
1109 {
1110 const struct bap_unicast_ac_param param = {
1111 .name = "AC_11_II",
1112 .conn_cnt = 2U,
1113 .snk_cnt = {1U, 1U},
1114 .src_cnt = {1U, 1U},
1115 .snk_chan_cnt = 1U,
1116 .src_chan_cnt = 1U,
1117 };
1118
1119 return cap_ac_unicast(sh, ¶m);
1120 }
1121 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1122 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1123 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
1124
1125 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
cmd_broadcast_start(const struct shell * sh,size_t argc,char * argv[])1126 static int cmd_broadcast_start(const struct shell *sh, size_t argc, char *argv[])
1127 {
1128 struct bt_le_ext_adv *adv = adv_sets[selected_adv];
1129 int err;
1130
1131 if (adv == NULL) {
1132 shell_info(sh, "Extended advertising set is NULL");
1133
1134 return -ENOEXEC;
1135 }
1136
1137 if (default_source.cap_source == NULL || !default_source.is_cap) {
1138 shell_info(sh, "CAP Broadcast source not created");
1139
1140 return -ENOEXEC;
1141 }
1142
1143 err = bt_cap_initiator_broadcast_audio_start(default_source.cap_source,
1144 adv_sets[selected_adv]);
1145 if (err != 0) {
1146 shell_error(sh, "Unable to start broadcast source: %d", err);
1147
1148 return -ENOEXEC;
1149 }
1150
1151 return 0;
1152 }
1153
cmd_broadcast_update(const struct shell * sh,size_t argc,char * argv[])1154 static int cmd_broadcast_update(const struct shell *sh, size_t argc, char *argv[])
1155 {
1156 uint8_t meta[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
1157 size_t len;
1158 int err;
1159
1160 if (default_source.cap_source == NULL || !default_source.is_cap) {
1161 shell_info(sh, "CAP Broadcast source not created");
1162
1163 return -ENOEXEC;
1164 }
1165
1166 len = hex2bin(argv[1], strlen(argv[1]), meta, sizeof(meta));
1167 if (len == 0) {
1168 shell_print(sh, "Unable to parse metadata (len was %zu, max len is %d)",
1169 strlen(argv[1]) / 2U + strlen(argv[1]) % 2U,
1170 CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE);
1171
1172 return -ENOEXEC;
1173 }
1174
1175 err = bt_cap_initiator_broadcast_audio_update(default_source.cap_source, meta, len);
1176 if (err != 0) {
1177 shell_error(sh, "Unable to update broadcast source: %d", err);
1178
1179 return -ENOEXEC;
1180 }
1181
1182 shell_print(sh, "CAP Broadcast source updated with new metadata. Update the advertised "
1183 "base via `bt per-adv-data`");
1184
1185 return 0;
1186 }
1187
cmd_broadcast_stop(const struct shell * sh,size_t argc,char * argv[])1188 static int cmd_broadcast_stop(const struct shell *sh, size_t argc, char *argv[])
1189 {
1190 int err;
1191
1192 if (default_source.cap_source == NULL || !default_source.is_cap) {
1193 shell_info(sh, "CAP Broadcast source not created");
1194
1195 return -ENOEXEC;
1196 }
1197
1198 err = bt_cap_initiator_broadcast_audio_stop(default_source.cap_source);
1199 if (err != 0) {
1200 shell_error(sh, "Unable to stop broadcast source: %d", err);
1201
1202 return -ENOEXEC;
1203 }
1204
1205 return 0;
1206 }
1207
cmd_broadcast_delete(const struct shell * sh,size_t argc,char * argv[])1208 static int cmd_broadcast_delete(const struct shell *sh, size_t argc, char *argv[])
1209 {
1210 int err;
1211
1212 if (default_source.cap_source == NULL || !default_source.is_cap) {
1213 shell_info(sh, "CAP Broadcast source not created");
1214
1215 return -ENOEXEC;
1216 }
1217
1218 err = bt_cap_initiator_broadcast_audio_delete(default_source.cap_source);
1219 if (err != 0) {
1220 shell_error(sh, "Unable to stop broadcast source: %d", err);
1221
1222 return -ENOEXEC;
1223 }
1224
1225 default_source.cap_source = NULL;
1226 default_source.is_cap = false;
1227
1228 return 0;
1229 }
1230
cap_ac_broadcast(const struct shell * sh,size_t argc,char ** argv,const struct bap_broadcast_ac_param * param)1231 int cap_ac_broadcast(const struct shell *sh, size_t argc, char **argv,
1232 const struct bap_broadcast_ac_param *param)
1233 {
1234 /* TODO: Use CAP API when the CAP shell has broadcast support */
1235 struct bt_cap_initiator_broadcast_stream_param stream_params[BAP_UNICAST_AC_MAX_SRC] = {0};
1236 uint8_t stereo_data[] = {
1237 BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1238 BT_AUDIO_LOCATION_FRONT_RIGHT | BT_AUDIO_LOCATION_FRONT_LEFT)};
1239 uint8_t right_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1240 BT_AUDIO_LOCATION_FRONT_RIGHT)};
1241 uint8_t left_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
1242 BT_AUDIO_LOCATION_FRONT_LEFT)};
1243 struct bt_cap_initiator_broadcast_subgroup_param subgroup_param = {0};
1244 struct bt_cap_initiator_broadcast_create_param create_param = {0};
1245 struct bt_le_ext_adv *adv;
1246 int err;
1247
1248 if (default_source.cap_source != NULL) {
1249 shell_error(sh, "Broadcast Source already created, please delete first");
1250 return -ENOEXEC;
1251 }
1252
1253 adv = adv_sets[selected_adv];
1254 if (adv == NULL) {
1255 shell_error(sh, "Extended advertising set is NULL");
1256 return -ENOEXEC;
1257 }
1258
1259 copy_broadcast_source_preset(&default_source, &default_broadcast_source_preset);
1260 default_source.qos.sdu *= param->chan_cnt;
1261
1262 for (size_t i = 0U; i < param->stream_cnt; i++) {
1263 stream_params[i].stream = &broadcast_source_streams[i].stream;
1264
1265 if (param->stream_cnt == 1U) {
1266 stream_params[i].data_len = ARRAY_SIZE(stereo_data);
1267 stream_params[i].data = stereo_data;
1268 } else if (i == 0U) {
1269 stream_params[i].data_len = ARRAY_SIZE(left_data);
1270 stream_params[i].data = left_data;
1271 } else if (i == 1U) {
1272 stream_params[i].data_len = ARRAY_SIZE(right_data);
1273 stream_params[i].data = right_data;
1274 }
1275 }
1276
1277 subgroup_param.stream_count = param->stream_cnt;
1278 subgroup_param.stream_params = stream_params;
1279 subgroup_param.codec_cfg = &default_source.codec_cfg;
1280 create_param.subgroup_count = 1U;
1281 create_param.subgroup_params = &subgroup_param;
1282 create_param.qos = &default_source.qos;
1283
1284 err = bt_cap_initiator_broadcast_audio_create(&create_param, &default_source.cap_source);
1285 if (err != 0) {
1286 shell_error(sh, "Failed to create broadcast source: %d", err);
1287 return -ENOEXEC;
1288 }
1289
1290 /* We don't start the broadcast source here, because in order to populate the BASE in the
1291 * periodic advertising data, the broadcast source needs to be created but not started.
1292 */
1293 shell_print(sh,
1294 "CAP Broadcast source for %s created. "
1295 "Start via `cap_initiator broadcast_start`, "
1296 "and update / set the base via `bt per-adv data`",
1297 param->name);
1298 default_source.is_cap = true;
1299
1300 return 0;
1301 }
1302
cmd_cap_ac_12(const struct shell * sh,size_t argc,char ** argv)1303 static int cmd_cap_ac_12(const struct shell *sh, size_t argc, char **argv)
1304 {
1305 const struct bap_broadcast_ac_param param = {
1306 .name = "AC_12",
1307 .stream_cnt = 1U,
1308 .chan_cnt = 1U,
1309 };
1310
1311 return cap_ac_broadcast(sh, argc, argv, ¶m);
1312 }
1313
1314 #if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1
cmd_cap_ac_13(const struct shell * sh,size_t argc,char ** argv)1315 static int cmd_cap_ac_13(const struct shell *sh, size_t argc, char **argv)
1316 {
1317 const struct bap_broadcast_ac_param param = {
1318 .name = "AC_13",
1319 .stream_cnt = 2U,
1320 .chan_cnt = 1U,
1321 };
1322
1323 return cap_ac_broadcast(sh, argc, argv, ¶m);
1324 }
1325 #endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1 */
1326
cmd_cap_ac_14(const struct shell * sh,size_t argc,char ** argv)1327 static int cmd_cap_ac_14(const struct shell *sh, size_t argc, char **argv)
1328 {
1329 const struct bap_broadcast_ac_param param = {
1330 .name = "AC_14",
1331 .stream_cnt = 2U,
1332 .chan_cnt = 2U,
1333 };
1334
1335 return cap_ac_broadcast(sh, argc, argv, ¶m);
1336 }
1337 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1338
cmd_cap_initiator(const struct shell * sh,size_t argc,char ** argv)1339 static int cmd_cap_initiator(const struct shell *sh, size_t argc, char **argv)
1340 {
1341 if (argc > 1) {
1342 shell_error(sh, "%s unknown parameter: %s",
1343 argv[0], argv[1]);
1344 } else {
1345 shell_error(sh, "%s Missing subcommand", argv[0]);
1346 }
1347
1348 return -ENOEXEC;
1349 }
1350
1351 SHELL_STATIC_SUBCMD_SET_CREATE(
1352 cap_initiator_cmds,
1353 #if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
1354 SHELL_CMD_ARG(discover, NULL, "Discover CAS", cmd_cap_initiator_discover, 1, 0),
1355 SHELL_CMD_ARG(unicast_start, NULL,
1356 "Unicast Start [csip] [sinks <cnt> (default 1)] "
1357 "[sources <cnt> (default 1)] "
1358 "[conns (<cnt> | all) (default 1)]",
1359 cmd_cap_initiator_unicast_start, 1, 7),
1360 SHELL_CMD_ARG(unicast_list, NULL, "Unicast list streams", cmd_cap_initiator_unicast_list, 1,
1361 0),
1362 SHELL_CMD_ARG(unicast_update, NULL, "Unicast Update <all | stream [stream [stream...]]>",
1363 cmd_cap_initiator_unicast_update, 2, CAP_UNICAST_CLIENT_STREAM_COUNT),
1364 SHELL_CMD_ARG(unicast_stop, NULL,
1365 "Unicast stop streams [stream [stream [stream...]]] (all by default)",
1366 cmd_cap_initiator_unicast_stop, 1, CAP_UNICAST_CLIENT_STREAM_COUNT),
1367 SHELL_CMD_ARG(unicast_cancel, NULL, "Unicast cancel current procedure",
1368 cmd_cap_initiator_unicast_cancel, 1, 0),
1369 #if UNICAST_SINK_SUPPORTED
1370 SHELL_CMD_ARG(ac_1, NULL, "Unicast audio configuration 1", cmd_cap_ac_1, 1, 0),
1371 #endif /* UNICAST_SINK_SUPPORTED */
1372 #if UNICAST_SRC_SUPPORTED
1373 SHELL_CMD_ARG(ac_2, NULL, "Unicast audio configuration 2", cmd_cap_ac_2, 1, 0),
1374 #endif /* UNICAST_SRC_SUPPORTED */
1375 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1376 SHELL_CMD_ARG(ac_3, NULL, "Unicast audio configuration 3", cmd_cap_ac_3, 1, 0),
1377 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1378 #if UNICAST_SINK_SUPPORTED
1379 SHELL_CMD_ARG(ac_4, NULL, "Unicast audio configuration 4", cmd_cap_ac_4, 1, 0),
1380 #endif /* UNICAST_SINK_SUPPORTED */
1381 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1382 SHELL_CMD_ARG(ac_5, NULL, "Unicast audio configuration 5", cmd_cap_ac_5, 1, 0),
1383 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1384 #if UNICAST_SINK_SUPPORTED
1385 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
1386 SHELL_CMD_ARG(ac_6_i, NULL, "Unicast audio configuration 6(i)", cmd_cap_ac_6_i, 1, 0),
1387 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1388 #if CONFIG_BT_MAX_CONN >= 2
1389 SHELL_CMD_ARG(ac_6_ii, NULL, "Unicast audio configuration 6(ii)", cmd_cap_ac_6_ii, 1, 0),
1390 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1391 #endif /* UNICAST_SINK_SUPPORTED */
1392 #if UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED
1393 SHELL_CMD_ARG(ac_7_i, NULL, "Unicast audio configuration 7(i)", cmd_cap_ac_7_i, 1, 0),
1394 #if CONFIG_BT_MAX_CONN >= 2
1395 SHELL_CMD_ARG(ac_7_ii, NULL, "Unicast audio configuration 7(ii)", cmd_cap_ac_7_ii, 1, 0),
1396 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1397 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1
1398 SHELL_CMD_ARG(ac_8_i, NULL, "Unicast audio configuration 8(i)", cmd_cap_ac_8_i, 1, 0),
1399 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 */
1400 #if CONFIG_BT_MAX_CONN >= 2
1401 SHELL_CMD_ARG(ac_8_ii, NULL, "Unicast audio configuration 8(ii)", cmd_cap_ac_8_ii, 1, 0),
1402 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1403 #if UNICAST_SRC_SUPPORTED
1404 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
1405 SHELL_CMD_ARG(ac_9_i, NULL, "Unicast audio configuration 9(i)", cmd_cap_ac_9_i, 1, 0),
1406 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 */
1407 #if CONFIG_BT_MAX_CONN >= 2
1408 SHELL_CMD_ARG(ac_9_ii, NULL, "Unicast audio configuration 9(ii)", cmd_cap_ac_9_ii, 1, 0),
1409 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1410 SHELL_CMD_ARG(ac_10, NULL, "Unicast audio configuration 10", cmd_cap_ac_10, 1, 0),
1411 #endif /* UNICAST_SRC_SUPPORTED */
1412 #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1
1413 SHELL_CMD_ARG(ac_11_i, NULL, "Unicast audio configuration 11(i)", cmd_cap_ac_11_i, 1, 0),
1414 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 1 && \
1415 * CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 1 \
1416 */
1417 #if CONFIG_BT_MAX_CONN >= 2
1418 SHELL_CMD_ARG(ac_11_ii, NULL, "Unicast audio configuration 11(ii)", cmd_cap_ac_11_ii, 1, 0),
1419 #endif /* CONFIG_BT_MAX_CONN >= 2 */
1420 #endif /* UNICAST_SINK_SUPPORTED && UNICAST_SRC_SUPPORTED */
1421 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
1422 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1423 SHELL_CMD_ARG(broadcast_start, NULL, "", cmd_broadcast_start, 1, 0),
1424 SHELL_CMD_ARG(broadcast_update, NULL, "<meta>", cmd_broadcast_update, 2, 0),
1425 SHELL_CMD_ARG(broadcast_stop, NULL, "", cmd_broadcast_stop, 1, 0),
1426 SHELL_CMD_ARG(broadcast_delete, NULL, "", cmd_broadcast_delete, 1, 0),
1427 SHELL_CMD_ARG(ac_12, NULL, "Broadcast audio configuration 12", cmd_cap_ac_12, 1, 0),
1428 #if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1
1429 SHELL_CMD_ARG(ac_13, NULL, "Broadcast audio configuration 13", cmd_cap_ac_13, 1, 0),
1430 #endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT > 1 */
1431 SHELL_CMD_ARG(ac_14, NULL, "Broadcast audio configuration 14", cmd_cap_ac_14, 1, 0),
1432 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1433 SHELL_SUBCMD_SET_END);
1434
1435 SHELL_CMD_ARG_REGISTER(cap_initiator, &cap_initiator_cmds,
1436 "Bluetooth CAP initiator shell commands",
1437 cmd_cap_initiator, 1, 1);
1438
nonconnectable_ad_data_add(struct bt_data * data_array,const size_t data_array_size)1439 static ssize_t nonconnectable_ad_data_add(struct bt_data *data_array, const size_t data_array_size)
1440 {
1441 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1442 if (default_source.cap_source != NULL && default_source.is_cap) {
1443 static uint8_t ad_cap_broadcast_announcement[5] = {
1444 BT_UUID_16_ENCODE(BT_UUID_BROADCAST_AUDIO_VAL),
1445 };
1446 uint32_t broadcast_id;
1447 int err;
1448
1449 err = bt_cap_initiator_broadcast_get_id(default_source.cap_source, &broadcast_id);
1450 if (err != 0) {
1451 printk("Unable to get broadcast ID: %d\n", err);
1452
1453 return -1;
1454 }
1455
1456 sys_put_le24(broadcast_id, &ad_cap_broadcast_announcement[2]);
1457 data_array[0].type = BT_DATA_SVC_DATA16;
1458 data_array[0].data_len = ARRAY_SIZE(ad_cap_broadcast_announcement);
1459 data_array[0].data = ad_cap_broadcast_announcement;
1460
1461 return 1;
1462 }
1463 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1464
1465 return 0;
1466 }
1467
cap_initiator_ad_data_add(struct bt_data * data_array,const size_t data_array_size,const bool discoverable,const bool connectable)1468 ssize_t cap_initiator_ad_data_add(struct bt_data *data_array, const size_t data_array_size,
1469 const bool discoverable, const bool connectable)
1470 {
1471 if (!discoverable) {
1472 return 0;
1473 }
1474
1475 if (!connectable) {
1476 return nonconnectable_ad_data_add(data_array, data_array_size);
1477 }
1478
1479 return 0;
1480 }
1481
cap_initiator_pa_data_add(struct bt_data * data_array,const size_t data_array_size)1482 ssize_t cap_initiator_pa_data_add(struct bt_data *data_array, const size_t data_array_size)
1483 {
1484 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
1485 if (default_source.cap_source != NULL && default_source.is_cap) {
1486 /* Required size of the buffer depends on what has been
1487 * configured. We just use the maximum size possible.
1488 */
1489 NET_BUF_SIMPLE_DEFINE_STATIC(base_buf, UINT8_MAX);
1490 int err;
1491
1492 net_buf_simple_reset(&base_buf);
1493
1494 err = bt_cap_initiator_broadcast_get_base(default_source.cap_source, &base_buf);
1495 if (err != 0) {
1496 printk("Unable to get BASE: %d\n", err);
1497
1498 return -1;
1499 }
1500
1501 data_array[0].type = BT_DATA_SVC_DATA16;
1502 data_array[0].data_len = base_buf.len;
1503 data_array[0].data = base_buf.data;
1504
1505 return 1;
1506 }
1507 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
1508
1509 return 0;
1510 }
1511