Skip to content

Commit 422bb94

Browse files
committed
Bluetooth: CAP: Implement cmd_cap_handover_broadcast_to_unicast
Implement the shell command for handing over broadcast to unicast. The command supports various configurations and is only limited by the Kconfig values. Signed-off-by: Emil Gydesen <[email protected]>
1 parent e1d0334 commit 422bb94

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

subsys/bluetooth/audio/shell/cap_handover.c

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,17 +289,280 @@ static int cmd_cap_handover_unicast_to_broadcast(const struct shell *sh, size_t
289289
return 0;
290290
}
291291

292+
struct cap_broadcast_source_stream_lookup {
293+
struct bt_cap_stream *streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
294+
size_t cnt;
295+
};
296+
297+
static bool cap_initiator_broadcast_foreach_stream_cb(struct bt_cap_stream *cap_stream,
298+
void *user_data)
299+
{
300+
struct cap_broadcast_source_stream_lookup *data = user_data;
301+
const struct bt_bap_stream *bap_stream = &cap_stream->bap_stream;
302+
303+
if (bap_stream->ep != NULL) {
304+
struct bt_bap_ep_info ep_info;
305+
int err;
306+
307+
err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
308+
__ASSERT_NO_MSG(err == 0);
309+
310+
/* Abort if broadcast source is not in the streaming state */
311+
if (ep_info.state != BT_BAP_EP_STATE_STREAMING) {
312+
return true;
313+
}
314+
}
315+
316+
data->streams[data->cnt++] = cap_stream;
317+
318+
return false;
319+
}
320+
321+
static void populate_connected_sink_conns(struct bt_conn *conn, void *data)
322+
{
323+
struct bt_conn **connected_conns = (struct bt_conn **)data;
324+
325+
for (int i = 0; i < CONFIG_BT_MAX_CONN; i++) {
326+
const struct bt_bap_ep *snk_ep = snks[bt_conn_index(conn)][0];
327+
328+
if (snk_ep == NULL) {
329+
bt_shell_info("Conn %p does not have any sink endpoints", conn);
330+
continue;
331+
}
332+
333+
if (connected_conns[i] == NULL) {
334+
335+
connected_conns[i] = conn;
336+
return;
337+
}
338+
}
339+
}
340+
292341
static int cmd_cap_handover_broadcast_to_unicast(const struct shell *sh, size_t argc, char *argv[])
293342
{
343+
struct bt_cap_unicast_audio_start_stream_param
344+
stream_params[CONFIG_BT_MAX_CONN * CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT] = {
345+
0};
346+
struct bt_cap_unicast_group_stream_param
347+
group_stream_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
348+
struct bt_cap_unicast_group_stream_pair_param
349+
pair_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
350+
struct bt_cap_unicast_audio_start_param audio_start_param = {0};
351+
struct bt_cap_handover_broadcast_to_unicast_param param = {0};
352+
struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN] = {0};
353+
struct bt_cap_unicast_group_param group_param = {0};
354+
struct bt_le_ext_adv *adv = adv_sets[selected_adv];
355+
const struct named_lc3_preset *named_preset;
356+
struct bt_le_ext_adv_info adv_info;
357+
unsigned long conn_cnt = 1U;
358+
bool all_conn = false;
294359
int err;
295360

361+
if (adv == NULL) {
362+
shell_error(sh, "No advertising set");
363+
364+
return -ENOEXEC;
365+
}
366+
367+
if (default_source.cap_source == NULL || !default_source.is_cap) {
368+
shell_error(sh, "No CAP broadcast source");
369+
370+
return -ENOEXEC;
371+
}
372+
373+
if (default_source.handover_in_progress) {
374+
shell_error(sh, "Handover already in progress");
375+
376+
return -ENOEXEC;
377+
}
378+
379+
err = bt_le_ext_adv_get_info(adv, &adv_info);
380+
if (err != 0) {
381+
shell_error(sh, "Failed to get adv info: %d\n", err);
382+
return -ENOEXEC;
383+
}
384+
385+
audio_start_param.type = BT_CAP_SET_TYPE_AD_HOC;
386+
387+
named_preset = &default_broadcast_source_preset;
388+
389+
for (size_t i = 1U; i < argc; i++) {
390+
const char *arg = argv[i];
391+
392+
if (strcmp(arg, "csip") == 0) {
393+
audio_start_param.type = BT_CAP_SET_TYPE_CSIP;
394+
} else if (strcmp(arg, "conns") == 0) {
395+
if (++i == argc) {
396+
shell_help(sh);
397+
398+
return SHELL_CMD_HELP_PRINTED;
399+
}
400+
401+
if (strcmp(argv[i], "all") == 0) {
402+
all_conn = true;
403+
conn_cnt = 0;
404+
} else {
405+
conn_cnt = shell_strtoul(argv[i], 10, &err);
406+
407+
if (err != 0) {
408+
shell_error(
409+
sh,
410+
"Failed to parse conn_cnt argument: %s: %s (%d)",
411+
arg, argv[i], err);
412+
413+
return err;
414+
}
415+
}
416+
} else if (strcmp(arg, "preset") == 0) {
417+
if (++i == argc) {
418+
shell_help(sh);
419+
420+
return SHELL_CMD_HELP_PRINTED;
421+
}
422+
423+
arg = argv[i];
424+
425+
named_preset = bap_get_named_preset(false, BT_AUDIO_DIR_SINK, arg);
426+
if (named_preset == NULL) {
427+
shell_error(sh, "Unable to parse named_preset %s", arg);
428+
429+
return -ENOEXEC;
430+
}
431+
} else {
432+
shell_help(sh);
433+
434+
return SHELL_CMD_HELP_PRINTED;
435+
}
436+
}
437+
438+
/* Populate the array of connected connections and verify conn_cnt */
439+
(void)memset(connected_conns, 0, sizeof(connected_conns));
440+
bt_conn_foreach(BT_CONN_TYPE_LE, populate_connected_sink_conns, (void *)connected_conns);
441+
for (size_t i = 0U; i < ARRAY_SIZE(connected_conns); i++) {
442+
443+
if (connected_conns[i] == NULL) {
444+
if (conn_cnt > (i + 1)) {
445+
shell_error(sh,
446+
"Cannot perform action on %lu connections, only %zu "
447+
"connected",
448+
conn_cnt, i);
449+
450+
return -ENOEXEC;
451+
}
452+
453+
break;
454+
}
455+
456+
if (all_conn) {
457+
conn_cnt = i + 1;
458+
}
459+
}
460+
461+
if (conn_cnt == 0U) {
462+
shell_error(sh, "Not connections for action");
463+
464+
return -ENOEXEC;
465+
}
466+
296467
err = register_callbacks();
297468
if (err != 0) {
298469
shell_error(sh, "Failed to register callbacks: %d", err);
299470

300471
return -ENOEXEC;
301472
}
302473

474+
audio_start_param.stream_params = stream_params;
475+
audio_start_param.count = 0U;
476+
477+
struct cap_broadcast_source_stream_lookup lookup_data = {0};
478+
err = bt_cap_initiator_broadcast_foreach_stream(
479+
default_source.cap_source, cap_initiator_broadcast_foreach_stream_cb, &lookup_data);
480+
if (err != 0) {
481+
shell_error(sh, "Broadcast source not actively streaming");
482+
483+
return -ENOEXEC;
484+
}
485+
486+
if (lookup_data.cnt == 1U || conn_cnt == 1U) {
487+
/* If there is a single BIS, we attempt to setup a sink stream to all connected
488+
* devices
489+
*
490+
* If there is a multiple BIS and a single ACL, we attempt to setup as many sink
491+
* streams to that connection as possible
492+
*/
493+
494+
/* conn_0 will use stream_params[0..count - 1],
495+
* conn_1 will use stream_params[count..count * 2 - 1],
496+
* conn_3 will use stream_params[count * 2..count * 3 - 1], etc.
497+
*/
498+
499+
if (MAX(lookup_data.cnt, conn_cnt) >
500+
CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
501+
shell_error(sh,
502+
"Cannot setup %zu unicast streams in a single group (max %d)",
503+
(size_t)MAX(lookup_data.cnt, conn_cnt),
504+
CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT);
505+
506+
return -ENOEXEC;
507+
}
508+
509+
for (size_t i = 0U; i < conn_cnt; i++) {
510+
for (size_t j = 0U; j < lookup_data.cnt; j++) {
511+
struct bt_cap_unicast_audio_start_stream_param *stream_param =
512+
&stream_params[audio_start_param.count];
513+
struct shell_stream *sh_stream = CONTAINER_OF(
514+
lookup_data.streams[j], struct shell_stream, stream);
515+
516+
struct bt_bap_ep *snk_ep =
517+
snks[bt_conn_index(connected_conns[i])][j];
518+
519+
if (snk_ep == NULL) {
520+
shell_info(sh,
521+
"Could only setup %zu/%zu sink endpoints on %p",
522+
j, lookup_data.cnt, connected_conns[i]);
523+
break;
524+
}
525+
526+
stream_param->member.member = connected_conns[i];
527+
stream_param->stream = lookup_data.streams[j];
528+
stream_param->ep = snk_ep;
529+
copy_unicast_stream_preset(sh_stream, &default_sink_preset);
530+
stream_param->codec_cfg = &sh_stream->codec_cfg;
531+
532+
group_stream_params[audio_start_param.count].stream =
533+
stream_param[audio_start_param.count].stream;
534+
group_stream_params[audio_start_param.count].qos_cfg =
535+
&sh_stream->qos;
536+
pair_params[audio_start_param.count].tx_param =
537+
&group_stream_params[audio_start_param.count];
538+
539+
audio_start_param.count++;
540+
}
541+
}
542+
} else {
543+
/* If there are multiple BIS and multiple ACL, then we attempt to set up one BIS per
544+
* ACL */
545+
}
546+
547+
group_param.params = pair_params;
548+
group_param.params_count = audio_start_param.count;
549+
group_param.packing = BT_ISO_PACKING_SEQUENTIAL;
550+
551+
param.adv_type = adv_info.addr->type;
552+
param.adv_sid = adv_info.sid;
553+
param.broadcast_id = default_source.broadcast_id;
554+
param.broadcast_source = default_source.cap_source;
555+
param.unicast_group_param = &group_param;
556+
param.unicast_start_param = &audio_start_param;
557+
param.reception_stop_param = NULL; /* may be NULL */
558+
559+
err = bt_cap_handover_broadcast_to_unicast(&param);
560+
if (err != 0) {
561+
shell_error(sh, "Failed to handover unicast audio to broadcast: %d\n", err);
562+
563+
return -ENOEXEC;
564+
}
565+
303566
return 0;
304567
}
305568

0 commit comments

Comments
 (0)