Bluetooth: CAP: Add support for doing just disable for unicast stop
The unicast_stop function is changed to primarily do a BAP disable instead of a release, with optional support for releasing the streams once they have been disabled. This also adds unittests for the procedure which also allow us to remove the invalid param testing from the BSIM tests. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
ff34d575bc
commit
fa4f2ffc47
17 changed files with 1022 additions and 125 deletions
|
@ -312,6 +312,9 @@ struct bt_cap_unicast_audio_stop_param {
|
|||
|
||||
/** Array of streams to stop */
|
||||
struct bt_cap_stream **streams;
|
||||
|
||||
/** Whether to release the streams after they have stopped */
|
||||
bool release;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -379,7 +382,10 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda
|
|||
*
|
||||
* @param param Stop parameters.
|
||||
*
|
||||
* @return 0 on success or negative error value on failure.
|
||||
* @return 0 on success
|
||||
* @retval -EBUSY if a CAP procedure is already in progress
|
||||
* @retval -EINVAL if any parameter is invalid
|
||||
* @retval -EALREADY if no state changes will occur
|
||||
*/
|
||||
int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_param *param);
|
||||
|
||||
|
|
|
@ -509,7 +509,7 @@ void bt_bap_stream_detach(struct bt_bap_stream *stream)
|
|||
{
|
||||
const bool is_broadcast = bt_bap_stream_is_broadcast(stream);
|
||||
|
||||
LOG_DBG("stream %p", stream);
|
||||
LOG_DBG("stream %p conn %p ep %p", stream, (void *)stream->conn, (void *)stream->ep);
|
||||
|
||||
if (stream->conn != NULL) {
|
||||
bt_conn_unref(stream->conn);
|
||||
|
@ -587,7 +587,7 @@ int bt_bap_stream_config(struct bt_conn *conn, struct bt_bap_stream *stream, str
|
|||
codec_cfg, codec_cfg ? codec_cfg->id : 0, codec_cfg ? codec_cfg->cid : 0,
|
||||
codec_cfg ? codec_cfg->vid : 0);
|
||||
|
||||
CHECKIF(conn == NULL || stream == NULL || codec_cfg == NULL) {
|
||||
CHECKIF(conn == NULL || stream == NULL || codec_cfg == NULL || ep == NULL) {
|
||||
LOG_DBG("NULL value(s) supplied)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,11 @@ void bt_cap_common_set_subproc(enum bt_cap_common_subproc_type subproc_type)
|
|||
active_proc.subproc_type = subproc_type;
|
||||
}
|
||||
|
||||
bool bt_cap_common_proc_is_type(enum bt_cap_common_proc_type proc_type)
|
||||
{
|
||||
return active_proc.proc_type == proc_type;
|
||||
}
|
||||
|
||||
bool bt_cap_common_subproc_is_type(enum bt_cap_common_subproc_type subproc_type)
|
||||
{
|
||||
return active_proc.subproc_type == subproc_type;
|
||||
|
@ -122,7 +127,12 @@ void bt_cap_common_abort_proc(struct bt_conn *conn, int err)
|
|||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_CAP_INITIATOR_UNICAST)
|
||||
LOG_DBG("Aborting proc %d with subproc %d for %p: %d", active_proc.proc_type,
|
||||
active_proc.subproc_type, (void *)conn, err);
|
||||
#else /* !CONFIG_BT_CAP_INITIATOR_UNICAST */
|
||||
LOG_DBG("Aborting proc %d for %p: %d", active_proc.proc_type, (void *)conn, err);
|
||||
#endif /* CONFIG_BT_CAP_INITIATOR_UNICAST */
|
||||
|
||||
active_proc.err = err;
|
||||
active_proc.failed_conn = conn;
|
||||
|
|
|
@ -513,6 +513,17 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc)
|
|||
state = stream_get_state(bap_stream);
|
||||
|
||||
switch (subproc_type) {
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_DISABLE:
|
||||
if (state == BT_BAP_EP_STATE_QOS_CONFIGURED ||
|
||||
state == BT_BAP_EP_STATE_DISABLING) {
|
||||
proc_done_cnt++;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_STOP:
|
||||
if (state == BT_BAP_EP_STATE_QOS_CONFIGURED) {
|
||||
proc_done_cnt++;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_RELEASE:
|
||||
if (state == BT_BAP_EP_STATE_IDLE ||
|
||||
state == BT_BAP_EP_STATE_CODEC_CONFIGURED) {
|
||||
|
@ -574,47 +585,60 @@ get_next_proc_param(struct bt_cap_common_proc *active_proc)
|
|||
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;
|
||||
|
||||
proc_param = &active_proc->proc_param.initiator[i];
|
||||
cap_stream = proc_param->stream;
|
||||
bap_stream = &cap_stream->bap_stream;
|
||||
state = stream_get_state(bap_stream);
|
||||
|
||||
switch (subproc_type) {
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_CODEC_CONFIG:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_IDLE)) {
|
||||
if (state == BT_BAP_EP_STATE_IDLE) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_CODEC_CONFIGURED)) {
|
||||
if (state == BT_BAP_EP_STATE_CODEC_CONFIGURED) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_ENABLE:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_QOS_CONFIGURED)) {
|
||||
if (state == BT_BAP_EP_STATE_QOS_CONFIGURED) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_CONNECT:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_ENABLING) &&
|
||||
!proc_param->start.connected) {
|
||||
if (state == BT_BAP_EP_STATE_ENABLING && !proc_param->start.connected) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_START:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_ENABLING)) {
|
||||
if (state == BT_BAP_EP_STATE_ENABLING) {
|
||||
/* TODO: Add check for connected */
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_META_UPDATE:
|
||||
if (stream_is_in_state(bap_stream, BT_BAP_EP_STATE_ENABLING) ||
|
||||
stream_is_in_state(bap_stream, BT_BAP_EP_STATE_STREAMING)) {
|
||||
if (state == BT_BAP_EP_STATE_ENABLING ||
|
||||
state == BT_BAP_EP_STATE_STREAMING) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_DISABLE:
|
||||
if (state == BT_BAP_EP_STATE_ENABLING ||
|
||||
state == BT_BAP_EP_STATE_STREAMING) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_STOP:
|
||||
if (state == BT_BAP_EP_STATE_DISABLING) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
case BT_CAP_COMMON_SUBPROC_TYPE_RELEASE:
|
||||
if (!stream_is_in_state(bap_stream, BT_BAP_EP_STATE_IDLE)) {
|
||||
if (proc_param->stop.release && state != BT_BAP_EP_STATE_IDLE &&
|
||||
state != BT_BAP_EP_STATE_CODEC_CONFIGURED) {
|
||||
return proc_param;
|
||||
}
|
||||
break;
|
||||
|
@ -848,6 +872,7 @@ static int cap_initiator_unicast_audio_configure(
|
|||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for optimization.
|
||||
* This applies to all BAP calls in this file.
|
||||
*/
|
||||
err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg);
|
||||
if (err != 0) {
|
||||
|
@ -946,12 +971,6 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
|
|||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for
|
||||
* optimization.
|
||||
*/
|
||||
err = bt_bap_stream_config(conn, next_bap_stream, ep, codec_cfg);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to config stream %p: %d", next_cap_stream, err);
|
||||
|
@ -1042,10 +1061,6 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
|
|||
void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
|
||||
{
|
||||
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
int err;
|
||||
|
||||
if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
|
||||
/* State change happened outside of a procedure; ignore */
|
||||
|
@ -1054,7 +1069,9 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
|
|||
|
||||
LOG_DBG("cap_stream %p", cap_stream);
|
||||
|
||||
if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG)) {
|
||||
if (!(bt_cap_common_proc_is_type(BT_CAP_COMMON_PROC_TYPE_START) &&
|
||||
bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG)) &&
|
||||
!(bt_cap_common_proc_is_type(BT_CAP_COMMON_PROC_TYPE_STOP))) {
|
||||
/* Unexpected callback - Abort */
|
||||
bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
|
||||
} else {
|
||||
|
@ -1072,36 +1089,60 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
/* Not yet finished, wait for all */
|
||||
return;
|
||||
}
|
||||
if (bt_cap_common_proc_is_type(BT_CAP_COMMON_PROC_TYPE_START)) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
int err;
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_ENABLE);
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
if (proc_param == NULL) {
|
||||
/* If proc_param is NULL then this step is a no-op and we can skip to the next step
|
||||
*/
|
||||
bt_cap_initiator_enabled(active_proc->proc_param.initiator[0].stream);
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
/* Not yet finished, wait for all */
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_ENABLE);
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
if (proc_param == NULL) {
|
||||
/* If proc_param is NULL then this step is a no-op and we can skip to the
|
||||
* next step
|
||||
*/
|
||||
bt_cap_initiator_enabled(active_proc->proc_param.initiator[0].stream);
|
||||
|
||||
next_cap_stream = proc_param->stream;
|
||||
bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for optimization.
|
||||
*/
|
||||
err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta,
|
||||
bap_stream->codec_cfg->meta_len);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err);
|
||||
next_cap_stream = proc_param->stream;
|
||||
bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
bt_cap_common_abort_proc(bap_stream->conn, err);
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta,
|
||||
bap_stream->codec_cfg->meta_len);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err);
|
||||
|
||||
bt_cap_common_abort_proc(bap_stream->conn, err);
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
} else if (bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE)) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *next_bap_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");
|
||||
|
||||
next_cap_stream = proc_param->stream;
|
||||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_release(next_bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to release stream %p: %d", next_cap_stream, err);
|
||||
|
||||
bt_cap_common_abort_proc(next_bap_stream->conn, err);
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,12 +1189,6 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
|
|||
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for
|
||||
* optimization.
|
||||
*/
|
||||
err = bt_bap_stream_enable(next_bap_stream, next_bap_stream->codec_cfg->meta,
|
||||
next_bap_stream->codec_cfg->meta_len);
|
||||
if (err != 0) {
|
||||
|
@ -1276,12 +1311,6 @@ 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)) {
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for
|
||||
* optimization.
|
||||
*/
|
||||
err = bt_bap_stream_start(bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to start stream %p: %d", proc_param->stream, err);
|
||||
|
@ -1330,12 +1359,6 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream)
|
|||
if (stream_is_dir(next_bap_stream, BT_AUDIO_DIR_SOURCE)) {
|
||||
int err;
|
||||
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is
|
||||
* room for optimization.
|
||||
*/
|
||||
err = bt_bap_stream_start(next_bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to start stream %p: %d", next_cap_stream, err);
|
||||
|
@ -1478,6 +1501,9 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda
|
|||
¶m->stream_params[i];
|
||||
struct bt_cap_stream *cap_stream = stream_param->stream;
|
||||
|
||||
/* Ensure that ops are registered before any procedures are started */
|
||||
bt_cap_stream_ops_register_bap(cap_stream);
|
||||
|
||||
active_proc->proc_param.initiator[i].stream = cap_stream;
|
||||
active_proc->proc_param.initiator[i].meta_update.meta_len = stream_param->meta_len;
|
||||
memcpy(&active_proc->proc_param.initiator[i].meta_update.meta, stream_param->meta,
|
||||
|
@ -1562,13 +1588,6 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
|
|||
bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for
|
||||
* optimization.
|
||||
*/
|
||||
|
||||
err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to update metadata for stream %p: %d", next_cap_stream,
|
||||
|
@ -1584,13 +1603,43 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
|
|||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
|
||||
static bool can_release(const struct bt_bap_stream *bap_stream)
|
||||
static bool can_release_stream(const struct bt_bap_stream *bap_stream)
|
||||
{
|
||||
enum bt_bap_ep_state state;
|
||||
|
||||
if (bap_stream->conn == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state = stream_get_state(bap_stream);
|
||||
|
||||
/* We cannot release idle endpoints.
|
||||
* We do not know if we can release endpoints in the Codec Configured state as servers may
|
||||
* cache it, so treat it as idle
|
||||
*/
|
||||
return state != BT_BAP_EP_STATE_IDLE && state != BT_BAP_EP_STATE_CODEC_CONFIGURED;
|
||||
}
|
||||
|
||||
static bool can_disable_stream(const struct bt_bap_stream *bap_stream)
|
||||
{
|
||||
enum bt_bap_ep_state state;
|
||||
|
||||
if (bap_stream->conn == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state = stream_get_state(bap_stream);
|
||||
|
||||
return state == BT_BAP_EP_STATE_STREAMING || state == BT_BAP_EP_STATE_ENABLING;
|
||||
}
|
||||
|
||||
static bool can_stop_stream(const struct bt_bap_stream *bap_stream)
|
||||
{
|
||||
if (bap_stream->conn == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !stream_is_in_state(bap_stream, BT_BAP_EP_STATE_IDLE);
|
||||
return stream_is_in_state(bap_stream, BT_BAP_EP_STATE_DISABLING);
|
||||
}
|
||||
|
||||
static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_stop_param *param)
|
||||
|
@ -1637,6 +1686,18 @@ static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_sto
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (param->type == BT_CAP_SET_TYPE_CSIP) {
|
||||
struct bt_cap_common_client *client = bt_cap_common_get_client_by_acl(conn);
|
||||
|
||||
if (client->csis_inst == NULL) {
|
||||
LOG_DBG("param->streams[%zu]->bap_stream.conn not part of a "
|
||||
"coordinated set",
|
||||
i);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CHECKIF(bap_stream->group == NULL) {
|
||||
LOG_DBG("param->streams[%zu] is not in a unicast group", i);
|
||||
return false;
|
||||
|
@ -1653,12 +1714,6 @@ static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_sto
|
|||
}
|
||||
}
|
||||
|
||||
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 "
|
||||
|
@ -1676,8 +1731,9 @@ static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_sto
|
|||
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;
|
||||
bool can_release = false;
|
||||
bool can_disable = false;
|
||||
bool can_stop = false;
|
||||
int err;
|
||||
|
||||
if (bt_cap_common_proc_is_active()) {
|
||||
|
@ -1692,34 +1748,259 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p
|
|||
|
||||
for (size_t i = 0U; i < param->count; i++) {
|
||||
struct bt_cap_stream *cap_stream = param->streams[i];
|
||||
struct bt_bap_stream *bap_stream = &cap_stream->bap_stream;
|
||||
|
||||
/* Ensure that ops are registered before any procedures are started */
|
||||
bt_cap_stream_ops_register_bap(cap_stream);
|
||||
|
||||
active_proc->proc_param.initiator[i].stream = cap_stream;
|
||||
active_proc->proc_param.initiator[i].stop.release = param->release;
|
||||
|
||||
if (!can_disable && can_disable_stream(bap_stream)) {
|
||||
can_disable = true;
|
||||
}
|
||||
|
||||
if (!can_stop && can_stop_stream(bap_stream)) {
|
||||
can_stop = true;
|
||||
}
|
||||
|
||||
if (!can_release && param->release && can_release_stream(bap_stream)) {
|
||||
can_release = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_disable && !can_stop && !can_release) {
|
||||
LOG_DBG("Cannot %s any streams", !can_disable ? "disable"
|
||||
: !can_stop ? "stop"
|
||||
: "release");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_STOP, param->count);
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE);
|
||||
|
||||
/** TODO: If this is a CSIP set, then the order of the procedures may
|
||||
* not match the order in the parameters, and the CSIP ordered access
|
||||
* procedure should be used.
|
||||
*/
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
__ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param");
|
||||
|
||||
bap_stream = &proc_param->stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
if (can_disable) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
|
||||
err = bt_bap_stream_release(bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to stop bap_stream %p: %d", proc_param->stream, err);
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_DISABLE);
|
||||
|
||||
bt_cap_common_clear_active_proc();
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
__ASSERT(proc_param != NULL,
|
||||
"proc is not started, but could not get next proc_param");
|
||||
bap_stream = &proc_param->stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_disable(bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to disable bap_stream %p: %d", proc_param->stream, err);
|
||||
|
||||
bt_cap_common_clear_active_proc();
|
||||
}
|
||||
} else if (can_stop) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_STOP);
|
||||
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
__ASSERT(proc_param != NULL,
|
||||
"proc is not started, but could not get next proc_param");
|
||||
bap_stream = &proc_param->stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_stop(bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to stop bap_stream %p: %d", proc_param->stream, err);
|
||||
|
||||
bt_cap_common_clear_active_proc();
|
||||
}
|
||||
} else {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE);
|
||||
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
__ASSERT(proc_param != NULL,
|
||||
"proc is not started, but could not get next proc_param");
|
||||
bap_stream = &proc_param->stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_release(bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to release bap_stream %p: %d", proc_param->stream, err);
|
||||
|
||||
bt_cap_common_clear_active_proc();
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream)
|
||||
{
|
||||
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
|
||||
|
||||
if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
|
||||
/* State change happened outside of a procedure; ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_DISABLE)) {
|
||||
/* Unexpected callback - Abort */
|
||||
bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
|
||||
} else {
|
||||
update_proc_done_cnt(active_proc);
|
||||
|
||||
LOG_DBG("Stream %p disabled (%zu/%zu streams done)", cap_stream,
|
||||
active_proc->proc_done_cnt, active_proc->proc_cnt);
|
||||
}
|
||||
|
||||
if (bt_cap_common_proc_is_aborted()) {
|
||||
if (bt_cap_common_proc_all_handled()) {
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *next_bap_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");
|
||||
next_cap_stream = proc_param->stream;
|
||||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_disable(next_bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to disable stream %p: %d", next_cap_stream, err);
|
||||
|
||||
bt_cap_common_abort_proc(next_bap_stream->conn, err);
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
} else {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *next_bap_stream;
|
||||
int err;
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_STOP);
|
||||
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
if (proc_param == NULL) {
|
||||
/* If proc_param is NULL then this step is a no-op and we can skip to the
|
||||
* next step
|
||||
*/
|
||||
bt_cap_initiator_stopped(active_proc->proc_param.initiator[0].stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
next_cap_stream = proc_param->stream;
|
||||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_stop(next_bap_stream);
|
||||
if (err != 0) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream)
|
||||
{
|
||||
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
|
||||
|
||||
if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
|
||||
/* State change happened outside of a procedure; ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_cap_common_proc_is_type(BT_CAP_COMMON_PROC_TYPE_STOP)) {
|
||||
/* Unexpected callback - Abort */
|
||||
bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
|
||||
} else {
|
||||
if (bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_STOP)) {
|
||||
update_proc_done_cnt(active_proc);
|
||||
|
||||
LOG_DBG("Stream %p stopped (%zu/%zu streams done)", cap_stream,
|
||||
active_proc->proc_done_cnt, active_proc->proc_cnt);
|
||||
} else {
|
||||
/* We are still doing disable - Wait for those to be done, as stopped may
|
||||
* also be called when we are disabling sink ASEs
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_cap_common_proc_is_aborted()) {
|
||||
if (bt_cap_common_proc_all_handled()) {
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *next_bap_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");
|
||||
next_cap_stream = proc_param->stream;
|
||||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
|
||||
active_proc->proc_initiated_cnt++;
|
||||
|
||||
err = bt_bap_stream_stop(next_bap_stream);
|
||||
if (err != 0) {
|
||||
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 {
|
||||
/* 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
|
||||
* it returns a non-NULL value, it means that we need to start releasing streams.
|
||||
* However, since the QoS Configured state is better suited to trigger this, we
|
||||
* simply wait until bt_cap_initiator_qos_configured is called.
|
||||
*/
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
/* We are still disabling or stopping some */
|
||||
return;
|
||||
}
|
||||
|
||||
bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE);
|
||||
|
||||
proc_param = get_next_proc_param(active_proc);
|
||||
if (proc_param == NULL) {
|
||||
/* If proc_param is NULL then this step is a no-op and we can finish the
|
||||
* procedure
|
||||
*/
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
|
||||
return;
|
||||
} /* wait for bt_cap_initiator_qos_configured */
|
||||
}
|
||||
}
|
||||
|
||||
void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
|
||||
{
|
||||
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
|
||||
|
@ -1748,23 +2029,22 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
|
|||
}
|
||||
|
||||
if (!bt_cap_common_proc_is_done()) {
|
||||
struct bt_cap_stream *next_cap_stream =
|
||||
active_proc->proc_param.initiator[active_proc->proc_done_cnt].stream;
|
||||
struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream;
|
||||
struct bt_cap_initiator_proc_param *proc_param;
|
||||
struct bt_cap_stream *next_cap_stream;
|
||||
struct bt_bap_stream *next_bap_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");
|
||||
next_cap_stream = proc_param->stream;
|
||||
next_bap_stream = &next_cap_stream->bap_stream;
|
||||
active_proc->proc_initiated_cnt++;
|
||||
/* 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.
|
||||
* TODO: We should always be able to do one per ACL, so there is room for
|
||||
* optimization.
|
||||
*/
|
||||
err = bt_bap_stream_release(bap_stream);
|
||||
|
||||
err = bt_bap_stream_release(next_bap_stream);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to release stream %p: %d", next_cap_stream, err);
|
||||
|
||||
bt_cap_common_abort_proc(bap_stream->conn, err);
|
||||
bt_cap_common_abort_proc(next_bap_stream->conn, err);
|
||||
cap_initiator_unicast_audio_proc_complete();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -30,6 +30,8 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream);
|
|||
void bt_cap_initiator_started(struct bt_cap_stream *cap_stream);
|
||||
void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream);
|
||||
void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream);
|
||||
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);
|
||||
|
||||
|
@ -61,6 +63,8 @@ enum bt_cap_common_subproc_type {
|
|||
BT_CAP_COMMON_SUBPROC_TYPE_CONNECT,
|
||||
BT_CAP_COMMON_SUBPROC_TYPE_START,
|
||||
BT_CAP_COMMON_SUBPROC_TYPE_META_UPDATE,
|
||||
BT_CAP_COMMON_SUBPROC_TYPE_DISABLE,
|
||||
BT_CAP_COMMON_SUBPROC_TYPE_STOP,
|
||||
BT_CAP_COMMON_SUBPROC_TYPE_RELEASE,
|
||||
};
|
||||
|
||||
|
@ -79,6 +83,9 @@ struct bt_cap_initiator_proc_param {
|
|||
/** Codec Specific Capabilities Metadata */
|
||||
uint8_t meta[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
|
||||
} meta_update;
|
||||
struct {
|
||||
bool release;
|
||||
} stop;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -173,6 +180,7 @@ struct bt_cap_common_proc *bt_cap_common_get_active_proc(void);
|
|||
void bt_cap_common_clear_active_proc(void);
|
||||
void bt_cap_common_start_proc(enum bt_cap_common_proc_type proc_type, size_t proc_cnt);
|
||||
void bt_cap_common_set_subproc(enum bt_cap_common_subproc_type subproc_type);
|
||||
bool bt_cap_common_proc_is_type(enum bt_cap_common_proc_type proc_type);
|
||||
bool bt_cap_common_subproc_is_type(enum bt_cap_common_subproc_type subproc_type);
|
||||
struct bt_conn *bt_cap_common_get_member_conn(enum bt_cap_set_type type,
|
||||
const union bt_cap_set_member *member);
|
||||
|
|
|
@ -132,6 +132,11 @@ static void cap_stream_disabled_cb(struct bt_bap_stream *bap_stream)
|
|||
|
||||
LOG_DBG("%p", cap_stream);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
|
||||
stream_is_central(bap_stream)) {
|
||||
bt_cap_initiator_disabled(cap_stream);
|
||||
}
|
||||
|
||||
if (ops != NULL && ops->disabled != NULL) {
|
||||
ops->disabled(bap_stream);
|
||||
}
|
||||
|
@ -188,6 +193,11 @@ static void cap_stream_stopped_cb(struct bt_bap_stream *bap_stream, uint8_t reas
|
|||
|
||||
LOG_DBG("%p", cap_stream);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
|
||||
stream_is_central(bap_stream)) {
|
||||
bt_cap_initiator_stopped(cap_stream);
|
||||
}
|
||||
|
||||
if (ops != NULL && ops->stopped != NULL) {
|
||||
ops->stopped(bap_stream, reason);
|
||||
}
|
||||
|
|
|
@ -532,6 +532,7 @@ static int cmd_cap_initiator_unicast_stop(const struct shell *sh, size_t argc,
|
|||
|
||||
param.streams = streams;
|
||||
param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||
param.release = true;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err != 0) {
|
||||
|
|
|
@ -17,4 +17,5 @@ target_sources(testbinary
|
|||
src/main.c
|
||||
src/test_common.c
|
||||
src/test_unicast_start.c
|
||||
src/test_unicast_stop.c
|
||||
)
|
||||
|
|
|
@ -24,3 +24,5 @@ CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT=2
|
|||
CONFIG_ASSERT=y
|
||||
CONFIG_ASSERT_LEVEL=2
|
||||
CONFIG_ASSERT_VERBOSE=y
|
||||
|
||||
CONFIG_BT_BAP_STREAM_LOG_LEVEL_DBG=y
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#include "ztest_assert.h"
|
||||
#include "ztest_test.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture)
|
||||
{
|
||||
test_mocks_init();
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "test_common.h"
|
||||
#include "ztest_assert.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
void test_mocks_init(void)
|
||||
{
|
||||
mock_cap_initiator_init();
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#include "ztest_assert.h"
|
||||
#include "ztest_test.h"
|
||||
|
||||
#define FFF_GLOBALS
|
||||
|
||||
struct cap_initiator_test_unicast_start_fixture {
|
||||
struct bt_cap_stream cap_streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
struct bt_bap_ep eps[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
|
|
482
tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c
Normal file
482
tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c
Normal file
|
@ -0,0 +1,482 @@
|
|||
/* test_unicast_stop.c - unit test for unicast stop procedure */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/autoconf.h>
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
#include <zephyr/bluetooth/hci_types.h>
|
||||
#include <zephyr/fff.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "bap_endpoint.h"
|
||||
#include "cap_initiator.h"
|
||||
#include "conn.h"
|
||||
#include "expects_util.h"
|
||||
#include "test_common.h"
|
||||
#include "ztest_assert.h"
|
||||
#include "ztest_test.h"
|
||||
|
||||
struct cap_initiator_test_unicast_stop_fixture {
|
||||
struct bt_cap_stream cap_streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
struct bt_bap_ep eps[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
struct bt_bap_unicast_group unicast_group;
|
||||
struct bt_conn conns[CONFIG_BT_MAX_CONN];
|
||||
struct bt_bap_lc3_preset preset;
|
||||
};
|
||||
|
||||
static void cap_initiator_test_unicast_stop_fixture_init(
|
||||
struct cap_initiator_test_unicast_stop_fixture *fixture)
|
||||
{
|
||||
fixture->preset = (struct bt_bap_lc3_preset)BT_BAP_LC3_UNICAST_PRESET_16_2_1(
|
||||
BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(fixture->conns); i++) {
|
||||
test_conn_init(&fixture->conns[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(fixture->eps); i++) {
|
||||
fixture->eps[i].dir = (i & 1) + 1; /* Makes it either 1 or 2 (sink or source)*/
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(fixture->cap_streams); i++) {
|
||||
struct bt_cap_stream *cap_stream = &fixture->cap_streams[i];
|
||||
struct bt_bap_stream *bap_stream = &cap_stream->bap_stream;
|
||||
|
||||
sys_slist_append(&fixture->unicast_group.streams, &bap_stream->_node);
|
||||
bap_stream->group = &fixture->unicast_group;
|
||||
}
|
||||
}
|
||||
|
||||
static void *cap_initiator_test_unicast_stop_setup(void)
|
||||
{
|
||||
struct cap_initiator_test_unicast_stop_fixture *fixture;
|
||||
|
||||
fixture = malloc(sizeof(*fixture));
|
||||
zassert_not_null(fixture);
|
||||
|
||||
return fixture;
|
||||
}
|
||||
|
||||
static void cap_initiator_test_unicast_stop_before(void *f)
|
||||
{
|
||||
int err;
|
||||
|
||||
memset(f, 0, sizeof(struct cap_initiator_test_unicast_stop_fixture));
|
||||
cap_initiator_test_unicast_stop_fixture_init(f);
|
||||
|
||||
err = bt_cap_initiator_register_cb(&mock_cap_initiator_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
static void cap_initiator_test_unicast_stop_after(void *f)
|
||||
{
|
||||
struct cap_initiator_test_unicast_stop_fixture *fixture = f;
|
||||
|
||||
bt_cap_initiator_unregister_cb(&mock_cap_initiator_cb);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
|
||||
mock_bt_conn_disconnected(&fixture->conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
}
|
||||
|
||||
/* In the case of a test failing, we cancel the procedure so that subsequent won't fail */
|
||||
bt_cap_initiator_unicast_audio_cancel();
|
||||
}
|
||||
|
||||
static void cap_initiator_test_unicast_stop_teardown(void *f)
|
||||
{
|
||||
free(f);
|
||||
}
|
||||
|
||||
ZTEST_SUITE(cap_initiator_test_unicast_stop, NULL, cap_initiator_test_unicast_stop_setup,
|
||||
cap_initiator_test_unicast_stop_before, cap_initiator_test_unicast_stop_after,
|
||||
cap_initiator_test_unicast_stop_teardown);
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop,
|
||||
test_initiator_unicast_stop_disable_state_codec_configured)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = false,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_CODEC_CONFIGURED);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EALREADY, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = bap_stream->ep->status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_CODEC_CONFIGURED,
|
||||
"[%zu]: Stream %p unexpected state: %d", i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop,
|
||||
test_initiator_unicast_stop_disable_state_qos_configured)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = false,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_QOS_CONFIGURED);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EALREADY, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = bap_stream->ep->status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_QOS_CONFIGURED,
|
||||
"[%zu]: Stream %p unexpected state: %d", i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_disable_state_enabling)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = false,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_ENABLING);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, 0, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 1,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = bap_stream->ep->status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_QOS_CONFIGURED,
|
||||
"[%zu]: Stream %p unexpected state: %d", i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_disable_state_streaming)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = false,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_STREAMING);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, 0, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 1,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(fixture->cap_streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = bap_stream->ep->status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_QOS_CONFIGURED,
|
||||
"[%zu]: Stream %p unexpected state: %d", i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop,
|
||||
test_initiator_unicast_stop_release_state_codec_configured)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_CODEC_CONFIGURED);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EALREADY, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = bap_stream->ep->status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_CODEC_CONFIGURED,
|
||||
"[%zu]: Stream %p unexpected state: %d", i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop,
|
||||
test_initiator_unicast_stop_release_state_qos_configured)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_QOS_CONFIGURED);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, 0, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 1,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = fixture->eps[i].status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_IDLE, "[%zu]: Stream %p unexpected state: %d",
|
||||
i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_release_state_enabling)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_ENABLING);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, 0, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 1,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = fixture->eps[i].status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_IDLE, "[%zu]: Stream %p unexpected state: %d",
|
||||
i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_release_state_streaming)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_ENABLING);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, 0, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 1,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(fixture->cap_streams); i++) {
|
||||
const struct bt_bap_stream *bap_stream = &fixture->cap_streams[i].bap_stream;
|
||||
const enum bt_bap_ep_state state = fixture->eps[i].status.state;
|
||||
|
||||
zassert_equal(state, BT_BAP_EP_STATE_IDLE, "[%zu]: Stream %p unexpected state: %d",
|
||||
i, bap_stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_inval_param_null)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(NULL);
|
||||
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop,
|
||||
test_initiator_unicast_stop_inval_param_null_streams)
|
||||
{
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT,
|
||||
.streams = NULL,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_inval_missing_cas)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_CSIP, /* CSIP requires CAS */
|
||||
.count = ARRAY_SIZE(streams),
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
|
||||
test_unicast_set_state(streams[i], &fixture->conns[i % ARRAY_SIZE(fixture->conns)],
|
||||
&fixture->eps[i], &fixture->preset,
|
||||
BT_BAP_EP_STATE_STREAMING);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_inval_param_zero_count)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = 0U,
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
}
|
||||
|
||||
static ZTEST_F(cap_initiator_test_unicast_stop, test_initiator_unicast_stop_inval_param_inval_count)
|
||||
{
|
||||
struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0};
|
||||
const struct bt_cap_unicast_audio_stop_param param = {
|
||||
.type = BT_CAP_SET_TYPE_AD_HOC,
|
||||
.count = CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT + 1U,
|
||||
.streams = streams,
|
||||
.release = true,
|
||||
};
|
||||
int err;
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i] = &fixture->cap_streams[i];
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_initiator_cb.unicast_stop_complete_cb", 0,
|
||||
mock_cap_initiator_unicast_stop_complete_cb_fake.call_count);
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/bluetooth/hci_types.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <sys/errno.h>
|
||||
|
@ -149,7 +150,6 @@ int bt_bap_unicast_client_connect(struct bt_bap_stream *stream)
|
|||
if (stream->ep != NULL && stream->ep->dir == BT_AUDIO_DIR_SINK) {
|
||||
/* Mocking that the unicast server automatically starts the stream */
|
||||
stream->ep->status.state = BT_BAP_EP_STATE_STREAMING;
|
||||
printk("A %s %p\n", __func__, stream);
|
||||
|
||||
if (stream->ops != NULL && stream->ops->started != NULL) {
|
||||
stream->ops->started(stream);
|
||||
|
@ -184,18 +184,105 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream)
|
|||
|
||||
int bt_bap_unicast_client_disable(struct bt_bap_stream *stream)
|
||||
{
|
||||
zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
|
||||
printk("%s %p %d\n", __func__, stream, stream->ep->dir);
|
||||
|
||||
if (stream == NULL || stream->ep == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (stream->ep->status.state) {
|
||||
case BT_BAP_EP_STATE_ENABLING:
|
||||
case BT_BAP_EP_STATE_STREAMING:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Even though the ASCS spec does not have the disabling state for sink ASEs, the unicast
|
||||
* client implementation fakes the behavior of it and always calls the disabled callback
|
||||
* when leaving the streaming state in a non-release manner
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
if (stream->ops != NULL && stream->ops->disabled != NULL) {
|
||||
stream->ops->disabled(stream);
|
||||
}
|
||||
|
||||
if (stream->ops != NULL && stream->ops->stopped != NULL) {
|
||||
stream->ops->stopped(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
|
||||
}
|
||||
|
||||
if (stream->ops != NULL && stream->ops->qos_set != NULL) {
|
||||
stream->ops->qos_set(stream);
|
||||
}
|
||||
} else if (stream->ep->dir == BT_AUDIO_DIR_SOURCE) {
|
||||
stream->ep->status.state = BT_BAP_EP_STATE_DISABLING;
|
||||
|
||||
if (stream->ops != NULL && stream->ops->disabled != NULL) {
|
||||
stream->ops->disabled(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_bap_unicast_client_stop(struct bt_bap_stream *stream)
|
||||
{
|
||||
zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
|
||||
printk("%s %p\n", __func__, stream);
|
||||
|
||||
/* As per the ASCS spec, only source streams can be stopped by the client */
|
||||
if (stream == NULL || stream->ep == NULL || stream->ep->dir == BT_AUDIO_DIR_SINK) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (stream->ep->status.state) {
|
||||
case BT_BAP_EP_STATE_DISABLING:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
|
||||
|
||||
if (stream->ops != NULL && stream->ops->stopped != NULL) {
|
||||
stream->ops->stopped(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
|
||||
}
|
||||
|
||||
if (stream->ops != NULL && stream->ops->qos_set != NULL) {
|
||||
stream->ops->qos_set(stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_bap_unicast_client_release(struct bt_bap_stream *stream)
|
||||
{
|
||||
zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
|
||||
printk("%s %p\n", __func__, stream);
|
||||
|
||||
if (stream == NULL || stream->ep == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (stream->ep->status.state) {
|
||||
case BT_BAP_EP_STATE_CODEC_CONFIGURED:
|
||||
case BT_BAP_EP_STATE_QOS_CONFIGURED:
|
||||
case BT_BAP_EP_STATE_ENABLING:
|
||||
case BT_BAP_EP_STATE_STREAMING:
|
||||
case BT_BAP_EP_STATE_DISABLING:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream->ep->status.state = BT_BAP_EP_STATE_IDLE;
|
||||
bt_bap_stream_reset(stream);
|
||||
|
||||
if (stream->ops != NULL && stream->ops->released != NULL) {
|
||||
stream->ops->released(stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -438,6 +438,7 @@ static uint8_t btp_cap_unicast_audio_stop(const void *cmd, uint16_t cmd_len,
|
|||
param.streams = streams;
|
||||
param.count = stream_cnt;
|
||||
param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||
param.release = true;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err != 0) {
|
||||
|
|
|
@ -682,17 +682,6 @@ static void unicast_audio_update(void)
|
|||
printk("READ LONG META\n");
|
||||
}
|
||||
|
||||
static void unicast_audio_stop_inval(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(NULL);
|
||||
if (err == 0) {
|
||||
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;
|
||||
|
@ -701,9 +690,31 @@ static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
|
|||
param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||
param.count = non_idle_streams_cnt;
|
||||
param.streams = non_idle_streams;
|
||||
param.release = false;
|
||||
|
||||
/* Stop without release first to verify that we enter the QoS Configured state */
|
||||
UNSET_FLAG(flag_stopped);
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err != 0) {
|
||||
FAIL("Failed to stop unicast audio without release: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_stopped);
|
||||
|
||||
/* Verify that it cannot be stopped twice */
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err == 0) {
|
||||
FAIL("bt_cap_initiator_unicast_audio_stop without release with already-stopped "
|
||||
"streams did not fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop with release first to verify that we enter the idle state */
|
||||
UNSET_FLAG(flag_stopped);
|
||||
param.release = true;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err != 0) {
|
||||
FAIL("Failed to stop unicast audio: %d\n", err);
|
||||
|
@ -820,7 +831,6 @@ static void test_main_cap_initiator_unicast_inval(void)
|
|||
unicast_audio_update_inval();
|
||||
unicast_audio_update();
|
||||
|
||||
unicast_audio_stop_inval();
|
||||
unicast_audio_stop(unicast_group);
|
||||
|
||||
unicast_group_delete_inval();
|
||||
|
|
|
@ -929,6 +929,7 @@ static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
|
|||
param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||
param.count = started_unicast_streams_cnt;
|
||||
param.streams = started_unicast_streams;
|
||||
param.release = true;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(¶m);
|
||||
if (err != 0) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue