Bluetooth: BAP: Add ISO state callbacks

Add callbacks to the stream objects that reflects the
state of the isochronous channel. The connected callback
is called when the isochronous channel is connected, and
similarly the disconnected callback is called when it is
disconnected.

There is a special case for unicast, where if the ACL
disconnects first, then we won't get a ISO disconnect
callback. It should be assumed that the isochronous channel
is no longer valid when the BAP stream enters the idle state,
i.e. when the "released" callback is called.

The purpose of the new callbacks is to provide additional
information to the application. Especially the unicast client
can use this to determine when the stream_start function
can be called again, as there can only ever be 1 outstanding
CIS connection request at a time, but there can be multiple
GATT requests.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2023-12-28 13:50:56 +01:00 committed by Carles Cufí
commit b857ef7f83
13 changed files with 264 additions and 29 deletions

View file

@ -581,10 +581,38 @@ struct bt_bap_stream_ops {
*
* This callback is only used if the ISO data path is HCI.
*
* @param chan The channel which has sent data.
* @param stream Stream object.
*/
void (*sent)(struct bt_bap_stream *stream);
#endif /* CONFIG_BT_AUDIO_TX */
/**
* @brief Isochronous channel connected callback
*
* If this callback is provided it will be called whenever the isochronous channel for the
* stream has been connected. This does not mean that the stream is ready to be used, which
* is indicated by the @ref bt_bap_stream_ops.started callback.
*
* If the stream shares an isochronous channel with another stream, then this callback may
* still be called, without the stream going into the started state.
*
* @param stream Stream object.
*/
void (*connected)(struct bt_bap_stream *stream);
/**
* @brief Isochronous channel disconnected callback
*
* If this callback is provided it will be called whenever the isochronous channel is
* disconnected, including when a connection gets rejected.
*
* If the stream shares an isochronous channel with another stream, then this callback may
* not be called, even if the stream is leaving the streaming state.
*
* @param stream Stream object.
* @param reason BT_HCI_ERR_* reason for the disconnection.
*/
void (*disconnected)(struct bt_bap_stream *stream, uint8_t reason);
};
/**

View file

@ -386,7 +386,6 @@ static void ase_exit_state_streaming(struct bt_ascs_ase *ase)
struct bt_bap_stream *stream = ase->ep.stream;
struct bt_bap_stream_ops *ops;
const enum bt_bap_ep_state next_state = ascs_ep_get_state(&ase->ep);
uint8_t reason = ase->ep.reason;
__ASSERT_NO_MSG(stream != NULL);
@ -397,11 +396,6 @@ static void ase_exit_state_streaming(struct bt_ascs_ase *ase)
}
ops = stream->ops;
if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
/*
* On link-loss we go from streaming state to QOS configured state,
@ -415,6 +409,12 @@ static void ase_exit_state_streaming(struct bt_ascs_ase *ase)
LOG_WRN("No callback for disabled set");
}
}
if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
}
static void ase_exit_state_enabling(struct bt_ascs_ase *ase)
@ -940,6 +940,7 @@ static void ascs_update_sdu_size(struct bt_bap_ep *ep)
static void ascs_ep_iso_connected(struct bt_bap_ep *ep)
{
struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep);
const struct bt_bap_stream_ops *stream_ops;
struct bt_bap_stream *stream;
if (ep->status.state != BT_BAP_EP_STATE_ENABLING) {
@ -964,6 +965,13 @@ static void ascs_ep_iso_connected(struct bt_bap_ep *ep)
*/
ascs_update_sdu_size(ep);
LOG_DBG("stream %p ep %p dir %s", stream, ep, bt_audio_dir_str(ep->dir));
stream_ops = stream->ops;
if (stream_ops != NULL && stream_ops->connected != NULL) {
stream_ops->connected(stream);
}
if (ep->dir == BT_AUDIO_DIR_SINK && ep->receiver_ready) {
/* Source ASEs shall be ISO connected first, and then receive
* the receiver start ready command to enter the streaming
@ -971,8 +979,6 @@ static void ascs_ep_iso_connected(struct bt_bap_ep *ep)
*/
ascs_ep_set_state(ep, BT_BAP_EP_STATE_STREAMING);
}
LOG_DBG("stream %p ep %p dir %s", stream, ep, bt_audio_dir_str(ep->dir));
}
static void ascs_iso_connected(struct bt_iso_chan *chan)
@ -996,6 +1002,7 @@ static void ascs_iso_connected(struct bt_iso_chan *chan)
static void ascs_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t reason)
{
struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep);
const struct bt_bap_stream_ops *stream_ops;
struct bt_bap_stream *stream;
stream = ep->stream;
@ -1007,6 +1014,11 @@ static void ascs_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t reason)
LOG_DBG("stream %p ep %p state %s reason 0x%02x", stream, stream->ep,
bt_bap_ep_state_str(ep->status.state), reason);
stream_ops = stream->ops;
if (stream_ops != NULL && stream_ops->disconnected != NULL) {
stream_ops->disconnected(stream, reason);
}
/* Cancel ASE disconnect work if pending */
(void)k_work_cancel_delayable(&ase->disconnect_work);
ep->reason = reason;

View file

@ -296,10 +296,13 @@ static void broadcast_sink_iso_connected(struct bt_iso_chan *chan)
return;
}
ops = stream->ops;
LOG_DBG("stream %p", stream);
ops = stream->ops;
if (ops != NULL && ops->connected != NULL) {
ops->connected(stream);
}
sink = broadcast_sink_lookup_iso_chan(chan);
if (sink == NULL) {
LOG_ERR("Could not lookup sink by iso %p", chan);
@ -311,7 +314,7 @@ static void broadcast_sink_iso_connected(struct bt_iso_chan *chan)
if (ops != NULL && ops->started != NULL) {
ops->started(stream);
} else {
LOG_WRN("No callback for connected set");
LOG_WRN("No callback for started set");
}
all_connected = true;
@ -349,10 +352,13 @@ static void broadcast_sink_iso_disconnected(struct bt_iso_chan *chan,
return;
}
ops = stream->ops;
LOG_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason);
ops = stream->ops;
if (ops != NULL && ops->disconnected != NULL) {
ops->disconnected(stream, reason);
}
broadcast_sink_set_ep_state(ep, BT_BAP_EP_STATE_IDLE);
sink = broadcast_sink_lookup_iso_chan(chan);

View file

@ -166,10 +166,13 @@ static void broadcast_source_iso_connected(struct bt_iso_chan *chan)
return;
}
ops = stream->ops;
LOG_DBG("stream %p ep %p", stream, ep);
ops = stream->ops;
if (ops != NULL && ops->connected != NULL) {
ops->connected(stream);
}
broadcast_source_set_ep_state(ep, BT_BAP_EP_STATE_STREAMING);
if (ops != NULL && ops->started != NULL) {
@ -197,10 +200,13 @@ static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t
return;
}
ops = stream->ops;
LOG_DBG("stream %p ep %p reason 0x%02x", stream, stream->ep, reason);
ops = stream->ops;
if (ops != NULL && ops->disconnected != NULL) {
ops->disconnected(stream, reason);
}
broadcast_source_set_ep_state(ep, BT_BAP_EP_STATE_QOS_CONFIGURED);
if (ops != NULL && ops->stopped != NULL) {

View file

@ -296,6 +296,7 @@ static void unicast_client_ep_iso_sent(struct bt_iso_chan *chan)
static void unicast_client_ep_iso_connected(struct bt_bap_ep *ep)
{
const struct bt_bap_stream_ops *stream_ops;
struct bt_bap_stream *stream;
if (ep->status.state != BT_BAP_EP_STATE_ENABLING) {
@ -313,6 +314,11 @@ static void unicast_client_ep_iso_connected(struct bt_bap_ep *ep)
LOG_DBG("stream %p ep %p dir %s receiver_ready %u",
stream, ep, bt_audio_dir_str(ep->dir), ep->receiver_ready);
stream_ops = stream->ops;
if (stream_ops != NULL && stream_ops->connected != NULL) {
stream_ops->connected(stream);
}
if (ep->receiver_ready && ep->dir == BT_AUDIO_DIR_SOURCE) {
const int err = unicast_client_send_start(ep);
@ -345,6 +351,7 @@ static void unicast_client_iso_connected(struct bt_iso_chan *chan)
static void unicast_client_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t reason)
{
const struct bt_bap_stream_ops *stream_ops;
struct bt_bap_stream *stream;
stream = ep->stream;
@ -356,6 +363,11 @@ static void unicast_client_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t rea
LOG_DBG("stream %p ep %p reason 0x%02x", stream, ep, reason);
ep->reason = reason;
stream_ops = stream->ops;
if (stream_ops != NULL && stream_ops->disconnected != NULL) {
stream_ops->disconnected(stream, reason);
}
/* If we were in the idle state when we started the ISO disconnection
* then we need to call unicast_client_ep_idle_state again when
* the ISO has finalized the disconnection
@ -714,6 +726,7 @@ static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_
static void unicast_client_ep_qos_state(struct bt_bap_ep *ep, struct net_buf_simple *buf,
uint8_t old_state)
{
const struct bt_bap_stream_ops *ops;
struct bt_ascs_ase_status_qos *qos;
struct bt_bap_stream *stream;
@ -727,17 +740,36 @@ static void unicast_client_ep_qos_state(struct bt_bap_ep *ep, struct net_buf_sim
LOG_ERR("No stream active for endpoint");
return;
}
ops = stream->ops;
if (ep->dir == BT_AUDIO_DIR_SINK && stream->ops != NULL && stream->ops->disabled != NULL) {
/* If the old state was enabling or streaming, then the sink
* ASE has been disabled. Since the sink ASE does not have a
* disabling state, we can check if by comparing the old_state
*/
const bool disabled = old_state == BT_BAP_EP_STATE_ENABLING ||
old_state == BT_BAP_EP_STATE_STREAMING;
if (ops != NULL) {
if (ep->dir == BT_AUDIO_DIR_SINK && ops->disabled != NULL) {
/* If the old state was enabling or streaming, then the sink
* ASE has been disabled. Since the sink ASE does not have a
* disabling state, we can check if by comparing the old_state
*/
const bool disabled = old_state == BT_BAP_EP_STATE_ENABLING ||
old_state == BT_BAP_EP_STATE_STREAMING;
if (disabled) {
stream->ops->disabled(stream);
if (disabled) {
ops->disabled(stream);
}
} else if (ep->dir == BT_AUDIO_DIR_SOURCE &&
old_state == BT_BAP_EP_STATE_DISABLING && ops->stopped != NULL) {
/* We left the disabling state, let the upper layers know that the stream is
* stopped
*/
uint8_t reason = ep->reason;
if (reason == BT_HCI_ERR_SUCCESS) {
/* Default to BT_HCI_ERR_UNSPECIFIED if no other reason is set */
reason = BT_HCI_ERR_UNSPECIFIED;
} else {
/* Reset reason */
ep->reason = BT_HCI_ERR_SUCCESS;
}
ops->stopped(stream, reason);
}
}

View file

@ -182,6 +182,28 @@ static void cap_stream_sent_cb(struct bt_bap_stream *bap_stream)
}
#endif /* CONFIG_BT_AUDIO_TX */
static void cap_stream_connected_cb(struct bt_bap_stream *bap_stream)
{
struct bt_cap_stream *cap_stream =
CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
struct bt_bap_stream_ops *ops = cap_stream->ops;
if (ops != NULL && ops->connected != NULL) {
ops->connected(bap_stream);
}
}
static void cap_stream_disconnected_cb(struct bt_bap_stream *bap_stream, uint8_t reason)
{
struct bt_cap_stream *cap_stream =
CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
struct bt_bap_stream_ops *ops = cap_stream->ops;
if (ops != NULL && ops->disconnected != NULL) {
ops->disconnected(bap_stream, reason);
}
}
static struct bt_bap_stream_ops bap_stream_ops = {
#if defined(CONFIG_BT_BAP_UNICAST)
.configured = cap_stream_configured_cb,
@ -199,6 +221,8 @@ static struct bt_bap_stream_ops bap_stream_ops = {
#if defined(CONFIG_BT_AUDIO_TX)
.sent = cap_stream_sent_cb,
#endif /* CONFIG_BT_AUDIO_TX */
.connected = cap_stream_connected_cb,
.disconnected = cap_stream_disconnected_cb,
};
void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream)

View file

@ -404,6 +404,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_streaming_state)
expect_bt_bap_stream_ops_qos_set_called_once(stream);
expect_bt_bap_stream_ops_disabled_called_once(stream);
expect_bt_bap_stream_ops_released_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb);
}
@ -448,6 +449,7 @@ static void test_cis_link_loss_in_disabling_state(struct ascs_test_suite_fixture
expect_bt_bap_stream_ops_qos_set_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
expect_bt_bap_stream_ops_released_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb);
}
@ -495,6 +497,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state)
/* Expected no change in ASE state */
expect_bt_bap_stream_ops_qos_set_not_called();
expect_bt_bap_stream_ops_released_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
err = bt_bap_stream_disable(stream);
zassert_equal(0, err, "Failed to disable stream: err %d", err);
@ -534,6 +537,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state_client_retries)
test_preamble_state_enabling(conn, ase_id, stream);
err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan);
zassert_equal(0, err, "Failed to connect iso: err %d", err);
expect_bt_bap_stream_ops_connected_called_once(stream);
/* Mock CIS disconnection */
mock_bt_iso_disconnected(chan, BT_HCI_ERR_CONN_FAIL_TO_ESTAB);
@ -541,6 +545,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state_client_retries)
/* Expected to not notify the upper layers */
expect_bt_bap_stream_ops_qos_set_not_called();
expect_bt_bap_stream_ops_released_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
/* Client retries to establish CIS */
err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan);
@ -552,6 +557,7 @@ ZTEST_F(ascs_test_suite, test_cis_link_loss_in_enabling_state_client_retries)
zassert_equal(0, err, "bt_bap_stream_start err %d", err);
}
expect_bt_bap_stream_ops_connected_called_twice(stream);
expect_bt_bap_stream_ops_started_called_once(stream);
bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb);

View file

@ -288,6 +288,7 @@ ZTEST_F(test_sink_ase_state_transition, test_client_streaming_to_releasing)
expect_bt_bap_stream_ops_stopped_called_once(stream, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
expect_bt_bap_stream_ops_released_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
}
ZTEST_F(test_sink_ase_state_transition, test_client_streaming_to_streaming)
@ -510,6 +511,7 @@ ZTEST_F(test_sink_ase_state_transition, test_server_enabling_to_streaming)
zassert_false(err < 0, "bt_bap_stream_start returned err %d", err);
/* Verification */
expect_bt_bap_stream_ops_connected_called_once(stream);
expect_bt_bap_stream_ops_started_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
/* XXX: unicast_server_cb->start is not called for Sink ASE */
@ -585,6 +587,7 @@ ZTEST_F(test_sink_ase_state_transition, test_server_streaming_to_releasing)
expect_bt_bap_stream_ops_stopped_called_once(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
expect_bt_bap_stream_ops_released_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
}
static void *test_source_ase_state_transition_setup(void)
@ -711,6 +714,7 @@ ZTEST_F(test_source_ase_state_transition, test_client_enabling_to_streaming)
/* Verification */
expect_bt_bap_unicast_server_cb_start_called_once(stream);
expect_bt_bap_stream_ops_connected_called_once(stream);
expect_bt_bap_stream_ops_started_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
}
@ -841,6 +845,7 @@ ZTEST_F(test_source_ase_state_transition, test_client_streaming_to_releasing)
expect_bt_bap_stream_ops_stopped_called_once(stream, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
expect_bt_bap_stream_ops_released_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
}
ZTEST_F(test_source_ase_state_transition, test_client_streaming_to_streaming)
@ -1160,4 +1165,5 @@ ZTEST_F(test_source_ase_state_transition, test_server_streaming_to_releasing)
expect_bt_bap_stream_ops_stopped_called_once(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
expect_bt_bap_stream_ops_released_called_once(stream);
expect_bt_bap_stream_ops_disabled_not_called();
expect_bt_bap_stream_ops_disconnected_called_once(stream);
}

View file

@ -218,6 +218,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send
err = bt_bap_broadcast_source_start(fixture->source, &ext_adv);
zassert_equal(0, err, "Unable to start broadcast source: err %d", err);
zexpect_call_count("bt_bap_stream_ops.connected", fixture->stream_cnt,
mock_bap_stream_connected_cb_fake.call_count);
zexpect_call_count("bt_bap_stream_ops.started", fixture->stream_cnt,
mock_bap_stream_started_cb_fake.call_count);
@ -239,6 +241,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send
err = bt_bap_broadcast_source_stop(fixture->source);
zassert_equal(0, err, "Unable to stop broadcast source: err %d", err);
zexpect_call_count("bt_bap_stream_ops.disconnected", fixture->stream_cnt,
mock_bap_stream_disconnected_cb_fake.call_count);
zexpect_call_count("bt_bap_stream_ops.stopped", fixture->stream_cnt,
mock_bap_stream_stopped_cb_fake.call_count);

View file

@ -27,5 +27,7 @@ DECLARE_FAKE_VOID_FUNC(mock_bap_stream_stopped_cb, struct bt_bap_stream *, uint8
DECLARE_FAKE_VOID_FUNC(mock_bap_stream_recv_cb, struct bt_bap_stream *,
const struct bt_iso_recv_info *, struct net_buf *);
DECLARE_FAKE_VOID_FUNC(mock_bap_stream_sent_cb, struct bt_bap_stream *);
DECLARE_FAKE_VOID_FUNC(mock_bap_stream_connected_cb, struct bt_bap_stream *);
DECLARE_FAKE_VOID_FUNC(mock_bap_stream_disconnected_cb, struct bt_bap_stream *, uint8_t);
#endif /* MOCKS_BAP_STREAM_H_ */

View file

@ -135,7 +135,7 @@ static inline void expect_bt_bap_stream_ops_released_called(const struct bt_bap_
}
}
static inline void expect_bt_bap_stream_ops_released_called_once(struct bt_bap_stream *stream)
static inline void expect_bt_bap_stream_ops_released_called_once(const struct bt_bap_stream *stream)
{
expect_bt_bap_stream_ops_released_called(&stream, 1);
}
@ -193,6 +193,45 @@ static inline void expect_bt_bap_stream_ops_stopped_not_called(void)
zexpect_call_count(func_name, 0, mock_bap_stream_stopped_cb_fake.call_count);
}
static inline void
expect_bt_bap_stream_ops_connected_called_once(const struct bt_bap_stream *stream)
{
const char *func_name = "bt_bap_stream_ops.connected";
zexpect_call_count(func_name, 1, mock_bap_stream_connected_cb_fake.call_count);
if (mock_bap_stream_connected_cb_fake.call_count > 0) {
zexpect_equal_ptr(stream, mock_bap_stream_connected_cb_fake.arg0_val,
"'%s()' was called with incorrect '%s'", func_name, "stream");
}
}
static inline void
expect_bt_bap_stream_ops_connected_called_twice(const struct bt_bap_stream *stream)
{
const char *func_name = "bt_bap_stream_ops.connected";
zexpect_call_count(func_name, 2, mock_bap_stream_connected_cb_fake.call_count);
if (mock_bap_stream_connected_cb_fake.call_count > 0) {
zexpect_equal_ptr(stream, mock_bap_stream_connected_cb_fake.arg0_val,
"'%s()' was called with incorrect '%s'", func_name, "stream");
}
}
static inline void
expect_bt_bap_stream_ops_disconnected_called_once(const struct bt_bap_stream *stream)
{
const char *func_name = "bt_bap_stream_ops.disconnected";
zexpect_call_count(func_name, 1, mock_bap_stream_disconnected_cb_fake.call_count);
if (mock_bap_stream_disconnected_cb_fake.call_count > 0) {
zexpect_equal_ptr(stream, mock_bap_stream_disconnected_cb_fake.arg0_val,
"'%s()' was called with incorrect '%s'", func_name, "stream");
}
}
static inline void expect_bt_bap_stream_ops_recv_called_once(struct bt_bap_stream *stream,
const struct bt_iso_recv_info *info,
struct net_buf *buf)

View file

@ -20,6 +20,8 @@
FAKE(mock_bap_stream_stopped_cb) \
FAKE(mock_bap_stream_recv_cb) \
FAKE(mock_bap_stream_sent_cb) \
FAKE(mock_bap_stream_connected_cb) \
FAKE(mock_bap_stream_disconnected_cb)
struct bt_bap_stream_ops mock_bap_stream_ops;
@ -35,6 +37,8 @@ DEFINE_FAKE_VOID_FUNC(mock_bap_stream_stopped_cb, struct bt_bap_stream *, uint8_
DEFINE_FAKE_VOID_FUNC(mock_bap_stream_recv_cb, struct bt_bap_stream *,
const struct bt_iso_recv_info *, struct net_buf *);
DEFINE_FAKE_VOID_FUNC(mock_bap_stream_sent_cb, struct bt_bap_stream *);
DEFINE_FAKE_VOID_FUNC(mock_bap_stream_connected_cb, struct bt_bap_stream *);
DEFINE_FAKE_VOID_FUNC(mock_bap_stream_disconnected_cb, struct bt_bap_stream *, uint8_t);
void mock_bap_stream_init(void)
{
@ -56,6 +60,8 @@ void mock_bap_stream_init(void)
#if defined(CONFIG_BT_AUDIO_TX)
mock_bap_stream_ops.sent = mock_bap_stream_sent_cb;
#endif /* CONFIG_BT_AUDIO_TX */
mock_bap_stream_ops.connected = mock_bap_stream_connected_cb;
mock_bap_stream_ops.disconnected = mock_bap_stream_disconnected_cb;
}
void mock_bap_stream_cleanup(void)

View file

@ -45,7 +45,10 @@ static atomic_t flag_stream_qos_configured;
CREATE_FLAG(flag_stream_enabled);
CREATE_FLAG(flag_stream_metadata);
CREATE_FLAG(flag_stream_started);
CREATE_FLAG(flag_stream_connected);
CREATE_FLAG(flag_stream_disconnected);
CREATE_FLAG(flag_stream_disabled);
CREATE_FLAG(flag_stream_stopped);
CREATE_FLAG(flag_stream_released);
CREATE_FLAG(flag_operation_success);
@ -86,6 +89,20 @@ static void stream_started(struct bt_bap_stream *stream)
SET_FLAG(flag_stream_started);
}
static void stream_connected(struct bt_bap_stream *stream)
{
printk("Connected stream %p\n", stream);
SET_FLAG(flag_stream_connected);
}
static void stream_disconnected(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Disconnected stream %p with reason %u\n", stream, reason);
SET_FLAG(flag_stream_disconnected);
}
static void stream_metadata_updated(struct bt_bap_stream *stream)
{
printk("Metadata updated stream %p\n", stream);
@ -107,6 +124,8 @@ static void stream_disabled(struct bt_bap_stream *stream)
static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stopped stream %p with reason 0x%02X\n", stream, reason);
SET_FLAG(flag_stream_stopped);
}
static void stream_released(struct bt_bap_stream *stream)
@ -198,6 +217,8 @@ static struct bt_bap_stream_ops stream_ops = {
.released = stream_released,
.recv = stream_recv_cb,
.sent = stream_sent_cb,
.connected = stream_connected,
.disconnected = stream_disconnected,
};
static void unicast_client_location_cb(struct bt_conn *conn,
@ -764,6 +785,8 @@ static void start_streams(void)
source_stream = pair_params[0].rx_param == NULL ? NULL : pair_params[0].rx_param->stream;
sink_stream = pair_params[0].tx_param == NULL ? NULL : pair_params[0].tx_param->stream;
UNSET_FLAG(flag_stream_connected);
if (sink_stream != NULL) {
const int err = start_stream(sink_stream);
@ -783,6 +806,8 @@ static void start_streams(void)
return;
}
}
WAIT_FOR_FLAG(flag_stream_connected);
}
static void transceive_streams(void)
@ -843,6 +868,42 @@ static void disable_streams(size_t stream_cnt)
}
}
static void stop_streams(size_t stream_cnt)
{
UNSET_FLAG(flag_stream_disconnected);
for (size_t i = 0; i < stream_cnt; i++) {
struct bt_bap_stream *source_stream;
int err;
/* We can only stop source streams */
source_stream =
pair_params[i].rx_param == NULL ? NULL : pair_params[i].rx_param->stream;
if (source_stream == NULL) {
continue;
}
UNSET_FLAG(flag_operation_success);
UNSET_FLAG(flag_stream_stopped);
do {
err = bt_bap_stream_stop(source_stream);
if (err == -EBUSY) {
k_sleep(BAP_STREAM_RETRY_WAIT);
} else if (err != 0) {
FAIL("Could not stop stream: %d\n", err);
return;
}
} while (err == -EBUSY);
WAIT_FOR_FLAG(flag_operation_success);
WAIT_FOR_FLAG(flag_stream_stopped);
}
WAIT_FOR_FLAG(flag_stream_disconnected);
}
static void release_streams(size_t stream_cnt)
{
for (size_t i = 0; i < stream_cnt; i++) {
@ -994,9 +1055,12 @@ static void test_main(void)
printk("Starting transceiving\n");
transceive_streams();
printk("Stopping streams\n");
printk("Disabling streams\n");
disable_streams(stream_cnt);
printk("Stopping streams\n");
stop_streams(stream_cnt);
printk("Releasing streams\n");
release_streams(stream_cnt);