Bluetooth: CAP: Make unicast stop more similar to unicast start

Modify the parameters for bt_cap_initiator_unicast_audio_stop
so that they are more similar to
bt_cap_initiator_unicast_audio_start.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-01-18 12:05:25 +01:00 committed by David Leach
commit 065253c173
7 changed files with 274 additions and 44 deletions

View file

@ -255,6 +255,18 @@ struct bt_cap_unicast_audio_update_param {
struct bt_cap_unicast_audio_update_stream_param *stream_params;
};
/** Parameters for the bt_cap_initiator_unicast_audio_stop() function */
struct bt_cap_unicast_audio_stop_param {
/** The type of the set. */
enum bt_cap_set_type type;
/** The number of streams in @p streams */
size_t count;
/** Array of streams to stop */
struct bt_cap_stream **streams;
};
/**
* @brief Register Common Audio Profile Initiator callbacks
*
@ -297,19 +309,19 @@ int bt_cap_initiator_unicast_audio_start(const struct bt_cap_unicast_audio_start
int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_update_param *param);
/**
* @brief Stop unicast audio streams for a unicast group.
* @brief Stop unicast audio streams.
*
* This will stop one or more streams.
*
* @note @kconfig{CONFIG_BT_CAP_INITIATOR} and
* @kconfig{CONFIG_BT_BAP_UNICAST_CLIENT} must be enabled for this function
* to be enabled.
*
* @param unicast_group The group of unicast devices to stop. The audio streams
* in this will be stopped and reset, and the
* @p unicast_group will be invalidated.
* @param param Stop parameters.
*
* @return 0 on success or negative error value on failure.
*/
int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_group);
int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_param *param);
/** @brief Cancel any current Common Audio Profile procedure
*

View file

@ -1135,12 +1135,98 @@ static bool can_release(const struct bt_bap_stream *bap_stream)
return ep_info.state != BT_BAP_EP_STATE_IDLE;
}
int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_stop_param *param)
{
struct bt_bap_unicast_group *unicast_group = NULL;
CHECKIF(param == NULL) {
LOG_DBG("param is NULL");
return false;
}
CHECKIF(param->count == 0) {
LOG_DBG("Invalid param->count: %u", param->count);
return false;
}
CHECKIF(param->streams == NULL) {
LOG_DBG("param->streams is NULL");
return false;
}
CHECKIF(param->count > CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
LOG_DBG("param->count (%zu) is larger than "
"CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT (%d)",
param->count, CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT);
return false;
}
for (size_t i = 0U; i < param->count; i++) {
const struct bt_cap_stream *cap_stream = param->streams[i];
const struct bt_bap_stream *bap_stream;
struct bt_cap_common_client *client;
struct bt_conn *conn;
CHECKIF(cap_stream == NULL) {
LOG_DBG("param->streams[%zu] is NULL", i);
return false;
}
bap_stream = &cap_stream->bap_stream;
conn = bap_stream->conn;
CHECKIF(conn == NULL) {
LOG_DBG("param->streams[%zu]->bap_stream.conn is NULL", i);
return -EINVAL;
}
client = bt_cap_common_get_client_by_acl(conn);
if (!client->cas_found) {
LOG_DBG("CAS was not found for param->streams[%zu]", i);
return false;
}
CHECKIF(bap_stream->group == NULL) {
LOG_DBG("param->streams[%zu] is not in a unicast group", i);
return false;
}
/* Use the group of the first stream for comparison */
if (unicast_group == NULL) {
unicast_group = bap_stream->group;
} else {
CHECKIF(bap_stream->group != unicast_group) {
LOG_DBG("param->streams[%zu] is not in this group %p", i,
unicast_group);
return false;
}
}
if (!can_release(bap_stream)) {
LOG_DBG("Cannot stop param->streams[%zu]", i);
return false;
}
for (size_t j = 0U; j < i; j++) {
if (param->streams[j] == cap_stream) {
LOG_DBG("param->stream_params[%zu] (%p) is "
"duplicated by "
"param->stream_params[%zu] (%p)",
j, param->streams[j], i, cap_stream);
return false;
}
}
}
return true;
}
int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_param *param)
{
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
struct bt_cap_initiator_proc_param *proc_param;
struct bt_bap_stream *bap_stream;
size_t stream_cnt;
int err;
if (bt_cap_common_proc_is_active()) {
@ -1149,28 +1235,17 @@ int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_gro
return -EBUSY;
}
CHECKIF(unicast_group == NULL) {
LOG_DBG("unicast_group is NULL");
if (!valid_unicast_audio_stop_param(param)) {
return -EINVAL;
}
stream_cnt = 0U;
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, bap_stream, _node) {
if (can_release(bap_stream)) {
struct bt_cap_stream *cap_stream =
CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
active_proc->proc_param.initiator[stream_cnt].stream = cap_stream;
stream_cnt++;
}
for (size_t i = 0U; i < param->count; i++) {
struct bt_cap_stream *cap_stream = param->streams[i];
active_proc->proc_param.initiator[i].stream = cap_stream;
}
if (stream_cnt == 0U) {
LOG_DBG("All streams are already stopped");
return -EALREADY;
}
bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_STOP, stream_cnt);
bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_STOP, param->count);
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE);

View file

@ -444,17 +444,75 @@ static int cmd_cap_initiator_unicast_update(const struct shell *sh, size_t argc,
static int cmd_cap_initiator_unicast_stop(const struct shell *sh, size_t argc,
char *argv[])
{
struct bt_cap_stream *streams[CAP_UNICAST_CLIENT_STREAM_COUNT];
struct bt_cap_unicast_audio_stop_param param = {0};
int err = 0;
if (default_conn == NULL) {
shell_error(sh, "Not connected");
return -ENOEXEC;
} else if (default_unicast_group == NULL) {
shell_error(sh, "No unicast group started");
}
if (argc == 2 && strcmp(argv[1], "all") == 0) {
for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
struct bt_cap_stream *stream = &unicast_streams[i].stream;
struct bt_bap_ep_info ep_info;
if (stream->bap_stream.conn == NULL) {
break;
}
err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
if (err != 0) {
shell_error(sh, "Failed to get endpoint info: %d", err);
return -ENOEXEC;
}
streams[param.count] = stream;
param.count++;
}
} else {
for (size_t i = 1U; i < argc; i++) {
struct bt_cap_stream *stream = (void *)shell_strtoul(argv[i], 16, &err);
struct bt_bap_ep_info ep_info;
if (err != 0) {
shell_error(sh, "Failed to parse stream argument %s: %d", argv[i],
err);
return err;
}
if (!PART_OF_ARRAY(unicast_streams, stream)) {
shell_error(sh, "Pointer %p is not a CAP stream pointer", stream);
return -ENOEXEC;
}
err = bt_bap_ep_get_info(stream->bap_stream.ep, &ep_info);
if (err != 0) {
shell_error(sh, "Failed to get endpoint info: %d", err);
return -ENOEXEC;
}
streams[param.count] = stream;
param.count++;
}
}
if (param.count == 0) {
shell_error(sh, "No streams to update");
return -ENOEXEC;
}
err = bt_cap_initiator_unicast_audio_stop(default_unicast_group);
param.streams = streams;
param.type = BT_CAP_SET_TYPE_AD_HOC;
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
shell_print(sh, "Failed to update unicast audio: %d", err);
}

View file

@ -405,16 +405,40 @@ static uint8_t btp_cap_unicast_audio_update(const void *cmd, uint16_t cmd_len,
static uint8_t btp_cap_unicast_audio_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
struct bt_cap_stream
*streams[ARRAY_SIZE(btp_csip_set_members) * BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
struct bt_cap_unicast_audio_stop_param param = {0};
int err;
const struct btp_cap_unicast_audio_stop_cmd *cp = cmd;
struct btp_bap_unicast_group *group;
size_t stream_cnt = 0U;
LOG_DBG("");
group = btp_bap_unicast_group_find(cp->cig_id);
/* Get generate the same stream list as used by btp_cap_unicast_audio_start */
for (size_t conn_index = 0; conn_index < ARRAY_SIZE(btp_csip_set_members); conn_index++) {
struct btp_bap_unicast_connection *u_conn = btp_bap_unicast_conn_get(conn_index);
err = bt_cap_initiator_unicast_audio_stop(group->cig);
if (u_conn->end_points_count == 0) {
/* Connection not initialized */
continue;
}
for (size_t i = 0; i < ARRAY_SIZE(u_conn->streams); i++) {
struct btp_bap_unicast_stream *u_stream = &u_conn->streams[i];
if (!u_stream->in_use || u_stream->cig_id != cp->cig_id) {
continue;
}
streams[stream_cnt++] = stream_unicast_to_cap(u_stream);
}
}
param.streams = streams;
param.count = stream_cnt;
param.type = BT_CAP_SET_TYPE_AD_HOC;
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
LOG_ERR("Failed to start unicast audio: %d", err);

View file

@ -55,6 +55,8 @@ static struct bt_conn *connected_conns[CAP_AC_MAX_CONN];
static size_t connected_conn_cnt;
static const struct named_lc3_preset *snk_named_preset;
static const struct named_lc3_preset *src_named_preset;
static struct bt_cap_stream *non_idle_streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
static size_t non_idle_streams_cnt;
CREATE_FLAG(flag_discovered);
CREATE_FLAG(flag_codec_found);
@ -106,8 +108,19 @@ static const struct named_lc3_preset lc3_unicast_presets[] = {
static void unicast_stream_configured(struct bt_bap_stream *stream,
const struct bt_audio_codec_qos_pref *pref)
{
struct bt_cap_stream *cap_stream = cap_stream_from_bap_stream(stream);
printk("Configured stream %p\n", stream);
for (size_t i = 0U; i < ARRAY_SIZE(non_idle_streams); i++) {
if (non_idle_streams[i] == NULL) {
non_idle_streams[i] = cap_stream;
non_idle_streams_cnt++;
return;
}
}
FAIL("Could not store cap_stream in non_idle_streams\n");
/* TODO: The preference should be used/taken into account when
* setting the QoS
*/
@ -145,7 +158,19 @@ static void unicast_stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
static void unicast_stream_released(struct bt_bap_stream *stream)
{
struct bt_cap_stream *cap_stream = cap_stream_from_bap_stream(stream);
printk("Released stream %p\n", stream);
for (size_t i = 0U; i < ARRAY_SIZE(non_idle_streams); i++) {
if (non_idle_streams[i] == cap_stream) {
non_idle_streams[i] = NULL;
non_idle_streams_cnt--;
return;
}
}
FAIL("Could not find cap_stream in non_idle_streams\n");
}
static struct bt_bap_stream_ops unicast_stream_ops = {
@ -347,6 +372,10 @@ static void init(void)
for (size_t i = 0; i < ARRAY_SIZE(unicast_client_source_streams); i++) {
bt_cap_stream_ops_register(&unicast_client_source_streams[i], &unicast_stream_ops);
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_streams); i++) {
bt_cap_stream_ops_register(&unicast_streams[i].stream, &unicast_stream_ops);
}
}
static void cap_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
@ -744,30 +773,35 @@ static void unicast_audio_stop_inval(void)
err = bt_cap_initiator_unicast_audio_stop(NULL);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_stop with NULL group did not fail\n");
FAIL("bt_cap_initiator_unicast_audio_stop with NULL param did not fail\n");
return;
}
}
static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
{
struct bt_cap_unicast_audio_stop_param param;
int err;
param.type = BT_CAP_SET_TYPE_AD_HOC;
param.count = non_idle_streams_cnt;
param.streams = non_idle_streams;
UNSET_FLAG(flag_stopped);
err = bt_cap_initiator_unicast_audio_stop(unicast_group);
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
FAIL("Failed to start unicast audio: %d\n", err);
FAIL("Failed to stop unicast audio: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_stopped);
/* Verify that it cannot be stopped twice */
err = bt_cap_initiator_unicast_audio_stop(unicast_group);
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_stop with already-stopped unicast group did "
"not fail\n");
FAIL("bt_cap_initiator_unicast_audio_stop with already-stopped streams did not "
"fail\n");
return;
}
}

View file

@ -139,6 +139,16 @@ struct audio_test_stream {
size_t rx_cnt;
};
static inline struct bt_cap_stream *cap_stream_from_bap_stream(struct bt_bap_stream *bap_stream)
{
return CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
}
static inline struct bt_bap_stream *bap_stream_from_cap_stream(struct bt_cap_stream *cap_stream)
{
return &cap_stream->bap_stream;
}
static inline struct audio_test_stream *
audio_test_stream_from_cap_stream(struct bt_cap_stream *cap_stream)
{
@ -148,10 +158,7 @@ audio_test_stream_from_cap_stream(struct bt_cap_stream *cap_stream)
static inline struct audio_test_stream *
audio_test_stream_from_bap_stream(struct bt_bap_stream *bap_stream)
{
struct bt_cap_stream *cap_stream =
CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
return audio_test_stream_from_cap_stream(cap_stream);
return audio_test_stream_from_cap_stream(cap_stream_from_bap_stream(bap_stream));
}
static inline struct bt_cap_stream *
@ -163,7 +170,7 @@ cap_stream_from_audio_test_stream(struct audio_test_stream *test_stream)
static inline struct bt_bap_stream *
bap_stream_from_audio_test_stream(struct audio_test_stream *test_stream)
{
return &cap_stream_from_audio_test_stream(test_stream)->bap_stream;
return bap_stream_from_cap_stream(cap_stream_from_audio_test_stream(test_stream));
}
#endif /* ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_ */

View file

@ -106,6 +106,8 @@ struct named_lc3_preset named_preset;
static struct audio_test_stream broadcast_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
static struct unicast_stream unicast_streams[GMAP_UNICAST_AC_MAX_STREAM];
static struct bt_cap_stream *started_unicast_streams[GMAP_UNICAST_AC_MAX_STREAM];
static size_t started_unicast_streams_cnt;
static struct bt_bap_ep
*sink_eps[GMAP_UNICAST_AC_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_bap_ep
@ -685,6 +687,7 @@ static int gmap_ac_cap_unicast_start(const struct gmap_unicast_ac_param *param,
size_t stream_cnt = 0U;
size_t snk_ep_cnt = 0U;
size_t src_ep_cnt = 0U;
int err;
for (size_t i = 0U; i < param->conn_cnt; i++) {
#if UNICAST_SINK_SUPPORTED
@ -797,7 +800,16 @@ static int gmap_ac_cap_unicast_start(const struct gmap_unicast_ac_param *param,
start_param.count = stream_cnt;
start_param.type = BT_CAP_SET_TYPE_AD_HOC;
return bt_cap_initiator_unicast_audio_start(&start_param);
err = bt_cap_initiator_unicast_audio_start(&start_param);
if (err == 0) {
for (size_t i = 0U; i < start_param.count; i++) {
started_unicast_streams[i] = start_param.stream_params[i].stream;
}
started_unicast_streams_cnt = start_param.count;
}
return err;
}
static int gmap_ac_unicast(const struct gmap_unicast_ac_param *param,
@ -887,17 +899,25 @@ static int gmap_ac_unicast(const struct gmap_unicast_ac_param *param,
static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
{
struct bt_cap_unicast_audio_stop_param param;
int err;
UNSET_FLAG(flag_stopped);
err = bt_cap_initiator_unicast_audio_stop(unicast_group);
param.type = BT_CAP_SET_TYPE_AD_HOC;
param.count = started_unicast_streams_cnt;
param.streams = started_unicast_streams;
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
FAIL("Failed to start unicast audio: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_stopped);
started_unicast_streams_cnt = 0U;
memset(started_unicast_streams, 0, sizeof(started_unicast_streams));
}
static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group)