From c9da274eb2008af1d3a8a260810107dabc76155e Mon Sep 17 00:00:00 2001 From: Fredrik Danebjer Date: Fri, 2 Aug 2024 11:08:57 +0200 Subject: [PATCH] Bluetooth: ascs: Add dynamic ASE registration Added option to set the ASE count through the bap API, making ASE configuration runtime available. The upper limit of ASEs are still bound by the Kconfig options set for ASEs. Signed-off-by: Fredrik Danebjer --- .../bluetooth/api/audio/shell/bap.rst | 2 +- doc/releases/migration-guide-4.0.rst | 13 ++ include/zephyr/bluetooth/audio/bap.h | 49 ++++ include/zephyr/bluetooth/audio/gmap.h | 8 +- samples/bluetooth/bap_unicast_server/prj.conf | 4 +- .../bluetooth/bap_unicast_server/src/main.c | 14 +- samples/bluetooth/cap_acceptor/prj.conf | 4 +- .../cap_acceptor/src/cap_acceptor_unicast.c | 11 + samples/bluetooth/hap_ha/prj.conf | 4 +- samples/bluetooth/hap_ha/src/bap_unicast_sr.c | 13 +- samples/bluetooth/tmap_peripheral/prj.conf | 4 +- .../tmap_peripheral/src/bap_unicast_sr.c | 11 +- subsys/bluetooth/audio/Kconfig.ascs | 12 +- subsys/bluetooth/audio/ascs.c | 218 ++++++++++++++---- subsys/bluetooth/audio/ascs_internal.h | 4 + subsys/bluetooth/audio/bap_unicast_server.c | 28 ++- subsys/bluetooth/audio/gmap_server.c | 21 +- subsys/bluetooth/audio/shell/audio.h | 3 +- subsys/bluetooth/audio/shell/bap.c | 43 +++- subsys/bluetooth/audio/shell/gmap.c | 16 +- tests/bluetooth/audio/ascs/prj.conf | 4 +- tests/bluetooth/audio/ascs/src/main.c | 217 +++++++++++++++-- .../audio/ascs/src/test_ase_control_params.c | 83 ++++--- .../ascs/src/test_ase_state_transition.c | 68 +++++- .../src/test_ase_state_transition_invalid.c | 38 ++- tests/bluetooth/audio/ascs/testcase.yaml | 4 +- tests/bluetooth/audio/mocks/CMakeLists.txt | 9 + tests/bluetooth/audio/mocks/include/gatt.h | 2 + tests/bluetooth/audio/mocks/src/gatt.c | 199 +++++++++++++++- tests/bluetooth/shell/audio.conf | 8 +- tests/bluetooth/shell/testcase.yaml | 8 +- tests/bluetooth/tester/overlay-le-audio.conf | 4 +- .../tester/src/audio/btp_bap_audio_stream.c | 2 +- .../tester/src/audio/btp_bap_unicast.c | 12 + .../tester/src/audio/btp_bap_unicast.h | 4 +- tests/bsim/bluetooth/audio/prj.conf | 4 +- .../audio/src/bap_unicast_server_test.c | 14 +- .../bluetooth/audio/src/cap_acceptor_test.c | 16 +- .../bsim/bluetooth/audio/src/gmap_ugt_test.c | 14 +- 39 files changed, 1017 insertions(+), 175 deletions(-) diff --git a/doc/connectivity/bluetooth/api/audio/shell/bap.rst b/doc/connectivity/bluetooth/api/audio/shell/bap.rst index 873c4091f60e88..2efbb4e61d14de 100644 --- a/doc/connectivity/bluetooth/api/audio/shell/bap.rst +++ b/doc/connectivity/bluetooth/api/audio/shell/bap.rst @@ -16,7 +16,7 @@ Commands bap --help Subcommands: - init + init : [ase_sink_count, ase_source_count] select_broadcast : create_broadcast : [preset ] [enc ] start_broadcast : diff --git a/doc/releases/migration-guide-4.0.rst b/doc/releases/migration-guide-4.0.rst index bb2c4bf1223ab6..2a45691dbf5c8e 100644 --- a/doc/releases/migration-guide-4.0.rst +++ b/doc/releases/migration-guide-4.0.rst @@ -208,6 +208,19 @@ Bluetooth Audio This needs to be added to all instances of VCP Volume Renderer callback functions defined. (:github:`76992`) +* The Unicast Server has a new registration function :c:func:`bt_bap_unicast_server_register` which + takes a :c:struct:`bt_bap_unicast_server_register_param` as argument. This allows the Unicast + Server to dynamically register Source and Sink ASE count at runtime. The old + :kconfig:option:`CONFIG_BT_ASCS_ASE_SRC_COUNT` and :kconfig:option:`CONFIG_BT_ASCS_ASE_SNK_COUNT` + has been renamed to :kconfig:option:`CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT` and + :kconfig:option:`CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT` to reflect that they now serve as a + compile-time maximum configuration of ASEs to be used. + :c:func:`bt_bap_unicast_server_register` needs to be called once before using the Unicast Server, + and more specfically prior to calling :c:func:`bt_bap_unicast_server_register_cb` for the first + time. It does not need to be called again until the new function + :c:func:`bt_bap_unicast_server_unregister` has been called. + (:github:`76632`) + Bluetooth Classic ================= diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 3de92a88ae9626..0c9c5fa0fc95d9 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -635,6 +635,22 @@ struct bt_bap_stream_ops { void (*disconnected)(struct bt_bap_stream *stream, uint8_t reason); }; +/** Structure for registering Unicast Server */ +struct bt_bap_unicast_server_register_param { + /** + * @brief Sink Count to register. + * + * Should be in range [0, @kconfig{CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT}] + */ + uint8_t snk_cnt; + + /** @brief Source Count to register. + * + * Should be in range [0, @kconfig{CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT}] + */ + uint8_t src_cnt; +}; + /** * @brief Register Audio callbacks for a stream. * @@ -1019,11 +1035,41 @@ struct bt_bap_unicast_server_cb { int (*release)(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp); }; +/** + * @brief Register the Unicast Server. + * + * Register the Unicast Server. Only a single Unicast Server can be registered at any one time. + * This will register ASCS in the GATT database. + * + * @param param Registration parameters for ascs. + * + * @return 0 in case of success, negative error code otherwise. + */ +int bt_bap_unicast_server_register(const struct bt_bap_unicast_server_register_param *param); + +/** + * @brief Unregister the Unicast Server. + * + * Unregister the Unicast Server. + * This will unregister ASCS in the GATT database. + * Before calling this function, any callbacks registered through + * bt_bap_unicast_server_register_cb() needs to be unregistered with + * bt_bap_unicast_server_unregister_cb(). + * + * Calling this function will issue an release operation on any ASE + * in a non-idle state. + * + * @return 0 in case of success, negative error code otherwise. + */ +int bt_bap_unicast_server_unregister(void); + /** * @brief Register unicast server callbacks. * * Only one callback structure can be registered, and attempting to * registering more than one will result in an error. + * Prior to calling this function the Unicast Server needs to be + * registered with bt_bap_unicast_server_register(). * * @param cb Unicast server callback structure. * @@ -1037,6 +1083,9 @@ int bt_bap_unicast_server_register_cb(const struct bt_bap_unicast_server_cb *cb) * May only unregister a callback structure that has previously been * registered by bt_bap_unicast_server_register_cb(). * + * Calling this function will issue an release operation on any ASE + * in a non-idle state. + * * @param cb Unicast server callback structure. * * @return 0 in case of success or negative value in case of error. diff --git a/include/zephyr/bluetooth/audio/gmap.h b/include/zephyr/bluetooth/audio/gmap.h index 0fb5c3b064e2db..edb38d042dad6c 100644 --- a/include/zephyr/bluetooth/audio/gmap.h +++ b/include/zephyr/bluetooth/audio/gmap.h @@ -89,7 +89,7 @@ enum bt_gmap_ugt_feat { /** * @brief Source support * - * Requires @kconfig{CONFIG_BT_ASCS_ASE_SRC_COUNT} > 0 + * Requires @kconfig{CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT} > 0 */ BT_GMAP_UGT_FEAT_SOURCE = BIT(0), /** @@ -101,7 +101,7 @@ enum bt_gmap_ugt_feat { /** * @brief Sink support * - * Requires @kconfig{CONFIG_BT_ASCS_ASE_SNK_COUNT} > 0 + * Requires @kconfig{CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT} > 0 */ BT_GMAP_UGT_FEAT_SINK = BIT(2), /** @@ -119,14 +119,14 @@ enum bt_gmap_ugt_feat { /** * @brief Support for receiving at least two audio channels, each in a separate CIS * - * Requires @kconfig{CONFIG_BT_ASCS_ASE_SNK_COUNT} > 1 and + * Requires @kconfig{CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT} > 1 and * @kconfig{CONFIG_BT_ASCS_MAX_ACTIVE_ASES} > 1, and BT_GMAP_UGT_FEAT_SINK to be set as well */ BT_GMAP_UGT_FEAT_MULTISINK = BIT(5), /** * @brief Support for sending at least two audio channels, each in a separate CIS * - * Requires @kconfig{CONFIG_BT_ASCS_ASE_SRC_COUNT} > 1 and + * Requires @kconfig{CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT} > 1 and * @kconfig{CONFIG_BT_ASCS_MAX_ACTIVE_ASES} > 1, and BT_GMAP_UGT_FEAT_SOURCE to be set * as well */ diff --git a/samples/bluetooth/bap_unicast_server/prj.conf b/samples/bluetooth/bap_unicast_server/prj.conf index 4e72b59df4c914..60230d0d0d1219 100644 --- a/samples/bluetooth/bap_unicast_server/prj.conf +++ b/samples/bluetooth/bap_unicast_server/prj.conf @@ -5,8 +5,8 @@ CONFIG_BT_ISO_PERIPHERAL=y CONFIG_BT_AUDIO=y CONFIG_BT_BAP_UNICAST_SERVER=y CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=2 -CONFIG_BT_ASCS_ASE_SRC_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=1 CONFIG_BT_ISO_TX_BUF_COUNT=2 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=4 diff --git a/samples/bluetooth/bap_unicast_server/src/main.c b/samples/bluetooth/bap_unicast_server/src/main.c index dd57b46597714b..4ea4119ae7fdad 100644 --- a/samples/bluetooth/bap_unicast_server/src/main.c +++ b/samples/bluetooth/bap_unicast_server/src/main.c @@ -29,7 +29,7 @@ BT_AUDIO_CONTEXT_TYPE_MEDIA | \ BT_AUDIO_CONTEXT_TYPE_GAME) -NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ASCS_ASE_SRC_COUNT, +NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); @@ -40,13 +40,13 @@ static const struct bt_audio_codec_cap lc3_codec_cap = BT_AUDIO_CODEC_CAP_LC3( static struct bt_conn *default_conn; static struct k_work_delayable audio_send_work; -static struct bt_bap_stream sink_streams[CONFIG_BT_ASCS_ASE_SNK_COUNT]; +static struct bt_bap_stream sink_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT]; static struct audio_source { struct bt_bap_stream stream; uint16_t seq_num; uint16_t max_sdu; size_t len_to_send; -} source_streams[CONFIG_BT_ASCS_ASE_SRC_COUNT]; +} source_streams[CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static size_t configured_source_stream_count; static const struct bt_audio_codec_qos_pref qos_pref = @@ -466,6 +466,11 @@ static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static const struct bt_bap_unicast_server_cb unicast_server_cb = { .config = lc3_config, .reconfig = lc3_reconfig, @@ -727,6 +732,7 @@ int main(void) printk("Bluetooth initialized\n"); + bt_bap_unicast_server_register(¶m); bt_bap_unicast_server_register_cb(&unicast_server_cb); bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap_sink); @@ -780,7 +786,7 @@ int main(void) printk("Advertising successfully started\n"); - if (CONFIG_BT_ASCS_ASE_SRC_COUNT > 0) { + if (CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0) { /* Start send timer */ k_work_init_delayable(&audio_send_work, audio_timer_timeout); } diff --git a/samples/bluetooth/cap_acceptor/prj.conf b/samples/bluetooth/cap_acceptor/prj.conf index 4a67de1e58226d..36da8d2bb04f6d 100644 --- a/samples/bluetooth/cap_acceptor/prj.conf +++ b/samples/bluetooth/cap_acceptor/prj.conf @@ -23,8 +23,8 @@ CONFIG_BT_ATT_PREPARE_COUNT=1 # Support an ISO channel per ASE CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=1 -CONFIG_BT_ASCS_ASE_SRC_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=1 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=2 diff --git a/samples/bluetooth/cap_acceptor/src/cap_acceptor_unicast.c b/samples/bluetooth/cap_acceptor/src/cap_acceptor_unicast.c index a9ae04039d9ed0..cbdb859eb8f1be 100644 --- a/samples/bluetooth/cap_acceptor/src/cap_acceptor_unicast.c +++ b/samples/bluetooth/cap_acceptor/src/cap_acceptor_unicast.c @@ -402,6 +402,17 @@ int init_cap_acceptor_unicast(struct peer_config *peer) if (!cbs_registered) { int err; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + + err = bt_bap_unicast_server_register(¶m); + if (err != 0) { + LOG_ERR("Failed to register BAP unicast server: %d", err); + + return -ENOEXEC; + } err = bt_bap_unicast_server_register_cb(&unicast_server_cb); if (err != 0) { diff --git a/samples/bluetooth/hap_ha/prj.conf b/samples/bluetooth/hap_ha/prj.conf index 83d6f70db37621..222da719c760fd 100644 --- a/samples/bluetooth/hap_ha/prj.conf +++ b/samples/bluetooth/hap_ha/prj.conf @@ -18,8 +18,8 @@ CONFIG_BT_ATT_PREPARE_COUNT=1 CONFIG_BT_AUDIO=y CONFIG_BT_BAP_UNICAST_SERVER=y CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=1 -CONFIG_BT_ASCS_ASE_SRC_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=1 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=2 diff --git a/samples/bluetooth/hap_ha/src/bap_unicast_sr.c b/samples/bluetooth/hap_ha/src/bap_unicast_sr.c index 6244bb76bf1101..c671ad0ccd7662 100644 --- a/samples/bluetooth/hap_ha/src/bap_unicast_sr.c +++ b/samples/bluetooth/hap_ha/src/bap_unicast_sr.c @@ -18,7 +18,7 @@ #include #include -NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ASCS_ASE_SRC_COUNT, +NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); @@ -29,11 +29,12 @@ static const struct bt_audio_codec_cap lc3_codec_cap = BT_AUDIO_CODEC_CAP_LC3( static struct bt_conn *default_conn; static struct k_work_delayable audio_send_work; -static struct bt_bap_stream streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT]; +static struct bt_bap_stream streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static struct audio_source { struct bt_bap_stream *stream; uint16_t seq_num; -} source_streams[CONFIG_BT_ASCS_ASE_SRC_COUNT]; +} source_streams[CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static size_t configured_source_stream_count; static const struct bt_audio_codec_qos_pref qos_pref = @@ -316,6 +317,11 @@ static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static const struct bt_bap_unicast_server_cb unicast_server_cb = { .config = lc3_config, .reconfig = lc3_reconfig, @@ -394,6 +400,7 @@ static struct bt_pacs_cap cap_source = { int bap_unicast_sr_init(void) { + bt_bap_unicast_server_register(¶m); bt_bap_unicast_server_register_cb(&unicast_server_cb); bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap_sink); diff --git a/samples/bluetooth/tmap_peripheral/prj.conf b/samples/bluetooth/tmap_peripheral/prj.conf index 35f5d15502c2e0..69e721a3c9c77d 100644 --- a/samples/bluetooth/tmap_peripheral/prj.conf +++ b/samples/bluetooth/tmap_peripheral/prj.conf @@ -29,8 +29,8 @@ CONFIG_BT_MCC=y # Support an ISO channel per ASE CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=1 -CONFIG_BT_ASCS_ASE_SRC_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=1 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=2 diff --git a/samples/bluetooth/tmap_peripheral/src/bap_unicast_sr.c b/samples/bluetooth/tmap_peripheral/src/bap_unicast_sr.c index 8e325855e655a1..a4e81dd0838ba3 100644 --- a/samples/bluetooth/tmap_peripheral/src/bap_unicast_sr.c +++ b/samples/bluetooth/tmap_peripheral/src/bap_unicast_sr.c @@ -30,11 +30,12 @@ static const struct bt_audio_codec_cap lc3_codec_cap = (AVAILABLE_SINK_CONTEXT | AVAILABLE_SOURCE_CONTEXT)); static struct bt_conn *default_conn; -static struct bt_bap_stream streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT]; +static struct bt_bap_stream streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static struct audio_source { struct bt_bap_stream *stream; uint16_t seq_num; -} source_streams[CONFIG_BT_ASCS_ASE_SRC_COUNT]; +} source_streams[CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static size_t configured_source_stream_count; static const struct bt_audio_codec_qos_pref qos_pref = @@ -271,6 +272,11 @@ static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static const struct bt_bap_unicast_server_cb unicast_server_cb = { .config = lc3_config, .reconfig = lc3_reconfig, @@ -351,6 +357,7 @@ static struct bt_pacs_cap cap = { int bap_unicast_sr_init(void) { + bt_bap_unicast_server_register(¶m); bt_bap_unicast_server_register_cb(&unicast_server_cb); if (IS_ENABLED(CONFIG_BT_PAC_SNK)) { diff --git a/subsys/bluetooth/audio/Kconfig.ascs b/subsys/bluetooth/audio/Kconfig.ascs index acf539f3ac8c6e..61d92c2b4846ef 100644 --- a/subsys/bluetooth/audio/Kconfig.ascs +++ b/subsys/bluetooth/audio/Kconfig.ascs @@ -12,16 +12,16 @@ config BT_ASCS This option enables support for Audio Stream Control Service. if BT_ASCS -config BT_ASCS_ASE_SNK_COUNT - int "Number of Audio Stream Endpoint Sink Characteristics" +config BT_ASCS_MAX_ASE_SNK_COUNT + int "Maximum number of Audio Stream Endpoint Sink Characteristics" default 2 range 0 $(UINT8_MAX) help An ASE Sink characteristic represents the state of an ASE, which is coupled to a single direction of a unicast Audio Stream. -config BT_ASCS_ASE_SRC_COUNT - int "Number of Audio Stream Endpoint Source Characteristics" +config BT_ASCS_MAX_ASE_SRC_COUNT + int "Maximum number of Audio Stream Endpoint Source Characteristics" default 2 range 0 $(UINT8_MAX) help @@ -29,12 +29,12 @@ config BT_ASCS_ASE_SRC_COUNT coupled to a single direction of a unicast Audio Stream. config BT_ASCS_ASE_SNK - def_bool BT_ASCS_ASE_SNK_COUNT > 0 + def_bool BT_ASCS_MAX_ASE_SNK_COUNT > 0 select BT_PAC_SNK select BT_AUDIO_RX config BT_ASCS_ASE_SRC - def_bool BT_ASCS_ASE_SRC_COUNT > 0 + def_bool BT_ASCS_MAX_ASE_SRC_COUNT > 0 select BT_PAC_SRC select BT_AUDIO_TX diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 113425728ff683..c2256512179f96 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -4,6 +4,7 @@ /* * Copyright (c) 2020 Intel Corporation * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -54,8 +55,8 @@ LOG_MODULE_REGISTER(bt_ascs, CONFIG_BT_ASCS_LOG_LEVEL); #define ASE_BUF_SEM_TIMEOUT K_MSEC(CONFIG_BT_ASCS_ASE_BUF_TIMEOUT) #define MAX_ASES_SESSIONS CONFIG_BT_MAX_CONN * \ - (CONFIG_BT_ASCS_ASE_SNK_COUNT + \ - CONFIG_BT_ASCS_ASE_SRC_COUNT) + (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + \ + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT) #define NTF_HEADER_SIZE (3) /* opcode (1) + handle (2) */ @@ -67,13 +68,13 @@ BUILD_ASSERT(CONFIG_BT_ASCS_MAX_ACTIVE_ASES <= MAX(MAX_ASES_SESSIONS, #define ASE_ID(_ase) ase->ep.status.id #define ASE_DIR(_id) \ - (_id > CONFIG_BT_ASCS_ASE_SNK_COUNT ? BT_AUDIO_DIR_SOURCE : BT_AUDIO_DIR_SINK) + (_id > CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT ? BT_AUDIO_DIR_SOURCE : BT_AUDIO_DIR_SINK) #define ASE_UUID(_id) \ - (_id > CONFIG_BT_ASCS_ASE_SNK_COUNT ? BT_UUID_ASCS_ASE_SRC : BT_UUID_ASCS_ASE_SNK) -#define ASE_COUNT (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT) + (_id > CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT ? BT_UUID_ASCS_ASE_SRC : BT_UUID_ASCS_ASE_SNK) +#define ASE_COUNT (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT) #define BT_BAP_ASCS_RSP_NULL ((struct bt_bap_ascs_rsp[]) { BT_BAP_ASCS_RSP(0, 0) }) -static struct bt_ascs_ase { +struct bt_ascs_ase { struct bt_conn *conn; struct bt_bap_ep ep; const struct bt_gatt_attr *attr; @@ -81,7 +82,14 @@ static struct bt_ascs_ase { struct k_work_delayable state_transition_work; enum bt_bap_ep_state state_pending; bool unexpected_iso_link_loss; -} ase_pool[CONFIG_BT_ASCS_MAX_ACTIVE_ASES]; +}; + +struct bt_ascs { + /* Whether the service has been registered or not */ + bool registered; + + struct bt_ascs_ase ase_pool[CONFIG_BT_ASCS_MAX_ACTIVE_ASES]; +} ascs; /* Minimum state size when in the codec configured state */ #define MIN_CONFIG_STATE_SIZE (1 + 1 + 1 + 1 + 1 + 2 + 3 + 3 + 3 + 3 + 5 + 1) @@ -825,8 +833,8 @@ static int ascs_iso_accept(const struct bt_iso_accept_info *info, struct bt_iso_ { LOG_DBG("conn %p", (void *)info->acl); - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; enum bt_bap_ep_state state; struct bt_iso_chan *chan; @@ -1282,8 +1290,8 @@ int bt_ascs_disable_ase(struct bt_bap_ep *ep) static void disconnected(struct bt_conn *conn, uint8_t reason) { - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; if (ase->conn != conn) { continue; @@ -1407,9 +1415,9 @@ static struct bt_ascs_ase *ase_new(struct bt_conn *conn, uint8_t id) __ASSERT(id > 0 && id <= ASE_COUNT, "invalid ASE_ID 0x%02x", id); - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - if (ase_pool[i].conn == NULL) { - ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + if (ascs.ase_pool[i].conn == NULL) { + ase = &ascs.ase_pool[i]; break; } } @@ -1427,8 +1435,8 @@ static struct bt_ascs_ase *ase_new(struct bt_conn *conn, uint8_t id) static struct bt_ascs_ase *ase_find(struct bt_conn *conn, uint8_t id) { - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; if (ase->conn == conn && ase->ep.status.id == id) { return ase; @@ -1685,8 +1693,8 @@ static int ase_config(struct bt_ascs_ase *ase, const struct bt_ascs_config *cfg) static struct bt_bap_ep *ep_lookup_stream(struct bt_conn *conn, struct bt_bap_stream *stream) { - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; if (ase->conn == conn && ase->ep.stream == stream) { return &ase->ep; @@ -1896,8 +1904,8 @@ static ssize_t ascs_config(struct bt_conn *conn, struct net_buf_simple *buf) void bt_ascs_foreach_ep(struct bt_conn *conn, bt_bap_ep_func_t func, void *user_data) { - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; if (ase->conn == conn) { func(&ase->ep, user_data); @@ -3080,22 +3088,120 @@ static ssize_t ascs_cp_write(struct bt_conn *conn, BT_AUDIO_CCC(ascs_ase_cfg_changed) #define BT_ASCS_ASE_SNK_DEFINE(_n, ...) BT_ASCS_ASE_DEFINE(BT_UUID_ASCS_ASE_SNK, (_n) + 1) #define BT_ASCS_ASE_SRC_DEFINE(_n, ...) BT_ASCS_ASE_DEFINE(BT_UUID_ASCS_ASE_SRC, (_n) + 1 + \ - CONFIG_BT_ASCS_ASE_SNK_COUNT) - -BT_GATT_SERVICE_DEFINE(ascs_svc, - BT_GATT_PRIMARY_SERVICE(BT_UUID_ASCS), - BT_AUDIO_CHRC(BT_UUID_ASCS_ASE_CP, - BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, - BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_PREPARE_WRITE, - NULL, ascs_cp_write, NULL), - BT_AUDIO_CCC(ascs_cp_cfg_changed), -#if CONFIG_BT_ASCS_ASE_SNK_COUNT > 0 - LISTIFY(CONFIG_BT_ASCS_ASE_SNK_COUNT, BT_ASCS_ASE_SNK_DEFINE, (,)), -#endif /* CONFIG_BT_ASCS_ASE_SNK_COUNT > 0 */ -#if CONFIG_BT_ASCS_ASE_SRC_COUNT > 0 - LISTIFY(CONFIG_BT_ASCS_ASE_SRC_COUNT, BT_ASCS_ASE_SRC_DEFINE, (,)), -#endif /* CONFIG_BT_ASCS_ASE_SRC_COUNT > 0 */ -); + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT) + +#define BT_ASCS_CHR_ASE_CONTROL_POINT \ + BT_AUDIO_CHRC(BT_UUID_ASCS_ASE_CP, \ + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \ + BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_PREPARE_WRITE, \ + NULL, ascs_cp_write, NULL), \ + BT_AUDIO_CCC(ascs_cp_cfg_changed) + +#if CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 0 +#define BT_ASCS_ASE_SINKS \ + LISTIFY(CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, BT_ASCS_ASE_SNK_DEFINE, (,)), +#else +#define BT_ASCS_ASE_SINKS +#endif /* CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 0 */ + +#if CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0 +#define BT_ASCS_ASE_SOURCES \ + LISTIFY(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, BT_ASCS_ASE_SRC_DEFINE, (,)), +#else +#define BT_ASCS_ASE_SOURCES +#endif /* CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0 */ + +#define BT_ASCS_SERVICE_DEFINITION() { \ + BT_GATT_PRIMARY_SERVICE(BT_UUID_ASCS), \ + BT_AUDIO_CHRC(BT_UUID_ASCS_ASE_CP, \ + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \ + BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_PREPARE_WRITE, \ + NULL, ascs_cp_write, NULL), \ + BT_AUDIO_CCC(ascs_cp_cfg_changed), \ + BT_ASCS_ASE_SINKS \ + BT_ASCS_ASE_SOURCES \ + } + +#define ASCS_ASE_CHAR_ATTR_COUNT 3 /* declaration + value + cccd */ + +static struct bt_gatt_attr ascs_attrs[] = BT_ASCS_SERVICE_DEFINITION(); +static struct bt_gatt_service ascs_svc = (struct bt_gatt_service)BT_GATT_SERVICE(ascs_attrs); + +static void configure_ase_char(uint8_t snk_cnt, uint8_t src_cnt) +{ + uint8_t snk_ases_to_rem = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT - snk_cnt; + uint8_t src_ases_to_rem = CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT - src_cnt; + size_t attrs_to_rem; + + /* Remove the Source ASEs. The ones to remove will always be at the very tail of the + * attributes, so we just decrease the count withe the amount of sources we want to remove. + */ + attrs_to_rem = src_ases_to_rem * ASCS_ASE_CHAR_ATTR_COUNT; + ascs_svc.attr_count -= attrs_to_rem; + + /* Remove the Sink ASEs. + */ + attrs_to_rem = snk_ases_to_rem * ASCS_ASE_CHAR_ATTR_COUNT; + if (CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT == 0 || CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + == src_ases_to_rem) { + /* If there are no Source ASEs present, then we can just decrease the + * attribute count + */ + ascs_svc.attr_count -= attrs_to_rem; + } else { + /* As Source ASEs are present, we need to iterate backwards (as this will likely be + * the shortest distance). Find the first Sink to save, and move all Sources + * backwards to it. + */ + size_t src_start_idx = ascs_svc.attr_count - (src_cnt * ASCS_ASE_CHAR_ATTR_COUNT); + size_t new_src_start_idx = src_start_idx - (snk_ases_to_rem * + ASCS_ASE_CHAR_ATTR_COUNT); + + for (size_t i = 0; i < src_cnt * ASCS_ASE_CHAR_ATTR_COUNT; i++) { + ascs_svc.attrs[new_src_start_idx + i] = ascs_svc.attrs[src_start_idx + i]; + } + + ascs_svc.attr_count -= attrs_to_rem; + } +} + +int bt_ascs_register(uint8_t snk_cnt, uint8_t src_cnt) +{ + int err = 0; + + if (ascs.registered) { + LOG_DBG("ASCS already registered"); + + return -EALREADY; + } + + if (snk_cnt > CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT || + src_cnt > CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT) { + LOG_DBG("Provided ASE count above maximum"); + + return -EINVAL; + } + + /* At least one ASE has been registered */ + if (snk_cnt == 0 && src_cnt == 0) { + LOG_DBG("Can't register ASCS with zero ASEs"); + + return -EINVAL; + } + + configure_ase_char(snk_cnt, src_cnt); + + err = bt_gatt_service_register(&ascs_svc); + if (err != 0) { + LOG_DBG("Failed to register ASCS in gatt DB"); + + return err; + } + + ascs.registered = true; + + return err; +} static int control_point_notify(struct bt_conn *conn, const void *data, uint16_t len) { @@ -3111,6 +3217,10 @@ int bt_ascs_init(const struct bt_bap_unicast_server_cb *cb) { int err; + if (!ascs.registered) { + return -ENOTSUP; + } + if (unicast_server_cb != NULL) { return -EALREADY; } @@ -3128,17 +3238,47 @@ int bt_ascs_init(const struct bt_bap_unicast_server_cb *cb) void bt_ascs_cleanup(void) { - for (size_t i = 0; i < ARRAY_SIZE(ase_pool); i++) { - struct bt_ascs_ase *ase = &ase_pool[i]; + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + struct bt_ascs_ase *ase = &ascs.ase_pool[i]; if (ase->conn != NULL) { bt_ascs_release_ase(&ase->ep); } } - if (unicast_server_cb != NULL) { bt_iso_server_unregister(&iso_server); unicast_server_cb = NULL; } } + +int bt_ascs_unregister(void) +{ + int err; + struct bt_gatt_attr _ascs_attrs[] = BT_ASCS_SERVICE_DEFINITION(); + + if (!ascs.registered) { + LOG_DBG("No ascs instance registered"); + return -EALREADY; + } + + for (size_t i = 0; i < ARRAY_SIZE(ascs.ase_pool); i++) { + if (ascs.ase_pool[i].ep.status.state != BT_BAP_EP_STATE_IDLE) { + return -EBUSY; + } + } + + err = bt_gatt_service_unregister(&ascs_svc); + /* If unregistration was succesfull, make sure to reset ascs_attrs so it can be used for + * new registrations + */ + if (err != 0) { + LOG_DBG("Failed to unregister ASCS"); + return err; + } + + memcpy(&ascs_attrs, &_ascs_attrs, sizeof(struct bt_gatt_attr)); + ascs.registered = false; + + return err; +} #endif /* BT_BAP_UNICAST_SERVER */ diff --git a/subsys/bluetooth/audio/ascs_internal.h b/subsys/bluetooth/audio/ascs_internal.h index f96a1905ec5252..67ba2aefe1d060 100644 --- a/subsys/bluetooth/audio/ascs_internal.h +++ b/subsys/bluetooth/audio/ascs_internal.h @@ -3,6 +3,7 @@ * Copyright (c) 2020 Intel Corporation * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -357,4 +358,7 @@ int bt_ascs_release_ase(struct bt_bap_ep *ep); void bt_ascs_foreach_ep(struct bt_conn *conn, bt_bap_ep_func_t func, void *user_data); +int bt_ascs_register(uint8_t snk_cnt, uint8_t src_cnt); +int bt_ascs_unregister(void); + #endif /* BT_ASCS_INTERNAL_H */ diff --git a/subsys/bluetooth/audio/bap_unicast_server.c b/subsys/bluetooth/audio/bap_unicast_server.c index 0c631186c03a49..ee5160e186a30b 100644 --- a/subsys/bluetooth/audio/bap_unicast_server.c +++ b/subsys/bluetooth/audio/bap_unicast_server.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -28,6 +29,26 @@ LOG_MODULE_REGISTER(bt_bap_unicast_server, CONFIG_BT_BAP_UNICAST_SERVER_LOG_LEVE static const struct bt_bap_unicast_server_cb *unicast_server_cb; +int bt_bap_unicast_server_register(const struct bt_bap_unicast_server_register_param *param) +{ + if (param == NULL) { + LOG_DBG("param is NULL"); + return -EINVAL; + } + + return bt_ascs_register(param->snk_cnt, param->src_cnt); +} + +int bt_bap_unicast_server_unregister(void) +{ + if (unicast_server_cb != NULL) { + LOG_DBG("Callbacks are still registered"); + return -EAGAIN; + } + + return bt_ascs_unregister(); +} + int bt_bap_unicast_server_register_cb(const struct bt_bap_unicast_server_cb *cb) { int err; @@ -54,7 +75,12 @@ int bt_bap_unicast_server_register_cb(const struct bt_bap_unicast_server_cb *cb) int bt_bap_unicast_server_unregister_cb(const struct bt_bap_unicast_server_cb *cb) { - CHECKIF(cb == NULL) { + if (unicast_server_cb == NULL) { + LOG_DBG("no callback is registered"); + return -EALREADY; + } + + if (cb == NULL) { LOG_DBG("cb is NULL"); return -EINVAL; } diff --git a/subsys/bluetooth/audio/gmap_server.c b/subsys/bluetooth/audio/gmap_server.c index d80003bf210a79..b5c96fe3dade85 100644 --- a/subsys/bluetooth/audio/gmap_server.c +++ b/subsys/bluetooth/audio/gmap_server.c @@ -226,26 +226,27 @@ static bool valid_gmap_features(enum bt_gmap_role role, struct bt_gmap_feat feat } if ((ugt_feat & BT_GMAP_UGT_FEAT_SOURCE) != 0 && - CONFIG_BT_ASCS_ASE_SRC_COUNT == 0) { + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT == 0) { LOG_DBG("Cannot support BT_GMAP_UGT_FEAT_SOURCE with " - "CONFIG_BT_ASCS_ASE_SRC_COUNT == 0"); + "CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT == 0"); return false; } if ((ugt_feat & BT_GMAP_UGT_FEAT_MULTISOURCE) != 0 && - (CONFIG_BT_ASCS_ASE_SRC_COUNT < 2 || CONFIG_BT_ASCS_MAX_ACTIVE_ASES < 2)) { + (CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT < 2 || CONFIG_BT_ASCS_MAX_ACTIVE_ASES < 2)) { LOG_DBG("Cannot support BT_GMAP_UGT_FEAT_MULTISOURCE with " - "CONFIG_BT_ASCS_ASE_SRC_COUNT (%d) or " + "CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT (%d) or " "CONFIG_BT_ASCS_MAX_ACTIVE_ASES (%d) < 2", - CONFIG_BT_ASCS_ASE_SRC_COUNT, CONFIG_BT_ASCS_MAX_ACTIVE_ASES); + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, CONFIG_BT_ASCS_MAX_ACTIVE_ASES); return false; } - if ((ugt_feat & BT_GMAP_UGT_FEAT_SINK) != 0 && CONFIG_BT_ASCS_ASE_SNK_COUNT == 0) { + if ((ugt_feat & BT_GMAP_UGT_FEAT_SINK) != 0 + && CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT == 0) { LOG_DBG("Cannot support BT_GMAP_UGT_FEAT_SINK with " - "CONFIG_BT_ASCS_ASE_SNK_COUNT == 0"); + "CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT == 0"); return false; } @@ -262,11 +263,11 @@ static bool valid_gmap_features(enum bt_gmap_role role, struct bt_gmap_feat feat } if ((ugt_feat & BT_GMAP_UGT_FEAT_MULTISINK) != 0 && - (CONFIG_BT_ASCS_ASE_SNK_COUNT < 2 || CONFIG_BT_ASCS_MAX_ACTIVE_ASES < 2)) { + (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT < 2 || CONFIG_BT_ASCS_MAX_ACTIVE_ASES < 2)) { LOG_DBG("Cannot support BT_GMAP_UGT_FEAT_MULTISINK with " - "CONFIG_BT_ASCS_ASE_SNK_COUNT (%d) or " + "CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT (%d) or " "CONFIG_BT_ASCS_MAX_ACTIVE_ASES (%d) < 2", - CONFIG_BT_ASCS_ASE_SNK_COUNT, CONFIG_BT_ASCS_MAX_ACTIVE_ASES); + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, CONFIG_BT_ASCS_MAX_ACTIVE_ASES); return false; } diff --git a/subsys/bluetooth/audio/shell/audio.h b/subsys/bluetooth/audio/shell/audio.h index 811a0e3aca3f5a..4c8b66f33c0690 100644 --- a/subsys/bluetooth/audio/shell/audio.h +++ b/subsys/bluetooth/audio/shell/audio.h @@ -199,7 +199,8 @@ struct broadcast_sink { #if defined(CONFIG_BT_BAP_UNICAST) #define UNICAST_SERVER_STREAM_COUNT \ - COND_CODE_1(CONFIG_BT_ASCS, (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT), \ + COND_CODE_1(CONFIG_BT_ASCS, \ + (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT), \ (0)) #define UNICAST_CLIENT_STREAM_COUNT \ COND_CODE_1(CONFIG_BT_BAP_UNICAST_CLIENT, \ diff --git a/subsys/bluetooth/audio/shell/bap.c b/subsys/bluetooth/audio/shell/bap.c index 089dfcb4960f6c..0cb175ec2eed83 100644 --- a/subsys/bluetooth/audio/shell/bap.c +++ b/subsys/bluetooth/audio/shell/bap.c @@ -3683,7 +3683,47 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[]) return -ENOEXEC; } + if (argc != 1 && (IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER) && argc != 3)) { + shell_error(sh, "Invalid argument count"); + shell_help(sh); + + return SHELL_CMD_HELP_PRINTED; + } + #if defined(CONFIG_BT_BAP_UNICAST_SERVER) + unsigned long snk_cnt, src_cnt; + struct bt_bap_unicast_server_register_param unicast_server_param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + + + if (argc == 3) { + snk_cnt = shell_strtoul(argv[1], 0, &err); + if (snk_cnt > CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT) { + shell_error(sh, "Invalid Sink ASE count: %lu. Valid interval: [0, %u]", + snk_cnt, CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT); + + return -ENOEXEC; + } + + unicast_server_param.snk_cnt = snk_cnt; + + src_cnt = shell_strtoul(argv[2], 0, &err); + if (src_cnt > CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT) { + shell_error(sh, "Invalid Source ASE count: %lu. Valid interval: [0, %u]", + src_cnt, CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT); + + return -ENOEXEC; + } + + unicast_server_param.src_cnt = src_cnt; + } else { + snk_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT; + src_cnt = CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT; + } + + bt_bap_unicast_server_register(&unicast_server_param); bt_bap_unicast_server_register_cb(&unicast_server_cb); #endif /* CONFIG_BT_BAP_UNICAST_SERVER */ @@ -4004,7 +4044,8 @@ static int cmd_print_ase_info(const struct shell *sh, size_t argc, char *argv[]) "[bcast_flag]" HELP_SEP "[extended ]" HELP_SEP "[vendor ]]" SHELL_STATIC_SUBCMD_SET_CREATE( - bap_cmds, SHELL_CMD_ARG(init, NULL, NULL, cmd_init, 1, 0), + bap_cmds, SHELL_CMD_ARG(init, NULL, NULL, cmd_init, 1, + IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER) ? 2 : 0), #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) SHELL_CMD_ARG(select_broadcast, NULL, "", cmd_select_broadcast_source, 2, 0), SHELL_CMD_ARG(create_broadcast, NULL, "[preset ] [enc ]", diff --git a/subsys/bluetooth/audio/shell/gmap.c b/subsys/bluetooth/audio/shell/gmap.c index 2ec5d86e2d60dd..964fbfbadc7a52 100644 --- a/subsys/bluetooth/audio/shell/gmap.c +++ b/subsys/bluetooth/audio/shell/gmap.c @@ -96,18 +96,18 @@ static void set_gmap_features(struct bt_gmap_feat *features) } if (IS_ENABLED(CONFIG_BT_GMAP_UGT_SUPPORTED)) { -#if CONFIG_BT_ASCS_ASE_SRC_COUNT > 0 +#if CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0 features->ugt_feat |= (BT_GMAP_UGT_FEAT_SOURCE | BT_GMAP_UGT_FEAT_80KBPS_SOURCE); -#if CONFIG_BT_ASCS_ASE_SRC_COUNT > 1 +#if CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 1 features->ugt_feat |= BT_GMAP_UGT_FEAT_MULTISOURCE; -#endif /* CONFIG_BT_ASCS_ASE_SRC_COUNT > 1 */ -#endif /* CONFIG_BT_ASCS_ASE_SRC_COUNT > 0 */ -#if CONFIG_BT_ASCS_ASE_SNK_COUNT > 0 +#endif /* CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 1 */ +#endif /* CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0 */ +#if CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 0 features->ugt_feat |= (BT_GMAP_UGT_FEAT_SINK | BT_GMAP_UGT_FEAT_64KBPS_SINK); -#if CONFIG_BT_ASCS_ASE_SNK_COUNT > 1 +#if CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 1 features->ugt_feat |= BT_GMAP_UGT_FEAT_MULTISINK; -#endif /* CONFIG_BT_ASCS_ASE_SNK_COUNT > 1 */ -#endif /* CONFIG_BT_ASCS_ASE_SNK_COUNT > 0 */ +#endif /* CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 1 */ +#endif /* CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 0 */ } if (IS_ENABLED(CONFIG_BT_GMAP_BGS_SUPPORTED)) { diff --git a/tests/bluetooth/audio/ascs/prj.conf b/tests/bluetooth/audio/ascs/prj.conf index 2ed3485e335841..4b1098ceac6a2a 100644 --- a/tests/bluetooth/audio/ascs/prj.conf +++ b/tests/bluetooth/audio/ascs/prj.conf @@ -7,8 +7,8 @@ CONFIG_BT_ISO_PERIPHERAL=y CONFIG_BT_ISO_MAX_CHAN=1 CONFIG_BT_AUDIO=y CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=1 -CONFIG_BT_ASCS_ASE_SRC_COUNT=1 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=2 CONFIG_BT_ASCS_MAX_ACTIVE_ASES=1 CONFIG_BT_BAP_UNICAST_SERVER=y diff --git a/tests/bluetooth/audio/ascs/src/main.c b/tests/bluetooth/audio/ascs/src/main.c index 69e738dceb8251..e91368e230566c 100644 --- a/tests/bluetooth/audio/ascs/src/main.c +++ b/tests/bluetooth/audio/ascs/src/main.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -60,18 +61,26 @@ struct ascs_test_suite_fixture { static void ascs_test_suite_fixture_init(struct ascs_test_suite_fixture *fixture) { + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + int err; + memset(fixture, 0, sizeof(*fixture)); + err = bt_bap_unicast_server_register(¶m); + fixture->ase_cp = test_ase_control_point_get(); test_conn_init(&fixture->conn); - test_ase_snk_get(1, &fixture->ase_snk.attr); + test_ase_snk_get(CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, &fixture->ase_snk.attr); if (fixture->ase_snk.attr != NULL) { fixture->ase_snk.id = test_ase_id_get(fixture->ase_snk.attr); } - test_ase_src_get(1, &fixture->ase_src.attr); + test_ase_src_get(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, &fixture->ase_src.attr); if (fixture->ase_src.attr != NULL) { fixture->ase_src.id = test_ase_id_get(fixture->ase_src.attr); } @@ -84,11 +93,15 @@ static void *ascs_test_suite_setup(void) fixture = malloc(sizeof(*fixture)); zassert_not_null(fixture); - ascs_test_suite_fixture_init(fixture); - return fixture; } +static void ascs_test_suite_before(void *f) +{ + memset(f, 0, sizeof(struct ascs_test_suite_fixture)); + ascs_test_suite_fixture_init(f); +} + static void ascs_test_suite_teardown(void *f) { free(f); @@ -96,11 +109,15 @@ static void ascs_test_suite_teardown(void *f) static void ascs_test_suite_after(void *f) { - bt_ascs_cleanup(); + /* We skip error-checking this, as somehow this breaks the tests, due to seemingly + * memory corruption, causing incorrect lookup of attributes in following 'before' calls + */ + bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + bt_bap_unicast_server_unregister(); } -ZTEST_SUITE(ascs_test_suite, NULL, ascs_test_suite_setup, NULL, ascs_test_suite_after, - ascs_test_suite_teardown); +ZTEST_SUITE(ascs_test_suite, NULL, ascs_test_suite_setup, ascs_test_suite_before, + ascs_test_suite_after, ascs_test_suite_teardown); ZTEST_F(ascs_test_suite, test_has_sink_ase_chrc) { @@ -136,6 +153,158 @@ ZTEST_F(ascs_test_suite, test_sink_ase_read_state_idle) zassert_equal(0x00, hdr.ase_state, "unexpected ASE_State 0x%02x", hdr.ase_state); } +ZTEST_F(ascs_test_suite, test_cb_register_without_ascs_registered) +{ + int err; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, -ENOTSUP, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_with_null_param) +{ + int err; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register(NULL); + zassert_equal(err, -EINVAL, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_twice) +{ + int err; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + + /* Setup already registered once, so calling once here should be sufficient */ + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, -EALREADY, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_too_many_sinks) +{ + int err; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + 1, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, -EINVAL, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_too_many_sources) +{ + int err; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1 + }; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, -EINVAL, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_zero_ases) +{ + int err; + struct bt_bap_unicast_server_register_param param = { + 0, + 0 + }; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, -EINVAL, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_register_fewer_than_max_ases) +{ + int err; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT > 0 ? CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT - 1 : 0, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT > 0 ? CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT - 1 : 0 + }; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, 0, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_unregister_without_register) +{ + int err; + + /* Unregister ASCS, as its registered through setup */ + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_unregister(); + zassert_equal(err, -EALREADY, "unexpected err response %d", err); +} + +ZTEST_F(ascs_test_suite, test_ascs_unregister_with_ases_in_config_state) +{ + const struct test_ase_chrc_value_hdr *hdr; + const struct bt_gatt_attr *ase; + struct bt_bap_stream *stream = &fixture->stream; + struct bt_conn *conn = &fixture->conn; + struct bt_gatt_notify_params *notify_params; + uint8_t ase_id; + int err; + + if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { + ase = fixture->ase_snk.attr; + ase_id = fixture->ase_snk.id; + } else { + ase = fixture->ase_src.attr; + ase_id = fixture->ase_src.id; + } + + zexpect_not_null(ase); + zexpect_true(ase_id != 0x00); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + /* Set ASE to non-idle state */ + test_ase_control_client_config_codec(conn, ase_id, stream); + + err = bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_unregister(); + + /* Expected to notify the upper layers */ + expect_bt_bap_unicast_server_cb_release_called_once(stream); + expect_bt_bap_stream_ops_released_called_once(stream); + + zassert_equal(err, 0, "unexpected err response %d", err); +} + ZTEST_F(ascs_test_suite, test_release_ase_on_callback_unregister) { const struct test_ase_chrc_value_hdr *hdr; @@ -144,6 +313,7 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_callback_unregister) struct bt_conn *conn = &fixture->conn; struct bt_gatt_notify_params *notify_params; uint8_t ase_id; + int err; if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { ase = fixture->ase_snk.attr; @@ -156,7 +326,8 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_callback_unregister) zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); /* Set ASE to non-idle state */ test_ase_control_client_config_codec(conn, ase_id, stream); @@ -225,6 +396,7 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_acl_disconnection) const struct bt_gatt_attr *ase; struct bt_iso_chan *chan; uint8_t ase_id; + int err; if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { ase = fixture->ase_snk.attr; @@ -237,7 +409,8 @@ ZTEST_F(ascs_test_suite, test_release_ase_on_acl_disconnection) zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); /* Set ASE to non-idle state */ test_preamble_state_streaming(conn, ase_id, stream, &chan, @@ -282,7 +455,8 @@ ZTEST_F(ascs_test_suite, test_release_ase_pair_on_acl_disconnection) ase_src_id = fixture->ase_src.id; zexpect_true(ase_src_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_ase_control_client_config_codec(conn, ase_snk_id, &snk_stream); test_ase_control_client_config_qos(conn, ase_snk_id); @@ -327,10 +501,12 @@ ZTEST_F(ascs_test_suite, test_recv_in_streaming_state) }; struct bt_iso_chan *chan; struct net_buf buf; + int err; Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_streaming(conn, ase_id, stream, &chan, false); @@ -357,7 +533,8 @@ ZTEST_F(ascs_test_suite, test_recv_in_enabling_state) Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_enabling(conn, ase_id, stream); @@ -381,6 +558,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_streaming_state) const struct bt_gatt_attr *ase; struct bt_iso_chan *chan; uint8_t ase_id; + int err; if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { ase = fixture->ase_snk.attr; @@ -392,7 +570,8 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_streaming_state) zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_streaming(conn, ase_id, stream, &chan, !IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)); @@ -426,7 +605,8 @@ static void test_cis_link_loss_in_disabling_state(struct ascs_test_suite_fixture zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_enabling(conn, ase_id, stream); err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan); @@ -485,7 +665,8 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state) zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_enabling(conn, ase_id, stream); err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan); @@ -532,7 +713,8 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state_client_retries) zexpect_not_null(ase); zexpect_true(ase_id != 0x00); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); test_preamble_state_enabling(conn, ase_id, stream); err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan); @@ -606,7 +788,8 @@ ZTEST_F(ascs_test_suite, test_ase_state_notification_retry) cp = test_ase_control_point_get(); zexpect_not_null(cp); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); stream_allocated = stream; mock_bap_unicast_server_cb_config_fake.custom_fake = unicast_server_cb_config_custom_fake; diff --git a/tests/bluetooth/audio/ascs/src/test_ase_control_params.c b/tests/bluetooth/audio/ascs/src/test_ase_control_params.c index a89af5bf170d97..45605a538f7e6d 100644 --- a/tests/bluetooth/audio/ascs/src/test_ase_control_params.c +++ b/tests/bluetooth/audio/ascs/src/test_ase_control_params.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -41,30 +42,50 @@ static void *test_ase_control_params_setup(void) fixture = malloc(sizeof(*fixture)); zassert_not_null(fixture); - test_conn_init(&fixture->conn); - fixture->ase_cp = test_ase_control_point_get(); - - if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { - test_ase_snk_get(1, &fixture->ase); - } else { - test_ase_src_get(1, &fixture->ase); - } - return fixture; } static void test_ase_control_params_before(void *f) { struct test_ase_control_params_fixture *fixture = f; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + int err; ARG_UNUSED(fixture); - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + test_conn_init(&fixture->conn); + fixture->ase_cp = test_ase_control_point_get(); + + if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SNK)) { + test_ase_snk_get(CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, &fixture->ase); + } else { + test_ase_src_get(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, &fixture->ase); + } + } static void test_ase_control_params_after(void *f) { - bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + int err; + + err = bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_unregister(); + while (err != 0) { + zassert_equal(err, -EBUSY, "unexpected err response %d", err); + k_sleep(K_MSEC(10)); + err = bt_bap_unicast_server_unregister(); + } } static void test_ase_control_params_teardown(void *f) @@ -171,7 +192,8 @@ ZTEST_F(test_ase_control_params, test_codec_configure_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_codec_configure_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { @@ -367,13 +389,13 @@ static int unicast_server_cb_config_custom_fake(struct bt_conn *conn, const stru ZTEST_F(test_ase_control_params, test_codec_configure_invalid_ase_id_unavailable) { /* Test requires support for at least 2 ASEs */ - if (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT < 2) { + if (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT < 2) { ztest_test_skip(); } const uint8_t ase_id_valid = 0x01; - const uint8_t ase_id_invalid = CONFIG_BT_ASCS_ASE_SNK_COUNT + - CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint8_t ase_id_invalid = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; const uint8_t buf[] = { 0x01, /* Opcode = Config Codec */ 0x02, /* Number_of_ASEs */ @@ -546,7 +568,8 @@ ZTEST_F(test_ase_control_params, test_config_qos_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_config_qos_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { @@ -655,7 +678,8 @@ ZTEST_F(test_ase_control_params, test_enable_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_enable_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { @@ -720,13 +744,13 @@ ZTEST_F(test_ase_control_params, test_enable_metadata_too_short) ZTEST_F(test_ase_control_params, test_enable_invalid_ase_id) { /* Test requires support for at least 2 ASEs */ - if (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT < 2) { + if (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT < 2) { ztest_test_skip(); } const uint8_t ase_id_valid = 0x01; - const uint8_t ase_id_invalid = CONFIG_BT_ASCS_ASE_SNK_COUNT + - CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint8_t ase_id_invalid = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; const uint8_t buf[] = { 0x03, /* Opcode = Enable */ 0x02, /* Number_of_ASEs */ @@ -831,7 +855,7 @@ ZTEST_F(test_ase_control_params, test_receiver_start_ready_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_receiver_start_ready_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; const struct bt_gatt_attr *ase; Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SRC); @@ -932,7 +956,8 @@ ZTEST_F(test_ase_control_params, test_disable_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_disable_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { @@ -1021,7 +1046,7 @@ ZTEST_F(test_ase_control_params, test_receiver_stop_ready_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_receiver_stop_ready_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; const struct bt_gatt_attr *ase; Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SRC); @@ -1123,7 +1148,8 @@ ZTEST_F(test_ase_control_params, test_update_metadata_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_update_metadata_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { @@ -1188,13 +1214,13 @@ ZTEST_F(test_ase_control_params, test_update_metadata_metadata_too_short) ZTEST_F(test_ase_control_params, test_update_metadata_invalid_ase_id) { /* Test requires support for at least 2 ASEs */ - if (CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT < 2) { + if (CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT < 2) { ztest_test_skip(); } const uint8_t ase_id_valid = 0x01; - const uint8_t ase_id_invalid = CONFIG_BT_ASCS_ASE_SNK_COUNT + - CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint8_t ase_id_invalid = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; const uint8_t buf[] = { 0x07, /* Opcode = Update Metadata */ 0x02, /* Number_of_ASEs */ @@ -1260,7 +1286,8 @@ ZTEST_F(test_ase_control_params, test_release_number_of_ases_0x00) ZTEST_F(test_ase_control_params, test_release_number_of_ases_above_max) { - const uint16_t ase_cnt = CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT + 1; + const uint16_t ase_cnt = CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + 1; /* Skip if number of ASEs configured is high enough to support any value in the write req */ if (ase_cnt > UINT8_MAX) { diff --git a/tests/bluetooth/audio/ascs/src/test_ase_state_transition.c b/tests/bluetooth/audio/ascs/src/test_ase_state_transition.c index 0d4e6928d5f8d2..f93e5276f391ad 100644 --- a/tests/bluetooth/audio/ascs/src/test_ase_state_transition.c +++ b/tests/bluetooth/audio/ascs/src/test_ase_state_transition.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -47,24 +48,74 @@ static void *test_sink_ase_state_transition_setup(void) fixture = malloc(sizeof(*fixture)); zassert_not_null(fixture); - memset(fixture, 0, sizeof(*fixture)); + return fixture; +} + +static void test_ase_snk_state_transition_before(void *f) +{ + struct test_ase_state_transition_fixture *fixture = + (struct test_ase_state_transition_fixture *) f; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + int err; + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + memset(fixture, 0, sizeof(struct test_ase_state_transition_fixture)); test_conn_init(&fixture->conn); test_ase_snk_get(1, &fixture->ase.attr); if (fixture->ase.attr != NULL) { fixture->ase.id = test_ase_id_get(fixture->ase.attr); } - return fixture; + bt_bap_stream_cb_register(&fixture->stream, &mock_bap_stream_ops); } -static void test_ase_state_transition_before(void *f) +static void test_ase_src_state_transition_before(void *f) { - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + struct test_ase_state_transition_fixture *fixture = + (struct test_ase_state_transition_fixture *) f; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + int err; + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + memset(fixture, 0, sizeof(struct test_ase_state_transition_fixture)); + test_conn_init(&fixture->conn); + test_ase_src_get(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, &fixture->ase.attr); + if (fixture->ase.attr != NULL) { + fixture->ase.id = test_ase_id_get(fixture->ase.attr); + } + + bt_bap_stream_cb_register(&fixture->stream, &mock_bap_stream_ops); } static void test_ase_state_transition_after(void *f) { - bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + int err; + + err = bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_unregister(); + while (err != 0) { + zassert_equal(err, -EBUSY, "unexpected err response %d", err); + k_sleep(K_MSEC(10)); + err = bt_bap_unicast_server_unregister(); + } } static void test_ase_state_transition_teardown(void *f) @@ -73,7 +124,7 @@ static void test_ase_state_transition_teardown(void *f) } ZTEST_SUITE(test_sink_ase_state_transition, NULL, test_sink_ase_state_transition_setup, - test_ase_state_transition_before, test_ase_state_transition_after, + test_ase_snk_state_transition_before, test_ase_state_transition_after, test_ase_state_transition_teardown); ZTEST_F(test_sink_ase_state_transition, test_client_idle_to_codec_configured) @@ -90,7 +141,6 @@ ZTEST_F(test_sink_ase_state_transition, test_client_idle_to_codec_configured) expect_bt_bap_unicast_server_cb_config_called_once(conn, EMPTY, BT_AUDIO_DIR_SINK, EMPTY); expect_bt_bap_stream_ops_configured_called_once(stream, EMPTY); } - ZTEST_F(test_sink_ase_state_transition, test_client_codec_configured_to_qos_configured) { struct bt_bap_stream *stream = &fixture->stream; @@ -602,7 +652,7 @@ static void *test_source_ase_state_transition_setup(void) memset(fixture, 0, sizeof(*fixture)); test_conn_init(&fixture->conn); - test_ase_src_get(1, &fixture->ase.attr); + test_ase_src_get(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, &fixture->ase.attr); if (fixture->ase.attr != NULL) { fixture->ase.id = test_ase_id_get(fixture->ase.attr); } @@ -611,7 +661,7 @@ static void *test_source_ase_state_transition_setup(void) } ZTEST_SUITE(test_source_ase_state_transition, NULL, test_source_ase_state_transition_setup, - test_ase_state_transition_before, test_ase_state_transition_after, + test_ase_src_state_transition_before, test_ase_state_transition_after, test_ase_state_transition_teardown); ZTEST_F(test_source_ase_state_transition, test_client_idle_to_codec_configured) diff --git a/tests/bluetooth/audio/ascs/src/test_ase_state_transition_invalid.c b/tests/bluetooth/audio/ascs/src/test_ase_state_transition_invalid.c index c36943c0956acf..454a3d85ac402b 100644 --- a/tests/bluetooth/audio/ascs/src/test_ase_state_transition_invalid.c +++ b/tests/bluetooth/audio/ascs/src/test_ase_state_transition_invalid.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -40,23 +41,46 @@ static void *test_ase_state_transition_invalid_setup(void) fixture = malloc(sizeof(*fixture)); zassert_not_null(fixture); - memset(fixture, 0, sizeof(*fixture)); - fixture->ase_cp = test_ase_control_point_get(); - test_conn_init(&fixture->conn); - test_ase_snk_get(1, &fixture->ase_snk); - test_ase_src_get(1, &fixture->ase_src); return fixture; } static void test_ase_state_transition_invalid_before(void *f) { - bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + struct test_ase_state_transition_invalid_fixture *fixture = + (struct test_ase_state_transition_invalid_fixture *)f; + struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT + }; + int err; + + err = bt_bap_unicast_server_register(¶m); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + memset(fixture, 0, sizeof(struct test_ase_state_transition_invalid_fixture)); + fixture->ase_cp = test_ase_control_point_get(); + test_conn_init(&fixture->conn); + test_ase_snk_get(CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, &fixture->ase_snk); + test_ase_src_get(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, &fixture->ase_src); } static void test_ase_state_transition_invalid_after(void *f) { - bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + int err; + + err = bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb); + zassert_equal(err, 0, "unexpected err response %d", err); + + err = bt_bap_unicast_server_unregister(); + while (err != 0) { + zassert_equal(err, -EBUSY, "unexpected err response %d", err); + k_sleep(K_MSEC(10)); + err = bt_bap_unicast_server_unregister(); + } } static void test_ase_state_transition_invalid_teardown(void *f) diff --git a/tests/bluetooth/audio/ascs/testcase.yaml b/tests/bluetooth/audio/ascs/testcase.yaml index e646ea28bbf118..ba6e549c306b70 100644 --- a/tests/bluetooth/audio/ascs/testcase.yaml +++ b/tests/bluetooth/audio/ascs/testcase.yaml @@ -8,11 +8,11 @@ tests: bluetooth.audio.ascs.test_snk_only: type: unit extra_configs: - - CONFIG_BT_ASCS_ASE_SRC_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=0 bluetooth.audio.ascs.test_src_only: type: unit extra_configs: - - CONFIG_BT_ASCS_ASE_SNK_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=0 bluetooth.audio.ascs.test_unicast_client_enabled: type: unit extra_configs: diff --git a/tests/bluetooth/audio/mocks/CMakeLists.txt b/tests/bluetooth/audio/mocks/CMakeLists.txt index ead819b901abc0..4f12849fc2e827 100644 --- a/tests/bluetooth/audio/mocks/CMakeLists.txt +++ b/tests/bluetooth/audio/mocks/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2023 Codecoup +# Coperight (c) 2024 Demant A/S # # SPDX-License-Identifier: Apache-2.0 # @@ -24,6 +25,14 @@ target_include_directories(mocks PUBLIC ${ZEPHYR_BASE}/tests/bluetooth/audio ${ZEPHYR_BASE}/subsys/bluetooth ${ZEPHYR_BASE}/subsys/bluetooth/audio + ${ZEPHYR_BASE}/subsys/bluetooth/common + ${ZEPHYR_BASE}/include/zephyr +) + +target_sources(testbinary PRIVATE + ${ZEPHYR_BASE}/subsys/bluetooth/common/bt_str.c + ${ZEPHYR_BASE}/subsys/bluetooth/host/uuid.c + ${ZEPHYR_BASE}/include/zephyr/kernel.h ) add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks) diff --git a/tests/bluetooth/audio/mocks/include/gatt.h b/tests/bluetooth/audio/mocks/include/gatt.h index 43b56dac29e451..619d35f13e9c68 100644 --- a/tests/bluetooth/audio/mocks/include/gatt.h +++ b/tests/bluetooth/audio/mocks/include/gatt.h @@ -20,5 +20,7 @@ DECLARE_FAKE_VALUE_FUNC(bool, mock_bt_gatt_is_subscribed, struct bt_conn *, void bt_gatt_notify_cb_reset(void); uint16_t bt_gatt_get_mtu(struct bt_conn *conn); +int bt_gatt_service_register(struct bt_gatt_service *svc); +int bt_gatt_service_unregister(struct bt_gatt_service *svc); #endif /* MOCKS_GATT_H_ */ diff --git a/tests/bluetooth/audio/mocks/src/gatt.c b/tests/bluetooth/audio/mocks/src/gatt.c index 3a9db441cb517c..a158b13d176d2c 100644 --- a/tests/bluetooth/audio/mocks/src/gatt.c +++ b/tests/bluetooth/audio/mocks/src/gatt.c @@ -1,18 +1,30 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 */ +#include +#include #include +#include + +#include +#include +#include #include #include #include #include +#include #include +#include +#include #include "gatt.h" #include "conn.h" +#include "common/bt_str.h" #define LOG_LEVEL CONFIG_BT_GATT_LOG_LEVEL #include @@ -28,6 +40,9 @@ DEFINE_FAKE_VALUE_FUNC(int, mock_bt_gatt_notify_cb, struct bt_conn *, DEFINE_FAKE_VALUE_FUNC(bool, mock_bt_gatt_is_subscribed, struct bt_conn *, const struct bt_gatt_attr *, uint16_t); +static uint16_t last_static_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; +static sys_slist_t db; + ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { @@ -182,9 +197,6 @@ void bt_gatt_notify_cb_reset(void) RESET_FAKE(mock_bt_gatt_notify_cb); } -#define foreach_attr_type_dyndb(...) -#define last_static_handle BT_ATT_LAST_ATTRIBUTE_HANDLE - /* Exact copy of subsys/bluetooth/host/gatt.c:gatt_foreach_iter() */ static uint8_t gatt_foreach_iter(const struct bt_gatt_attr *attr, uint16_t handle, uint16_t start_handle, @@ -226,6 +238,39 @@ static uint8_t gatt_foreach_iter(const struct bt_gatt_attr *attr, return result; } +/* Exact copy of subsys/bluetooth/host/gatt.c:foreach_attr_type_dyndb() */ +static void foreach_attr_type_dyndb(uint16_t start_handle, uint16_t end_handle, + const struct bt_uuid *uuid, const void *attr_data, + uint16_t num_matches, bt_gatt_attr_func_t func, void *user_data) +{ + size_t i; + struct bt_gatt_service *svc; + + LOG_DBG("foreach_attr_type_dyndb"); + + SYS_SLIST_FOR_EACH_CONTAINER(&db, svc, node) { + struct bt_gatt_service *next; + + next = SYS_SLIST_PEEK_NEXT_CONTAINER(svc, node); + if (next) { + /* Skip ahead if start is not within service handles */ + if (next->attrs[0].handle <= start_handle) { + continue; + } + } + + for (i = 0; i < svc->attr_count; i++) { + struct bt_gatt_attr *attr = &svc->attrs[i]; + + if (gatt_foreach_iter(attr, attr->handle, start_handle, end_handle, uuid, + attr_data, &num_matches, func, + user_data) == BT_GATT_ITER_STOP) { + return; + } + } + } +} + /* Exact copy of subsys/bluetooth/host/gatt.c:bt_gatt_foreach_attr_type() */ void bt_gatt_foreach_attr_type(uint16_t start_handle, uint16_t end_handle, const struct bt_uuid *uuid, @@ -234,6 +279,8 @@ void bt_gatt_foreach_attr_type(uint16_t start_handle, uint16_t end_handle, { size_t i; + LOG_DBG("bt_gatt_foreach_attr_type"); + if (!num_matches) { num_matches = UINT16_MAX; } @@ -255,17 +302,163 @@ void bt_gatt_foreach_attr_type(uint16_t start_handle, uint16_t end_handle, attr_data, &num_matches, func, user_data) == BT_GATT_ITER_STOP) { + LOG_DBG("Returning after searching static DB"); return; } } } } + LOG_DBG("foreach_attr_type_dyndb"); /* Iterate over dynamic db */ foreach_attr_type_dyndb(start_handle, end_handle, uuid, attr_data, num_matches, func, user_data); } +static void bt_gatt_service_init(void) +{ + last_static_handle = 0U; + + STRUCT_SECTION_FOREACH(bt_gatt_service_static, svc) { + last_static_handle += svc->attr_count; + } +} + +/* Exact copy of subsys/bluetooth/host/gatt.c:found_attr() */ +static uint8_t found_attr(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data) +{ + const struct bt_gatt_attr **found = user_data; + + *found = attr; + + return BT_GATT_ITER_STOP; +} + +/* Exact copy of subsys/bluetooth/host/gatt.c:find_attr() */ +static const struct bt_gatt_attr *find_attr(uint16_t handle) +{ + const struct bt_gatt_attr *attr = NULL; + + bt_gatt_foreach_attr(handle, handle, found_attr, &attr); + + return attr; +} + +/* Exact copy of subsys/bluetooth/host/gatt.c:gatt_insert() */ +static void gatt_insert(struct bt_gatt_service *svc, uint16_t last_handle) +{ + struct bt_gatt_service *tmp, *prev = NULL; + + if (last_handle == 0 || svc->attrs[0].handle > last_handle) { + sys_slist_append(&db, &svc->node); + return; + } + + /* DB shall always have its service in ascending order */ + SYS_SLIST_FOR_EACH_CONTAINER(&db, tmp, node) { + if (tmp->attrs[0].handle > svc->attrs[0].handle) { + if (prev) { + sys_slist_insert(&db, &prev->node, &svc->node); + } else { + sys_slist_prepend(&db, &svc->node); + } + return; + } + + prev = tmp; + } +} + +/* Exact copy of subsys/bluetooth/host/gatt.c:gatt_register() */ +static int gatt_register(struct bt_gatt_service *svc) +{ + struct bt_gatt_service *last; + uint16_t handle, last_handle; + struct bt_gatt_attr *attrs = svc->attrs; + uint16_t count = svc->attr_count; + + if (sys_slist_is_empty(&db)) { + handle = last_static_handle; + last_handle = 0; + goto populate; + } + + last = SYS_SLIST_PEEK_TAIL_CONTAINER(&db, last, node); + handle = last->attrs[last->attr_count - 1].handle; + last_handle = handle; + +populate: + /* Populate the handles and append them to the list */ + for (; attrs && count; attrs++, count--) { + if (!attrs->handle) { + /* Allocate handle if not set already */ + attrs->handle = ++handle; + } else if (attrs->handle > handle) { + /* Use existing handle if valid */ + handle = attrs->handle; + } else if (find_attr(attrs->handle)) { + /* Service has conflicting handles */ + LOG_ERR("Mock: Unable to register handle 0x%04x", attrs->handle); + return -EINVAL; + } + + LOG_DBG("attr %p handle 0x%04x uuid %s perm 0x%02x", attrs, attrs->handle, + bt_uuid_str(attrs->uuid), attrs->perm); + } + + gatt_insert(svc, last_handle); + + return 0; +} + +static int gatt_unregister(struct bt_gatt_service *svc) +{ + if (!sys_slist_find_and_remove(&db, &svc->node)) { + return -ENOENT; + } + + return 0; +} + +int bt_gatt_service_register(struct bt_gatt_service *svc) +{ + int err; + + __ASSERT(svc, "invalid parameters\n"); + __ASSERT(svc->attrs, "invalid parameters\n"); + __ASSERT(svc->attr_count, "invalid parameters\n"); + + /* Init GATT core services */ + bt_gatt_service_init(); + + /* Do no allow to register mandatory services twice */ + if (!bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GAP) || + !bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GATT)) { + return -EALREADY; + } + + err = gatt_register(svc); + if (err < 0) { + return err; + } + + return 0; +} + +int bt_gatt_service_unregister(struct bt_gatt_service *svc) +{ + int err; + + __ASSERT(svc, "invalid parameters\n"); + + err = gatt_unregister(svc); + if (err) { + return err; + } + + return 0; +} + /* Exact copy of subsys/bluetooth/host/gatt.c:bt_gatt_attr_read() */ ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t buf_len, uint16_t offset, diff --git a/tests/bluetooth/shell/audio.conf b/tests/bluetooth/shell/audio.conf index e783677ad0fc50..404a04c885279b 100644 --- a/tests/bluetooth/shell/audio.conf +++ b/tests/bluetooth/shell/audio.conf @@ -58,8 +58,8 @@ CONFIG_BT_ISO_RX_MTU=310 CONFIG_BT_AUDIO=y CONFIG_BT_BAP_UNICAST_SERVER=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=2 -CONFIG_BT_ASCS_ASE_SRC_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=2 CONFIG_BT_BAP_UNICAST_CLIENT=y CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT=4 @@ -70,8 +70,8 @@ CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=255 CONFIG_BT_AUDIO_CODEC_CAP_MAX_METADATA_SIZE=255 CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=2 -CONFIG_BT_ASCS_ASE_SRC_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=2 CONFIG_BT_BAP_UNICAST_CLIENT=y CONFIG_BT_BAP_BROADCAST_SOURCE=y CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT=4 diff --git a/tests/bluetooth/shell/testcase.yaml b/tests/bluetooth/shell/testcase.yaml index 4a3bca95fb11bb..269d9319b7cbff 100644 --- a/tests/bluetooth/shell/testcase.yaml +++ b/tests/bluetooth/shell/testcase.yaml @@ -262,12 +262,12 @@ tests: extra_args: CONF_FILE="audio.conf" build_only: true extra_configs: - - CONFIG_BT_ASCS_ASE_SNK_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=0 bluetooth.audio_shell.no_server_ase_src: extra_args: CONF_FILE="audio.conf" build_only: true extra_configs: - - CONFIG_BT_ASCS_ASE_SRC_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=0 bluetooth.audio_shell.no_client_ase_snk: extra_args: CONF_FILE="audio.conf" build_only: true @@ -294,14 +294,14 @@ tests: extra_configs: - CONFIG_BT_BAP_BROADCAST_SOURCE=n - CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT=0 - - CONFIG_BT_ASCS_ASE_SRC_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=0 bluetooth.audio_shell.no_audio_rx: extra_args: CONF_FILE="audio.conf" build_only: true extra_configs: - CONFIG_BT_BAP_BROADCAST_SINK=n - CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT=0 - - CONFIG_BT_ASCS_ASE_SNK_COUNT=0 + - CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=0 bluetooth.audio_shell.no_has: extra_args: CONF_FILE="audio.conf" build_only: true diff --git a/tests/bluetooth/tester/overlay-le-audio.conf b/tests/bluetooth/tester/overlay-le-audio.conf index 6b0186e55d8595..d0c40206f0a8c0 100644 --- a/tests/bluetooth/tester/overlay-le-audio.conf +++ b/tests/bluetooth/tester/overlay-le-audio.conf @@ -57,8 +57,8 @@ CONFIG_BT_BUF_ACL_RX_SIZE=255 # ASCS CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=2 -CONFIG_BT_ASCS_ASE_SRC_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=2 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=4 diff --git a/tests/bluetooth/tester/src/audio/btp_bap_audio_stream.c b/tests/bluetooth/tester/src/audio/btp_bap_audio_stream.c index e294cacbaea18f..6cd9bad48a5e08 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_audio_stream.c +++ b/tests/bluetooth/tester/src/audio/btp_bap_audio_stream.c @@ -21,7 +21,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL); #include "btp/btp.h" #include "btp_bap_audio_stream.h" -NET_BUF_POOL_FIXED_DEFINE(tx_pool, MAX(CONFIG_BT_ASCS_ASE_SRC_COUNT, +NET_BUF_POOL_FIXED_DEFINE(tx_pool, MAX(CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT, CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT), BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); diff --git a/tests/bluetooth/tester/src/audio/btp_bap_unicast.c b/tests/bluetooth/tester/src/audio/btp_bap_unicast.c index 531605689fcf9b..0a78441f0132cf 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_unicast.c +++ b/tests/bluetooth/tester/src/audio/btp_bap_unicast.c @@ -897,6 +897,11 @@ static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir) btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_SUCCESS); } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static struct bt_bap_unicast_client_cb unicast_client_cbs = { .location = unicast_client_location_cb, .available_contexts = available_contexts_cb, @@ -1652,6 +1657,13 @@ int btp_bap_unicast_init(void) (void)memset(connections, 0, sizeof(connections)); + err = bt_bap_unicast_server_register(¶m); + if (err != 0) { + LOG_DBG("Failed to register unicast server (err %d)\n", err); + + return err; + } + err = bt_bap_unicast_server_register_cb(&unicast_server_cb); if (err != 0) { LOG_DBG("Failed to register client callbacks: %d", err); diff --git a/tests/bluetooth/tester/src/audio/btp_bap_unicast.h b/tests/bluetooth/tester/src/audio/btp_bap_unicast.h index e4111289da8c6c..1ac0bac1aa9353 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_unicast.h +++ b/tests/bluetooth/tester/src/audio/btp_bap_unicast.h @@ -9,9 +9,9 @@ #include #define BTP_BAP_UNICAST_MAX_SNK_STREAMS_COUNT MIN(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, \ - CONFIG_BT_ASCS_ASE_SNK_COUNT) + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT) #define BTP_BAP_UNICAST_MAX_SRC_STREAMS_COUNT MIN(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT, \ - CONFIG_BT_ASCS_ASE_SRC_COUNT) + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT) #define BTP_BAP_UNICAST_MAX_STREAMS_COUNT BTP_BAP_UNICAST_MAX_SNK_STREAMS_COUNT + \ BTP_BAP_UNICAST_MAX_SRC_STREAMS_COUNT #define BTP_BAP_UNICAST_MAX_END_POINTS_COUNT CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT + \ diff --git a/tests/bsim/bluetooth/audio/prj.conf b/tests/bsim/bluetooth/audio/prj.conf index 7b3d19740e0080..f10854441e2332 100644 --- a/tests/bsim/bluetooth/audio/prj.conf +++ b/tests/bsim/bluetooth/audio/prj.conf @@ -28,8 +28,8 @@ CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT=4 CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT=2 CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT=2 CONFIG_BT_ASCS=y -CONFIG_BT_ASCS_ASE_SNK_COUNT=2 -CONFIG_BT_ASCS_ASE_SRC_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=2 +CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=2 CONFIG_BT_BAP_BROADCAST_SOURCE=y CONFIG_BT_BAP_BROADCAST_SINK=y CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=196 diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c index 7d4d097a615da0..c48d86732e7d86 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c @@ -68,7 +68,7 @@ static const struct bt_audio_codec_cap lc3_codec_cap = { }; static struct audio_test_stream - test_streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT]; + test_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static const struct bt_audio_codec_qos_pref qos_pref = BT_AUDIO_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 40000, 40000, 40000, 40000); @@ -225,6 +225,11 @@ static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static const struct bt_bap_unicast_server_cb unicast_server_cb = { .config = lc3_config, .reconfig = lc3_reconfig, @@ -487,6 +492,13 @@ static void init(void) printk("Bluetooth initialized\n"); + err = bt_bap_unicast_server_register(¶m); + if (err != 0) { + FAIL("Failed to register unicast server (err %d)\n", err); + + return; + } + bt_bap_unicast_server_register_cb(&unicast_server_cb); err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap); diff --git a/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c b/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c index 3d424eafa39a5b..dbbc308718eb6e 100644 --- a/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c @@ -76,8 +76,8 @@ static uint32_t bis_index_bitfield; #define UNICAST_CHANNEL_COUNT_1 BIT(0) -static struct bt_cap_stream unicast_streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + - CONFIG_BT_ASCS_ASE_SRC_COUNT]; +static struct bt_cap_stream unicast_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; static bool subgroup_data_func_cb(struct bt_data *data, void *user_data) { @@ -560,6 +560,11 @@ static int unicast_server_release(struct bt_bap_stream *stream, struct bt_bap_as return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static struct bt_bap_unicast_server_cb unicast_server_cbs = { .config = unicast_server_config, .reconfig = unicast_server_reconfig, @@ -728,6 +733,13 @@ static void init(void) return; } + err = bt_bap_unicast_server_register(¶m); + if (err != 0) { + FAIL("Failed to register unicast server (err %d)\n", err); + + return; + } + err = bt_bap_unicast_server_register_cb(&unicast_server_cbs); if (err != 0) { FAIL("Failed to register unicast server callbacks (err %d)\n", diff --git a/tests/bsim/bluetooth/audio/src/gmap_ugt_test.c b/tests/bsim/bluetooth/audio/src/gmap_ugt_test.c index 4ac3c1cfe0e869..09de70135fefa6 100644 --- a/tests/bsim/bluetooth/audio/src/gmap_ugt_test.c +++ b/tests/bsim/bluetooth/audio/src/gmap_ugt_test.c @@ -48,7 +48,7 @@ static const struct bt_audio_codec_qos_pref unicast_qos_pref = #define UNICAST_CHANNEL_COUNT_1 BIT(0) static struct bt_cap_stream - unicast_streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT]; + unicast_streams[CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; CREATE_FLAG(flag_unicast_stream_started); CREATE_FLAG(flag_gmap_discovered); @@ -219,6 +219,11 @@ static int unicast_server_release(struct bt_bap_stream *stream, struct bt_bap_as return 0; } +static struct bt_bap_unicast_server_register_param param = { + CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT, + CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT +}; + static struct bt_bap_unicast_server_cb unicast_server_cbs = { .config = unicast_server_config, .reconfig = unicast_server_reconfig, @@ -404,6 +409,13 @@ static void test_main(void) return; } + err = bt_bap_unicast_server_register(¶m); + if (err != 0) { + FAIL("Failed to register unicast server (err %d)\n", err); + + return; + } + err = bt_bap_unicast_server_register_cb(&unicast_server_cbs); if (err != 0) { FAIL("Failed to register unicast server callbacks (err %d)\n", err);