summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2025-09-02 11:11:40 -0400
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2025-12-01 15:58:54 -0500
commit33b2835f0b7e2a458473b0e3a23b54b92108b6b0 (patch)
treee2ea0848dd1e0784dc75cf58e2e7636b244c9d02
parent97fdb2ea06efdde784480934d1ff60eb21e88b58 (diff)
Bluetooth: HCI: Add initial support for PAST
This adds PAST related commands (HCI_OP_LE_PAST, HCI_OP_LE_PAST_SET_INFO and HCI_OP_LE_PAST_PARAMS) and events (HCI_EV_LE_PAST_RECEIVED) along with handling of PAST sender and receiver features bits including new MGMG settings ( HCI_EV_LE_PAST_RECEIVED and MGMT_SETTING_PAST_RECEIVER) which userspace can use to determine if PAST is supported by the controller. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-rw-r--r--include/net/bluetooth/hci.h54
-rw-r--r--include/net/bluetooth/hci_core.h12
-rw-r--r--include/net/bluetooth/mgmt.h2
-rw-r--r--net/bluetooth/hci_event.c79
-rw-r--r--net/bluetooth/hci_sync.c3
-rw-r--r--net/bluetooth/iso.c25
-rw-r--r--net/bluetooth/mgmt.c12
7 files changed, 177 insertions, 10 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index cb4c02d00759..d883ad233ebc 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -647,6 +647,8 @@ enum {
#define HCI_LE_EXT_ADV 0x10
#define HCI_LE_PERIODIC_ADV 0x20
#define HCI_LE_CHAN_SEL_ALG2 0x40
+#define HCI_LE_PAST_SENDER 0x01
+#define HCI_LE_PAST_RECEIVER 0x02
#define HCI_LE_CIS_CENTRAL 0x10
#define HCI_LE_CIS_PERIPHERAL 0x20
#define HCI_LE_ISO_BROADCASTER 0x40
@@ -2068,6 +2070,44 @@ struct hci_cp_le_set_privacy_mode {
__u8 mode;
} __packed;
+#define HCI_OP_LE_PAST 0x205a
+struct hci_cp_le_past {
+ __le16 handle;
+ __le16 service_data;
+ __le16 sync_handle;
+} __packed;
+
+struct hci_rp_le_past {
+ __u8 status;
+ __le16 handle;
+} __packed;
+
+#define HCI_OP_LE_PAST_SET_INFO 0x205b
+struct hci_cp_le_past_set_info {
+ __le16 handle;
+ __le16 service_data;
+ __u8 adv_handle;
+} __packed;
+
+struct hci_rp_le_past_set_info {
+ __u8 status;
+ __le16 handle;
+} __packed;
+
+#define HCI_OP_LE_PAST_PARAMS 0x205c
+struct hci_cp_le_past_params {
+ __le16 handle;
+ __u8 mode;
+ __le16 skip;
+ __le16 sync_timeout;
+ __u8 cte_type;
+} __packed;
+
+struct hci_rp_le_past_params {
+ __u8 status;
+ __le16 handle;
+} __packed;
+
#define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
struct hci_rp_le_read_buffer_size_v2 {
__u8 status;
@@ -2800,6 +2840,20 @@ struct hci_evt_le_ext_adv_set_term {
__u8 num_evts;
} __packed;
+#define HCI_EV_LE_PAST_RECEIVED 0x18
+struct hci_ev_le_past_received {
+ __u8 status;
+ __le16 handle;
+ __le16 service_data;
+ __le16 sync_handle;
+ __u8 sid;
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+ __u8 phy;
+ __le16 interval;
+ __u8 clock_accuracy;
+} __packed;
+
#define HCI_EVT_LE_CIS_ESTABLISHED 0x19
struct hci_evt_le_cis_established {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 0cb87687837f..1bd12c303e25 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -2053,6 +2053,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define sync_recv_capable(dev) \
((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
#define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev))
+#define past_sender_capable(dev) \
+ ((dev)->le_features[3] & HCI_LE_PAST_SENDER)
+#define past_receiver_capable(dev) \
+ ((dev)->le_features[3] & HCI_LE_PAST_RECEIVER)
+#define past_capable(dev) \
+ (past_sender_capable(dev) || past_receiver_capable(dev))
+#define past_sender_enabled(dev) \
+ (le_enabled(dev) && past_sender_capable(dev))
+#define past_receiver_enabled(dev) \
+ (le_enabled(dev) && past_receiver_capable(dev))
+#define past_enabled(dev) \
+ (past_sender_enabled(dev) || past_receiver_enabled(dev))
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index f5be96f08b9d..8234915854b6 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_ISO_BROADCASTER BIT(20)
#define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21)
#define MGMT_SETTING_LL_PRIVACY BIT(22)
+#define MGMT_SETTING_PAST_SENDER BIT(23)
+#define MGMT_SETTING_PAST_RECEIVER BIT(24)
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3838b90343d9..af34c9938509 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5936,6 +5936,71 @@ unlock:
hci_dev_unlock(hdev);
}
+static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
+{
+ struct hci_cp_le_pa_term_sync cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+
+ return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
+}
+
+static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_ev_le_past_received *ev = data;
+ int mask = hdev->link_mode;
+ __u8 flags = 0;
+ struct hci_conn *pa_sync, *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
+
+ hci_dev_lock(hdev);
+
+ hci_dev_clear_flag(hdev, HCI_PA_SYNC);
+
+ conn = hci_conn_hash_lookup_create_pa_sync(hdev);
+ if (!conn) {
+ bt_dev_err(hdev,
+ "Unable to find connection for dst %pMR sid 0x%2.2x",
+ &ev->bdaddr, ev->sid);
+ goto unlock;
+ }
+
+ conn->sync_handle = le16_to_cpu(ev->sync_handle);
+ conn->sid = HCI_SID_INVALID;
+
+ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK,
+ &flags);
+ if (!(mask & HCI_LM_ACCEPT)) {
+ hci_le_pa_term_sync(hdev, ev->sync_handle);
+ goto unlock;
+ }
+
+ if (!(flags & HCI_PROTO_DEFER))
+ goto unlock;
+
+ /* Add connection to indicate PA sync event */
+ pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
+ HCI_ROLE_SLAVE);
+
+ if (IS_ERR(pa_sync))
+ goto unlock;
+
+ pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
+
+ if (ev->status) {
+ set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
+
+ /* Notify iso layer */
+ hci_connect_cfm(pa_sync, ev->status);
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -6412,16 +6477,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
-static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
-{
- struct hci_cp_le_pa_term_sync cp;
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = handle;
-
- return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
-}
-
static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -7206,6 +7261,10 @@ static const struct hci_le_ev {
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
sizeof(struct hci_evt_le_ext_adv_set_term)),
+ /* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */
+ HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED,
+ hci_le_past_received_evt,
+ sizeof(struct hci_ev_le_past_received)),
/* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt,
sizeof(struct hci_evt_le_cis_established)),
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 6e76798ec786..54ce93236551 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -4393,6 +4393,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (ext_adv_capable(hdev))
events[2] |= 0x02; /* LE Advertising Set Terminated */
+ if (past_receiver_capable(hdev))
+ events[2] |= 0x80; /* LE PAST Received */
+
if (cis_capable(hdev)) {
events[3] |= 0x01; /* LE CIS Established */
if (cis_peripheral_capable(hdev))
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 5859ec1c04dd..d0a79f601e8d 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -80,6 +80,7 @@ static struct bt_iso_qos default_qos;
static bool check_ucast_qos(struct bt_iso_qos *qos);
static bool check_bcast_qos(struct bt_iso_qos *qos);
static bool iso_match_sid(struct sock *sk, void *data);
+static bool iso_match_sid_past(struct sock *sk, void *data);
static bool iso_match_sync_handle(struct sock *sk, void *data);
static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data);
static void iso_sock_disconn(struct sock *sk);
@@ -2090,6 +2091,16 @@ static bool iso_match_sid(struct sock *sk, void *data)
return ev->sid == iso_pi(sk)->bc_sid;
}
+static bool iso_match_sid_past(struct sock *sk, void *data)
+{
+ struct hci_ev_le_past_received *ev = data;
+
+ if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
+ return true;
+
+ return ev->sid == iso_pi(sk)->bc_sid;
+}
+
static bool iso_match_sync_handle(struct sock *sk, void *data)
{
struct hci_evt_le_big_info_adv_report *ev = data;
@@ -2109,6 +2120,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data)
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
{
struct hci_ev_le_pa_sync_established *ev1;
+ struct hci_ev_le_past_received *ev1a;
struct hci_evt_le_big_info_adv_report *ev2;
struct hci_ev_le_per_adv_report *ev3;
struct sock *sk;
@@ -2122,6 +2134,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
* SID to listen to and once sync is established its handle needs to
* be stored in iso_pi(sk)->sync_handle so it can be matched once
* receiving the BIG Info.
+ * 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1.
* 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a
* a BIG Info it attempts to check if there any listening socket with
* the same sync_handle and if it does then attempt to create a sync.
@@ -2141,6 +2154,18 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
goto done;
}
+ ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED);
+ if (ev1a) {
+ sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
+ iso_match_sid_past, ev1a);
+ if (sk && !ev1a->status) {
+ iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
+ iso_pi(sk)->bc_sid = ev1a->sid;
+ }
+
+ goto done;
+ }
+
ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
if (ev2) {
/* Check if BIGInfo report has already been handled */
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 262bf984d2aa..7dbd3d57e66c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -852,6 +852,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (ll_privacy_capable(hdev))
settings |= MGMT_SETTING_LL_PRIVACY;
+ if (past_sender_capable(hdev))
+ settings |= MGMT_SETTING_PAST_SENDER;
+
+ if (past_receiver_capable(hdev))
+ settings |= MGMT_SETTING_PAST_RECEIVER;
+
settings |= MGMT_SETTING_PHY_CONFIGURATION;
return settings;
@@ -937,6 +943,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (ll_privacy_enabled(hdev))
settings |= MGMT_SETTING_LL_PRIVACY;
+ if (past_sender_enabled(hdev))
+ settings |= MGMT_SETTING_PAST_SENDER;
+
+ if (past_receiver_enabled(hdev))
+ settings |= MGMT_SETTING_PAST_RECEIVER;
+
return settings;
}