Bluetooth: ISO: Do not emit LE BIG Create Sync if previous is pending
authorIulia Tanasescu <iulia.tanasescu@nxp.com>
Fri, 1 Nov 2024 08:23:38 +0000 (10:23 +0200)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 14 Nov 2024 20:37:02 +0000 (15:37 -0500)
The Bluetooth Core spec does not allow a LE BIG Create sync command to be
sent to Controller if another one is pending (Vol 4, Part E, page 2586).

In order to avoid this issue, the HCI_CONN_CREATE_BIG_SYNC was added
to mark that the LE BIG Create Sync command has been sent for a hcon.
Once the BIG 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>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c
net/bluetooth/iso.c

index 4becf20..5bb4eaa 100644 (file)
@@ -29,6 +29,7 @@
 #define HCI_MAX_ACL_SIZE       1024
 #define HCI_MAX_SCO_SIZE       255
 #define HCI_MAX_ISO_SIZE       251
+#define HCI_MAX_ISO_BIS                31
 #define HCI_MAX_EVENT_SIZE     260
 #define HCI_MAX_FRAME_SIZE     (HCI_MAX_ACL_SIZE + 4)
 
index 43474b7..c95f7e6 100644 (file)
@@ -711,6 +711,9 @@ struct hci_conn {
        __s8            tx_power;
        __s8            max_tx_power;
        struct bt_iso_qos iso_qos;
+       __u8            num_bis;
+       __u8            bis[HCI_MAX_ISO_BIS];
+
        unsigned long   flags;
 
        enum conn_reasons conn_reason;
@@ -946,6 +949,7 @@ enum {
        HCI_CONN_PER_ADV,
        HCI_CONN_BIG_CREATED,
        HCI_CONN_CREATE_CIS,
+       HCI_CONN_CREATE_BIG_SYNC,
        HCI_CONN_BIG_SYNC,
        HCI_CONN_BIG_SYNC_FAILED,
        HCI_CONN_CREATE_PA_SYNC,
@@ -1295,6 +1299,30 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
        return NULL;
 }
 
+static inline struct hci_conn *
+hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev,
+                                  __u8 handle, __u8 num_bis)
+{
+       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)
+                       continue;
+
+               if (handle == c->iso_qos.bcast.big && num_bis == c->num_bis) {
+                       rcu_read_unlock();
+                       return c;
+               }
+       }
+
+       rcu_read_unlock();
+
+       return NULL;
+}
+
 static inline struct hci_conn *
 hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle,  __u16 state)
 {
@@ -1479,6 +1507,7 @@ 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_le_big_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,
index f9da123..e996e97 100644 (file)
@@ -2180,34 +2180,93 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
        return conn;
 }
 
+static bool hci_conn_check_create_big_sync(struct hci_conn *conn)
+{
+       if (!conn->num_bis)
+               return false;
+
+       return true;
+}
+
+int hci_le_big_create_sync_pending(struct hci_dev *hdev)
+{
+       DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
+       struct hci_conn *conn;
+
+       rcu_read_lock();
+
+       pdu->num_bis = 0;
+
+       /* The spec allows only one pending LE BIG Create Sync command at
+        * a time. If the command is pending now, don't do anything. We
+        * check for pending connections after each BIG Sync Established
+        * event.
+        *
+        * BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
+        * page 2586:
+        *
+        * If the Host sends this command when the Controller is in the
+        * process of synchronizing to any BIG, i.e. the HCI_LE_BIG_Sync_
+        * Established event has not been generated, 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_BIG_SYNC, &conn->flags))
+                       goto unlock;
+       }
+
+       list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
+               if (hci_conn_check_create_big_sync(conn)) {
+                       struct bt_iso_qos *qos = &conn->iso_qos;
+
+                       set_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+
+                       pdu->handle = qos->bcast.big;
+                       pdu->sync_handle = cpu_to_le16(conn->sync_handle);
+                       pdu->encryption = qos->bcast.encryption;
+                       memcpy(pdu->bcode, qos->bcast.bcode,
+                              sizeof(pdu->bcode));
+                       pdu->mse = qos->bcast.mse;
+                       pdu->timeout = cpu_to_le16(qos->bcast.timeout);
+                       pdu->num_bis = conn->num_bis;
+                       memcpy(pdu->bis, conn->bis, conn->num_bis);
+
+                       break;
+               }
+       }
+
+unlock:
+       rcu_read_unlock();
+
+       if (!pdu->num_bis)
+               return 0;
+
+       return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
+                           struct_size(pdu, bis, pdu->num_bis), pdu);
+}
+
 int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
                           struct bt_iso_qos *qos,
                           __u16 sync_handle, __u8 num_bis, __u8 bis[])
 {
-       DEFINE_FLEX(struct hci_cp_le_big_create_sync, pdu, bis, num_bis, 0x11);
        int err;
 
-       if (num_bis < 0x01 || num_bis > pdu->num_bis)
+       if (num_bis < 0x01 || num_bis > ISO_MAX_NUM_BIS)
                return -EINVAL;
 
        err = qos_set_big(hdev, qos);
        if (err)
                return err;
 
-       if (hcon)
-               hcon->iso_qos.bcast.big = qos->bcast.big;
+       if (hcon) {
+               /* Update hcon QoS */
+               hcon->iso_qos = *qos;
 
-       pdu->handle = qos->bcast.big;
-       pdu->sync_handle = cpu_to_le16(sync_handle);
-       pdu->encryption = qos->bcast.encryption;
-       memcpy(pdu->bcode, qos->bcast.bcode, sizeof(pdu->bcode));
-       pdu->mse = qos->bcast.mse;
-       pdu->timeout = cpu_to_le16(qos->bcast.timeout);
-       pdu->num_bis = num_bis;
-       memcpy(pdu->bis, bis, num_bis);
+               hcon->num_bis = num_bis;
+               memcpy(hcon->bis, bis, num_bis);
+       }
 
-       return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
-                           struct_size(pdu, bis, num_bis), pdu);
+       return hci_le_big_create_sync_pending(hdev);
 }
 
 static void create_big_complete(struct hci_dev *hdev, void *data, int err)
index fd269fc..2b5ba8a 100644 (file)
@@ -6920,7 +6920,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
                                            struct sk_buff *skb)
 {
        struct hci_evt_le_big_sync_estabilished *ev = data;
-       struct hci_conn *bis;
+       struct hci_conn *bis, *conn;
        int i;
 
        bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
@@ -6931,6 +6931,20 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
 
        hci_dev_lock(hdev);
 
+       conn = hci_conn_hash_lookup_big_sync_pend(hdev, ev->handle,
+                                                 ev->num_bis);
+       if (!conn) {
+               bt_dev_err(hdev,
+                          "Unable to find connection for big 0x%2.2x",
+                          ev->handle);
+               goto unlock;
+       }
+
+       clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+
+       conn->num_bis = 0;
+       memset(conn->bis, 0, sizeof(conn->num_bis));
+
        for (i = 0; i < ev->num_bis; i++) {
                u16 handle = le16_to_cpu(ev->bis[i]);
                __le32 interval;
@@ -6980,6 +6994,10 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
                        hci_connect_cfm(bis, ev->status);
                }
 
+unlock:
+       /* Handle any other pending BIG sync command */
+       hci_le_big_create_sync_pending(hdev);
+
        hci_dev_unlock(hdev);
 }
 
index 0d98cc1..9499ddf 100644 (file)
@@ -1957,6 +1957,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 
                if (sk) {
                        int err;
+                       struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
 
                        iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
 
@@ -1965,7 +1966,8 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 
                        if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
                            !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
-                               err = hci_le_big_create_sync(hdev, NULL,
+                               err = hci_le_big_create_sync(hdev,
+                                                            hcon,
                                                             &iso_pi(sk)->qos,
                                                             iso_pi(sk)->sync_handle,
                                                             iso_pi(sk)->bc_num_bis,