]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: HCI: Add initial support for PAST
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 2 Sep 2025 15:11:40 +0000 (11:11 -0400)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 1 Dec 2025 20:58:54 +0000 (15:58 -0500)
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>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
net/bluetooth/hci_event.c
net/bluetooth/hci_sync.c
net/bluetooth/iso.c
net/bluetooth/mgmt.c

index cb4c02d0075904119627c1f4d8195b37c536fad5..d883ad233ebcdb1320b81c737db9123e163d9fab 100644 (file)
@@ -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;
index 0cb87687837f7efef431d7e8999974762c6cf9f1..1bd12c303e2535bf44177559ded7d9908b645adf 100644 (file)
@@ -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)))
index f5be96f08b9d9847316815b4e0e4a379e36b2689..8234915854b682c0d5ae2c1dfde24d93fcef4c20 100644 (file)
@@ -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
index 3838b90343d910bbb0f695c995f6e5a19ac78467..af34c9938509fff45401b9a5b5844fbb48204e32 100644 (file)
@@ -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)),
index 6e76798ec786b1bd0844971eb24d2e1242373601..54ce93236551fec43c4d52ece08d1e8f68a13eb9 100644 (file)
@@ -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))
index 5859ec1c04ddae15c3f93c091bed6bfef7d7f72d..d0a79f601e8d3fa227a8294edf3693e45d993c91 100644 (file)
@@ -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 */
index 262bf984d2aafa259c87d3e7b74b85209be3901c..7dbd3d57e66c212b93c94928911e234076acf319 100644 (file)
@@ -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;
 }