Bluetooth: hci_conn: Add support for linking multiple hcon

Since it is required for some configurations to have multiple CIS with
the same peer which is now covered by iso-tester in the following test
cases:

    ISO AC 6(i) - Success
    ISO AC 7(i) - Success
    ISO AC 8(i) - Success
    ISO AC 9(i) - Success
    ISO AC 11(i) - Success
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent e4eea890
...@@ -770,7 +770,10 @@ struct hci_conn { ...@@ -770,7 +770,10 @@ struct hci_conn {
void *iso_data; void *iso_data;
struct amp_mgr *amp_mgr; struct amp_mgr *amp_mgr;
struct hci_conn *link; struct list_head link_list;
struct hci_conn *parent;
struct hci_link *link;
struct bt_codec codec; struct bt_codec codec;
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
...@@ -780,6 +783,11 @@ struct hci_conn { ...@@ -780,6 +783,11 @@ struct hci_conn {
void (*cleanup)(struct hci_conn *conn); void (*cleanup)(struct hci_conn *conn);
}; };
struct hci_link {
struct list_head list;
struct hci_conn *conn;
};
struct hci_chan { struct hci_chan {
struct list_head list; struct list_head list;
__u16 handle; __u16 handle;
...@@ -1383,12 +1391,14 @@ static inline void hci_conn_put(struct hci_conn *conn) ...@@ -1383,12 +1391,14 @@ static inline void hci_conn_put(struct hci_conn *conn)
put_device(&conn->dev); put_device(&conn->dev);
} }
static inline void hci_conn_hold(struct hci_conn *conn) static inline struct hci_conn *hci_conn_hold(struct hci_conn *conn)
{ {
BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt)); BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt));
atomic_inc(&conn->refcnt); atomic_inc(&conn->refcnt);
cancel_delayed_work(&conn->disc_work); cancel_delayed_work(&conn->disc_work);
return conn;
} }
static inline void hci_conn_drop(struct hci_conn *conn) static inline void hci_conn_drop(struct hci_conn *conn)
......
...@@ -330,8 +330,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle) ...@@ -330,8 +330,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle)
static bool find_next_esco_param(struct hci_conn *conn, static bool find_next_esco_param(struct hci_conn *conn,
const struct sco_param *esco_param, int size) const struct sco_param *esco_param, int size)
{ {
if (!conn->parent)
return false;
for (; conn->attempt <= size; conn->attempt++) { for (; conn->attempt <= size; conn->attempt++) {
if (lmp_esco_2m_capable(conn->link) || if (lmp_esco_2m_capable(conn->parent) ||
(esco_param[conn->attempt - 1].pkt_type & ESCO_2EV3)) (esco_param[conn->attempt - 1].pkt_type & ESCO_2EV3))
break; break;
BT_DBG("hcon %p skipped attempt %d, eSCO 2M not supported", BT_DBG("hcon %p skipped attempt %d, eSCO 2M not supported",
...@@ -461,7 +464,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) ...@@ -461,7 +464,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
break; break;
case BT_CODEC_CVSD: case BT_CODEC_CVSD:
if (lmp_esco_capable(conn->link)) { if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd, if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd))) ARRAY_SIZE(esco_param_cvsd)))
return -EINVAL; return -EINVAL;
...@@ -531,7 +534,7 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) ...@@ -531,7 +534,7 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
param = &esco_param_msbc[conn->attempt - 1]; param = &esco_param_msbc[conn->attempt - 1];
break; break;
case SCO_AIRMODE_CVSD: case SCO_AIRMODE_CVSD:
if (lmp_esco_capable(conn->link)) { if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd, if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd))) ARRAY_SIZE(esco_param_cvsd)))
return false; return false;
...@@ -637,21 +640,22 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, ...@@ -637,21 +640,22 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
/* Device _must_ be locked */ /* Device _must_ be locked */
void hci_sco_setup(struct hci_conn *conn, __u8 status) void hci_sco_setup(struct hci_conn *conn, __u8 status)
{ {
struct hci_conn *sco = conn->link; struct hci_link *link;
if (!sco) link = list_first_entry_or_null(&conn->link_list, struct hci_link, list);
if (!link || !link->conn)
return; return;
BT_DBG("hcon %p", conn); BT_DBG("hcon %p", conn);
if (!status) { if (!status) {
if (lmp_esco_capable(conn->hdev)) if (lmp_esco_capable(conn->hdev))
hci_setup_sync(sco, conn->handle); hci_setup_sync(link->conn, conn->handle);
else else
hci_add_sco(sco, conn->handle); hci_add_sco(link->conn, conn->handle);
} else { } else {
hci_connect_cfm(sco, status); hci_connect_cfm(link->conn, status);
hci_conn_del(sco); hci_conn_del(link->conn);
} }
} }
...@@ -1042,6 +1046,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, ...@@ -1042,6 +1046,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
skb_queue_head_init(&conn->data_q); skb_queue_head_init(&conn->data_q);
INIT_LIST_HEAD(&conn->chan_list); INIT_LIST_HEAD(&conn->chan_list);
INIT_LIST_HEAD(&conn->link_list);
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept); INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
...@@ -1069,15 +1074,39 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, ...@@ -1069,15 +1074,39 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
return conn; return conn;
} }
static bool hci_conn_unlink(struct hci_conn *conn) static void hci_conn_unlink(struct hci_conn *conn)
{ {
struct hci_dev *hdev = conn->hdev;
bt_dev_dbg(hdev, "hcon %p", conn);
if (!conn->parent) {
struct hci_link *link, *t;
list_for_each_entry_safe(link, t, &conn->link_list, list)
hci_conn_unlink(link->conn);
return;
}
if (!conn->link) if (!conn->link)
return false; return;
hci_conn_put(conn->parent);
conn->parent = NULL;
list_del_rcu(&conn->link->list);
synchronize_rcu();
conn->link->link = NULL; kfree(conn->link);
conn->link = NULL; conn->link = NULL;
return true; /* Due to race, SCO connection might be not established
* yet at this point. Delete it now, otherwise it is
* possible for it to be stuck and can't be deleted.
*/
if (conn->handle == HCI_CONN_HANDLE_UNSET)
hci_conn_del(conn);
} }
int hci_conn_del(struct hci_conn *conn) int hci_conn_del(struct hci_conn *conn)
...@@ -1091,18 +1120,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1091,18 +1120,7 @@ int hci_conn_del(struct hci_conn *conn)
cancel_delayed_work_sync(&conn->idle_work); cancel_delayed_work_sync(&conn->idle_work);
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
struct hci_conn *link = conn->link;
if (link) {
hci_conn_unlink(conn); hci_conn_unlink(conn);
/* Due to race, SCO connection might be not established
* yet at this point. Delete it now, otherwise it is
* possible for it to be stuck and can't be deleted.
*/
if (link->handle == HCI_CONN_HANDLE_UNSET)
hci_conn_del(link);
}
/* Unacked frames */ /* Unacked frames */
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) { } else if (conn->type == LE_LINK) {
...@@ -1113,7 +1131,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1113,7 +1131,7 @@ int hci_conn_del(struct hci_conn *conn)
else else
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else { } else {
struct hci_conn *acl = conn->link; struct hci_conn *acl = conn->parent;
if (acl) { if (acl) {
hci_conn_unlink(conn); hci_conn_unlink(conn);
...@@ -1600,11 +1618,40 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -1600,11 +1618,40 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
return acl; return acl;
} }
static struct hci_link *hci_conn_link(struct hci_conn *parent,
struct hci_conn *conn)
{
struct hci_dev *hdev = parent->hdev;
struct hci_link *link;
bt_dev_dbg(hdev, "parent %p hcon %p", parent, conn);
if (conn->link)
return conn->link;
if (conn->parent)
return NULL;
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
link->conn = hci_conn_hold(conn);
conn->link = link;
conn->parent = hci_conn_get(parent);
/* Use list_add_tail_rcu append to the list */
list_add_tail_rcu(&link->list, &parent->link_list);
return link;
}
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting, struct bt_codec *codec) __u16 setting, struct bt_codec *codec)
{ {
struct hci_conn *acl; struct hci_conn *acl;
struct hci_conn *sco; struct hci_conn *sco;
struct hci_link *link;
acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING, acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING,
CONN_REASON_SCO_CONNECT); CONN_REASON_SCO_CONNECT);
...@@ -1620,10 +1667,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, ...@@ -1620,10 +1667,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
} }
} }
acl->link = sco; link = hci_conn_link(acl, sco);
sco->link = acl; if (!link) {
hci_conn_drop(acl);
hci_conn_hold(sco); hci_conn_drop(sco);
return NULL;
}
sco->setting = setting; sco->setting = setting;
sco->codec = *codec; sco->codec = *codec;
...@@ -1890,7 +1939,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) ...@@ -1890,7 +1939,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
u8 cig; u8 cig;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle); cmd.cis[0].acl_handle = cpu_to_le16(conn->parent->handle);
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
cmd.cp.num_cis++; cmd.cp.num_cis++;
cig = conn->iso_qos.ucast.cig; cig = conn->iso_qos.ucast.cig;
...@@ -1903,11 +1952,12 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) ...@@ -1903,11 +1952,12 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
if (conn == data || conn->type != ISO_LINK || if (conn == data || conn->type != ISO_LINK ||
conn->state == BT_CONNECTED || conn->iso_qos.ucast.cig != cig) conn->state == BT_CONNECTED ||
conn->iso_qos.ucast.cig != cig)
continue; continue;
/* Check if all CIS(s) belonging to a CIG are ready */ /* Check if all CIS(s) belonging to a CIG are ready */
if (!conn->link || conn->link->state != BT_CONNECTED || if (!conn->parent || conn->parent->state != BT_CONNECTED ||
conn->state != BT_CONNECT) { conn->state != BT_CONNECT) {
cmd.cp.num_cis = 0; cmd.cp.num_cis = 0;
break; break;
...@@ -1924,7 +1974,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) ...@@ -1924,7 +1974,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
* command have been generated, the Controller shall return the * command have been generated, the Controller shall return the
* error code Command Disallowed (0x0C). * error code Command Disallowed (0x0C).
*/ */
cis->acl_handle = cpu_to_le16(conn->link->handle); cis->acl_handle = cpu_to_le16(conn->parent->handle);
cis->cis_handle = cpu_to_le16(conn->handle); cis->cis_handle = cpu_to_le16(conn->handle);
cmd.cp.num_cis++; cmd.cp.num_cis++;
} }
...@@ -1943,15 +1993,33 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data) ...@@ -1943,15 +1993,33 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
int hci_le_create_cis(struct hci_conn *conn) int hci_le_create_cis(struct hci_conn *conn)
{ {
struct hci_conn *cis; struct hci_conn *cis;
struct hci_link *link, *t;
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
int err; int err;
bt_dev_dbg(hdev, "hcon %p", conn);
switch (conn->type) { switch (conn->type) {
case LE_LINK: case LE_LINK:
if (!conn->link || conn->state != BT_CONNECTED) if (conn->state != BT_CONNECTED || list_empty(&conn->link_list))
return -EINVAL; return -EINVAL;
cis = conn->link;
break; cis = NULL;
/* hci_conn_link uses list_add_tail_rcu so the list is in
* the same order as the connections are requested.
*/
list_for_each_entry_safe(link, t, &conn->link_list, list) {
if (link->conn->state == BT_BOUND) {
err = hci_le_create_cis(link->conn);
if (err)
return err;
cis = link->conn;
}
}
return cis ? 0 : -EINVAL;
case ISO_LINK: case ISO_LINK:
cis = conn; cis = conn;
break; break;
...@@ -2172,6 +2240,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -2172,6 +2240,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
{ {
struct hci_conn *le; struct hci_conn *le;
struct hci_conn *cis; struct hci_conn *cis;
struct hci_link *link;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
le = hci_connect_le(hdev, dst, dst_type, false, le = hci_connect_le(hdev, dst, dst_type, false,
...@@ -2197,16 +2266,18 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -2197,16 +2266,18 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
return cis; return cis;
} }
le->link = cis; link = hci_conn_link(le, cis);
cis->link = le; if (!link) {
hci_conn_drop(le);
hci_conn_hold(cis); hci_conn_drop(cis);
return NULL;
}
/* If LE is already connected and CIS handle is already set proceed to /* If LE is already connected and CIS handle is already set proceed to
* Create CIS immediately. * Create CIS immediately.
*/ */
if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET) if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET)
hci_le_create_cis(le); hci_le_create_cis(cis);
return cis; return cis;
} }
......
...@@ -2345,7 +2345,8 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) ...@@ -2345,7 +2345,8 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
{ {
struct hci_cp_add_sco *cp; struct hci_cp_add_sco *cp;
struct hci_conn *acl, *sco; struct hci_conn *acl;
struct hci_link *link;
__u16 handle; __u16 handle;
bt_dev_dbg(hdev, "status 0x%2.2x", status); bt_dev_dbg(hdev, "status 0x%2.2x", status);
...@@ -2365,12 +2366,13 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) ...@@ -2365,12 +2366,13 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
acl = hci_conn_hash_lookup_handle(hdev, handle); acl = hci_conn_hash_lookup_handle(hdev, handle);
if (acl) { if (acl) {
sco = acl->link; link = list_first_entry_or_null(&acl->link_list,
if (sco) { struct hci_link, list);
sco->state = BT_CLOSED; if (link && link->conn) {
link->conn->state = BT_CLOSED;
hci_connect_cfm(sco, status); hci_connect_cfm(link->conn, status);
hci_conn_del(sco); hci_conn_del(link->conn);
} }
} }
...@@ -2637,74 +2639,61 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) ...@@ -2637,74 +2639,61 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) static void hci_setup_sync_conn_status(struct hci_dev *hdev, __u16 handle,
__u8 status)
{ {
struct hci_cp_setup_sync_conn *cp; struct hci_conn *acl;
struct hci_conn *acl, *sco; struct hci_link *link;
__u16 handle;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
if (!status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN);
if (!cp)
return;
handle = __le16_to_cpu(cp->handle); bt_dev_dbg(hdev, "handle 0x%4.4x status 0x%2.2x", handle, status);
bt_dev_dbg(hdev, "handle 0x%4.4x", handle);
hci_dev_lock(hdev); hci_dev_lock(hdev);
acl = hci_conn_hash_lookup_handle(hdev, handle); acl = hci_conn_hash_lookup_handle(hdev, handle);
if (acl) { if (acl) {
sco = acl->link; link = list_first_entry_or_null(&acl->link_list,
if (sco) { struct hci_link, list);
sco->state = BT_CLOSED; if (link && link->conn) {
link->conn->state = BT_CLOSED;
hci_connect_cfm(sco, status); hci_connect_cfm(link->conn, status);
hci_conn_del(sco); hci_conn_del(link->conn);
} }
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status) static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
{ {
struct hci_cp_enhanced_setup_sync_conn *cp; struct hci_cp_setup_sync_conn *cp;
struct hci_conn *acl, *sco;
__u16 handle;
bt_dev_dbg(hdev, "status 0x%2.2x", status); bt_dev_dbg(hdev, "status 0x%2.2x", status);
if (!status) if (!status)
return; return;
cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN); cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN);
if (!cp) if (!cp)
return; return;
handle = __le16_to_cpu(cp->handle); hci_setup_sync_conn_status(hdev, __le16_to_cpu(cp->handle), status);
}
bt_dev_dbg(hdev, "handle 0x%4.4x", handle); static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_enhanced_setup_sync_conn *cp;
hci_dev_lock(hdev); bt_dev_dbg(hdev, "status 0x%2.2x", status);
acl = hci_conn_hash_lookup_handle(hdev, handle); if (!status)
if (acl) { return;
sco = acl->link;
if (sco) {
sco->state = BT_CLOSED;
hci_connect_cfm(sco, status); cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN);
hci_conn_del(sco); if (!cp)
} return;
}
hci_dev_unlock(hdev); hci_setup_sync_conn_status(hdev, __le16_to_cpu(cp->handle), status);
} }
static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
...@@ -3834,19 +3823,20 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data, ...@@ -3834,19 +3823,20 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
if (conn->type != ISO_LINK || conn->iso_qos.ucast.cig != rp->cig_id || if (conn->type != ISO_LINK ||
conn->iso_qos.ucast.cig != rp->cig_id ||
conn->state == BT_CONNECTED) conn->state == BT_CONNECTED)
continue; continue;
conn->handle = __le16_to_cpu(rp->handle[i++]); conn->handle = __le16_to_cpu(rp->handle[i++]);
bt_dev_dbg(hdev, "%p handle 0x%4.4x link %p", conn, bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p", conn,
conn->handle, conn->link); conn->handle, conn->parent);
/* Create CIS if LE is already connected */ /* Create CIS if LE is already connected */
if (conn->link && conn->link->state == BT_CONNECTED) { if (conn->parent && conn->parent->state == BT_CONNECTED) {
rcu_read_unlock(); rcu_read_unlock();
hci_le_create_cis(conn->link); hci_le_create_cis(conn);
rcu_read_lock(); rcu_read_lock();
} }
...@@ -5031,7 +5021,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data, ...@@ -5031,7 +5021,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
if (conn->out) { if (conn->out) {
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
(hdev->esco_type & EDR_ESCO_MASK); (hdev->esco_type & EDR_ESCO_MASK);
if (hci_setup_sync(conn, conn->link->handle)) if (hci_setup_sync(conn, conn->parent->handle))
goto unlock; goto unlock;
} }
fallthrough; fallthrough;
......
...@@ -1657,8 +1657,12 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) ...@@ -1657,8 +1657,12 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
/* Check if LE link has failed */ /* Check if LE link has failed */
if (status) { if (status) {
if (hcon->link) struct hci_link *link, *t;
iso_conn_del(hcon->link, bt_to_errno(status));
list_for_each_entry_safe(link, t, &hcon->link_list,
list)
iso_conn_del(link->conn, bt_to_errno(status));
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment