Commit 2dc1113a authored by Emil Gydesen's avatar Emil Gydesen Committed by Carles Cufi
Browse files

Bluetooth: CAP: Add support for handling ASE errors

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. This was also fixed as part
of this.

Signed-off-by: default avatarEmil Gydesen <emil.gydesen@nordicsemi.no>
parent 756f5f94
Loading
Loading
Loading
Loading
+145 −17
Changes for subsys/bluetooth/audio/cap_initiator.c: 145 added lines, 17 removed lines.
Original line number Diff line number Diff line
@@ -353,6 +353,10 @@ static enum bt_bap_ep_state stream_get_state(const struct bt_bap_stream *bap_str
	struct bt_bap_ep_info ep_info;
	int err;

	if (bap_stream->ep == NULL) {
		return BT_BAP_EP_STATE_IDLE;
	}

	err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
	if (err != 0) {
		LOG_DBG("Failed to get endpoint info %p: %d", bap_stream, err);
@@ -391,24 +395,48 @@ static bool stream_is_dir(const struct bt_bap_stream *bap_stream, enum bt_audio_
	return ep_info.dir == dir;
}

static bool iso_is_in_state(const struct bt_cap_stream *cap_stream, enum bt_iso_state state)
static enum bt_iso_state bap_stream_get_iso_state(const struct bt_bap_stream *bap_stream)
{
	const struct bt_bap_stream *bap_stream = &cap_stream->bap_stream;
	struct bt_bap_ep_info ep_info;
	int err;

	if (bap_stream->ep == NULL) {
		return BT_ISO_STATE_DISCONNECTED;
	}

	err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
	if (err != 0) {
		LOG_DBG("Failed to get endpoint info %p: %d", bap_stream, err);

		return false;
		return BT_ISO_STATE_DISCONNECTED;
	}

	if (ep_info.iso_chan == NULL) {
		return state == BT_ISO_STATE_DISCONNECTED;
		return BT_ISO_STATE_DISCONNECTED;
	}

	return ep_info.iso_chan->state;
}

static bool iso_is_in_state(const struct bt_cap_stream *cap_stream, enum bt_iso_state state)
{
	const struct bt_bap_stream *bap_stream = &cap_stream->bap_stream;

	return bap_stream_get_iso_state(bap_stream) == state;
}

static void set_cap_stream_in_progress(struct bt_cap_stream *cap_stream, bool value)
{
	struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();

	for (size_t i = 0U; i < active_proc->proc_cnt; i++) {
		if (cap_stream == active_proc->proc_param.initiator[i].stream) {
			active_proc->proc_param.initiator[i].in_progress = value;
			return;
		}
	}

	return state == ep_info.iso_chan->state;
	__ASSERT(false, "CAP stream %p not in active_proc", cap_stream);
}

/**
@@ -441,7 +469,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;
@@ -502,7 +530,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;
@@ -540,7 +568,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;
@@ -589,6 +617,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);
@@ -814,6 +847,43 @@ 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;
	}

	LOG_DBG("cap_stream %p", cap_stream);

	set_cap_stream_in_progress(cap_stream, false);

	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)
{
@@ -869,6 +939,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.
@@ -971,6 +1042,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) {
@@ -1038,7 +1110,16 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
			break;
		}

		for (size_t j = 0U; j < active_proc->proc_cnt; j++) {
			proc_param = &active_proc->proc_param.initiator[j];
			if (proc_param->stream->bap_stream.conn == conns[i]) {
				active_proc->proc_initiated_cnt++;
				proc_param->in_progress = false;
				break;
			}
		}

		proc_param->in_progress = true;

		err = bt_bap_stream_qos(conns[i], unicast_group);
		if (err != 0) {
@@ -1115,6 +1196,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);
@@ -1136,6 +1218,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) {
@@ -1189,6 +1272,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);
@@ -1213,6 +1297,7 @@ 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) {
@@ -1220,6 +1305,7 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
		 * 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);
@@ -1247,6 +1333,8 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream)

	LOG_DBG("cap_stream %p", cap_stream);

	set_cap_stream_in_progress(cap_stream, false);

	if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CONNECT)) {
		/* Unexpected callback - Abort */
		bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
@@ -1277,14 +1365,18 @@ 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");
		if (proc_param != NULL) {
			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_connect(next_bap_stream);
			if (err == 0 || err == -EALREADY) {
				if (err == -EALREADY) {
					proc_param->in_progress = false;
				}
				/* Pending connected - wait for connected callback */
			} else if (err != 0) {
				LOG_DBG("Failed to connect stream %p: %d", next_cap_stream, err);
@@ -1292,6 +1384,7 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream)
				bt_cap_common_abort_proc(next_bap_stream->conn, err);
				cap_initiator_unicast_audio_proc_complete();
			}
		} /* else pending connection - wait for connected callback */

		return;
	}
@@ -1312,6 +1405,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);
@@ -1336,8 +1431,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 {
@@ -1353,16 +1452,19 @@ 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");
		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;

				proc_param->in_progress = true;

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

					/* End and mark procedure as aborted.
					 * If we have sent any requests over air, we will abort
@@ -1373,6 +1475,7 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream)

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

		/* Return to await for response from server */
@@ -1521,6 +1624,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) {
@@ -1588,6 +1692,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) {
@@ -1636,10 +1741,21 @@ static bool can_disable_stream(const struct bt_bap_stream *bap_stream)

static bool can_stop_stream(const struct bt_bap_stream *bap_stream)
{
	enum bt_iso_state iso_state;

	if (bap_stream->conn == NULL) {
		return false;
	}

	if (stream_is_dir(bap_stream, BT_AUDIO_DIR_SINK)) {
		return false;
	}

	iso_state = bap_stream_get_iso_state(bap_stream);
	if (iso_state != BT_ISO_STATE_CONNECTED && iso_state != BT_ISO_STATE_CONNECTING) {
		return false;
	}

	return stream_is_in_state(bap_stream, BT_BAP_EP_STATE_DISABLING);
}

@@ -1794,6 +1910,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) {
@@ -1812,6 +1929,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) {
@@ -1830,6 +1948,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) {
@@ -1880,6 +1999,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) {
@@ -1909,14 +2029,17 @@ 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) {
		if (err != 0 && err != -EALREADY) {
			LOG_DBG("Failed to stop stream %p: %d", next_cap_stream, err);

			bt_cap_common_abort_proc(next_bap_stream->conn, err);
			cap_initiator_unicast_audio_proc_complete();
		}
		} else if (err == -EALREADY) {
			proc_param->in_progress = false;
		} /* else wait for server notification*/
	}
}

@@ -1961,19 +2084,23 @@ void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream)
		int err;

		proc_param = get_next_proc_param(active_proc);
		__ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param");
		if (proc_param != NULL) {
			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) {
			if (err != 0 && err != -EALREADY) {
				LOG_DBG("Failed to stop stream %p: %d", next_cap_stream, err);

				bt_cap_common_abort_proc(next_bap_stream->conn, err);
				cap_initiator_unicast_audio_proc_complete();
			} else if (err == -EALREADY) {
				proc_param->in_progress = false;
			}
		} /* else await notification from server */
	} else {
		/* We are done stopping streams now - We mark the next subproc. If
		 * get_next_proc_param returns a NULL value it means that we are complete done. If
@@ -2040,6 +2167,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) {
+3 −0
Changes for subsys/bluetooth/audio/cap_internal.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream);
void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream);
void bt_cap_initiator_released(struct bt_cap_stream *cap_stream);
void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream);
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);

enum bt_cap_common_proc_state {
	BT_CAP_COMMON_PROC_STATE_ACTIVE,
@@ -88,6 +90,7 @@ struct bt_cap_initiator_proc_param {
			bool release;
		} stop;
	};
	bool in_progress;
};

#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT)
+33 −0
Changes for subsys/bluetooth/audio/cap_stream.c: 33 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <zephyr/bluetooth/iso.h>
#include <zephyr/logging/log.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/check.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
@@ -280,9 +281,41 @@ static struct bt_bap_stream_ops bap_stream_ops = {
	.disconnected = cap_stream_disconnected_cb,
};

static void unicast_client_cp_cb(struct bt_bap_stream *bap_stream,
				 enum bt_bap_ascs_rsp_code rsp_code, enum bt_bap_ascs_reason reason)
{
	if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
	    stream_is_central(bap_stream)) {
		struct bt_cap_stream *cap_stream =
			CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);

		bt_cap_initiator_cp_cb(cap_stream, rsp_code, reason);
	}
}

void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream)
{
	bt_bap_stream_cb_register(&cap_stream->bap_stream, &bap_stream_ops);

	if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT)) {
		/* The CAP initiator can use the same callback for all of these as the result is the
		 * same: Abort current procedure
		 */
		static struct bt_bap_unicast_client_cb unicast_client_cb = {
			.config = unicast_client_cp_cb,
			.qos = unicast_client_cp_cb,
			.enable = unicast_client_cp_cb,
			.start = unicast_client_cp_cb,
			.stop = unicast_client_cp_cb,
			.disable = unicast_client_cp_cb,
			.metadata = unicast_client_cp_cb,
			.release = unicast_client_cp_cb,
		};
		int err;

		err = bt_bap_unicast_client_register_cb(&unicast_client_cb);
		__ASSERT_NO_MSG(err == 0 || err == -EEXIST);
	}
}

void bt_cap_stream_ops_register(struct bt_cap_stream *stream,
+2 −1
Changes for tests/bluetooth/audio/cap_initiator/prj.conf: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@ CONFIG_BT_BAP_UNICAST_CLIENT=y
CONFIG_BT_CSIP_SET_COORDINATOR=y

CONFIG_BT_CAP_INITIATOR=y
CONFIG_BT_CAP_INITIATOR_LOG_LEVEL_DBG=y

# Support setting up a sink and source stream on 2 acceptors
CONFIG_BT_MAX_CONN=2
@@ -26,3 +25,5 @@ CONFIG_ASSERT_LEVEL=2
CONFIG_ASSERT_VERBOSE=y

CONFIG_BT_BAP_STREAM_LOG_LEVEL_DBG=y
CONFIG_BT_CAP_COMMON_LOG_LEVEL_DBG=y
CONFIG_BT_CAP_INITIATOR_LOG_LEVEL_DBG=y
+75 −0
Changes for tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c: 75 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -12,14 +12,18 @@

#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/slist.h>
#include <sys/errno.h>

#include "bap_endpoint.h"
#include "bap_iso.h"
#include "ztest_assert.h"

static struct bt_bap_unicast_client_cb *unicast_client_cb;

bool bt_bap_ep_is_unicast_client(const struct bt_bap_ep *ep)
{
	return false;
@@ -40,6 +44,11 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream,
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->config != NULL) {
		unicast_client_cb->config(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					  BT_BAP_ASCS_REASON_NONE);
	}

	stream->ep->status.state = BT_BAP_EP_STATE_CODEC_CONFIGURED;

	if (stream->ops != NULL && stream->ops->configured != NULL) {
@@ -73,6 +82,11 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group

	SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
		if (stream->conn == conn) {
			if (unicast_client_cb != NULL && unicast_client_cb->qos != NULL) {
				unicast_client_cb->qos(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
						       BT_BAP_ASCS_REASON_NONE);
			}

			stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;

			if (stream->ops != NULL && stream->ops->qos_set != NULL) {
@@ -98,6 +112,11 @@ int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t met
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->enable != NULL) {
		unicast_client_cb->enable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					  BT_BAP_ASCS_REASON_NONE);
	}

	stream->ep->status.state = BT_BAP_EP_STATE_ENABLING;

	if (stream->ops != NULL && stream->ops->enabled != NULL) {
@@ -122,6 +141,11 @@ int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, const uint8_t m
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->metadata != NULL) {
		unicast_client_cb->metadata(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					    BT_BAP_ASCS_REASON_NONE);
	}

	if (stream->ops != NULL && stream->ops->metadata_updated != NULL) {
		stream->ops->metadata_updated(stream);
	}
@@ -173,6 +197,11 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream)
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->start != NULL) {
		unicast_client_cb->start(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					 BT_BAP_ASCS_REASON_NONE);
	}

	stream->ep->status.state = BT_BAP_EP_STATE_STREAMING;

	if (stream->ops != NULL && stream->ops->started != NULL) {
@@ -203,6 +232,11 @@ int bt_bap_unicast_client_disable(struct bt_bap_stream *stream)
	 * when leaving the streaming state in a non-release manner
	 */

	if (unicast_client_cb != NULL && unicast_client_cb->disable != NULL) {
		unicast_client_cb->disable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					   BT_BAP_ASCS_REASON_NONE);
	}

	/* Disabled sink ASEs go directly to the QoS configured state */
	if (stream->ep->dir == BT_AUDIO_DIR_SINK) {
		stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
@@ -245,6 +279,11 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream)
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->stop != NULL) {
		unicast_client_cb->stop(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					BT_BAP_ASCS_REASON_NONE);
	}

	stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;

	if (stream->ops != NULL && stream->ops->stopped != NULL) {
@@ -255,6 +294,30 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream)
		stream->ops->qos_set(stream);
	}

	/* If the stream can be disconnected, BAP will disconnect the stream once it reaches the
	 * QoS Configured state. We simulator that behavior here, and if the stream is disconnected,
	 * then the Unicast Server will set any paired stream to the QoS Configured state
	 * autonomously as well.
	 */
	if (bt_bap_stream_can_disconnect(stream)) {
		struct bt_bap_ep *pair_ep = bt_bap_iso_get_paired_ep(stream->ep);

		if (pair_ep != NULL && pair_ep->stream != NULL) {
			struct bt_bap_stream *pair_stream = pair_ep->stream;

			pair_stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;

			if (pair_stream->ops != NULL && pair_stream->ops->stopped != NULL) {
				pair_stream->ops->stopped(pair_stream,
							  BT_HCI_ERR_LOCALHOST_TERM_CONN);
			}

			if (pair_stream->ops != NULL && pair_stream->ops->qos_set != NULL) {
				pair_stream->ops->qos_set(pair_stream);
			}
		}
	}

	return 0;
}

@@ -277,6 +340,11 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream)
		return -EINVAL;
	}

	if (unicast_client_cb != NULL && unicast_client_cb->release != NULL) {
		unicast_client_cb->release(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
					   BT_BAP_ASCS_REASON_NONE);
	}

	stream->ep->status.state = BT_BAP_EP_STATE_IDLE;
	bt_bap_stream_reset(stream);

@@ -286,3 +354,10 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream)

	return 0;
}

int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb)
{
	unicast_client_cb = cb;

	return 0;
}
Loading