Skip to content

Commit

Permalink
Bluetooth: Modify stream_ops->stopped to be called on leaving streaming
Browse files Browse the repository at this point in the history
When a stream leaves the streaming state the `stopped` callback
will now be called, similar to how the `started` callback works.

This ensures that `stopped` is always called and not just when
the CIS disconnects.

Signed-off-by: Emil Gydesen <[email protected]>
  • Loading branch information
Thalley committed Jun 29, 2023
1 parent 2f5068f commit 32408ad
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 43 deletions.
36 changes: 30 additions & 6 deletions subsys/bluetooth/audio/ascs.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,27 @@ void ascs_ep_set_state(struct bt_bap_ep *ep, uint8_t state)

stream = ep->stream;

if (state_changed && old_state == BT_BAP_EP_STATE_STREAMING) {
/* We left the streaming state, let the upper layers know that the stream is stopped
*/
struct bt_bap_stream_ops *ops = stream->ops;
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;
}

if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
}

if (stream->ops != NULL) {
const struct bt_bap_stream_ops *ops = stream->ops;

Expand Down Expand Up @@ -799,12 +820,7 @@ static void ascs_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t reason)

/* Cancel ASE disconnect work if pending */
(void)k_work_cancel_delayable(&ase->disconnect_work);

if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
ep->reason = reason;

if (ep->status.state == BT_BAP_EP_STATE_RELEASING) {
ascs_ep_set_state(ep, BT_BAP_EP_STATE_IDLE);
Expand Down Expand Up @@ -941,6 +957,9 @@ static void ase_release(struct bt_ascs_ase *ase)
return;
}

/* Set reason in case this exits the streaming state */
ase->ep.reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;

ascs_ep_set_state(&ase->ep, BT_BAP_EP_STATE_RELEASING);
/* At this point, `ase` object might have been free'd if automously went to Idle */

Expand Down Expand Up @@ -993,6 +1012,9 @@ static void ase_disable(struct bt_ascs_ase *ase)
return;
}

/* Set reason in case this exits the streaming state */
ep->reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;

/* The ASE state machine goes into different states from this operation
* based on whether it is a source or a sink ASE.
*/
Expand All @@ -1015,6 +1037,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
}

if (ase->ep.status.state != BT_BAP_EP_STATE_IDLE) {
ase->ep.reason = reason;
ase_release(ase);
/* At this point, `ase` object have been free'd */
}
Expand Down Expand Up @@ -1096,6 +1119,7 @@ void ascs_ep_init(struct bt_bap_ep *ep, uint8_t id)
(void)memset(ep, 0, sizeof(*ep));
ep->status.id = id;
ep->dir = ASE_DIR(id);
ep->reason = BT_HCI_ERR_SUCCESS;
}

static void ase_init(struct bt_ascs_ase *ase, struct bt_conn *conn, uint8_t id)
Expand Down
3 changes: 3 additions & 0 deletions subsys/bluetooth/audio/bap_endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ struct bt_bap_ep {
struct bt_codec_qos_pref qos_pref;
struct bt_bap_iso *iso;

/* unicast stopped reason */
uint8_t reason;

/* Used by the unicast server and client */
bool receiver_ready;

Expand Down
98 changes: 64 additions & 34 deletions subsys/bluetooth/audio/bap_unicast_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,10 @@ static int unicast_client_ep_start(struct bt_bap_ep *ep,

static int unicast_client_ase_discover(struct bt_conn *conn, uint16_t start_handle);

static void unicast_client_reset(struct bt_bap_ep *ep);
static void unicast_client_reset(struct bt_bap_ep *ep, uint8_t reason);

static void delayed_ase_read_handler(struct k_work *work);
static void unicast_client_ep_set_status(struct bt_bap_ep *ep, struct net_buf_simple *buf);

static int unicast_client_send_start(struct bt_bap_ep *ep)
{
Expand Down Expand Up @@ -168,7 +169,7 @@ static int unicast_client_send_start(struct bt_bap_ep *ep)
return 0;
}

static int unicast_client_ep_idle_state(struct bt_bap_ep *ep);
static void unicast_client_ep_idle_state(struct bt_bap_ep *ep);

static struct bt_bap_stream *audio_stream_by_ep_id(const struct bt_conn *conn,
uint8_t id)
Expand Down Expand Up @@ -350,14 +351,15 @@ 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;

/* 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
*/
if (ep->status.state == BT_BAP_EP_STATE_IDLE) {

(void)unicast_client_ep_idle_state(ep);
unicast_client_ep_idle_state(ep);

if (stream->conn != NULL) {
struct bt_conn_info conn_info;
Expand All @@ -368,15 +370,9 @@ static void unicast_client_ep_iso_disconnected(struct bt_bap_ep *ep, uint8_t rea
/* Retrigger the reset of the EP if the ACL is disconnected before
* the ISO is disconnected
*/
unicast_client_reset(ep);
unicast_client_reset(ep, reason);
}
}
} else {
if (stream->ops != NULL && stream->ops->stopped != NULL) {
stream->ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
}
}

Expand Down Expand Up @@ -440,6 +436,7 @@ static void unicast_client_ep_init(struct bt_bap_ep *ep, uint16_t handle, uint8_
client_ep->handle = handle;
ep->status.id = 0U;
ep->dir = dir;
ep->reason = BT_HCI_ERR_SUCCESS;
k_work_init_delayable(&client_ep->ase_read_work, delayed_ase_read_handler);
}

Expand Down Expand Up @@ -539,15 +536,28 @@ static struct bt_bap_ep *unicast_client_ep_get(struct bt_conn *conn, enum bt_aud
return unicast_client_ep_new(conn, dir, handle);
}

static int unicast_client_ep_idle_state(struct bt_bap_ep *ep)
static void unicast_client_ep_set_local_idle_state(struct bt_bap_ep *ep)
{
struct bt_ascs_ase_status status = {
.id = ep->status.id,
.state = BT_BAP_EP_STATE_IDLE,
};
struct net_buf_simple buf;

net_buf_simple_init_with_data(&buf, &status, sizeof(status));

unicast_client_ep_set_status(ep, &buf);
}

static void unicast_client_ep_idle_state(struct bt_bap_ep *ep)
{
struct bt_bap_unicast_client_ep *client_ep =
CONTAINER_OF(ep, struct bt_bap_unicast_client_ep, ep);
struct bt_bap_stream *stream = ep->stream;
const struct bt_bap_stream_ops *ops;

if (stream == NULL) {
return -EINVAL;
return;
}

/* If CIS is connected, disconnect and wait for CIS disconnection */
Expand All @@ -561,10 +571,10 @@ static int unicast_client_ep_idle_state(struct bt_bap_ep *ep)
LOG_ERR("Failed to disconnect stream: %d", err);
}

return err;
return;
} else if (ep->iso != NULL && ep->iso->chan.state == BT_ISO_STATE_DISCONNECTING) {
/* Wait */
return -EBUSY;
/* Wait for disconnection */
return;
}

bt_bap_stream_reset(stream);
Expand Down Expand Up @@ -593,8 +603,6 @@ static int unicast_client_ep_idle_state(struct bt_bap_ep *ep)
} else {
LOG_WRN("No callback for released set");
}

return 0;
}

static void unicast_client_ep_qos_update(struct bt_bap_ep *ep,
Expand Down Expand Up @@ -635,8 +643,9 @@ static void unicast_client_ep_config_state(struct bt_bap_ep *ep, struct net_buf_

if (client_ep->release_requested) {
LOG_DBG("Released was requested, change local state to idle");
ep->status.state = BT_BAP_EP_STATE_IDLE;
unicast_client_ep_idle_state(ep);
ep->reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;
unicast_client_ep_set_local_idle_state(ep);
return;
}

if (buf->len < sizeof(*cfg)) {
Expand Down Expand Up @@ -947,14 +956,39 @@ static void unicast_client_ep_set_status(struct bt_bap_ep *ep, struct net_buf_si
ep->status = *status;
state_changed = old_state != ep->status.state;

if (state_changed && old_state == BT_BAP_EP_STATE_STREAMING) {
/* We left the streaming state, let the upper layers know that the stream is stopped
*/
struct bt_bap_stream *stream = ep->stream;

if (stream != NULL) {
struct bt_bap_stream_ops *ops = stream->ops;
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;
}

if (ops != NULL && ops->stopped != NULL) {
ops->stopped(stream, reason);
} else {
LOG_WRN("No callback for stopped set");
}
}
}

LOG_DBG("ep %p handle 0x%04x id 0x%02x dir %s state %s -> %s", ep, client_ep->handle,
status->id, bt_audio_dir_str(ep->dir), bt_bap_ep_state_str(old_state),
bt_bap_ep_state_str(status->state));

switch (status->state) {
case BT_BAP_EP_STATE_IDLE:
ep->receiver_ready = false;
(void)unicast_client_ep_idle_state(ep);
unicast_client_ep_idle_state(ep);
break;
case BT_BAP_EP_STATE_CODEC_CONFIGURED:
switch (old_state) {
Expand Down Expand Up @@ -2037,24 +2071,20 @@ int bt_bap_unicast_client_ep_send(struct bt_conn *conn, struct bt_bap_ep *ep,
return err;
}

static void unicast_client_reset(struct bt_bap_ep *ep)
static void unicast_client_reset(struct bt_bap_ep *ep, uint8_t reason)
{
struct bt_bap_unicast_client_ep *client_ep =
CONTAINER_OF(ep, struct bt_bap_unicast_client_ep, ep);
int err;

LOG_DBG("ep %p", ep);
ep->reason = reason;

/* Pretend we received an idle state notification from the server to trigger all cleanup */
ep->status.state = BT_BAP_EP_STATE_IDLE;
err = unicast_client_ep_idle_state(ep);
if (err != 0) {
LOG_DBG("unicast_client_ep_idle_state returned %d", err);
unicast_client_ep_set_local_idle_state(ep);

if (err == -EBUSY) {
/* Wait for ISO disconnected event */
return;
}
if (ep->iso != NULL && ep->iso->chan.state == BT_ISO_STATE_DISCONNECTING) {
/* Wait for ISO disconnected event */
return;
}

(void)k_work_cancel_delayable(&client_ep->ase_read_work);
Expand All @@ -2068,7 +2098,7 @@ static void unicast_client_reset(struct bt_bap_ep *ep)
/* Need to keep the subscribe params intact for the callback */
}

static void unicast_client_ep_reset(struct bt_conn *conn)
static void unicast_client_ep_reset(struct bt_conn *conn, uint8_t reason)
{
uint8_t index;

Expand All @@ -2080,15 +2110,15 @@ static void unicast_client_ep_reset(struct bt_conn *conn)
for (size_t i = 0U; i < ARRAY_SIZE(uni_cli_insts[index].snks); i++) {
struct bt_bap_ep *ep = &uni_cli_insts[index].snks[i].ep;

unicast_client_reset(ep);
unicast_client_reset(ep, reason);
}
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */

#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0
for (size_t i = 0U; i < ARRAY_SIZE(uni_cli_insts[index].srcs); i++) {
struct bt_bap_ep *ep = &uni_cli_insts[index].srcs[i].ep;

unicast_client_reset(ep);
unicast_client_reset(ep, reason);
}
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */
}
Expand Down Expand Up @@ -3971,7 +4001,7 @@ static void unicast_client_disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_DBG("conn %p reason 0x%02x", conn, reason);

unicast_client_ep_reset(conn);
unicast_client_ep_reset(conn, reason);
}

static struct bt_conn_cb conn_cbs = {
Expand Down
11 changes: 10 additions & 1 deletion subsys/bluetooth/audio/bap_unicast_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ int bt_bap_unicast_server_disable(struct bt_bap_stream *stream)

ep = stream->ep;

/* Set reason in case this exits the streaming state */
ep->reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;

/* The ASE state machine goes into different states from this operation
* based on whether it is a source or a sink ASE.
*/
Expand All @@ -182,6 +185,7 @@ int bt_bap_unicast_server_release(struct bt_bap_stream *stream)
{
struct bt_bap_ascs_rsp rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_SUCCESS,
BT_BAP_ASCS_REASON_NONE);
struct bt_bap_ep *ep;
int err;

if (unicast_server_cb != NULL && unicast_server_cb->release != NULL) {
Expand All @@ -195,10 +199,15 @@ int bt_bap_unicast_server_release(struct bt_bap_stream *stream)
return err;
}

ep = stream->ep;

/* Set reason in case this exits the streaming state */
ep->reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;

/* ase_process will set the state to IDLE after sending the
* notification, finalizing the release
*/
ascs_ep_set_state(stream->ep, BT_BAP_EP_STATE_RELEASING);
ascs_ep_set_state(ep, BT_BAP_EP_STATE_RELEASING);

return 0;
}
Expand Down
Loading

0 comments on commit 32408ad

Please sign in to comment.