OSDN Git Service

Bluetooth: ISO: Add support for periodic adv reports processing
authorClaudia Draghicescu <claudia.rosu@nxp.com>
Fri, 30 Jun 2023 09:59:28 +0000 (12:59 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 24 Aug 2023 19:22:56 +0000 (12:22 -0700)
In the case of a Periodic Synchronized Receiver,
the PA report received from a Broadcaster contains the BASE,
which has information about codec and other parameters of a BIG.
This isnformation is stored and the application can retrieve it
using getsockopt(BT_ISO_BASE).

Signed-off-by: Claudia Draghicescu <claudia.rosu@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci.h
net/bluetooth/hci_event.c
net/bluetooth/iso.c

index 5723405..c58425d 100644 (file)
@@ -2771,6 +2771,17 @@ struct hci_ev_le_enh_conn_complete {
        __u8      clk_accurancy;
 } __packed;
 
+#define HCI_EV_LE_PER_ADV_REPORT    0x0f
+struct hci_ev_le_per_adv_report {
+       __le16   sync_handle;
+       __u8     tx_power;
+       __u8     rssi;
+       __u8     cte_type;
+       __u8     data_status;
+       __u8     length;
+       __u8     data[];
+} __packed;
+
 #define HCI_EV_LE_EXT_ADV_SET_TERM     0x12
 struct hci_evt_le_ext_adv_set_term {
        __u8    status;
index b4b7207..35f2510 100644 (file)
@@ -6617,6 +6617,24 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
+                                     struct sk_buff *skb)
+{
+       struct hci_ev_le_per_adv_report *ev = data;
+       int mask = hdev->link_mode;
+       __u8 flags = 0;
+
+       bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle));
+
+       hci_dev_lock(hdev);
+
+       mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
+       if (!(mask & HCI_LM_ACCEPT))
+               hci_le_pa_term_sync(hdev, ev->sync_handle);
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
                                            struct sk_buff *skb)
 {
@@ -7213,6 +7231,11 @@ static const struct hci_le_ev {
        HCI_LE_EV(HCI_EV_LE_PA_SYNC_ESTABLISHED,
                  hci_le_pa_sync_estabilished_evt,
                  sizeof(struct hci_ev_le_pa_sync_established)),
+       /* [0x0f = HCI_EV_LE_PER_ADV_REPORT] */
+       HCI_LE_EV_VL(HCI_EV_LE_PER_ADV_REPORT,
+                                hci_le_per_adv_report_evt,
+                                sizeof(struct hci_ev_le_per_adv_report),
+                                HCI_MAX_EVENT_SIZE),
        /* [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)),
index 2a2c377..16da946 100644 (file)
@@ -1446,7 +1446,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_ISO_BASE:
-               if (sk->sk_state == BT_CONNECTED) {
+               if (sk->sk_state == BT_CONNECTED &&
+                   !bacmp(&iso_pi(sk)->dst, BDADDR_ANY)) {
                        base_len = iso_pi(sk)->conn->hcon->le_per_adv_data_len;
                        base = iso_pi(sk)->conn->hcon->le_per_adv_data;
                } else {
@@ -1655,6 +1656,9 @@ static void iso_conn_ready(struct iso_conn *conn)
 
                bacpy(&iso_pi(sk)->dst, &hcon->dst);
                iso_pi(sk)->dst_type = hcon->dst_type;
+               iso_pi(sk)->sync_handle = iso_pi(parent)->sync_handle;
+               memcpy(iso_pi(sk)->base, iso_pi(parent)->base, iso_pi(parent)->base_len);
+               iso_pi(sk)->base_len = iso_pi(parent)->base_len;
 
                hci_conn_hold(hcon);
                iso_chan_add(conn, sk, parent);
@@ -1692,12 +1696,20 @@ static bool iso_match_sync_handle(struct sock *sk, void *data)
        return le16_to_cpu(ev->sync_handle) == iso_pi(sk)->sync_handle;
 }
 
+static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data)
+{
+       struct hci_ev_le_per_adv_report *ev = data;
+
+       return le16_to_cpu(ev->sync_handle) == iso_pi(sk)->sync_handle;
+}
+
 /* ----- ISO interface with lower layer (HCI) ----- */
 
 int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
 {
        struct hci_ev_le_pa_sync_established *ev1;
        struct hci_evt_le_big_info_adv_report *ev2;
+       struct hci_ev_le_per_adv_report *ev3;
        struct sock *sk;
        int lm = 0;
 
@@ -1713,6 +1725,9 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
         * 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.
+        * 3. HCI_EV_LE_PER_ADV_REPORT: When a PA report is received, it is stored
+        * in iso_pi(sk)->base so it can be passed up to user, in the case of a
+        * broadcast sink.
         */
        ev1 = hci_recv_event_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
        if (ev1) {
@@ -1752,6 +1767,17 @@ 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) {
+               sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
+                                        iso_match_sync_handle_pa_report, ev3);
+
+               if (sk) {
+                       memcpy(iso_pi(sk)->base, ev3->data, ev3->length);
+                       iso_pi(sk)->base_len = ev3->length;
+               }
        } else {
                sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
        }