diff --git a/doc/releases/release-notes-4.0.rst b/doc/releases/release-notes-4.0.rst index e79f8dfd68b..0fcc7db7ead 100644 --- a/doc/releases/release-notes-4.0.rst +++ b/doc/releases/release-notes-4.0.rst @@ -113,6 +113,14 @@ Bluetooth * :c:func:`bt_audio_codec_cap_meta_get_assisted_listening_stream` * :c:func:`bt_audio_codec_cap_meta_set_assisted_listening_stream` + * Added APIs for getting and setting the broadcast name in codec capabilities + and codec configuration: + + * :c:func:`bt_audio_codec_cfg_meta_get_broadcast_name` + * :c:func:`bt_audio_codec_cfg_meta_set_broadcast_name` + * :c:func:`bt_audio_codec_cap_meta_get_broadcast_name` + * :c:func:`bt_audio_codec_cap_meta_set_broadcast_name` + * Host * Added API :c:func:`bt_gatt_get_uatt_mtu` to get current Unenhanced ATT MTU of a given diff --git a/include/zephyr/bluetooth/audio/audio.h b/include/zephyr/bluetooth/audio/audio.h index 03660dfb53b..1d052e38283 100644 --- a/include/zephyr/bluetooth/audio/audio.h +++ b/include/zephyr/bluetooth/audio/audio.h @@ -496,11 +496,14 @@ enum bt_audio_metadata_type { */ BT_AUDIO_METADATA_TYPE_ASSISTED_LISTENING_STREAM = 0x0A, + /** UTF-8 encoded Broadcast name */ + BT_AUDIO_METADATA_TYPE_BROADCAST_NAME = 0x0B, + /** Extended metadata */ - BT_AUDIO_METADATA_TYPE_EXTENDED = 0xFE, + BT_AUDIO_METADATA_TYPE_EXTENDED = 0xFE, /** Vendor specific metadata */ - BT_AUDIO_METADATA_TYPE_VENDOR = 0xFF, + BT_AUDIO_METADATA_TYPE_VENDOR = 0xFF, }; /** @@ -1367,6 +1370,36 @@ int bt_audio_codec_cfg_meta_get_assisted_listening_stream( int bt_audio_codec_cfg_meta_set_assisted_listening_stream( struct bt_audio_codec_cfg *codec_cfg, enum bt_audio_assisted_listening_stream val); +/** + * @brief Extract broadcast name + * + * See @ref BT_AUDIO_METADATA_TYPE_BROADCAST_NAME for more information about this value. + * + * @param[in] codec_cfg The codec data to search in. + * @param[out] broadcast_name Pointer to the UTF-8 formatted broadcast name. + * + * @retval length The length of the @p broadcast_name (may be 0) + * @retval -EINVAL if arguments are invalid + * @retval -ENODATA if not found + */ +int bt_audio_codec_cfg_meta_get_broadcast_name(const struct bt_audio_codec_cfg *codec_cfg, + const uint8_t **broadcast_name); + +/** + * @brief Set the broadcast name of a codec configuration metadata. + * + * @param codec_cfg The codec configuration to set data for. + * @param broadcast_name The broadcast name to set. + * @param broadcast_name_len The length of @p broadcast_name. + * + * @retval length The data_len of @p codec_cfg on success + * @retval -EINVAL if arguments are invalid + * @retval -ENOMEM if the new value could not set or added due to memory + */ +int bt_audio_codec_cfg_meta_set_broadcast_name(struct bt_audio_codec_cfg *codec_cfg, + const uint8_t *broadcast_name, + size_t broadcast_name_len); + /** * @brief Extract extended metadata * @@ -1938,6 +1971,35 @@ int bt_audio_codec_cap_meta_get_assisted_listening_stream( int bt_audio_codec_cap_meta_set_assisted_listening_stream( struct bt_audio_codec_cap *codec_cap, enum bt_audio_assisted_listening_stream val); +/** + * @brief Extract broadcast name + * + * See @ref BT_AUDIO_METADATA_TYPE_BROADCAST_NAME for more information about this value. + * + * @param[in] codec_cap The codec data to search in. + * @param[out] broadcast_name Pointer to the UTF-8 formatted broadcast name. + * + * @retval length The length of the @p broadcast_name (may be 0) + * @retval -EINVAL if arguments are invalid + * @retval -ENODATA if not found + */ +int bt_audio_codec_cap_meta_get_broadcast_name(const struct bt_audio_codec_cap *codec_cap, + const uint8_t **broadcast_name); + +/** + * @brief Set the broadcast name of a codec capability metadata. + * + * @param codec_cap The codec capability to set data for. + * @param broadcast_name The broadcast name to set. + * @param broadcast_name_len The length of @p broadcast_name. + * + * @retval length The data_len of @p codec_cap on success + * @retval -EINVAL if arguments are invalid + * @retval -ENOMEM if the new value could not set or added due to memory + */ +int bt_audio_codec_cap_meta_set_broadcast_name(struct bt_audio_codec_cap *codec_cap, + const uint8_t *broadcast_name, + size_t broadcast_name_len); /** * @brief Extract extended metadata * diff --git a/subsys/bluetooth/audio/codec.c b/subsys/bluetooth/audio/codec.c index 98135af9f87..f07c4dc3a61 100644 --- a/subsys/bluetooth/audio/codec.c +++ b/subsys/bluetooth/audio/codec.c @@ -1102,6 +1102,49 @@ static int codec_meta_set_assisted_listening_stream(uint8_t meta[], size_t meta_ sizeof(val_u8)); } +static int codec_meta_get_broadcast_name(const uint8_t meta[], size_t meta_len, + const uint8_t **broadcast_name) +{ + const uint8_t *data; + int ret; + + CHECKIF(meta == NULL) { + LOG_DBG("meta is NULL"); + return -EINVAL; + } + + CHECKIF(broadcast_name == NULL) { + LOG_DBG("broadcast_name is NULL"); + return -EINVAL; + } + + ret = codec_meta_get_val(meta, meta_len, BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, &data); + if (data == NULL) { + return -ENODATA; + } + + *broadcast_name = data; + + return ret; +} + +static int codec_meta_set_broadcast_name(uint8_t meta[], size_t meta_len, size_t meta_size, + const uint8_t *broadcast_name, size_t broadcast_name_len) +{ + CHECKIF(meta == NULL) { + LOG_DBG("meta is NULL"); + return -EINVAL; + } + + CHECKIF(broadcast_name == NULL) { + LOG_DBG("broadcast_name is NULL"); + return -EINVAL; + } + + return codec_meta_set_val(meta, meta_len, meta_size, BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, + broadcast_name, broadcast_name_len); +} + static int codec_meta_get_extended(const uint8_t meta[], size_t meta_len, const uint8_t **extended_meta) { @@ -1547,6 +1590,33 @@ int bt_audio_codec_cfg_meta_set_vendor(struct bt_audio_codec_cfg *codec_cfg, return ret; } + +int bt_audio_codec_cfg_meta_get_broadcast_name(const struct bt_audio_codec_cfg *codec_cfg, + const uint8_t **broadcast_name) +{ + CHECKIF(codec_cfg == NULL) { + LOG_DBG("codec_cfg is NULL"); + return -EINVAL; + } + + return codec_meta_get_broadcast_name(codec_cfg->meta, codec_cfg->meta_len, broadcast_name); +} + +int bt_audio_codec_cfg_meta_set_broadcast_name(struct bt_audio_codec_cfg *codec_cfg, + const uint8_t *broadcast_name, + size_t broadcast_name_len) +{ + int ret; + + ret = codec_meta_set_broadcast_name(codec_cfg->meta, codec_cfg->meta_len, + ARRAY_SIZE(codec_cfg->meta), broadcast_name, + broadcast_name_len); + if (ret >= 0) { + codec_cfg->meta_len = ret; + } + + return ret; +} #endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0 */ #if CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE > 0 @@ -1900,6 +1970,33 @@ int bt_audio_codec_cap_meta_set_vendor(struct bt_audio_codec_cap *codec_cap, return ret; } + +int bt_audio_codec_cap_meta_get_broadcast_name(const struct bt_audio_codec_cap *codec_cap, + const uint8_t **broadcast_name) +{ + CHECKIF(codec_cap == NULL) { + LOG_DBG("codec_cap is NULL"); + return -EINVAL; + } + + return codec_meta_get_broadcast_name(codec_cap->meta, codec_cap->meta_len, broadcast_name); +} + +int bt_audio_codec_cap_meta_set_broadcast_name(struct bt_audio_codec_cap *codec_cap, + const uint8_t *broadcast_name, + size_t broadcast_name_len) +{ + int ret; + + ret = codec_meta_set_broadcast_name(codec_cap->meta, codec_cap->meta_len, + ARRAY_SIZE(codec_cap->meta), broadcast_name, + broadcast_name_len); + if (ret >= 0) { + codec_cap->meta_len = ret; + } + + return ret; +} #endif /* CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE > 0 */ #endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0 || \ * CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE > 0 \ diff --git a/tests/bluetooth/audio/codec/src/main.c b/tests/bluetooth/audio/codec/src/main.c index 88a5d252301..a3a189e86ca 100644 --- a/tests/bluetooth/audio/codec/src/main.c +++ b/tests/bluetooth/audio/codec/src/main.c @@ -959,6 +959,45 @@ ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_assisted_listenin zassert_equal(ret, 0x00, "Unexpected return value %d", ret); } +ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_get_broadcast_name) +{ + const uint8_t expected_data[] = {'m', 'y', ' ', 'b', 'c', 'a', 's', 't'}; + const struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + const uint8_t *broadcast_name; + int ret; + + ret = bt_audio_codec_cfg_meta_get_broadcast_name(&codec_cfg, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(expected_data, broadcast_name, ARRAY_SIZE(expected_data)); +} + +ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_set_broadcast_name) +{ + const uint8_t expected_data[] = {'m', 'y', ' ', 'b', 'c', 'a', 's', 't'}; + const uint8_t new_expected_data[] = {'n', 'e', 'w', ' ', 'b', 'c', 'a', 's', 't'}; + struct bt_audio_codec_cfg codec_cfg = + BT_AUDIO_CODEC_CFG(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + const uint8_t *broadcast_name; + int ret; + + ret = bt_audio_codec_cfg_meta_get_broadcast_name(&codec_cfg, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(expected_data, broadcast_name, ARRAY_SIZE(expected_data)); + + ret = bt_audio_codec_cfg_meta_set_broadcast_name(&codec_cfg, new_expected_data, + ARRAY_SIZE(new_expected_data)); + zassert_true(ret > 0, "Unexpected return value %d", ret); + + ret = bt_audio_codec_cfg_meta_get_broadcast_name(&codec_cfg, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(new_expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(new_expected_data, broadcast_name, ARRAY_SIZE(new_expected_data)); +} + ZTEST(audio_codec_test_suite, test_bt_audio_codec_cfg_meta_get_extended) { const uint8_t expected_data[] = {0x00, 0x01, 0x02, 0x03}; @@ -1873,6 +1912,45 @@ ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_set_assisted_listenin zassert_equal(ret, 0x00, "Unexpected return value %d", ret); } +ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_get_broadcast_name) +{ + const uint8_t expected_data[] = {'m', 'y', ' ', 'b', 'c', 'a', 's', 't'}; + const struct bt_audio_codec_cap codec_cap = + BT_AUDIO_CODEC_CAP(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + const uint8_t *broadcast_name; + int ret; + + ret = bt_audio_codec_cap_meta_get_broadcast_name(&codec_cap, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(expected_data, broadcast_name, ARRAY_SIZE(expected_data)); +} + +ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_set_broadcast_name) +{ + const uint8_t expected_data[] = {'m', 'y', ' ', 'b', 'c', 'a', 's', 't'}; + const uint8_t new_expected_data[] = {'n', 'e', 'w', ' ', 'b', 'c', 'a', 's', 't'}; + struct bt_audio_codec_cap codec_cap = + BT_AUDIO_CODEC_CAP(BT_HCI_CODING_FORMAT_LC3, 0x0000, 0x0000, {}, + {BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_BROADCAST_NAME, 'm', + 'y', ' ', 'b', 'c', 'a', 's', 't')}); + const uint8_t *broadcast_name; + int ret; + + ret = bt_audio_codec_cap_meta_get_broadcast_name(&codec_cap, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(expected_data, broadcast_name, ARRAY_SIZE(expected_data)); + + ret = bt_audio_codec_cap_meta_set_broadcast_name(&codec_cap, new_expected_data, + ARRAY_SIZE(new_expected_data)); + zassert_true(ret > 0, "Unexpected return value %d", ret); + + ret = bt_audio_codec_cap_meta_get_broadcast_name(&codec_cap, &broadcast_name); + zassert_equal(ret, ARRAY_SIZE(new_expected_data), "Unexpected return value %d", ret); + zassert_mem_equal(new_expected_data, broadcast_name, ARRAY_SIZE(new_expected_data)); +} + ZTEST(audio_codec_test_suite, test_bt_audio_codec_cap_meta_get_extended) { const uint8_t expected_data[] = {0x00, 0x01, 0x02, 0x03};