]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Bluetooth: ISO: Fix not using SID from adv report
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 11 Apr 2025 16:37:50 +0000 (12:37 -0400)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 21 May 2025 14:28:08 +0000 (10:28 -0400)
Up until now it has been assumed that the application would be able to
enter the advertising SID in sockaddr_iso_bc.bc_sid, but userspace has
no access to SID since the likes of MGMT_EV_DEVICE_FOUND cannot carry
it, so it was left unset (0x00) which means it would be unable to
synchronize if the broadcast source is using a different SID e.g. 0x04:

> HCI Event: LE Meta Event (0x3e) plen 57
      LE Extended Advertising Report (0x0d)
        Num reports: 1
        Entry 0
          Event type: 0x0000
            Props: 0x0000
            Data status: Complete
          Address type: Random (0x01)
          Address: 0B:82:E8:50:6D:C8 (Non-Resolvable)
          Primary PHY: LE 1M
          Secondary PHY: LE 2M
          SID: 0x04
          TX power: 127 dBm
          RSSI: -55 dBm (0xc9)
          Periodic advertising interval: 180.00 msec (0x0090)
          Direct address type: Public (0x00)
          Direct address: 00:00:00:00:00:00 (OUI 00-00-00)
          Data length: 0x1f
        06 16 52 18 5b 0b e1 05 16 56 18 04 00 11 30 4c  ..R.[....V....0L
        75 69 7a 27 73 20 53 32 33 20 55 6c 74 72 61     uiz's S23 Ultra
        Service Data: Broadcast Audio Announcement (0x1852)
        Broadcast ID: 14748507 (0xe10b5b)
        Service Data: Public Broadcast Announcement (0x1856)
          Data[2]: 0400
        Unknown EIR field 0x30[16]: 4c75697a27732053323320556c747261
< HCI Command: LE Periodic Advertising Create Sync (0x08|0x0044) plen 14
        Options: 0x0000
        Use advertising SID, Advertiser Address Type and address
        Reporting initially enabled
        SID: 0x00 (<- Invalid)
        Adv address type: Random (0x01)
        Adv address: 0B:82:E8:50:6D:C8 (Non-Resolvable)
        Skip: 0x0000
        Sync timeout: 20000 msec (0x07d0)
        Sync CTE type: 0x0000

So instead this changes now allow application to set HCI_SID_INVALID
which will make hci_le_pa_create_sync to wait for a report, update the
conn->sid using the report SID and only then issue PA create sync
command:

< HCI Command: LE Periodic Advertising Create Sync
        Options: 0x0000
        Use advertising SID, Advertiser Address Type and address
        Reporting initially enabled
        SID: 0x04
        Adv address type: Random (0x01)
        Adv address: 0B:82:E8:50:6D:C8 (Non-Resolvable)
        Skip: 0x0000
        Sync timeout: 20000 msec (0x07d0)
        Sync CTE type: 0x0000
> HCI Event: LE Meta Event (0x3e) plen 16
      LE Periodic Advertising Sync Established (0x0e)
        Status: Success (0x00)
        Sync handle: 64
        Advertising SID: 0x04
        Advertiser address type: Random (0x01)
        Advertiser address: 0B:82:E8:50:6D:C8 (Non-Resolvable)
        Advertiser PHY: LE 2M (0x02)
        Periodic advertising interval: 180.00 msec (0x0090)
        Advertiser clock accuracy: 0x05

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sync.c
net/bluetooth/iso.c

index 946d2ae551f86cad9c68c41ab1a0dfd148d960cc..c0207812f43282dca4be6244761aa74ffd99807d 100644 (file)
@@ -2070,6 +2070,8 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
 {
        struct hci_conn *conn;
 
+       bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);
+
        conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
        if (IS_ERR(conn))
                return conn;
index df7d7bf32eb8e35f0fdcf4cd3722408fb1d7362f..a58fd3bf8851d6dc1453aa2e4ba0cd2d29d8b0b5 100644 (file)
@@ -4068,10 +4068,13 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
                return;
        }
 
-       err = hci_send_frame(hdev, skb);
-       if (err < 0) {
-               hci_cmd_sync_cancel_sync(hdev, -err);
-               return;
+       if (hci_skb_opcode(skb) != HCI_OP_NOP) {
+               err = hci_send_frame(hdev, skb);
+               if (err < 0) {
+                       hci_cmd_sync_cancel_sync(hdev, -err);
+                       return;
+               }
+               atomic_dec(&hdev->cmd_cnt);
        }
 
        if (hdev->req_status == HCI_REQ_PEND &&
@@ -4079,8 +4082,6 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
                kfree_skb(hdev->req_skb);
                hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
        }
-
-       atomic_dec(&hdev->cmd_cnt);
 }
 
 static void hci_cmd_work(struct work_struct *work)
index c38ada69c3d7f25d91e148d4ab490e18caa73649..4183560582a3ad60499ec58c6d838d15c67f4b9d 100644 (file)
@@ -6351,6 +6351,17 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
                        info->secondary_phy &= 0x1f;
                }
 
+               /* Check if PA Sync is pending and if the hci_conn SID has not
+                * been set update it.
+                */
+               if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
+                       struct hci_conn *conn;
+
+                       conn = hci_conn_hash_lookup_create_pa_sync(hdev);
+                       if (conn && conn->sid == HCI_SID_INVALID)
+                               conn->sid = info->sid;
+               }
+
                if (legacy_evt_type != LE_ADV_INVALID) {
                        process_adv_report(hdev, legacy_evt_type, &info->bdaddr,
                                           info->bdaddr_type, NULL, 0,
@@ -7155,7 +7166,8 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
 
        /* Only match event if command OGF is for LE */
        if (hdev->req_skb &&
-           hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 &&
+          (hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 ||
+           hci_skb_opcode(hdev->req_skb) == HCI_OP_NOP) &&
            hci_skb_event(hdev->req_skb) == ev->subevent) {
                *opcode = hci_skb_opcode(hdev->req_skb);
                hci_req_cmd_complete(hdev, *opcode, 0x00, req_complete,
@@ -7511,8 +7523,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                goto done;
        }
 
+       hci_dev_lock(hdev);
        kfree_skb(hdev->recv_event);
        hdev->recv_event = skb_clone(skb, GFP_KERNEL);
+       hci_dev_unlock(hdev);
 
        event = hdr->evt;
        if (!event) {
index e56b1cbedab908c3ddf740fead04a1cb224b7d9c..d00ff18f3be0dc5462481be34d9ff4b8e5e3e82f 100644 (file)
@@ -6898,20 +6898,37 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
 
 static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
 {
+       struct hci_conn *conn = data;
+       struct hci_conn *pa_sync;
+
        bt_dev_dbg(hdev, "err %d", err);
 
-       if (!err)
+       if (err == -ECANCELED)
                return;
 
+       hci_dev_lock(hdev);
+
        hci_dev_clear_flag(hdev, HCI_PA_SYNC);
 
-       if (err == -ECANCELED)
-               return;
+       if (!hci_conn_valid(hdev, conn))
+               clear_bit(HCI_CONN_CREATE_PA_SYNC, &conn->flags);
 
-       hci_dev_lock(hdev);
+       if (!err)
+               goto unlock;
 
-       hci_update_passive_scan_sync(hdev);
+       /* Add connection to indicate PA sync error */
+       pa_sync = hci_conn_add_unset(hdev, ISO_LINK, BDADDR_ANY,
+                                    HCI_ROLE_SLAVE);
 
+       if (IS_ERR(pa_sync))
+               goto unlock;
+
+       set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
+
+       /* Notify iso layer */
+       hci_connect_cfm(pa_sync, bt_status(err));
+
+unlock:
        hci_dev_unlock(hdev);
 }
 
@@ -6925,9 +6942,23 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
        if (!hci_conn_valid(hdev, conn))
                return -ECANCELED;
 
+       if (conn->sync_handle != HCI_SYNC_HANDLE_INVALID)
+               return -EINVAL;
+
        if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC))
                return -EBUSY;
 
+       /* Stop scanning if SID has not been set and active scanning is enabled
+        * so we use passive scanning which will be scanning using the allow
+        * list programmed to contain only the connection address.
+        */
+       if (conn->sid == HCI_SID_INVALID &&
+           hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+               hci_scan_disable_sync(hdev);
+               hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+       }
+
        /* Mark HCI_CONN_CREATE_PA_SYNC so hci_update_passive_scan_sync can
         * program the address in the allow list so PA advertisements can be
         * received.
@@ -6936,6 +6967,14 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
 
        hci_update_passive_scan_sync(hdev);
 
+       /* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
+        * it.
+        */
+       if (conn->sid == HCI_SID_INVALID)
+               __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, 0, NULL,
+                                        HCI_EV_LE_EXT_ADV_REPORT,
+                                        conn->conn_timeout, NULL);
+
        memset(&cp, 0, sizeof(cp));
        cp.options = qos->bcast.options;
        cp.sid = conn->sid;
index 2819cda616bce8214d72073a14fcb38cf2516369..7c0012ce1b890e172dfa08d66ca3b26e61702bd4 100644 (file)
@@ -941,7 +941,7 @@ static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
 
        iso_pi(sk)->dst_type = sa->iso_bc->bc_bdaddr_type;
 
-       if (sa->iso_bc->bc_sid > 0x0f)
+       if (sa->iso_bc->bc_sid > 0x0f && sa->iso_bc->bc_sid != HCI_SID_INVALID)
                return -EINVAL;
 
        iso_pi(sk)->bc_sid = sa->iso_bc->bc_sid;
@@ -2029,6 +2029,9 @@ static bool iso_match_sid(struct sock *sk, void *data)
 {
        struct hci_ev_le_pa_sync_established *ev = data;
 
+       if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
+               return true;
+
        return ev->sid == iso_pi(sk)->bc_sid;
 }
 
@@ -2075,8 +2078,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
        if (ev1) {
                sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
                                  iso_match_sid, ev1);
-               if (sk && !ev1->status)
+               if (sk && !ev1->status) {
                        iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
+                       iso_pi(sk)->bc_sid = ev1->sid;
+               }
 
                goto done;
        }