Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions applications/nrf5340_audio/src/audio/audio_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,19 +503,9 @@ int audio_system_encoder_num_ch_set(uint32_t locations)

int num_ch = POPCOUNT(locations);

if (locations == BT_AUDIO_LOCATION_MONO_AUDIO) {
num_ch = 1;
}

if (num_ch > CONFIG_AUDIO_ENCODE_CHANNELS_MAX) {
LOG_ERR("Invalid number of encoder channels: %d (max %d)", num_ch,
CONFIG_AUDIO_ENCODE_CHANNELS_MAX);
return -EINVAL;
}

LOG_DBG("Setting encoder channels to %d based on locations 0x%08x", num_ch, locations);

sw_codec_cfg.encoder.num_ch = num_ch;
sw_codec_cfg.encoder.num_ch = CONFIG_AUDIO_ENCODE_CHANNELS_MAX;
sw_codec_cfg.encoder.audio_loc = locations;

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ static void cap_start_worker(struct k_work *work)

/* Create a unicast group if it doesn't already exist */
if (unicast_group_created == false) {
LOG_DBG("Unicast group not created, creating unicast group");
unicast_group_create();
goto start_streams;
}
Expand All @@ -441,6 +442,7 @@ static void cap_start_worker(struct k_work *work)
group_length = sys_slist_len(&info.unicast_group->streams);
if (group_length >= CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
/* The group is as full as it can get, start the relevant streams */
LOG_DBG("Unicast group is full, cannot add more streams");
goto start_streams;
}

Expand All @@ -458,10 +460,16 @@ static void cap_start_worker(struct k_work *work)
ret = srv_store_foreach_server(server_stream_in_unicast_group_check, NULL);
if (ret == -ECANCELED) {
/* A new group will be created after the released_cb has been called */
unicast_client_stop(0);
ret = unicast_client_stop(0);
unicast_group_created = false;
srv_store_unlock();

if (ret == -EALREADY) {
srv_store_unlock();
unicast_group_create();
goto start_streams;
}
Comment on lines +466 to +470
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using -EALREADY as a control signal meaning 'group was deleted, recreate it now' is non-obvious and can easily collide with an existing/expected 'already stopped/already in desired state' meaning. Consider returning a more appropriate errno (e.g., -EAGAIN to indicate retry/recreate) or using an explicit out-parameter/flag to signal 'group_deleted' so callers don’t have to overload -EALREADY semantics.

Copilot uses AI. Check for mistakes.

srv_store_unlock();
return;
}

Expand Down Expand Up @@ -1513,11 +1521,43 @@ int unicast_client_discover(struct bt_conn *conn, enum unicast_discover_dir dir)
return 0;
}

static bool server_is_connected(struct server_store *server)
{
int ret;

/* Check that the server is connected */
struct bt_conn_info info;

if (server->conn == NULL) {
LOG_DBG("Server %s has no connection, not connected", server->name);
return false;
}

ret = bt_conn_get_info(server->conn, &info);
if (ret) {
LOG_ERR("Failed to get connection info for server %s, conn %p: %d", server->name,
(void *)server->conn, ret);
return false;
}

if (info.state == BT_CONN_STATE_CONNECTED) {
LOG_DBG("Connection %p is connected", (void *)server->conn);
return true;
}

return false;
}

static bool add_to_start_params(struct server_store *server, void *user_data)
{
int ret;
struct bt_cap_unicast_audio_start_param *param = user_data;

if (!server_is_connected(server)) {
LOG_DBG("Server %s is not connected, skipping", server->name);
return true;
}

for (int j = 0; j < MIN(server->snk.num_eps, POPCOUNT_ZERO(server->snk.locations)); j++) {
uint8_t state;

Expand Down Expand Up @@ -1641,6 +1681,21 @@ static bool add_to_stop_params(struct bt_cap_stream *stream, void *user_data)
return false;
}

static bool server_connected_check(struct bt_cap_stream *stream, void *user_data)
{
struct server_store *server = NULL;
bool *connected_server_found = (bool *)user_data;

srv_store_from_stream_get(&stream->bap_stream, &server);
if (server && server_is_connected(server)) {
*connected_server_found = true;
/* Found a connected server, will stop iterating */
return true;
}

return false;
}

int unicast_client_stop(uint8_t cig_index)
{
int ret;
Expand All @@ -1661,6 +1716,7 @@ int unicast_client_stop(uint8_t cig_index)
}

return ret;

} else if (ret) {
LOG_ERR("Failed to take sem_cap_procedure_proceed: %d", ret);
return ret;
Expand Down Expand Up @@ -1692,6 +1748,28 @@ int unicast_client_stop(uint8_t cig_index)
if (param.count == 0) {
LOG_DBG("No streams to stop");
k_sem_give(&sem_cap_procedure_proceed);

/* No streams found. Check if devices are connected, if no, delete the group */
bool connected_server_found = false;

ret = bt_cap_unicast_group_foreach_stream(unicast_group, server_connected_check,
&connected_server_found);
if (ret) {
LOG_ERR("Failed to check if servers are connected: %d", ret);
return ret;
}

if (!connected_server_found) {
LOG_DBG("No connected servers found, deleting unicast group");
ret = bt_cap_unicast_group_delete(unicast_group);
if (ret) {
LOG_ERR("Failed to delete unicast group: %d", ret);
}

unicast_group = NULL;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch clears unicast_group but does not update the separate unicast_group_created state. That can leave the system in an inconsistent state (e.g., unicast_group_created == true while unicast_group == NULL). Update the flag when the group is deleted (ideally in the same place where unicast_group is cleared), or refactor callers to derive ‘created’ from the pointer instead of maintaining two sources of truth.

Suggested change
unicast_group = NULL;
unicast_group = NULL;
unicast_group_created = false;

Copilot uses AI. Check for mistakes.
return -EALREADY;
}

return -EIO;
}
Comment on lines 1748 to 1774
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semaphore is released before performing additional CAP/group operations (foreach_stream and potential group delete). This can allow other procedures to run concurrently while the group is being inspected/deleted, leading to races. Keep the semaphore held until after the connected-server check and any group deletion completes (and ensure it’s released on all return paths in this branch).

Copilot uses AI. Check for mistakes.

Expand Down
Loading