diff options
author | Iulia Tanasescu <iulia.tanasescu@nxp.com> | 2024-02-23 16:14:42 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2024-05-02 17:35:19 +0300 |
commit | 489efc9ae36f164423f5fa7ace772a7ab8131cd8 (patch) | |
tree | 7d2463641003461db218186ef71909652624d643 /net | |
parent | 5dd0bd277a0a936708a33ecc447dce77a08cbde6 (diff) | |
download | linux-489efc9ae36f164423f5fa7ace772a7ab8131cd8.tar.xz |
Bluetooth: ISO: Reassemble PA data for bcast sink
[ Upstream commit 168d9bf9c7f01df71e6404cfff66d9c2a8e968fb ]
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>
Stable-dep-of: 2e7ed5f5e69b ("Bluetooth: hci_sync: Use advertised PHYs on hci_le_ext_create_conn_sync")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/iso.c | 50 |
1 files changed, 46 insertions, 4 deletions
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index a8b05baa8e5a..fa6c2e95d554 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1955,16 +1955,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); |