@@ -2127,7 +2127,8 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
-void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
+void *hci_le_meta_evt_data(struct hci_dev *hdev, __u8 subevent);
+void *hci_cmd_complete_data(struct hci_dev *hdev, __u16 opcode);
u32 hci_conn_get_phy(struct hci_conn *conn);
@@ -3120,10 +3120,11 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
}
-/* Get data from last received event */
-void *hci_recv_event_data(struct hci_dev *hdev, __u8 event)
+/* Get data from last received LE Meta event */
+void *hci_le_meta_evt_data(struct hci_dev *hdev, __u8 subevent)
{
struct hci_event_hdr *hdr;
+ struct hci_ev_le_meta *ev;
int offset;
if (!hdev->recv_event)
@@ -3132,21 +3133,42 @@ void *hci_recv_event_data(struct hci_dev *hdev, __u8 event)
hdr = (void *)hdev->recv_event->data;
offset = sizeof(*hdr);
- if (hdr->evt != event) {
- /* In case of LE metaevent check the subevent match */
- if (hdr->evt == HCI_EV_LE_META) {
- struct hci_ev_le_meta *ev;
+ if (hdr->evt != HCI_EV_LE_META)
+ return NULL;
- ev = (void *)hdev->recv_event->data + offset;
- offset += sizeof(*ev);
- if (ev->subevent == event)
- goto found;
- }
+ ev = (void *)hdev->recv_event->data + offset;
+ offset += sizeof(*ev);
+ if (ev->subevent != subevent)
+ return NULL;
+
+ bt_dev_dbg(hdev, "subevent 0x%2.2x", subevent);
+
+ return hdev->recv_event->data + offset;
+}
+
+/* Get data from last received Command Complete event */
+void *hci_cmd_complete_data(struct hci_dev *hdev, __u16 opcode)
+{
+ struct hci_event_hdr *hdr;
+ struct hci_ev_cmd_complete *ev;
+ int offset;
+
+ if (!hdev->recv_event)
+ return NULL;
+
+ hdr = (void *)hdev->recv_event->data;
+ offset = sizeof(*hdr);
+
+ if (hdr->evt != HCI_EV_CMD_COMPLETE)
+ return NULL;
+
+ ev = (void *)hdev->recv_event->data + offset;
+ offset += sizeof(*ev);
+
+ if (__le16_to_cpu(ev->opcode) != opcode)
return NULL;
- }
-found:
- bt_dev_dbg(hdev, "event 0x%2.2x", event);
+ bt_dev_dbg(hdev, "command complete event for 0x%4.4x", opcode);
return hdev->recv_event->data + offset;
}
@@ -1560,6 +1560,16 @@ struct iso_list_data {
int count;
};
+static bool iso_match_big_flag(struct sock *sk, void *data)
+{
+ struct hci_evt_le_big_sync_estabilished *ev = data;
+
+ if (!test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
+ return false;
+
+ return ev->handle == iso_pi(sk)->qos.bcast.big;
+}
+
static bool iso_match_big(struct sock *sk, void *data)
{
struct hci_evt_le_big_sync_estabilished *ev = data;
@@ -1567,15 +1577,28 @@ static bool iso_match_big(struct sock *sk, void *data)
return ev->handle == iso_pi(sk)->qos.bcast.big;
}
-static bool iso_match_pa_sync_flag(struct sock *sk, void *data)
+static bool iso_match_conn_big_flag(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->iso_qos.bcast.big == iso_pi(sk)->qos.bcast.big;
+}
+
+static bool iso_match_conn_big(struct sock *sk, void *data)
{
- return test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
+ struct hci_conn *hcon = data;
+
+ return hcon->iso_qos.bcast.big == iso_pi(sk)->qos.bcast.big;
}
static void iso_conn_ready(struct iso_conn *conn)
{
struct sock *parent = NULL;
struct sock *sk = conn->sk;
+ struct hci_rp_le_setup_iso_path *rp = NULL;
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;
@@ -1590,29 +1613,56 @@ static void iso_conn_ready(struct iso_conn *conn)
if (!hcon)
return;
- if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags) ||
- test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
- ev = hci_recv_event_data(hcon->hdev,
- HCI_EVT_LE_BIG_SYNC_ESTABILISHED);
+ if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags)) {
+ rp = hci_cmd_complete_data(hcon->hdev,
+ HCI_OP_LE_SETUP_ISO_PATH);
- /* Get reference to PA sync parent socket, if it exists */
- parent = iso_get_sock_listen(&hcon->src,
- &hcon->dst,
- iso_match_pa_sync_flag, NULL);
- if (!parent && ev)
+ if (rp) {
+ /* If defer setup was used to listen for this
+ * event, get reference to the socket marked
+ * with the BT_SK_PA_SYNC flag.
+ */
parent = iso_get_sock_listen(&hcon->src,
&hcon->dst,
- iso_match_big, ev);
+ iso_match_conn_big_flag,
+ hcon);
+
+ if (!parent)
+ parent = iso_get_sock_listen(&hcon->src,
+ &hcon->dst,
+ iso_match_conn_big,
+ hcon);
+ }
+ } else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
+ ev = hci_le_meta_evt_data(hcon->hdev,
+ HCI_EVT_LE_BIG_SYNC_ESTABILISHED);
+
+ if (ev) {
+ /* If defer setup was used to listen for this
+ * event, get reference to the socket marked
+ * with the BT_SK_PA_SYNC flag.
+ */
+ parent = iso_get_sock_listen(&hcon->src,
+ &hcon->dst,
+ iso_match_big_flag,
+ ev);
+
+ if (!parent)
+ parent = iso_get_sock_listen(&hcon->src,
+ &hcon->dst,
+ iso_match_big,
+ ev);
+ }
} else if (test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
- ev2 = hci_recv_event_data(hcon->hdev,
- HCI_EV_LE_PA_SYNC_ESTABLISHED);
+ ev2 = hci_le_meta_evt_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);
+ ev3 = hci_le_meta_evt_data(hcon->hdev,
+ HCI_EVT_LE_BIG_INFO_ADV_REPORT);
if (ev3)
parent = iso_get_sock_listen(&hcon->src,
&hcon->dst,
@@ -1700,6 +1750,16 @@ static bool iso_match_sid(struct sock *sk, void *data)
return ev->sid == iso_pi(sk)->bc_sid;
}
+static bool iso_match_sync_handle_flag(struct sock *sk, void *data)
+{
+ struct hci_evt_le_big_info_adv_report *ev = data;
+
+ if (!test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
+ return false;
+
+ return le16_to_cpu(ev->sync_handle) == iso_pi(sk)->sync_handle;
+}
+
static bool iso_match_sync_handle(struct sock *sk, void *data)
{
struct hci_evt_le_big_info_adv_report *ev = data;
@@ -1740,7 +1800,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
* 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);
+ ev1 = hci_le_meta_evt_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev1) {
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr, iso_match_sid,
ev1);
@@ -1750,11 +1810,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
goto done;
}
- ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
+ ev2 = hci_le_meta_evt_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
if (ev2) {
/* Try to get PA sync listening socket, if it exists */
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
- iso_match_pa_sync_flag, NULL);
+ iso_match_sync_handle_flag,
+ ev2);
if (!sk)
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
iso_match_sync_handle, ev2);
@@ -1780,7 +1841,7 @@ 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);
+ ev3 = hci_le_meta_evt_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);
This fixes the way broadcast events are handled by the ISO layer: A new slave BIS hcon is notified after the Command Complete for LE Setup ISO Data Path is received, not after BIG Sync Established. The iso_match_pa_sync_flag function has been replaced with more specific matching functions, depending on the event being handled. Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com> --- include/net/bluetooth/hci_core.h | 3 +- net/bluetooth/hci_core.c | 50 ++++++++++----- net/bluetooth/iso.c | 101 +++++++++++++++++++++++++------ 3 files changed, 119 insertions(+), 35 deletions(-)