]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: ISO: Reassemble PA data for bcast sink
authorIulia Tanasescu <iulia.tanasescu@nxp.com>
Fri, 23 Feb 2024 13:14:42 +0000 (15:14 +0200)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 6 Mar 2024 22:26:19 +0000 (17:26 -0500)
This adds support to reassemble PA data for a Broadcast Sink
listening socket. This is needed in case the BASE is received
fragmented in multiple PA reports.

PA data is first reassembled inside the hcon, before the BASE
is extracted and stored inside the socket. The length of the
le_per_adv_data hcon array has been raised to 1650, to accommodate
the maximum PA data length that can come fragmented, according to
spec.

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/iso.c

index 08cb5cb249a4972c3200262ab702ff276135bce3..21099bd3c8bcee685d51df32be17834da499121c 100644 (file)
@@ -2037,6 +2037,7 @@ struct hci_cp_le_set_per_adv_params {
 } __packed;
 
 #define HCI_MAX_PER_AD_LENGTH  252
+#define HCI_MAX_PER_AD_TOT_LEN 1650
 
 #define HCI_OP_LE_SET_PER_ADV_DATA             0x203f
 struct hci_cp_le_set_per_adv_data {
@@ -2797,6 +2798,10 @@ struct hci_ev_le_per_adv_report {
        __u8     data[];
 } __packed;
 
+#define LE_PA_DATA_COMPLETE    0x00
+#define LE_PA_DATA_MORE_TO_COME        0x01
+#define LE_PA_DATA_TRUNCATED   0x02
+
 #define HCI_EV_LE_EXT_ADV_SET_TERM     0x12
 struct hci_evt_le_ext_adv_set_term {
        __u8    status;
index 199a9f81cf502a6fcdd317a4b9d66145abef2b8d..da6aa6549b81bbdb12c434fc57e582fa9f9b71ee 100644 (file)
@@ -734,8 +734,9 @@ struct hci_conn {
        __u16           le_supv_timeout;
        __u8            le_adv_data[HCI_MAX_EXT_AD_LENGTH];
        __u8            le_adv_data_len;
-       __u8            le_per_adv_data[HCI_MAX_PER_AD_LENGTH];
-       __u8            le_per_adv_data_len;
+       __u8            le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN];
+       __u16           le_per_adv_data_len;
+       __u16           le_per_adv_data_offset;
        __u8            le_tx_phy;
        __u8            le_rx_phy;
        __s8            rssi;
index d9ee69fa1384b7606a6b6c5a86ab4b0f2c75a94f..30c777c469f96f7d5960423b763fa8651e904bb5 100644 (file)
@@ -1982,16 +1982,58 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 
        ev3 = hci_recv_event_data(hdev, HCI_EV_LE_PER_ADV_REPORT);
        if (ev3) {
-               size_t base_len = ev3->length;
+               size_t base_len = 0;
                u8 *base;
+               struct hci_conn *hcon;
 
                sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
                                         iso_match_sync_handle_pa_report, ev3);
-               base = eir_get_service_data(ev3->data, ev3->length,
-                                           EIR_BAA_SERVICE_UUID, &base_len);
-               if (base && sk && base_len <= sizeof(iso_pi(sk)->base)) {
+               if (!sk)
+                       goto done;
+
+               hcon = iso_pi(sk)->conn->hcon;
+               if (!hcon)
+                       goto done;
+
+               if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
+                       /* The controller was unable to retrieve PA data. */
+                       memset(hcon->le_per_adv_data, 0,
+                              HCI_MAX_PER_AD_TOT_LEN);
+                       hcon->le_per_adv_data_len = 0;
+                       hcon->le_per_adv_data_offset = 0;
+                       goto done;
+               }
+
+               if (hcon->le_per_adv_data_offset + ev3->length >
+                   HCI_MAX_PER_AD_TOT_LEN)
+                       goto done;
+
+               memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
+                      ev3->data, ev3->length);
+               hcon->le_per_adv_data_offset += ev3->length;
+
+               if (ev3->data_status == LE_PA_DATA_COMPLETE) {
+                       /* All PA data has been received. */
+                       hcon->le_per_adv_data_len =
+                               hcon->le_per_adv_data_offset;
+                       hcon->le_per_adv_data_offset = 0;
+
+                       /* Extract BASE */
+                       base = eir_get_service_data(hcon->le_per_adv_data,
+                                                   hcon->le_per_adv_data_len,
+                                                   EIR_BAA_SERVICE_UUID,
+                                                   &base_len);
+
+                       if (!base || base_len > BASE_MAX_LENGTH)
+                               goto done;
+
                        memcpy(iso_pi(sk)->base, base, base_len);
                        iso_pi(sk)->base_len = base_len;
+               } else {
+                       /* This is a PA data fragment. Keep pa_data_len set to 0
+                        * until all data has been reassembled.
+                        */
+                       hcon->le_per_adv_data_len = 0;
                }
        } else {
                sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);