Bluetooth: ISO: Do not emit LE PA Create Sync if previous is pending

The Bluetooth Core spec does not allow a LE PA Create sync command to be
sent to Controller if another one is pending (Vol 4, Part E, page 2493).

In order to avoid this issue, the HCI_CONN_CREATE_PA_SYNC was added
to mark that the LE PA Create Sync command has been sent for a hcon.
Once the PA Sync Established event is received, the hcon flag is
erased and the next pending hcon is handled.

Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Iulia Tanasescu 2024-11-01 10:23:36 +02:00 committed by Luiz Augusto von Dentz
parent 2dc98ac1cb
commit 4a5e0ba686
4 changed files with 139 additions and 40 deletions

View File

@ -1,7 +1,7 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright 2023 NXP
Copyright 2023-2024 NXP
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@ -697,6 +697,7 @@ enum {
#define HCI_RSSI_INVALID 127
#define HCI_SYNC_HANDLE_INVALID 0xffff
#define HCI_SID_INVALID 0xff
#define HCI_ROLE_MASTER 0x00
#define HCI_ROLE_SLAVE 0x01

View File

@ -668,6 +668,7 @@ struct hci_conn {
__u8 adv_instance;
__u16 handle;
__u16 sync_handle;
__u8 sid;
__u16 state;
__u16 mtu;
__u8 mode;
@ -947,6 +948,7 @@ enum {
HCI_CONN_CREATE_CIS,
HCI_CONN_BIG_SYNC,
HCI_CONN_BIG_SYNC_FAILED,
HCI_CONN_CREATE_PA_SYNC,
HCI_CONN_PA_SYNC,
HCI_CONN_PA_SYNC_FAILED,
};
@ -1099,6 +1101,30 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_sid(struct hci_dev *hdev,
__u8 sid,
bdaddr_t *dst,
__u8 dst_type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != ISO_LINK || bacmp(&c->dst, dst) ||
c->dst_type != dst_type || c->sid != sid)
continue;
rcu_read_unlock();
return c;
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
bdaddr_t *ba,
@ -1328,6 +1354,13 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
if (c->type != ISO_LINK)
continue;
/* Ignore the listen hcon, we are looking
* for the child hcon that was created as
* a result of the PA sync established event.
*/
if (c->state == BT_LISTEN)
continue;
if (c->sync_handle == sync_handle) {
rcu_read_unlock();
return c;
@ -1445,6 +1478,7 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
void hci_sco_setup(struct hci_conn *conn, __u8 status);
bool hci_iso_setup_path(struct hci_conn *conn);
int hci_le_create_cis_pending(struct hci_dev *hdev);
int hci_pa_create_sync_pending(struct hci_dev *hdev);
int hci_conn_check_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,

View File

@ -952,6 +952,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_INVALID;
conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
conn->sid = HCI_SID_INVALID;
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@ -2062,73 +2063,119 @@ static int create_big_sync(struct hci_dev *hdev, void *data)
static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
{
struct hci_cp_le_pa_create_sync *cp = data;
bt_dev_dbg(hdev, "");
if (err)
bt_dev_err(hdev, "Unable to create PA: %d", err);
}
kfree(cp);
static bool hci_conn_check_create_pa_sync(struct hci_conn *conn)
{
if (conn->type != ISO_LINK || conn->sid == HCI_SID_INVALID)
return false;
return true;
}
static int create_pa_sync(struct hci_dev *hdev, void *data)
{
struct hci_cp_le_pa_create_sync *cp = data;
int err;
struct hci_cp_le_pa_create_sync *cp = NULL;
struct hci_conn *conn;
int err = 0;
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_CREATE_SYNC,
sizeof(*cp), cp, HCI_CMD_TIMEOUT);
if (err) {
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
return err;
hci_dev_lock(hdev);
rcu_read_lock();
/* The spec allows only one pending LE Periodic Advertising Create
* Sync command at a time. If the command is pending now, don't do
* anything. We check for pending connections after each PA Sync
* Established event.
*
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
* page 2493:
*
* If the Host issues this command when another HCI_LE_Periodic_
* Advertising_Create_Sync command is pending, the Controller shall
* return the error code Command Disallowed (0x0C).
*/
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
if (test_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags))
goto unlock;
}
return hci_update_passive_scan_sync(hdev);
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
if (hci_conn_check_create_pa_sync(conn)) {
struct bt_iso_qos *qos = &conn->iso_qos;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp) {
err = -ENOMEM;
goto unlock;
}
cp->options = qos->bcast.options;
cp->sid = conn->sid;
cp->addr_type = conn->dst_type;
bacpy(&cp->addr, &conn->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;
break;
}
}
unlock:
rcu_read_unlock();
hci_dev_unlock(hdev);
if (cp) {
hci_dev_set_flag(hdev, HCI_PA_SYNC);
set_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_CREATE_SYNC,
sizeof(*cp), cp, HCI_CMD_TIMEOUT);
if (!err)
err = hci_update_passive_scan_sync(hdev);
kfree(cp);
if (err) {
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
}
}
return err;
}
int hci_pa_create_sync_pending(struct hci_dev *hdev)
{
/* Queue start pa_create_sync and scan */
return hci_cmd_sync_queue(hdev, create_pa_sync,
NULL, create_pa_complete);
}
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
struct bt_iso_qos *qos)
{
struct hci_cp_le_pa_create_sync *cp;
struct hci_conn *conn;
int err;
if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC))
return ERR_PTR(-EBUSY);
conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
if (IS_ERR(conn))
return conn;
conn->iso_qos = *qos;
conn->dst_type = dst_type;
conn->sid = sid;
conn->state = BT_LISTEN;
hci_conn_hold(conn);
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp) {
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
hci_conn_drop(conn);
return ERR_PTR(-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 */
err = hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
if (err < 0) {
hci_conn_drop(conn);
kfree(cp);
return ERR_PTR(err);
}
hci_pa_create_sync_pending(hdev);
return conn;
}

View File

@ -6352,7 +6352,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
struct hci_ev_le_pa_sync_established *ev = data;
int mask = hdev->link_mode;
__u8 flags = 0;
struct hci_conn *pa_sync;
struct hci_conn *pa_sync, *conn;
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
@ -6360,6 +6360,20 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
conn = hci_conn_hash_lookup_sid(hdev, ev->sid, &ev->bdaddr,
ev->bdaddr_type);
if (!conn) {
bt_dev_err(hdev,
"Unable to find connection for dst %pMR sid 0x%2.2x",
&ev->bdaddr, ev->sid);
goto unlock;
}
clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
conn->sync_handle = le16_to_cpu(ev->handle);
conn->sid = HCI_SID_INVALID;
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_pa_term_sync(hdev, ev->handle);
@ -6386,6 +6400,9 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
}
unlock:
/* Handle any other pending PA sync command */
hci_pa_create_sync_pending(hdev);
hci_dev_unlock(hdev);
}