@@ -289,17 +289,280 @@ static int cmd_cap_handover_unicast_to_broadcast(const struct shell *sh, size_t
289
289
return 0 ;
290
290
}
291
291
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
+
292
341
static int cmd_cap_handover_broadcast_to_unicast (const struct shell * sh , size_t argc , char * argv [])
293
342
{
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;
294
359
int err ;
295
360
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
+
296
467
err = register_callbacks ();
297
468
if (err != 0 ) {
298
469
shell_error (sh , "Failed to register callbacks: %d" , err );
299
470
300
471
return - ENOEXEC ;
301
472
}
302
473
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
+
303
566
return 0 ;
304
567
}
305
568
0 commit comments