Bluetooth: ISO: Fix cleanup connection

This fixes bt_iso_cleanup when there are still channels bound to the
ACL connection.

On top of it introduce bt_iso_chan_unbind which can be used to unbind
channels and thus release the reference to the ACL connection if that
has not been disconnected in which case the channels are unbind
automatically.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2021-01-29 15:57:47 -08:00 committed by Anas Nashif
commit af0fc963d8
2 changed files with 76 additions and 29 deletions

View file

@ -305,6 +305,20 @@ int bt_iso_server_register(struct bt_iso_server *server);
int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns,
struct bt_iso_chan **chans);
/** @brief Unbind ISO channel
*
* Unbind ISO channel from ACL connection, channel must be in BT_ISO_BOUND
* state.
*
* Note: Channels which the ACL connection has been disconnected are unbind
* automatically.
*
* @param chan Channel object.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_chan_unbind(struct bt_iso_chan *chan);
/** @brief Connect ISO channels
*
* Connect ISO channels, once the connection is completed each channel

View file

@ -257,7 +257,23 @@ void bt_iso_cleanup(struct bt_conn *conn)
{
int i;
if (conn->iso.acl) { /* CIS */
__ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO);
BT_DBG("%p", conn);
/* Check if ISO connection is in fact a BIS */
if (!conn->iso.acl) {
goto done;
}
/* If ACL is still connected there are channels to serve that means the
* connection is still in use.
*/
if (conn->iso.acl->state == BT_CONN_CONNECTED &&
!sys_slist_is_empty(&conn->channels)) {
goto done;
}
bt_conn_unref(conn->iso.acl);
conn->iso.acl = NULL;
@ -276,7 +292,8 @@ void bt_iso_cleanup(struct bt_conn *conn)
if (i == CONFIG_BT_ISO_MAX_CHAN) {
hci_le_remove_cig(conn->iso.cig_id);
}
}
done:
bt_conn_unref(conn);
}
@ -734,23 +751,25 @@ static void bt_iso_remove_data_path(struct bt_conn *conn)
hci_le_remove_iso_data_path(conn, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR);
}
static void bt_iso_chan_del(struct bt_iso_chan *chan)
static void bt_iso_chan_disconnected(struct bt_iso_chan *chan)
{
BT_DBG("%p", chan);
if (!chan->conn) {
goto done;
bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED);
return;
}
bt_iso_chan_set_state(chan, BT_ISO_BOUND);
/* Unbind if acting as slave */
if (chan->conn->role == BT_HCI_ROLE_SLAVE) {
bt_iso_chan_unbind(chan);
}
if (chan->ops->disconnected) {
chan->ops->disconnected(chan);
}
bt_conn_unref(chan->conn);
chan->conn = NULL;
done:
bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED);
}
void bt_iso_disconnected(struct bt_conn *conn)
@ -768,7 +787,7 @@ void bt_iso_disconnected(struct bt_conn *conn)
bt_iso_remove_data_path(conn);
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) {
bt_iso_chan_del(chan);
bt_iso_chan_disconnected(chan);
}
}
@ -832,12 +851,8 @@ void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state,
/* check transitions validness */
switch (state) {
case BT_ISO_DISCONNECTED:
/* regardless of old state always allows this state */
break;
case BT_ISO_BOUND:
if (chan->state != BT_ISO_DISCONNECTED) {
BT_WARN("%s()%d: invalid transition", func, line);
}
/* regardless of old state always allows these states */
break;
case BT_ISO_CONNECT:
if (chan->state != BT_ISO_BOUND) {
@ -916,6 +931,24 @@ int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns,
return 0;
}
int bt_iso_chan_unbind(struct bt_iso_chan *chan)
{
__ASSERT_NO_MSG(chan);
if (!chan->conn || chan->state != BT_ISO_BOUND) {
return -EINVAL;
}
bt_iso_chan_remove(chan->conn, chan);
bt_conn_unref(chan->conn);
chan->conn = NULL;
bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED);
return 0;
}
int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans)
{
struct bt_conn *conns[CONFIG_BT_ISO_MAX_CHAN];
@ -954,7 +987,7 @@ int bt_iso_chan_disconnect(struct bt_iso_chan *chan)
if (chan->state == BT_ISO_BOUND) {
bt_iso_chan_remove(chan->conn, chan);
bt_iso_chan_del(chan);
bt_iso_chan_disconnected(chan);
return 0;
}