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