mirror of
https://github.com/torvalds/linux
synced 2024-09-06 18:01:05 +00:00
Bluetooth: ISO: Add support for periodic adv reports processing
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>
This commit is contained in:
parent
3344d31833
commit
9c0826310b
|
@ -2771,6 +2771,17 @@ struct hci_ev_le_enh_conn_complete {
|
||||||
__u8 clk_accurancy;
|
__u8 clk_accurancy;
|
||||||
} __packed;
|
} __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
|
#define HCI_EV_LE_EXT_ADV_SET_TERM 0x12
|
||||||
struct hci_evt_le_ext_adv_set_term {
|
struct hci_evt_le_ext_adv_set_term {
|
||||||
__u8 status;
|
__u8 status;
|
||||||
|
|
|
@ -6617,6 +6617,24 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||||
hci_dev_unlock(hdev);
|
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,
|
static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
||||||
struct sk_buff *skb)
|
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_EV(HCI_EV_LE_PA_SYNC_ESTABLISHED,
|
||||||
hci_le_pa_sync_estabilished_evt,
|
hci_le_pa_sync_estabilished_evt,
|
||||||
sizeof(struct hci_ev_le_pa_sync_established)),
|
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] */
|
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
|
||||||
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
|
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)),
|
sizeof(struct hci_evt_le_ext_adv_set_term)),
|
||||||
|
|
|
@ -1446,7 +1446,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_ISO_BASE:
|
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_len = iso_pi(sk)->conn->hcon->le_per_adv_data_len;
|
||||||
base = iso_pi(sk)->conn->hcon->le_per_adv_data;
|
base = iso_pi(sk)->conn->hcon->le_per_adv_data;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1655,6 +1656,9 @@ static void iso_conn_ready(struct iso_conn *conn)
|
||||||
|
|
||||||
bacpy(&iso_pi(sk)->dst, &hcon->dst);
|
bacpy(&iso_pi(sk)->dst, &hcon->dst);
|
||||||
iso_pi(sk)->dst_type = hcon->dst_type;
|
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);
|
hci_conn_hold(hcon);
|
||||||
iso_chan_add(conn, sk, parent);
|
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;
|
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) ----- */
|
/* ----- ISO interface with lower layer (HCI) ----- */
|
||||||
|
|
||||||
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
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_pa_sync_established *ev1;
|
||||||
struct hci_evt_le_big_info_adv_report *ev2;
|
struct hci_evt_le_big_info_adv_report *ev2;
|
||||||
|
struct hci_ev_le_per_adv_report *ev3;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
int lm = 0;
|
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
|
* 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
|
* 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.
|
* 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);
|
ev1 = hci_recv_event_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
|
||||||
if (ev1) {
|
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 {
|
} else {
|
||||||
sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
|
sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue