Bluetooth: Set filter policy for LE connection
[platform/kernel/linux-starfive.git] / net / bluetooth / iso.c
index 7124816..0eeec64 100644 (file)
@@ -52,6 +52,7 @@ static void iso_sock_kill(struct sock *sk);
 enum {
        BT_SK_BIG_SYNC,
        BT_SK_PA_SYNC,
+       BT_SK_PA_SYNC_TERM,
 };
 
 struct iso_pinfo {
@@ -77,8 +78,14 @@ 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_sync_handle(struct sock *sk, void *data);
 static void iso_sock_disconn(struct sock *sk);
 
+typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
+
+static struct sock *iso_get_sock_listen(bdaddr_t *src, bdaddr_t *dst,
+                                       iso_sock_match_t match, void *data);
+
 /* ---- ISO timers ---- */
 #define ISO_CONN_TIMEOUT       (HZ * 40)
 #define ISO_DISCONN_TIMEOUT    (HZ * 2)
@@ -187,10 +194,21 @@ static void iso_chan_del(struct sock *sk, int err)
        sock_set_flag(sk, SOCK_ZAPPED);
 }
 
+static bool iso_match_conn_sync_handle(struct sock *sk, void *data)
+{
+       struct hci_conn *hcon = data;
+
+       if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
+               return false;
+
+       return hcon->sync_handle == iso_pi(sk)->sync_handle;
+}
+
 static void iso_conn_del(struct hci_conn *hcon, int err)
 {
        struct iso_conn *conn = hcon->iso_data;
        struct sock *sk;
+       struct sock *parent;
 
        if (!conn)
                return;
@@ -206,6 +224,25 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
 
        if (sk) {
                lock_sock(sk);
+
+               /* While a PA sync hcon is in the process of closing,
+                * mark parent socket with a flag, so that any residual
+                * BIGInfo adv reports that arrive before PA sync is
+                * terminated are not processed anymore.
+                */
+               if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
+                       parent = iso_get_sock_listen(&hcon->src,
+                                                    &hcon->dst,
+                                                    iso_match_conn_sync_handle,
+                                                    hcon);
+
+                       if (parent) {
+                               set_bit(BT_SK_PA_SYNC_TERM,
+                                       &iso_pi(parent)->flags);
+                               sock_put(parent);
+                       }
+               }
+
                iso_sock_clear_timer(sk);
                iso_chan_del(sk, err);
                release_sock(sk);
@@ -542,8 +579,6 @@ static struct sock *__iso_get_sock_listen_by_sid(bdaddr_t *ba, bdaddr_t *bc,
        return NULL;
 }
 
-typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
-
 /* Find socket listening:
  * source bdaddr (Unicast)
  * destination bdaddr (Broadcast only)
@@ -1202,7 +1237,6 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
                            test_bit(HCI_CONN_PA_SYNC, &pi->conn->hcon->flags)) {
                                iso_conn_big_sync(sk);
                                sk->sk_state = BT_LISTEN;
-                               set_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
                        } else {
                                iso_conn_defer_accept(pi->conn->hcon);
                                sk->sk_state = BT_CONFIG;
@@ -1579,6 +1613,7 @@ static void iso_conn_ready(struct iso_conn *conn)
        struct sock *sk = conn->sk;
        struct hci_ev_le_big_sync_estabilished *ev = NULL;
        struct hci_ev_le_pa_sync_established *ev2 = NULL;
+       struct hci_evt_le_big_info_adv_report *ev3 = NULL;
        struct hci_conn *hcon;
 
        BT_DBG("conn %p", conn);
@@ -1603,14 +1638,20 @@ static void iso_conn_ready(struct iso_conn *conn)
                                parent = iso_get_sock_listen(&hcon->src,
                                                             &hcon->dst,
                                                             iso_match_big, ev);
-               } else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags) ||
-                               test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
+               } else if (test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
                        ev2 = hci_recv_event_data(hcon->hdev,
                                                  HCI_EV_LE_PA_SYNC_ESTABLISHED);
                        if (ev2)
                                parent = iso_get_sock_listen(&hcon->src,
                                                             &hcon->dst,
                                                             iso_match_sid, ev2);
+               } else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
+                       ev3 = hci_recv_event_data(hcon->hdev,
+                                                 HCI_EVT_LE_BIG_INFO_ADV_REPORT);
+                       if (ev3)
+                               parent = iso_get_sock_listen(&hcon->src,
+                                                            &hcon->dst,
+                                                            iso_match_sync_handle, ev3);
                }
 
                if (!parent)
@@ -1650,11 +1691,13 @@ static void iso_conn_ready(struct iso_conn *conn)
                        hcon->sync_handle = iso_pi(parent)->sync_handle;
                }
 
-               if (ev2 && !ev2->status) {
-                       iso_pi(sk)->sync_handle = iso_pi(parent)->sync_handle;
+               if (ev3) {
                        iso_pi(sk)->qos = iso_pi(parent)->qos;
+                       iso_pi(sk)->qos.bcast.encryption = ev3->encryption;
+                       hcon->iso_qos = iso_pi(sk)->qos;
                        iso_pi(sk)->bc_num_bis = iso_pi(parent)->bc_num_bis;
                        memcpy(iso_pi(sk)->bc_bis, iso_pi(parent)->bc_bis, ISO_MAX_NUM_BIS);
+                       set_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
                }
 
                bacpy(&iso_pi(sk)->dst, &hcon->dst);
@@ -1747,9 +1790,20 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                /* Try to get PA sync listening socket, if it exists */
                sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
                                                iso_match_pa_sync_flag, NULL);
-               if (!sk)
+
+               if (!sk) {
                        sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
                                                 iso_match_sync_handle, ev2);
+
+                       /* If PA Sync is in process of terminating,
+                        * do not handle any more BIGInfo adv reports.
+                        */
+
+                       if (sk && test_bit(BT_SK_PA_SYNC_TERM,
+                                          &iso_pi(sk)->flags))
+                               return lm;
+               }
+
                if (sk) {
                        int err;