Bluetooth: audio: ascs: Fix possible race condition
This fixes possible ASE state race condition. The notification is sent immediately once the ASE state changed that eliminates a situation where the state was changed by user action (API function call) when the state was not yet notified to the remote Unicast Client. Fixes: BAP/USR/SCC/BV-158-C Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
parent
60bf310aae
commit
8cd6c55d96
2 changed files with 63 additions and 79 deletions
|
@ -73,8 +73,11 @@ K_MEM_SLAB_DEFINE(ase_slab, sizeof(struct bt_ascs_ase),
|
||||||
__alignof__(struct bt_ascs_ase));
|
__alignof__(struct bt_ascs_ase));
|
||||||
|
|
||||||
static struct bt_ascs sessions[CONFIG_BT_MAX_CONN];
|
static struct bt_ascs sessions[CONFIG_BT_MAX_CONN];
|
||||||
|
NET_BUF_SIMPLE_DEFINE_STATIC(ase_buf, CONFIG_BT_L2CAP_TX_MTU);
|
||||||
|
|
||||||
static int control_point_notify(struct bt_conn *conn, const void *data, uint16_t len);
|
static int control_point_notify(struct bt_conn *conn, const void *data, uint16_t len);
|
||||||
|
static int ascs_ep_get_status(struct bt_audio_ep *ep,
|
||||||
|
struct net_buf_simple *buf);
|
||||||
|
|
||||||
static void bt_ascs_ase_return_to_slab(struct bt_ascs_ase *ase)
|
static void bt_ascs_ase_return_to_slab(struct bt_ascs_ase *ase)
|
||||||
{
|
{
|
||||||
|
@ -100,7 +103,16 @@ static struct bt_ascs_ase *bt_ascs_ase_get_from_slab(void)
|
||||||
static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state,
|
static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state,
|
||||||
uint8_t state)
|
uint8_t state)
|
||||||
{
|
{
|
||||||
k_work_submit(&ep->work);
|
struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep);
|
||||||
|
struct bt_conn *conn = ase->ascs->conn;
|
||||||
|
|
||||||
|
LOG_DBG("ase %p, ep %p", ase, ep);
|
||||||
|
|
||||||
|
if (conn != NULL && conn->state == BT_CONN_CONNECTED) {
|
||||||
|
ascs_ep_get_status(ep, &ase_buf);
|
||||||
|
|
||||||
|
bt_gatt_notify(conn, ase->attr, ase_buf.data, ase_buf.len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
|
void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
|
||||||
|
@ -222,6 +234,15 @@ void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
|
||||||
ops->metadata_updated(stream);
|
ops->metadata_updated(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SINK ASEs can autonomously go into the streaming state if
|
||||||
|
* the CIS is connected
|
||||||
|
*/
|
||||||
|
if (ep->dir == BT_AUDIO_DIR_SINK &&
|
||||||
|
ep->iso != NULL &&
|
||||||
|
ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
|
||||||
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BT_AUDIO_EP_STATE_STREAMING:
|
case BT_AUDIO_EP_STATE_STREAMING:
|
||||||
switch (old_state) {
|
switch (old_state) {
|
||||||
|
@ -293,7 +314,27 @@ void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
break; /* no-op*/
|
if (ep->iso == NULL ||
|
||||||
|
ep->iso->chan.state == BT_ISO_STATE_DISCONNECTED) {
|
||||||
|
if (ep->iso != NULL) {
|
||||||
|
bt_audio_iso_unbind_ep(ep->iso, ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_audio_stream_detach(stream);
|
||||||
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE);
|
||||||
|
} else {
|
||||||
|
/* Either the client or the server may disconnect the
|
||||||
|
* CISes when entering the releasing state.
|
||||||
|
*/
|
||||||
|
const int err = bt_audio_stream_disconnect(stream);
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
LOG_ERR("Failed to disconnect stream %p: %d",
|
||||||
|
stream, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERR("Invalid state: %u", state);
|
LOG_ERR("Invalid state: %u", state);
|
||||||
break;
|
break;
|
||||||
|
@ -553,6 +594,11 @@ static void ascs_ep_iso_connected(struct bt_audio_ep *ep)
|
||||||
|
|
||||||
if (ep->dir == BT_AUDIO_DIR_SOURCE && !ep->receiver_ready) {
|
if (ep->dir == BT_AUDIO_DIR_SOURCE && !ep->receiver_ready) {
|
||||||
return;
|
return;
|
||||||
|
} else if (ep->dir == BT_AUDIO_DIR_SINK) {
|
||||||
|
/* SINK ASEs can autonomously go into the streaming state if
|
||||||
|
* the CIS is connected
|
||||||
|
*/
|
||||||
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = ep->stream;
|
stream = ep->stream;
|
||||||
|
@ -621,9 +667,8 @@ static void ascs_ep_iso_disconnected(struct bt_audio_ep *ep, uint8_t reason)
|
||||||
|
|
||||||
if (ep->status.state == BT_AUDIO_EP_STATE_RELEASING) {
|
if (ep->status.state == BT_AUDIO_EP_STATE_RELEASING) {
|
||||||
bt_audio_iso_unbind_ep(ep->iso, ep);
|
bt_audio_iso_unbind_ep(ep->iso, ep);
|
||||||
|
bt_audio_stream_detach(stream);
|
||||||
/* Trigger a call to ase_process to handle the cleanup */
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE);
|
||||||
k_work_submit(&ep->work);
|
|
||||||
} else {
|
} else {
|
||||||
/* The ASE state machine goes into different states from this operation
|
/* The ASE state machine goes into different states from this operation
|
||||||
* based on whether it is a source or a sink ASE.
|
* based on whether it is a source or a sink ASE.
|
||||||
|
@ -981,60 +1026,6 @@ static struct bt_ascs *ascs_get(struct bt_conn *conn)
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
NET_BUF_SIMPLE_DEFINE_STATIC(ase_buf, CONFIG_BT_L2CAP_TX_MTU);
|
|
||||||
|
|
||||||
static void ase_process(struct k_work *work)
|
|
||||||
{
|
|
||||||
struct bt_audio_ep *ep = CONTAINER_OF(work, struct bt_audio_ep, work);
|
|
||||||
struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep);
|
|
||||||
struct bt_audio_stream *stream = ep->stream;
|
|
||||||
const uint8_t ep_state = ep->status.state;
|
|
||||||
struct bt_conn *conn = ase->ascs->conn;
|
|
||||||
|
|
||||||
LOG_DBG("ase %p, ep %p, ep.stream %p", ase, ep, stream);
|
|
||||||
|
|
||||||
if (conn != NULL && conn->state == BT_CONN_CONNECTED) {
|
|
||||||
ascs_ep_get_status(ep, &ase_buf);
|
|
||||||
|
|
||||||
bt_gatt_notify(conn, ase->attr,
|
|
||||||
ase_buf.data, ase_buf.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stream shall be NULL in the idle state, and non-NULL otherwise */
|
|
||||||
__ASSERT(ep_state == BT_AUDIO_EP_STATE_IDLE ?
|
|
||||||
stream == NULL : stream != NULL,
|
|
||||||
"stream is NULL");
|
|
||||||
|
|
||||||
if (ep_state == BT_AUDIO_EP_STATE_RELEASING) {
|
|
||||||
if (ep->iso == NULL ||
|
|
||||||
ep->iso->chan.state == BT_ISO_STATE_DISCONNECTED) {
|
|
||||||
if (ep->iso != NULL) {
|
|
||||||
bt_audio_iso_unbind_ep(ep->iso, ep);
|
|
||||||
}
|
|
||||||
bt_audio_stream_detach(stream);
|
|
||||||
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_IDLE);
|
|
||||||
} else {
|
|
||||||
/* Either the client or the server may disconnect the
|
|
||||||
* CISes when entering the releasing state.
|
|
||||||
*/
|
|
||||||
const int err = bt_audio_stream_disconnect(stream);
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
LOG_ERR("Failed to disconnect stream %p: %d", stream, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ep_state == BT_AUDIO_EP_STATE_ENABLING) {
|
|
||||||
/* SINK ASEs can autonomously go into the streaming state if
|
|
||||||
* the CIS is connected
|
|
||||||
*/
|
|
||||||
if (ep->dir == BT_AUDIO_DIR_SINK &&
|
|
||||||
ep->iso != NULL &&
|
|
||||||
ep->iso->chan.state == BT_ISO_STATE_CONNECTED) {
|
|
||||||
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_STREAMING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t ase_attr_cb(const struct bt_gatt_attr *attr, uint16_t handle,
|
static uint8_t ase_attr_cb(const struct bt_gatt_attr *attr, uint16_t handle,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
|
@ -1056,8 +1047,6 @@ void ascs_ep_init(struct bt_audio_ep *ep, uint8_t id)
|
||||||
(void)memset(ep, 0, sizeof(*ep));
|
(void)memset(ep, 0, sizeof(*ep));
|
||||||
ep->status.id = id;
|
ep->status.id = id;
|
||||||
ep->dir = ASE_DIR(id);
|
ep->dir = ASE_DIR(id);
|
||||||
|
|
||||||
k_work_init(&ep->work, ase_process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ase_init(struct bt_ascs_ase *ase, uint8_t id)
|
static void ase_init(struct bt_ascs_ase *ase, uint8_t id)
|
||||||
|
@ -1527,6 +1516,19 @@ static int ase_stream_qos(struct bt_audio_stream *stream,
|
||||||
|
|
||||||
stream->qos = qos;
|
stream->qos = qos;
|
||||||
|
|
||||||
|
/* We setup the data path here, as this is the earliest where
|
||||||
|
* we have the ISO <-> EP coupling completed (due to setting
|
||||||
|
* the CIS ID in the QoS procedure).
|
||||||
|
*/
|
||||||
|
if (ep->dir == BT_AUDIO_DIR_SINK) {
|
||||||
|
bt_audio_codec_to_iso_path(&ep->iso->rx.path, stream->codec);
|
||||||
|
} else {
|
||||||
|
bt_audio_codec_to_iso_path(&ep->iso->tx.path, stream->codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
ep->cig_id = cig_id;
|
||||||
|
ep->cis_id = cis_id;
|
||||||
|
|
||||||
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
ascs_ep_set_state(ep, BT_AUDIO_EP_STATE_QOS_CONFIGURED);
|
||||||
|
|
||||||
bt_audio_stream_iso_listen(stream);
|
bt_audio_stream_iso_listen(stream);
|
||||||
|
@ -1589,23 +1591,8 @@ static void ase_qos(struct bt_ascs_ase *ase, const struct bt_ascs_qos *qos)
|
||||||
ascs_cp_rsp_add_errno(ASE_ID(ase), BT_ASCS_QOS_OP,
|
ascs_cp_rsp_add_errno(ASE_ID(ase), BT_ASCS_QOS_OP,
|
||||||
err, reason);
|
err, reason);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
/* We setup the data path here, as this is the earliest where
|
|
||||||
* we have the ISO <-> EP coupling completed (due to setting
|
|
||||||
* the CIS ID in the QoS procedure).
|
|
||||||
*/
|
|
||||||
if (ep->dir == BT_AUDIO_DIR_SINK) {
|
|
||||||
bt_audio_codec_to_iso_path(&ep->iso->rx.path,
|
|
||||||
stream->codec);
|
|
||||||
} else {
|
|
||||||
bt_audio_codec_to_iso_path(&ep->iso->tx.path,
|
|
||||||
stream->codec);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ep->cig_id = cig_id;
|
|
||||||
ep->cis_id = cis_id;
|
|
||||||
|
|
||||||
ascs_cp_rsp_success(ASE_ID(ase), BT_ASCS_QOS_OP);
|
ascs_cp_rsp_success(ASE_ID(ase), BT_ASCS_QOS_OP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,9 +50,6 @@ struct bt_audio_ep {
|
||||||
struct bt_audio_unicast_group *unicast_group;
|
struct bt_audio_unicast_group *unicast_group;
|
||||||
struct bt_audio_broadcast_source *broadcast_source;
|
struct bt_audio_broadcast_source *broadcast_source;
|
||||||
struct bt_audio_broadcast_sink *broadcast_sink;
|
struct bt_audio_broadcast_sink *broadcast_sink;
|
||||||
|
|
||||||
/* ASCS ASE Control Point Work */
|
|
||||||
struct k_work work;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_audio_unicast_group {
|
struct bt_audio_unicast_group {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue