Skip to content

Commit

Permalink
Bluetooth: CAP: Add support for handling ASE errors
Browse files Browse the repository at this point in the history
If we get an error/rejection from the CAP acceptor when
performing the Unicast Audio Start or Stop procedure then
we need to abort the procedure and let the application determine
what the next step is.

This change triggered a corner case when connecting to multiple
CAP acceptors as the CAP initiatior that was fixed as part
of this.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
  • Loading branch information
Thalley committed Sep 26, 2024
1 parent 742aabb commit 27ee8cb
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 47 deletions.
140 changes: 108 additions & 32 deletions subsys/bluetooth/audio/cap_initiator.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
* the states to determine how far we are.
*/
for (size_t i = 0U; i < active_proc->proc_cnt; i++) {
const struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_stream *cap_stream;
struct bt_bap_stream *bap_stream;
enum bt_bap_ep_state state;
Expand All @@ -455,11 +455,13 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
case BT_CAP_COMMON_SUBPROC_TYPE_CODEC_CONFIG:
if (state > BT_BAP_EP_STATE_IDLE) {
proc_done_cnt++;
proc_param->in_progress = false;
}
break;
case BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG:
if (state > BT_BAP_EP_STATE_CODEC_CONFIGURED) {
proc_done_cnt++;
proc_param->in_progress = false;
} else if (state < BT_BAP_EP_STATE_CODEC_CONFIGURED) {
/* Unexpected state - Abort */
bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG);
Expand All @@ -468,6 +470,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
case BT_CAP_COMMON_SUBPROC_TYPE_ENABLE:
if (state > BT_BAP_EP_STATE_QOS_CONFIGURED) {
proc_done_cnt++;
proc_param->in_progress = false;
} else if (state < BT_BAP_EP_STATE_QOS_CONFIGURED) {
/* Unexpected state - Abort */
bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG);
Expand All @@ -479,11 +482,13 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG);
} else if (proc_param->start.connected) {
proc_done_cnt++;
proc_param->in_progress = false;
}
break;
case BT_CAP_COMMON_SUBPROC_TYPE_START:
if (state > BT_BAP_EP_STATE_ENABLING) {
proc_done_cnt++;
proc_param->in_progress = false;
} else if (state < BT_BAP_EP_STATE_ENABLING ||
!iso_is_in_state(cap_stream, BT_ISO_STATE_CONNECTED)) {
/* Unexpected state - Abort */
Expand All @@ -501,7 +506,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
* the states to determine how far we are.
*/
for (size_t i = 0U; i < active_proc->proc_cnt; i++) {
const struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_stream *cap_stream;
struct bt_bap_stream *bap_stream;
enum bt_bap_ep_state state;
Expand All @@ -517,17 +522,20 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
if (state == BT_BAP_EP_STATE_QOS_CONFIGURED ||
state == BT_BAP_EP_STATE_DISABLING) {
proc_done_cnt++;
proc_param->in_progress = false;
}
break;
case BT_CAP_COMMON_SUBPROC_TYPE_STOP:
if (state == BT_BAP_EP_STATE_QOS_CONFIGURED) {
proc_done_cnt++;
proc_param->in_progress = false;
}
break;
case BT_CAP_COMMON_SUBPROC_TYPE_RELEASE:
if (state == BT_BAP_EP_STATE_IDLE ||
state == BT_BAP_EP_STATE_CODEC_CONFIGURED) {
proc_done_cnt++;
proc_param->in_progress = false;
}
break;
default:
Expand All @@ -539,7 +547,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
/* For metadata we cannot check the states for all streams, as it does not trigger a
* state change
*/
const struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_initiator_proc_param *proc_param;
struct bt_cap_stream *cap_stream;
struct bt_bap_stream *bap_stream;
enum bt_bap_ep_state state;
Expand All @@ -555,6 +563,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
if (state == BT_BAP_EP_STATE_ENABLING ||
state == BT_BAP_EP_STATE_STREAMING) {
proc_done_cnt = active_proc->proc_done_cnt + 1U;
proc_param->in_progress = false;
} else {
/* Unexpected state - Abort */
bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG);
Expand Down Expand Up @@ -588,6 +597,11 @@ get_next_proc_param(struct bt_cap_common_proc *active_proc)
enum bt_bap_ep_state state;

proc_param = &active_proc->proc_param.initiator[i];

if (proc_param->in_progress) {
continue;
}

cap_stream = proc_param->stream;
bap_stream = &cap_stream->bap_stream;
state = stream_get_state(bap_stream);
Expand Down Expand Up @@ -813,6 +827,39 @@ static void cap_initiator_unicast_audio_proc_complete(void)
}
}

void bt_cap_initiator_cp_cb(struct bt_cap_stream *cap_stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
/* State change happened outside of a procedure; ignore */
return;
}

if (rsp_code != BT_BAP_ASCS_RSP_CODE_SUCCESS) {
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();

/* In the case that the control point write is rejected, we will not get a ASE state
* change notification. This is considered an error that shall abort the current
* procedure.
*/
active_proc->proc_done_cnt++;

LOG_DBG("Control point operation on stream %p failed with %d and reason %d",
cap_stream, rsp_code, reason);

/* Unexpected callback - Abort */
bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);

if (bt_cap_common_proc_is_aborted()) {
if (bt_cap_common_proc_all_handled()) {
cap_initiator_unicast_audio_proc_complete();
}

return;
}
}
}

static int cap_initiator_unicast_audio_configure(
const struct bt_cap_unicast_audio_start_param *param)
{
Expand Down Expand Up @@ -868,6 +915,7 @@ static int cap_initiator_unicast_audio_configure(
conn = proc_param->start.conn;
ep = proc_param->start.ep;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a time.
Expand Down Expand Up @@ -970,6 +1018,7 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
codec_cfg = proc_param->start.codec_cfg;
next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_config(conn, next_bap_stream, ep, codec_cfg);
if (err != 0) {
Expand Down Expand Up @@ -1038,6 +1087,7 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
}

active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_qos(conns[i], unicast_group);
if (err != 0) {
Expand Down Expand Up @@ -1114,6 +1164,7 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta,
bap_stream->codec_cfg->meta_len);
Expand All @@ -1135,6 +1186,7 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_release(next_bap_stream);
if (err != 0) {
Expand Down Expand Up @@ -1188,6 +1240,7 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
next_bap_stream = &next_cap_stream->bap_stream;

active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_enable(next_bap_stream, next_bap_stream->codec_cfg->meta,
next_bap_stream->codec_cfg->meta_len);
Expand All @@ -1212,13 +1265,15 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
}

bap_stream = &proc_param->stream->bap_stream;
proc_param->in_progress = true;

err = bt_bap_stream_connect(bap_stream);
if (err == -EALREADY) {
/* If the stream is already connected we can just call the callback directly
* NOTE: It's important that we do not do any additional functionality after
* calling this
*/
proc_param->in_progress = false;
bt_cap_initiator_connected(proc_param->stream);
} else if (err != 0) {
LOG_DBG("Failed to connect stream %p: %d", proc_param->stream, err);
Expand Down Expand Up @@ -1276,21 +1331,23 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream)
struct bt_bap_stream *next_bap_stream;

proc_param = get_next_proc_param(active_proc);
__ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param");
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
if (proc_param != NULL) {
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;

active_proc->proc_initiated_cnt++;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_connect(next_bap_stream);
if (err == 0 || err == -EALREADY) {
/* Pending connected - wait for connected callback */
} else if (err != 0) {
LOG_DBG("Failed to connect stream %p: %d", next_cap_stream, err);
err = bt_bap_stream_connect(next_bap_stream);
if (err == 0 || err == -EALREADY) {
/* Pending connected - wait for connected callback */
} else if (err != 0) {
LOG_DBG("Failed to connect stream %p: %d", next_cap_stream, err);

bt_cap_common_abort_proc(next_bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
}
bt_cap_common_abort_proc(next_bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
}
} /* else pending connection - wait for connected callback */

return;
}
Expand All @@ -1311,6 +1368,8 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream)

bap_stream = &proc_param->stream->bap_stream;
if (stream_is_dir(bap_stream, BT_AUDIO_DIR_SOURCE)) {
proc_param->in_progress = true;

err = bt_bap_stream_start(bap_stream);
if (err != 0) {
LOG_DBG("Failed to start stream %p: %d", proc_param->stream, err);
Expand All @@ -1335,8 +1394,12 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream)
}

/* Streams may go into the streaming state while we are connecting or starting them */
if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_START) &&
!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CONNECT)) {
if (bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CONNECT)) {
/* If we are still connecting the streams, we terminate early as to not perform any
* start operations until all streams are connected
*/
return;
} else if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_START)) {
/* Unexpected callback - Abort */
bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
} else {
Expand All @@ -1352,25 +1415,29 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream)
struct bt_bap_stream *next_bap_stream;

proc_param = get_next_proc_param(active_proc);
__ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param");
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
if (proc_param != NULL) {
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;

if (stream_is_dir(next_bap_stream, BT_AUDIO_DIR_SOURCE)) {
int err;
if (stream_is_dir(next_bap_stream, BT_AUDIO_DIR_SOURCE)) {
int err;

err = bt_bap_stream_start(next_bap_stream);
if (err != 0) {
LOG_DBG("Failed to start stream %p: %d", next_cap_stream, err);
proc_param->in_progress = true;

/* End and mark procedure as aborted.
* If we have sent any requests over air, we will abort
* once all sent requests has completed
*/
bt_cap_common_abort_proc(next_bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
err = bt_bap_stream_start(next_bap_stream);
if (err != 0) {
LOG_DBG("Failed to start stream %p: %d", next_cap_stream,
err);

return;
/* End and mark procedure as aborted.
* If we have sent any requests over air, we will abort
* once all sent requests has completed
*/
bt_cap_common_abort_proc(next_bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();

return;
}
}
} /* else await notifications from server */

Expand Down Expand Up @@ -1520,6 +1587,7 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda
meta_len = proc_param->meta_update.meta_len;
meta = proc_param->meta_update.meta;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
if (err != 0) {
Expand Down Expand Up @@ -1587,6 +1655,7 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
if (err != 0) {
Expand Down Expand Up @@ -1793,6 +1862,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p
"proc is not started, but could not get next proc_param");
bap_stream = &proc_param->stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_disable(bap_stream);
if (err != 0) {
Expand All @@ -1811,6 +1881,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p
"proc is not started, but could not get next proc_param");
bap_stream = &proc_param->stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_stop(bap_stream);
if (err != 0) {
Expand All @@ -1829,6 +1900,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p
"proc is not started, but could not get next proc_param");
bap_stream = &proc_param->stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_release(bap_stream);
if (err != 0) {
Expand Down Expand Up @@ -1879,6 +1951,7 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_disable(next_bap_stream);
if (err != 0) {
Expand Down Expand Up @@ -1908,6 +1981,7 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_stop(next_bap_stream);
if (err != 0) {
Expand Down Expand Up @@ -1965,6 +2039,7 @@ void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream)
next_bap_stream = &next_cap_stream->bap_stream;

active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_stop(next_bap_stream);
if (err != 0) {
Expand Down Expand Up @@ -2039,6 +2114,7 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
next_cap_stream = proc_param->stream;
next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
proc_param->in_progress = true;

err = bt_bap_stream_release(next_bap_stream);
if (err != 0) {
Expand Down
Loading

0 comments on commit 27ee8cb

Please sign in to comment.