diff --git a/subsys/bluetooth/audio/bap_broadcast_source.c b/subsys/bluetooth/audio/bap_broadcast_source.c index 01e32ae9905..848682aaffb 100644 --- a/subsys/bluetooth/audio/bap_broadcast_source.c +++ b/subsys/bluetooth/audio/bap_broadcast_source.c @@ -658,6 +658,59 @@ static enum bt_bap_ep_state broadcast_source_get_state(struct bt_bap_broadcast_s return stream->ep->status.state; } +static bool merge_bis_and_subgroup_data_cb(struct bt_data *data, void *user_data) +{ + struct bt_audio_codec_cfg *codec_cfg = user_data; + int err; + + err = bt_audio_codec_cfg_set_val(codec_cfg, data->type, data->data, data->data_len); + if (err < 0) { + LOG_DBG("Failed to set type %u with len %u in codec_cfg: %d", data->type, + data->data_len, err); + + return false; + } + + return true; +} + +static int update_codec_cfg_data(struct bt_audio_codec_cfg *codec_cfg, + const struct bt_bap_broadcast_source_stream_param *stream_param) +{ + int err; + /* Merge subgroup codec configuration with the BIS configuration + * As per the BAP spec, if a value exist at level 2 (subgroup) and 3 (BIS), then it is + * the value at level 3 that shall be used + */ + if (codec_cfg->id == BT_HCI_CODING_FORMAT_LC3) { + err = bt_audio_data_parse(stream_param->data, stream_param->data_len, + merge_bis_and_subgroup_data_cb, codec_cfg); + if (err != 0) { + LOG_DBG("Could not merge BIS and subgroup config in codec_cfg: %d", err); + + return -EINVAL; + } + } else { + /* If it is not LC3, then we don't know how to merge the subgroup and BIS codecs, + * so we just append them + */ + if (codec_cfg->data_len + stream_param->data_len > + sizeof(codec_cfg->data)) { + LOG_DBG("Could not store BIS and subgroup config in codec_cfg (%u > %u)", + codec_cfg->data_len + stream_param->data_len, + sizeof(codec_cfg->data)); + + return -ENOMEM; + } + + memcpy(&codec_cfg->data[codec_cfg->data_len], stream_param->data, + stream_param->data_len); + codec_cfg->data_len += stream_param->data_len; + } + + return 0; +} + int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param, struct bt_bap_broadcast_source **out_source) { @@ -665,6 +718,7 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param, struct bt_audio_codec_qos *qos; size_t stream_count; uint8_t index; + uint8_t bis_count; int err; CHECKIF(out_source == NULL) { @@ -694,7 +748,7 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param, } stream_count = 0U; - + bis_count = 0U; qos = param->qos; /* Go through all subgroups and streams and setup each setup with an * endpoint @@ -726,12 +780,33 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param, for (size_t j = 0U; j < subgroup_param->params_count; j++) { const struct bt_bap_broadcast_source_stream_param *stream_param; struct bt_bap_stream *stream; + struct bt_audio_codec_cfg *codec_cfg; + codec_cfg = subgroup_param->codec_cfg; stream_param = &subgroup_param->params[j]; stream = stream_param->stream; + if (CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0) { + if (bis_count >= BROADCAST_STREAM_CNT) { + LOG_DBG("Stream count %d exceeded", bis_count); + return -ENOMEM; + } + + codec_cfg = &source->codec_cfg[bis_count]; + memcpy(codec_cfg, subgroup_param->codec_cfg, + sizeof(struct bt_audio_codec_cfg)); + err = update_codec_cfg_data(codec_cfg, stream_param); + if (err != 0) { + LOG_DBG("codec config update failed [%zu]: %d", i, err); + broadcast_source_cleanup(source); + return err; + } + + bis_count++; + } + err = broadcast_source_setup_stream(index, stream, - subgroup_param->codec_cfg, qos, source); + codec_cfg, qos, source); if (err != 0) { LOG_DBG("Failed to setup streams[%zu]: %d", i, err); broadcast_source_cleanup(source); @@ -789,6 +864,7 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source, enum bt_bap_ep_state broadcast_state; struct bt_audio_codec_qos *qos; size_t subgroup_cnt; + uint8_t bis_count; CHECKIF(source == NULL) { LOG_DBG("source is NULL"); @@ -859,7 +935,7 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source, } qos = param->qos; - + bis_count = 0U; /* We update up to the first param->params_count subgroups */ for (size_t i = 0U; i < param->params_count; i++) { const struct bt_bap_broadcast_source_subgroup_param *subgroup_param; @@ -885,6 +961,26 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source, stream_param = &subgroup_param->params[j]; stream = stream_param->stream; + if (CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0) { + int err; + + if (bis_count >= BROADCAST_STREAM_CNT) { + LOG_DBG("Stream count %d exceeded", bis_count); + return -ENOMEM; + } + + codec_cfg = &source->codec_cfg[bis_count]; + memcpy(codec_cfg, subgroup_param->codec_cfg, + sizeof(struct bt_audio_codec_cfg)); + err = update_codec_cfg_data(codec_cfg, stream_param); + + if (err != 0) { + LOG_DBG("codec config update failed [%zu]: %d", i, err); + return err; + } + + bis_count++; + } stream_idx = 0U; SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, subgroup_stream, _node) { diff --git a/subsys/bluetooth/audio/bap_endpoint.h b/subsys/bluetooth/audio/bap_endpoint.h index 4efea0dba67..4f52351e5fe 100644 --- a/subsys/bluetooth/audio/bap_endpoint.h +++ b/subsys/bluetooth/audio/bap_endpoint.h @@ -103,9 +103,13 @@ struct bt_bap_broadcast_source { /* The codec specific configured data for each stream in the subgroup */ struct bt_audio_broadcast_stream_data stream_data[BROADCAST_STREAM_CNT]; #endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */ - uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE]; + /* The complete codec specific configured data for each stream in the subgroup. + * This contains both the subgroup and the BIS-specific data for each stream. + */ + struct bt_audio_codec_cfg codec_cfg[BROADCAST_STREAM_CNT]; + /* The subgroups containing the streams used to create the broadcast source */ sys_slist_t subgroups; }; diff --git a/tests/bluetooth/audio/bap_broadcast_source/src/main.c b/tests/bluetooth/audio/bap_broadcast_source/src/main.c index 33b785e07ff..256931870dd 100644 --- a/tests/bluetooth/audio/bap_broadcast_source/src/main.c +++ b/tests/bluetooth/audio/bap_broadcast_source/src/main.c @@ -44,7 +44,8 @@ static void bap_broadcast_source_test_suite_fixture_init( { const uint8_t bis_cfg_data[] = { BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, - BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT), + BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_LEFT | + BT_AUDIO_LOCATION_FRONT_RIGHT)), }; const size_t streams_per_subgroup = CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT / CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT; @@ -228,6 +229,19 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send for (size_t j = 0U; j < create_param->params[i].params_count; j++) { struct bt_bap_stream *bap_stream = create_param->params[i].params[j].stream; + /* verify bap stream started cb stream parameter */ + zassert_equal(mock_bap_stream_started_cb_fake.arg0_history[i], bap_stream); + struct bt_audio_codec_cfg *codec_cfg = bap_stream->codec_cfg; + enum bt_audio_location chan_allocation; + /* verify subgroup codec data */ + zassert_equal(bt_audio_codec_cfg_get_freq(codec_cfg), + BT_AUDIO_CODEC_CFG_FREQ_16KHZ); + zassert_equal(bt_audio_codec_cfg_get_frame_dur(codec_cfg), + BT_AUDIO_CODEC_CFG_DURATION_10); + /* verify bis specific codec data */ + bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation); + zassert_equal(chan_allocation, + BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT); /* Since BAP doesn't care about the `buf` we can just provide NULL */ err = bt_bap_stream_send(bap_stream, NULL, 0); zassert_equal(0, err, @@ -1220,6 +1234,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_id_inval_stat zassert_not_equal(0, err, "Did not fail with deleted broadcast source"); } + + ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base_single_bis) { struct bt_bap_broadcast_source_param *create_param = fixture->param; @@ -1237,8 +1253,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base_single_b 0x04, /* meta length */ 0x03, 0x02, 0x01, 0x00, /* meta */ 0x01, /* bis index */ - 0x03, /* bis cc length */ - 0x02, 0x03, 0x03 /* bis cc length */ + 0x06, /* bis cc length */ + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00 /* bis cc length */ }; NET_BUF_SIMPLE_DEFINE(base_buf, 64); @@ -1294,8 +1310,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base) 0x04, /* meta length */ 0x03, 0x02, 0x01, 0x00, /* meta */ 0x01, /* bis index */ - 0x03, /* bis cc length */ - 0x02, 0x03, 0x03, /* bis cc length */ + 0x06, /* bis cc length */ + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, /* bis cc length */ 0x01, /* Subgroup 1: bis count */ 0x06, 0x00, 0x00, 0x00, 0x00, /* LC3 codec_id*/ 0x10, /* cc length */ @@ -1304,8 +1320,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base) 0x04, /* meta length */ 0x03, 0x02, 0x01, 0x00, /* meta */ 0x02, /* bis index */ - 0x03, /* bis cc length */ - 0x02, 0x03, 0x03 /* bis cc length */ + 0x06, /* bis cc length */ + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00 /* bis cc length */ }; NET_BUF_SIMPLE_DEFINE(base_buf, 128); diff --git a/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt b/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt index aea487e1b97..9eb91c84fa3 100644 --- a/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt +++ b/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(uut STATIC ${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_iso.c ${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_stream.c ${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_broadcast_source.c + ${ZEPHYR_BASE}/subsys/bluetooth/audio/codec.c ${ZEPHYR_BASE}/subsys/logging/log_minimal.c ${ZEPHYR_BASE}/subsys/net/buf_simple.c ) diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c index 4d020bd8e49..143375d6f9c 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c @@ -446,7 +446,7 @@ static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) */ ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation, false); if (ret == 0) { - if (chan_allocation != BT_AUDIO_LOCATION_FRONT_LEFT) { + if (chan_allocation != BT_AUDIO_LOCATION_FRONT_CENTER) { FAIL("Unexpected channel allocation: 0x%08X", chan_allocation); return; diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c index de9efd12fa2..cf55d2ee5bd 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c @@ -27,6 +27,11 @@ #include "bstests.h" #include "common.h" +#define SUPPORTED_CHAN_COUNTS BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2) +#define SUPPORTED_MIN_OCTETS_PER_FRAME 30 +#define SUPPORTED_MAX_OCTETS_PER_FRAME 155 +#define SUPPORTED_MAX_FRAMES_PER_SDU 1 + #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) /* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that * the controller is never idle @@ -50,9 +55,123 @@ static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2 static struct bt_bap_lc3_preset preset_16_1_1 = BT_BAP_LC3_BROADCAST_PRESET_16_1_1( BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); +static uint8_t bis_codec_data[] = { + BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, + BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_CENTER)), +}; + static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(broadcast_source_streams)); static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(broadcast_source_streams)); +static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) +{ + const struct bt_audio_codec_cfg *codec_cfg = stream->codec_cfg; + const struct bt_audio_codec_cfg *exp_codec_cfg = &preset_16_1_1.codec_cfg; + enum bt_audio_location chan_allocation; + uint8_t frames_blocks_per_sdu; + size_t min_sdu_size_required; + uint16_t octets_per_frame; + uint8_t chan_cnt; + int ret; + int exp_ret; + + ret = bt_audio_codec_cfg_get_freq(codec_cfg); + exp_ret = bt_audio_codec_cfg_get_freq(exp_codec_cfg); + if (ret >= 0) { + const int freq = bt_audio_codec_cfg_freq_to_freq_hz(ret); + const int exp_freq = bt_audio_codec_cfg_freq_to_freq_hz(exp_ret); + + if (freq != exp_freq) { + FAIL("Invalid frequency: %d Expected: %d\n", freq, exp_freq); + + return; + } + } else { + FAIL("Could not get frequency: %d\n", ret); + + return; + } + + ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg); + exp_ret = bt_audio_codec_cfg_get_frame_dur(exp_codec_cfg); + if (ret >= 0) { + const int frm_dur_us = bt_audio_codec_cfg_frame_dur_to_frame_dur_us(ret); + const int exp_frm_dur_us = bt_audio_codec_cfg_frame_dur_to_frame_dur_us(exp_ret); + + if (frm_dur_us != exp_frm_dur_us) { + FAIL("Invalid frame duration: %d Exp: %d\n", frm_dur_us, exp_frm_dur_us); + + return; + } + } else { + FAIL("Could not get frame duration: %d\n", ret); + + return; + } + + /* The broadcast source sets the channel allocation in the BIS to + * BT_AUDIO_LOCATION_FRONT_CENTER + */ + ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation); + if (ret == 0) { + if (chan_allocation != BT_AUDIO_LOCATION_FRONT_CENTER) { + FAIL("Unexpected channel allocation: 0x%08X", chan_allocation); + + return; + } + + chan_cnt = bt_audio_get_chan_count(chan_allocation); + } else { + FAIL("Could not get subgroup channel allocation: %d\n", ret); + + return; + } + + if (chan_cnt == 0 || (BIT(chan_cnt - 1) & SUPPORTED_CHAN_COUNTS) == 0) { + FAIL("Unsupported channel count: %u\n", chan_cnt); + + return; + } + + ret = bt_audio_codec_cfg_get_octets_per_frame(codec_cfg); + if (ret > 0) { + octets_per_frame = (uint16_t)ret; + } else { + FAIL("Could not get subgroup octets per frame: %d\n", ret); + + return; + } + + if (!IN_RANGE(octets_per_frame, SUPPORTED_MIN_OCTETS_PER_FRAME, + SUPPORTED_MAX_OCTETS_PER_FRAME)) { + FAIL("Unsupported octets per frame: %u\n", octets_per_frame); + + return; + } + + ret = bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, false); + if (ret > 0) { + frames_blocks_per_sdu = (uint8_t)ret; + } else { + printk("Could not get octets per frame: %d\n", ret); + /* Frame blocks per SDU is optional and is implicitly 1 */ + frames_blocks_per_sdu = 1U; + } + + /* An SDU can consist of X frame blocks, each with Y frames (one per channel) of size Z in + * them. The minimum SDU size required for this is X * Y * Z. + */ + min_sdu_size_required = chan_cnt * octets_per_frame * frames_blocks_per_sdu; + if (min_sdu_size_required > stream->qos->sdu) { + FAIL("With %zu channels and %u octets per frame and %u frames per block, SDUs " + "shall be at minimum %zu, but the stream has been configured for %u\n", + chan_cnt, octets_per_frame, frames_blocks_per_sdu, min_sdu_size_required, + stream->qos->sdu); + + return; + } +} + static void started_cb(struct bt_bap_stream *stream) { struct bt_bap_ep_info info; @@ -90,6 +209,7 @@ static void started_cb(struct bt_bap_stream *stream) } printk("Stream %p started\n", stream); + validate_stream_codec_cfg(stream); k_sem_give(&sem_started); } @@ -146,10 +266,6 @@ static struct bt_bap_stream_ops stream_ops = { static int setup_broadcast_source(struct bt_bap_broadcast_source **source) { - uint8_t bis_codec_data[] = { - BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, - BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_LEFT)), - }; struct bt_bap_broadcast_source_stream_param stream_params[ARRAY_SIZE(broadcast_source_streams)]; struct bt_bap_broadcast_source_subgroup_param @@ -297,10 +413,6 @@ static int setup_extended_adv(struct bt_bap_broadcast_source *source, struct bt_ static void test_broadcast_source_reconfig(struct bt_bap_broadcast_source *source) { - uint8_t bis_codec_data[] = { - BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_FREQ, - BT_BYTES_LIST_LE16(BT_AUDIO_CODEC_CFG_FREQ_16KHZ)), - }; struct bt_bap_broadcast_source_stream_param stream_params[ARRAY_SIZE(broadcast_source_streams)]; struct bt_bap_broadcast_source_subgroup_param