Commit ae4c6fe6 authored by Emil Gydesen's avatar Emil Gydesen Committed by Benjamin Cabé
Browse files

tests: Bluetooth: Tester: Refactor ACSC events for BAP unicast

This commit refactors when the BTP_ASCS_EV_OPERATION_COMPLETED
is sent to the upper tester. Previously it was send
in the stream changed callbacks, but that would send
the events to the BAP Unicast Server when it did not initiate
any changes, and would not properly match when the control
point operation was completed (which happens before the
ASE state changes).

This change makes it easier to determine when an operation was
actually completed, and easier to separate that from a
ASE state change which is another type of event.

Signed-off-by: default avatarEmil Gydesen <emil.gydesen@nordicsemi.no>
parent 2263fb8e
Loading
Loading
Loading
Loading
+228 −81
Changes for tests/bluetooth/tester/src/audio/btp_bap_unicast.c: 228 added lines, 81 removed lines.
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@
#include <errno.h>

#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/iso.h>
#include <zephyr/types.h>
#include <zephyr/kernel.h>
@@ -18,6 +20,7 @@
#include <zephyr/bluetooth/hci_types.h>
#include <hci_core.h>

#include "ascs_internal.h"
#include "bap_endpoint.h"
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
@@ -340,9 +343,6 @@ static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_
		*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_CONF_REJECTED,
				       BT_BAP_ASCS_REASON_CODEC_DATA);

		btp_send_ascs_operation_completed_ev(conn, info.id, BT_ASCS_CONFIG_OP,
						     BTP_ASCS_STATUS_FAILED);

		return -ENOTSUP;
	}

@@ -352,9 +352,6 @@ static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_
		LOG_DBG("No free stream available");
		*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_NO_MEM, BT_BAP_ASCS_REASON_NONE);

		btp_send_ascs_operation_completed_ev(conn, info.id, BT_ASCS_CONFIG_OP,
						     BTP_ASCS_STATUS_FAILED);

		return -ENOMEM;
	}

@@ -429,19 +426,9 @@ static bool data_func_cb(struct bt_data *data, void *user_data)
static int lc3_enable(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len,
		      struct bt_bap_ascs_rsp *rsp)
{
	int err;
	struct bt_bap_ep_info info;

	LOG_DBG("Metadata: stream %p meta_len %zu", stream, meta_len);

	err = bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
	if (err != 0) {
		(void)bt_bap_ep_get_info(stream->ep, &info);
		btp_send_ascs_operation_completed_ev(stream->conn, info.id,
						     BT_ASCS_ENABLE_OP, BTP_ASCS_STATUS_FAILED);
	}

	return err;
	return bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
}

static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
@@ -454,19 +441,9 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
static int lc3_metadata(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len,
			struct bt_bap_ascs_rsp *rsp)
{
	int err;
	struct bt_bap_ep_info info;

	LOG_DBG("Metadata: stream %p meta_count %zu", stream, meta_len);

	err = bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
	if (err != 0) {
		(void)bt_bap_ep_get_info(stream->ep, &info);
		btp_send_ascs_operation_completed_ev(stream->conn, info.id,
						     BT_ASCS_METADATA_OP, BTP_ASCS_STATUS_FAILED);
	}

	return err;
	return bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
}

static int lc3_disable(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
@@ -508,14 +485,20 @@ static void stream_state_changed(struct bt_bap_stream *stream)
	struct bt_bap_ep_info info;
	int err;

	if (stream->ep == NULL) {
		info.state = BT_BAP_EP_STATE_IDLE;
	} else {
		err = bt_bap_ep_get_info(stream->ep, &info);
		if (err != 0) {
			LOG_ERR("Failed to get info: %d", err);
	} else {
		btp_send_ascs_ase_state_changed_ev(stream->conn, u_stream->ase_id, info.state);

			return;
		}
	}

	btp_send_ascs_ase_state_changed_ev(stream->conn, u_stream->ase_id, info.state);
}

static void stream_configured_cb(struct bt_bap_stream *stream,
				 const struct bt_bap_qos_cfg_pref *pref)
{
@@ -530,24 +513,18 @@ static void stream_configured_cb(struct bt_bap_stream *stream,
	u_conn = &connections[u_stream->conn_id];
	u_stream->ase_id = info.id;

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_CONFIG_OP,
					     BTP_ASCS_STATUS_SUCCESS);
	stream_state_changed(stream);
}

static void stream_qos_set_cb(struct bt_bap_stream *stream)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("QoS set stream %p", stream);
	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
					     BT_ASCS_QOS_OP, BTP_ASCS_STATUS_SUCCESS);

	stream_state_changed(stream);
}

static void stream_enabled_cb(struct bt_bap_stream *stream)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
	struct bt_bap_ep_info info;
	struct bt_conn_info conn_info;
	int err;
@@ -558,40 +535,29 @@ static void stream_enabled_cb(struct bt_bap_stream *stream)
	(void)bt_conn_get_info(stream->conn, &conn_info);
	if (conn_info.role == BT_HCI_ROLE_PERIPHERAL && info.dir == BT_AUDIO_DIR_SINK) {
		/* Automatically do the receiver start ready operation */
		/* TODO: This should ideally be done by the upper tester */
		err = bt_bap_stream_start(stream);
		if (err != 0) {
			LOG_DBG("Failed to start stream %p", stream);
			btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
							     BT_ASCS_ENABLE_OP,
							     BTP_ASCS_STATUS_FAILED);

			return;
		}
	}

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
					     BT_ASCS_ENABLE_OP, BTP_ASCS_STATUS_SUCCESS);
	stream_state_changed(stream);
}

static void stream_metadata_updated_cb(struct bt_bap_stream *stream)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("Metadata updated stream %p", stream);
	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
					     BT_ASCS_METADATA_OP, BTP_ASCS_STATUS_SUCCESS);

	stream_state_changed(stream);
}

static void stream_disabled_cb(struct bt_bap_stream *stream)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("Disabled stream %p", stream);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
					     BT_ASCS_DISABLE_OP, BTP_ASCS_STATUS_SUCCESS);
	stream_state_changed(stream);
}

@@ -601,11 +567,13 @@ static void stream_released_cb(struct bt_bap_stream *stream)
	struct bt_bap_ep_info info;
	struct btp_bap_unicast_connection *u_conn;
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
	struct bt_conn *conn;

	LOG_DBG("Released stream %p", stream);

	u_conn = &connections[u_stream->conn_id];

	/* TODO: Fix this as stream->ep is always NULL in the released callback */
	if (stream->ep != NULL) {
		(void)bt_bap_ep_get_info(stream->ep, &info);
		if (info.dir == BT_AUDIO_DIR_SINK) {
@@ -615,7 +583,14 @@ static void stream_released_cb(struct bt_bap_stream *stream)
		}
	}

	stream_state_changed(stream);
	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &u_conn->address);
	if (conn == NULL) {
		LOG_ERR("Unknown connection");
		return;
	}

	btp_send_ascs_ase_state_changed_ev(conn, u_stream->ase_id, BT_BAP_EP_STATE_IDLE);
	bt_conn_unref(conn);

	cig_id = u_stream->cig_id;
	btp_bap_unicast_stream_free(u_stream);
@@ -664,20 +639,36 @@ static void stream_started_cb(struct bt_bap_stream *stream)
static void stream_connected_cb(struct bt_bap_stream *stream)
{
	struct bt_conn_info conn_info;
	struct bt_bap_ep_info ep_info;
	int err;

	LOG_DBG("Connected stream %p", stream);

	(void)bt_bap_ep_get_info(stream->ep, &ep_info);
	(void)bt_conn_get_info(stream->conn, &conn_info);
	if (conn_info.role == BT_HCI_ROLE_CENTRAL && ep_info.dir == BT_AUDIO_DIR_SOURCE) {
		/* Automatically do the receiver start ready operation for source ASEs as the client
	if (conn_info.role == BT_HCI_ROLE_CENTRAL) {
		struct bt_bap_ep_info ep_info;
		int err;

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

			return;
		}

		if (ep_info.dir == BT_AUDIO_DIR_SOURCE) {
			/* Automatically do the receiver start ready operation for source ASEs as
			 * the client
			 */
			err = bt_bap_stream_start(stream);
			if (err != 0) {
				LOG_ERR("Failed to start stream %p", stream);
			}
		} else {
			struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

			btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
							     BT_ASCS_START_OP,
							     BTP_ASCS_STATUS_SUCCESS);
		}
	}
}

@@ -781,70 +772,136 @@ static void unicast_client_location_cb(struct bt_conn *conn,
	LOG_DBG("dir %u loc %X", dir, loc);
}

static void available_contexts_cb(struct bt_conn *conn,
static void unicast_client_available_contexts_cb(struct bt_conn *conn,
						 enum bt_audio_context snk_ctx,
						 enum bt_audio_context src_ctx)
{
	LOG_DBG("snk ctx %u src ctx %u", snk_ctx, src_ctx);
}

static void config_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_config_cb(struct bt_bap_stream *stream,
				     enum bt_bap_ascs_rsp_code rsp_code,
				     enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p config operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_CONFIG_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void qos_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_qos_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
				  enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p qos operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_QOS_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void enable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_enable_cb(struct bt_bap_stream *stream,
				     enum bt_bap_ascs_rsp_code rsp_code,
				     enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p enable operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_ENABLE_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void start_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_start_cb(struct bt_bap_stream *stream,
				    enum bt_bap_ascs_rsp_code rsp_code,
				    enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
	struct bt_bap_ep_info ep_info;
	int err;

	/* Callback called on Receiver Start Ready notification from ASE Control Point */

	LOG_DBG("stream %p start operation rsp_code %u reason %u", stream, rsp_code, reason);
	u_stream->already_sent = false;

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

		return;
	}

	if (ep_info.dir == BT_AUDIO_DIR_SOURCE) {
		btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
					     BT_ASCS_START_OP, BTP_STATUS_SUCCESS);
						     BT_ASCS_START_OP, BTP_ASCS_STATUS_SUCCESS);
	}
}

static void stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
				   enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p stop operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_STOP_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void disable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_disable_cb(struct bt_bap_stream *stream,
				      enum bt_bap_ascs_rsp_code rsp_code,
				      enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p disable operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_DISABLE_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void metadata_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_metadata_cb(struct bt_bap_stream *stream,
				       enum bt_bap_ascs_rsp_code rsp_code,
				       enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p metadata operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_METADATA_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void release_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
static void unicast_client_release_cb(struct bt_bap_stream *stream,
				      enum bt_bap_ascs_rsp_code rsp_code,
				      enum bt_bap_ascs_reason reason)
{
	struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);

	LOG_DBG("stream %p release operation rsp_code %u reason %u", stream, rsp_code, reason);

	btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id, BT_ASCS_RELEASE_OP,
					     rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS
						     ? BTP_ASCS_STATUS_SUCCESS
						     : BTP_ASCS_STATUS_FAILED);
}

static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
static void unicast_client_pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
					 const struct bt_audio_codec_cap *codec_cap)
{
	LOG_DBG("");
@@ -866,7 +923,8 @@ static void btp_send_discovery_completed_ev(struct bt_conn *conn, uint8_t status
	tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_DISCOVERY_COMPLETED, &ev, sizeof(ev));
}

static void endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep)
static void unicast_client_endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir,
				       struct bt_bap_ep *ep)
{
	struct btp_bap_unicast_connection *u_conn;

@@ -894,7 +952,7 @@ static void endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_b
	}
}

static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
static void unicast_client_discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
{
	LOG_DBG("");

@@ -933,18 +991,18 @@ static struct bt_bap_unicast_server_register_param param = {

static struct bt_bap_unicast_client_cb unicast_client_cbs = {
	.location = unicast_client_location_cb,
	.available_contexts = available_contexts_cb,
	.config = config_cb,
	.qos = qos_cb,
	.enable = enable_cb,
	.start = start_cb,
	.stop = stop_cb,
	.disable = disable_cb,
	.metadata = metadata_cb,
	.release = release_cb,
	.pac_record = pac_record_cb,
	.endpoint = endpoint_cb,
	.discover = discover_cb,
	.available_contexts = unicast_client_available_contexts_cb,
	.config = unicast_client_config_cb,
	.qos = unicast_client_qos_cb,
	.enable = unicast_client_enable_cb,
	.start = unicast_client_start_cb,
	.stop = unicast_client_stop_cb,
	.disable = unicast_client_disable_cb,
	.metadata = unicast_client_metadata_cb,
	.release = unicast_client_release_cb,
	.pac_record = unicast_client_pac_record_cb,
	.endpoint = unicast_client_endpoint_cb,
	.discover = unicast_client_discover_cb,
};

uint8_t btp_bap_discover(const void *cmd, uint16_t cmd_len,
@@ -1251,6 +1309,11 @@ static int server_configure_codec(struct btp_bap_unicast_connection *u_conn, str
		err = bt_bap_stream_reconfig(stream_unicast_to_bap(stream), &stream->codec_cfg);
	}

	if (err == 0) {
		btp_send_ascs_operation_completed_ev(conn, stream->ase_id, BT_ASCS_CONFIG_OP,
						     BT_BAP_ASCS_RSP_CODE_SUCCESS);
	}

	return err;
}

@@ -1437,6 +1500,23 @@ uint8_t btp_ascs_disable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t
		return BTP_STATUS_FAILED;
	}

	if (err == 0) {
		struct bt_conn_info conn_info;

		err = bt_conn_get_info(conn, &conn_info);
		if (err != 0) {
			LOG_ERR("Failed to get conn info: %d", err);
			return BTP_STATUS_FAILED;
		}

		if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
			/* The server the operation completes immediately */
			btp_send_ascs_operation_completed_ev(conn, stream->ase_id,
							     BT_ASCS_DISABLE_OP,
							     BT_BAP_ASCS_RSP_CODE_SUCCESS);
		}
	}

	return BTP_STATUS_SUCCESS;
}

@@ -1448,6 +1528,7 @@ uint8_t btp_ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,
	struct btp_bap_unicast_connection *u_conn;
	struct btp_bap_unicast_stream *stream;
	struct bt_bap_stream *bap_stream;
	struct bt_conn_info conn_info;
	struct bt_bap_ep_info info;
	struct bt_conn *conn;

@@ -1459,6 +1540,18 @@ uint8_t btp_ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,
		return BTP_STATUS_FAILED;
	}

	err = bt_conn_get_info(conn, &conn_info);
	if (err != 0) {
		LOG_ERR("Failed to get conn info: %d", err);
		return BTP_STATUS_FAILED;
	}

	if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
		/* Cannot connect the CIS as the peripheral */
		LOG_DBG("Cannot connect the CIS as the peripheral");
		return BTP_STATUS_FAILED;
	}

	u_conn = &connections[bt_conn_index(conn)];
	bt_conn_unref(conn);

@@ -1476,6 +1569,10 @@ uint8_t btp_ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,

	LOG_DBG("Starting stream %p, ep %u, dir %u", bap_stream, cp->ase_id, info.dir);

	/* TODO: This function should not do the BAP stream connect, and should instead just the
	 * operation that function is named after. Connecting the BAP stream should be its own BTP
	 * command
	 */
	while (true) {
		err = bt_bap_stream_connect(bap_stream);
		if (err == -EBUSY) {
@@ -1528,6 +1625,22 @@ uint8_t btp_ascs_receiver_stop_ready(const void *cmd, uint16_t cmd_len,
		return BTP_STATUS_FAILED;
	}

	if (err == 0) {
		struct bt_conn_info conn_info;

		err = bt_conn_get_info(conn, &conn_info);
		if (err != 0) {
			LOG_ERR("Failed to get conn info: %d", err);
			return BTP_STATUS_FAILED;
		}

		if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
			/* The server the operation completes immediately */
			btp_send_ascs_operation_completed_ev(conn, stream->ase_id, BT_ASCS_STOP_OP,
							     BT_BAP_ASCS_RSP_CODE_SUCCESS);
		}
	}

	return BTP_STATUS_SUCCESS;
}

@@ -1562,6 +1675,23 @@ uint8_t btp_ascs_release(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t
		return BTP_STATUS_FAILED;
	}

	if (err == 0) {
		struct bt_conn_info conn_info;

		err = bt_conn_get_info(conn, &conn_info);
		if (err != 0) {
			LOG_ERR("Failed to get conn info: %d", err);
			return BTP_STATUS_FAILED;
		}

		if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
			/* The server the operation completes immediately */
			btp_send_ascs_operation_completed_ev(conn, stream->ase_id,
							     BT_ASCS_RELEASE_OP,
							     BT_BAP_ASCS_RSP_CODE_SUCCESS);
		}
	}

	return BTP_STATUS_SUCCESS;
}

@@ -1601,6 +1731,23 @@ uint8_t btp_ascs_update_metadata(const void *cmd, uint16_t cmd_len,
		return BTP_STATUS_FAILED;
	}

	if (err == 0) {
		struct bt_conn_info conn_info;

		err = bt_conn_get_info(conn, &conn_info);
		if (err != 0) {
			LOG_ERR("Failed to get conn info: %d", err);
			return BTP_STATUS_FAILED;
		}

		if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
			/* The server the operation completes immediately */
			btp_send_ascs_operation_completed_ev(conn, stream->ase_id,
							     BT_ASCS_METADATA_OP,
							     BT_BAP_ASCS_RSP_CODE_SUCCESS);
		}
	}

	return BTP_STATUS_SUCCESS;
}