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 <frdn@demant.com>
This commit is contained in:
Fredrik Danebjer 2024-08-02 11:08:57 +02:00 committed by Anas Nashif
commit c9da274eb2
39 changed files with 1017 additions and 175 deletions

View file

@ -16,7 +16,7 @@ Commands
bap --help
Subcommands:
init
init : [ase_sink_count, ase_source_count]
select_broadcast : <stream>
create_broadcast : [preset <preset_name>] [enc <broadcast_code>]
start_broadcast :

View file

@ -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
=================

View file

@ -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.

View file

@ -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
*/

View file

@ -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

View file

@ -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(&param);
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);
}

View file

@ -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

View file

@ -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(&param);
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) {

View file

@ -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

View file

@ -18,7 +18,7 @@
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/pacs.h>
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(&param);
bt_bap_unicast_server_register_cb(&unicast_server_cb);
bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap_sink);

View file

@ -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

View file

@ -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(&param);
bt_bap_unicast_server_register_cb(&unicast_server_cb);
if (IS_ENABLED(CONFIG_BT_PAC_SNK)) {

View file

@ -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

View file

@ -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 @@ respond:
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)
CONFIG_BT_ASCS_MAX_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 */
);
#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 */

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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, \

View file

@ -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 <meta>]" HELP_SEP "[vendor <meta>]]"
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, "<stream>", cmd_select_broadcast_source, 2, 0),
SHELL_CMD_ARG(create_broadcast, NULL, "[preset <preset_name>] [enc <broadcast_code>]",

View file

@ -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)) {

View file

@ -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

View file

@ -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(&param);
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,7 +153,120 @@ 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_release_ase_on_callback_unregister)
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(&param);
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(&param);
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(&param);
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(&param);
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(&param);
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;
@ -144,6 +274,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 +287,47 @@ 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);
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;
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);
@ -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;

View file

@ -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(&param);
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) {

View file

@ -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(&param);
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(&param);
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)

View file

@ -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(&param);
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)

View file

@ -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:

View file

@ -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)

View file

@ -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_ */

View file

@ -1,18 +1,30 @@
/*
* Copyright (c) 2023 Codecoup
* Copyright (c) 2024 Demant A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/sys/util.h>
#include <zephyr/types.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/att.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/fff.h>
#include <zephyr/sys/iterable_sections.h>
#include <ztest_test.h>
#include <ztest_assert.h>
#include "gatt.h"
#include "conn.h"
#include "common/bt_str.h"
#define LOG_LEVEL CONFIG_BT_GATT_LOG_LEVEL
#include <zephyr/logging/log.h>
@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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(&param);
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);

View file

@ -9,9 +9,9 @@
#include <zephyr/bluetooth/audio/cap.h>
#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 + \

View file

@ -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

View file

@ -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(&param);
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);

View file

@ -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(&param);
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",

View file

@ -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(&param);
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);