diff options
Diffstat (limited to 'net/bluetooth/hci_conn.c')
-rw-r--r-- | net/bluetooth/hci_conn.c | 467 |
1 files changed, 257 insertions, 210 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 17b946f9ba31..f75ef12f18f7 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. + Copyright 2023 NXP Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> @@ -68,7 +69,7 @@ static const struct sco_param esco_param_msbc[] = { }; /* This function requires the caller holds hdev->lock */ -static void hci_connect_le_scan_cleanup(struct hci_conn *conn) +static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status) { struct hci_conn_params *params; struct hci_dev *hdev = conn->hdev; @@ -88,9 +89,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn) params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr, bdaddr_type); - if (!params || !params->explicit_connect) + if (!params) return; + if (params->conn) { + hci_conn_drop(params->conn); + hci_conn_put(params->conn); + params->conn = NULL; + } + + if (!params->explicit_connect) + return; + + /* If the status indicates successful cancellation of + * the attempt (i.e. Unknown Connection Id) there's no point of + * notifying failure since we'll go back to keep trying to + * connect. The only exception is explicit connect requests + * where a timeout + cancel does indicate an actual failure. + */ + if (status && status != HCI_ERROR_UNKNOWN_CONN_ID) + mgmt_connect_failed(hdev, &conn->dst, conn->type, + conn->dst_type, status); + /* The connection attempt was doing scan for new RPA, and is * in scan phase. If params are not associated with any other * autoconnect action, remove them completely. If they are, just unmark @@ -178,7 +198,7 @@ static void le_scan_cleanup(struct work_struct *work) rcu_read_unlock(); if (c == conn) { - hci_connect_le_scan_cleanup(conn); + hci_connect_le_scan_cleanup(conn, 0x00); hci_conn_cleanup(conn); } @@ -310,8 +330,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle) static bool find_next_esco_param(struct hci_conn *conn, const struct sco_param *esco_param, int size) { + if (!conn->parent) + return false; + 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)) break; BT_DBG("hcon %p skipped attempt %d, eSCO 2M not supported", @@ -441,7 +464,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) break; 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, ARRAY_SIZE(esco_param_cvsd))) return -EINVAL; @@ -511,7 +534,7 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) param = &esco_param_msbc[conn->attempt - 1]; break; 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, ARRAY_SIZE(esco_param_cvsd))) return false; @@ -617,21 +640,22 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, /* Device _must_ be locked */ 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; BT_DBG("hcon %p", conn); if (!status) { if (lmp_esco_capable(conn->hdev)) - hci_setup_sync(sco, conn->handle); + hci_setup_sync(link->conn, conn->handle); else - hci_add_sco(sco, conn->handle); + hci_add_sco(link->conn, conn->handle); } else { - hci_connect_cfm(sco, status); - hci_conn_del(sco); + hci_connect_cfm(link->conn, status); + hci_conn_del(link->conn); } } @@ -776,8 +800,8 @@ static void bis_list(struct hci_conn *conn, void *data) if (bacmp(&conn->dst, BDADDR_ANY)) return; - if (d->big != conn->iso_qos.big || d->bis == BT_ISO_QOS_BIS_UNSET || - d->bis != conn->iso_qos.bis) + if (d->big != conn->iso_qos.bcast.big || d->bis == BT_ISO_QOS_BIS_UNSET || + d->bis != conn->iso_qos.bcast.bis) return; d->count++; @@ -897,10 +921,10 @@ static void bis_cleanup(struct hci_conn *conn) if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags)) return; - hci_le_terminate_big(hdev, conn->iso_qos.big, - conn->iso_qos.bis); + hci_le_terminate_big(hdev, conn->iso_qos.bcast.big, + conn->iso_qos.bcast.bis); } else { - hci_le_big_terminate(hdev, conn->iso_qos.big, + hci_le_big_terminate(hdev, conn->iso_qos.bcast.big, conn->sync_handle); } } @@ -940,7 +964,7 @@ static void cis_cleanup(struct hci_conn *conn) struct iso_list_data d; memset(&d, 0, sizeof(d)); - d.cig = conn->iso_qos.cig; + d.cig = conn->iso_qos.ucast.cig; /* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it. @@ -949,7 +973,7 @@ static void cis_cleanup(struct hci_conn *conn) if (d.count) return; - hci_le_remove_cig(hdev, conn->iso_qos.cig); + hci_le_remove_cig(hdev, conn->iso_qos.ucast.cig); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, @@ -1022,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); 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->auto_accept_work, hci_conn_auto_accept); @@ -1049,28 +1074,68 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, return conn; } -int hci_conn_del(struct hci_conn *conn) +static void hci_conn_unlink(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); + bt_dev_dbg(hdev, "hcon %p", conn); - cancel_delayed_work_sync(&conn->disc_work); - cancel_delayed_work_sync(&conn->auto_accept_work); - cancel_delayed_work_sync(&conn->idle_work); + if (!conn->parent) { + struct hci_link *link, *t; + + list_for_each_entry_safe(link, t, &conn->link_list, list) { + struct hci_conn *child = link->conn; + + hci_conn_unlink(child); + + /* If hdev is down it means + * hci_dev_close_sync/hci_conn_hash_flush is in progress + * and links don't need to be cleanup as all connections + * would be cleanup. + */ + if (!test_bit(HCI_UP, &hdev->flags)) + continue; - if (conn->type == ACL_LINK) { - struct hci_conn *sco = conn->link; - if (sco) { - sco->link = NULL; /* 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 (sco->handle == HCI_CONN_HANDLE_UNSET) - hci_conn_del(sco); + if ((child->type == SCO_LINK || + child->type == ESCO_LINK) && + child->handle == HCI_CONN_HANDLE_UNSET) + hci_conn_del(child); } + return; + } + + if (!conn->link) + return; + + list_del_rcu(&conn->link->list); + synchronize_rcu(); + + hci_conn_drop(conn->parent); + hci_conn_put(conn->parent); + conn->parent = NULL; + + kfree(conn->link); + conn->link = NULL; +} + +void hci_conn_del(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); + + hci_conn_unlink(conn); + + cancel_delayed_work_sync(&conn->disc_work); + cancel_delayed_work_sync(&conn->auto_accept_work); + cancel_delayed_work_sync(&conn->idle_work); + + if (conn->type == ACL_LINK) { /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { @@ -1081,13 +1146,6 @@ int hci_conn_del(struct hci_conn *conn) else hdev->acl_cnt += conn->sent; } else { - struct hci_conn *acl = conn->link; - - if (acl) { - acl->link = NULL; - hci_conn_drop(acl); - } - /* Unacked ISO frames */ if (conn->type == ISO_LINK) { if (hdev->iso_pkts) @@ -1110,8 +1168,6 @@ int hci_conn_del(struct hci_conn *conn) * rest of hci_conn_del. */ hci_conn_cleanup(conn); - - return 0; } struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type) @@ -1179,31 +1235,8 @@ EXPORT_SYMBOL(hci_get_route); static void hci_le_conn_failed(struct hci_conn *conn, u8 status) { struct hci_dev *hdev = conn->hdev; - struct hci_conn_params *params; - - params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, - conn->dst_type); - if (params && params->conn) { - hci_conn_drop(params->conn); - hci_conn_put(params->conn); - params->conn = NULL; - } - /* If the status indicates successful cancellation of - * the attempt (i.e. Unknown Connection Id) there's no point of - * notifying failure since we'll go back to keep trying to - * connect. The only exception is explicit connect requests - * where a timeout + cancel does indicate an actual failure. - */ - if (status != HCI_ERROR_UNKNOWN_CONN_ID || - (params && params->explicit_connect)) - mgmt_connect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, status); - - /* Since we may have temporarily stopped the background scanning in - * favor of connection establishment, we should restart it. - */ - hci_update_passive_scan(hdev); + hci_connect_le_scan_cleanup(conn, status); /* Enable advertising in case this was a failed connection * attempt as a peripheral. @@ -1237,15 +1270,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) { struct hci_conn *conn = data; + bt_dev_dbg(hdev, "err %d", err); + hci_dev_lock(hdev); if (!err) { - hci_connect_le_scan_cleanup(conn); + hci_connect_le_scan_cleanup(conn, 0x00); goto done; } - bt_dev_err(hdev, "request failed to create LE connection: err %d", err); - /* Check if connection is still pending */ if (conn != hci_lookup_le_connect(hdev)) goto done; @@ -1403,7 +1436,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) struct iso_list_data data; /* Allocate a BIG if not set */ - if (qos->big == BT_ISO_QOS_BIG_UNSET) { + if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) { for (data.big = 0x00; data.big < 0xef; data.big++) { data.count = 0; data.bis = 0xff; @@ -1418,7 +1451,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) return -EADDRNOTAVAIL; /* Update BIG */ - qos->big = data.big; + qos->bcast.big = data.big; } return 0; @@ -1429,7 +1462,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) struct iso_list_data data; /* Allocate BIS if not set */ - if (qos->bis == BT_ISO_QOS_BIS_UNSET) { + if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) { /* Find an unused adv set to advertise BIS, skip instance 0x00 * since it is reserved as general purpose set. */ @@ -1447,7 +1480,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) return -EADDRNOTAVAIL; /* Update BIS */ - qos->bis = data.bis; + qos->bcast.bis = data.bis; } return 0; @@ -1476,8 +1509,8 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, if (err) return ERR_PTR(err); - data.big = qos->big; - data.bis = qos->bis; + data.big = qos->bcast.big; + data.bis = qos->bcast.bis; data.count = 0; /* Check if there is already a matching BIG/BIS */ @@ -1485,7 +1518,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, if (data.count) return ERR_PTR(-EADDRINUSE); - conn = hci_conn_hash_lookup_bis(hdev, dst, qos->big, qos->bis); + conn = hci_conn_hash_lookup_bis(hdev, dst, qos->bcast.big, qos->bcast.bis); if (conn) return ERR_PTR(-EADDRINUSE); @@ -1591,11 +1624,40 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, 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, __u16 setting, struct bt_codec *codec) { struct hci_conn *acl; struct hci_conn *sco; + struct hci_link *link; acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING, CONN_REASON_SCO_CONNECT); @@ -1611,10 +1673,12 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, } } - acl->link = sco; - sco->link = acl; - - hci_conn_hold(sco); + link = hci_conn_link(acl, sco); + if (!link) { + hci_conn_drop(acl); + hci_conn_drop(sco); + return NULL; + } sco->setting = setting; sco->codec = *codec; @@ -1640,13 +1704,13 @@ static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) { struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis]; - cis->cis_id = qos->cis; - cis->c_sdu = cpu_to_le16(qos->out.sdu); - cis->p_sdu = cpu_to_le16(qos->in.sdu); - cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy; - cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy; - cis->c_rtn = qos->out.rtn; - cis->p_rtn = qos->in.rtn; + cis->cis_id = qos->ucast.cis; + cis->c_sdu = cpu_to_le16(qos->ucast.out.sdu); + cis->p_sdu = cpu_to_le16(qos->ucast.in.sdu); + cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy : qos->ucast.in.phy; + cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy : qos->ucast.out.phy; + cis->c_rtn = qos->ucast.out.rtn; + cis->p_rtn = qos->ucast.in.rtn; d->pdu.cp.num_cis++; } @@ -1659,8 +1723,8 @@ static void cis_list(struct hci_conn *conn, void *data) if (!bacmp(&conn->dst, BDADDR_ANY)) return; - if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET || - d->cis != conn->iso_qos.cis) + if (d->cig != conn->iso_qos.ucast.cig || d->cis == BT_ISO_QOS_CIS_UNSET || + d->cis != conn->iso_qos.ucast.cis) return; d->count++; @@ -1679,18 +1743,18 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) memset(&cp, 0, sizeof(cp)); - cp.handle = qos->big; - cp.adv_handle = qos->bis; + cp.handle = qos->bcast.big; + cp.adv_handle = qos->bcast.bis; cp.num_bis = 0x01; - hci_cpu_to_le24(qos->out.interval, cp.bis.sdu_interval); - cp.bis.sdu = cpu_to_le16(qos->out.sdu); - cp.bis.latency = cpu_to_le16(qos->out.latency); - cp.bis.rtn = qos->out.rtn; - cp.bis.phy = qos->out.phy; - cp.bis.packing = qos->packing; - cp.bis.framing = qos->framing; - cp.bis.encryption = 0x00; - memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode)); + hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval); + cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu); + cp.bis.latency = cpu_to_le16(qos->bcast.out.latency); + cp.bis.rtn = qos->bcast.out.rtn; + cp.bis.phy = qos->bcast.out.phy; + cp.bis.packing = qos->bcast.packing; + cp.bis.framing = qos->bcast.framing; + cp.bis.encryption = qos->bcast.encryption; + memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode)); return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp); } @@ -1703,7 +1767,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) memset(&data, 0, sizeof(data)); /* Allocate a CIG if not set */ - if (qos->cig == BT_ISO_QOS_CIG_UNSET) { + if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) { for (data.cig = 0x00; data.cig < 0xff; data.cig++) { data.count = 0; data.cis = 0xff; @@ -1723,22 +1787,22 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) return false; /* Update CIG */ - qos->cig = data.cig; + qos->ucast.cig = data.cig; } - data.pdu.cp.cig_id = qos->cig; - hci_cpu_to_le24(qos->out.interval, data.pdu.cp.c_interval); - hci_cpu_to_le24(qos->in.interval, data.pdu.cp.p_interval); - data.pdu.cp.sca = qos->sca; - data.pdu.cp.packing = qos->packing; - data.pdu.cp.framing = qos->framing; - data.pdu.cp.c_latency = cpu_to_le16(qos->out.latency); - data.pdu.cp.p_latency = cpu_to_le16(qos->in.latency); + data.pdu.cp.cig_id = qos->ucast.cig; + hci_cpu_to_le24(qos->ucast.out.interval, data.pdu.cp.c_interval); + hci_cpu_to_le24(qos->ucast.in.interval, data.pdu.cp.p_interval); + data.pdu.cp.sca = qos->ucast.sca; + data.pdu.cp.packing = qos->ucast.packing; + data.pdu.cp.framing = qos->ucast.framing; + data.pdu.cp.c_latency = cpu_to_le16(qos->ucast.out.latency); + data.pdu.cp.p_latency = cpu_to_le16(qos->ucast.in.latency); - if (qos->cis != BT_ISO_QOS_CIS_UNSET) { + if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) { data.count = 0; - data.cig = qos->cig; - data.cis = qos->cis; + data.cig = qos->ucast.cig; + data.cis = qos->ucast.cis; hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND, &data); @@ -1749,7 +1813,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) } /* Reprogram all CIS(s) with the same CIG */ - for (data.cig = qos->cig, data.cis = 0x00; data.cis < 0x11; + for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0x11; data.cis++) { data.count = 0; @@ -1759,14 +1823,14 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) continue; /* Allocate a CIS if not set */ - if (qos->cis == BT_ISO_QOS_CIS_UNSET) { + if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET) { /* Update CIS */ - qos->cis = data.cis; + qos->ucast.cis = data.cis; cis_add(&data, qos); } } - if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) + if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis) return false; if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS, @@ -1783,7 +1847,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *cis; - cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type); + cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig, + qos->ucast.cis); if (!cis) { cis = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER); if (!cis) @@ -1801,32 +1866,32 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, return cis; /* Update LINK PHYs according to QoS preference */ - cis->le_tx_phy = qos->out.phy; - cis->le_rx_phy = qos->in.phy; + cis->le_tx_phy = qos->ucast.out.phy; + cis->le_rx_phy = qos->ucast.in.phy; /* If output interval is not set use the input interval as it cannot be * 0x000000. */ - if (!qos->out.interval) - qos->out.interval = qos->in.interval; + if (!qos->ucast.out.interval) + qos->ucast.out.interval = qos->ucast.in.interval; /* If input interval is not set use the output interval as it cannot be * 0x000000. */ - if (!qos->in.interval) - qos->in.interval = qos->out.interval; + if (!qos->ucast.in.interval) + qos->ucast.in.interval = qos->ucast.out.interval; /* If output latency is not set use the input latency as it cannot be * 0x0000. */ - if (!qos->out.latency) - qos->out.latency = qos->in.latency; + if (!qos->ucast.out.latency) + qos->ucast.out.latency = qos->ucast.in.latency; /* If input latency is not set use the output latency as it cannot be * 0x0000. */ - if (!qos->in.latency) - qos->in.latency = qos->out.latency; + if (!qos->ucast.in.latency) + qos->ucast.in.latency = qos->ucast.out.latency; if (!hci_le_set_cig_params(cis, qos)) { hci_conn_drop(cis); @@ -1846,7 +1911,7 @@ bool hci_iso_setup_path(struct hci_conn *conn) memset(&cmd, 0, sizeof(cmd)); - if (conn->iso_qos.out.sdu) { + if (conn->iso_qos.ucast.out.sdu) { cmd.handle = cpu_to_le16(conn->handle); cmd.direction = 0x00; /* Input (Host to Controller) */ cmd.path = 0x00; /* HCI path if enabled */ @@ -1857,7 +1922,7 @@ bool hci_iso_setup_path(struct hci_conn *conn) return false; } - if (conn->iso_qos.in.sdu) { + if (conn->iso_qos.ucast.in.sdu) { cmd.handle = cpu_to_le16(conn->handle); cmd.direction = 0x01; /* Output (Controller to Host) */ cmd.path = 0x00; /* HCI path if enabled */ @@ -1873,76 +1938,39 @@ bool hci_iso_setup_path(struct hci_conn *conn) static int hci_create_cis_sync(struct hci_dev *hdev, void *data) { - struct { - struct hci_cp_le_create_cis cp; - struct hci_cis cis[0x1f]; - } cmd; - struct hci_conn *conn = data; - u8 cig; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle); - cmd.cis[0].cis_handle = cpu_to_le16(conn->handle); - cmd.cp.num_cis++; - cig = conn->iso_qos.cig; - - hci_dev_lock(hdev); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { - struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis]; - - if (conn == data || conn->type != ISO_LINK || - conn->state == BT_CONNECTED || conn->iso_qos.cig != cig) - continue; - - /* Check if all CIS(s) belonging to a CIG are ready */ - if (!conn->link || conn->link->state != BT_CONNECTED || - conn->state != BT_CONNECT) { - cmd.cp.num_cis = 0; - break; - } - - /* Group all CIS with state BT_CONNECT since the spec don't - * allow to send them individually: - * - * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E - * page 2566: - * - * If the Host issues this command before all the - * HCI_LE_CIS_Established events from the previous use of the - * command have been generated, the Controller shall return the - * error code Command Disallowed (0x0C). - */ - cis->acl_handle = cpu_to_le16(conn->link->handle); - cis->cis_handle = cpu_to_le16(conn->handle); - cmd.cp.num_cis++; - } - - rcu_read_unlock(); - - hci_dev_unlock(hdev); - - if (!cmd.cp.num_cis) - return 0; - - return hci_send_cmd(hdev, HCI_OP_LE_CREATE_CIS, sizeof(cmd.cp) + - sizeof(cmd.cis[0]) * cmd.cp.num_cis, &cmd); + return hci_le_create_cis_sync(hdev, data); } int hci_le_create_cis(struct hci_conn *conn) { struct hci_conn *cis; + struct hci_link *link, *t; struct hci_dev *hdev = conn->hdev; int err; + bt_dev_dbg(hdev, "hcon %p", conn); + switch (conn->type) { case LE_LINK: - if (!conn->link || conn->state != BT_CONNECTED) + if (conn->state != BT_CONNECTED || list_empty(&conn->link_list)) 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: cis = conn; break; @@ -1994,8 +2022,8 @@ static void hci_bind_bis(struct hci_conn *conn, struct bt_iso_qos *qos) { /* Update LINK PHYs according to QoS preference */ - conn->le_tx_phy = qos->out.phy; - conn->le_tx_phy = qos->out.phy; + conn->le_tx_phy = qos->bcast.out.phy; + conn->le_tx_phy = qos->bcast.out.phy; conn->iso_qos = *qos; conn->state = BT_BOUND; } @@ -2008,16 +2036,16 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; - if (qos->out.phy == 0x02) + if (qos->bcast.out.phy == 0x02) flags |= MGMT_ADV_FLAG_SEC_2M; /* Align intervals */ - interval = qos->out.interval / 1250; + interval = qos->bcast.out.interval / 1250; - if (qos->bis) - sync_interval = qos->sync_interval * 1600; + if (qos->bcast.bis) + sync_interval = qos->bcast.sync_interval * 1600; - err = hci_start_per_adv_sync(hdev, qos->bis, conn->le_per_adv_data_len, + err = hci_start_per_adv_sync(hdev, qos->bcast.bis, conn->le_per_adv_data_len, conn->le_per_adv_data, flags, interval, interval, sync_interval); if (err) @@ -2054,7 +2082,7 @@ static int create_pa_sync(struct hci_dev *hdev, void *data) } int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, - __u8 sid) + __u8 sid, struct bt_iso_qos *qos) { struct hci_cp_le_pa_create_sync *cp; @@ -2067,9 +2095,13 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type, return -ENOMEM; } + cp->options = qos->bcast.options; cp->sid = sid; cp->addr_type = dst_type; bacpy(&cp->addr, dst); + cp->skip = cpu_to_le16(qos->bcast.skip); + cp->sync_timeout = cpu_to_le16(qos->bcast.sync_timeout); + cp->sync_cte_type = qos->bcast.sync_cte_type; /* Queue start pa_create_sync and scan */ return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete); @@ -2092,8 +2124,12 @@ int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos, return err; memset(&pdu, 0, sizeof(pdu)); - pdu.cp.handle = qos->big; + pdu.cp.handle = qos->bcast.big; pdu.cp.sync_handle = cpu_to_le16(sync_handle); + pdu.cp.encryption = qos->bcast.encryption; + memcpy(pdu.cp.bcode, qos->bcast.bcode, sizeof(pdu.cp.bcode)); + pdu.cp.mse = qos->bcast.mse; + pdu.cp.timeout = cpu_to_le16(qos->bcast.timeout); pdu.cp.num_bis = num_bis; memcpy(pdu.bis, bis, num_bis); @@ -2143,7 +2179,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, return ERR_PTR(err); } - hci_iso_qos_setup(hdev, conn, &qos->out, + hci_iso_qos_setup(hdev, conn, &qos->bcast.out, conn->le_tx_phy ? conn->le_tx_phy : hdev->le_tx_def_phys); @@ -2155,6 +2191,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *le; struct hci_conn *cis; + struct hci_link *link; if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) le = hci_connect_le(hdev, dst, dst_type, false, @@ -2169,9 +2206,9 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, if (IS_ERR(le)) return le; - hci_iso_qos_setup(hdev, le, &qos->out, + hci_iso_qos_setup(hdev, le, &qos->ucast.out, le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys); - hci_iso_qos_setup(hdev, le, &qos->in, + hci_iso_qos_setup(hdev, le, &qos->ucast.in, le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys); cis = hci_bind_cis(hdev, dst, dst_type, qos); @@ -2180,16 +2217,18 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, return cis; } - le->link = cis; - cis->link = le; - - hci_conn_hold(cis); + link = hci_conn_link(le, cis); + if (!link) { + hci_conn_drop(le); + hci_conn_drop(cis); + return NULL; + } /* If LE is already connected and CIS handle is already set proceed to * Create CIS immediately. */ if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET) - hci_le_create_cis(le); + hci_le_create_cis(cis); return cis; } @@ -2429,16 +2468,21 @@ timer: /* Drop all connection on the device */ void hci_conn_hash_flush(struct hci_dev *hdev) { - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *c, *n; + struct list_head *head = &hdev->conn_hash.list; + struct hci_conn *conn; BT_DBG("hdev %s", hdev->name); - list_for_each_entry_safe(c, n, &h->list, list) { - c->state = BT_CLOSED; - - hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); - hci_conn_del(c); + /* We should not traverse the list here, because hci_conn_del + * can remove extra links, which may cause the list traversal + * to hit items that have already been released. + */ + while ((conn = list_first_entry_or_null(head, + struct hci_conn, + list)) != NULL) { + conn->state = BT_CLOSED; + hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM); + hci_conn_del(conn); } } @@ -2775,6 +2819,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) { int r = 0; + if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags)) + return 0; + switch (conn->state) { case BT_CONNECTED: case BT_CONFIG: |