summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_conn.c55
-rw-r--r--net/bluetooth/hci_event.c222
-rw-r--r--net/bluetooth/hci_sock.c2
-rw-r--r--net/bluetooth/hci_sync.c254
-rw-r--r--net/bluetooth/iso.c213
-rw-r--r--net/bluetooth/l2cap_sock.c4
-rw-r--r--net/bluetooth/mgmt.c160
-rw-r--r--net/bluetooth/rfcomm/core.c6
-rw-r--r--net/bluetooth/rfcomm/sock.c5
-rw-r--r--net/bluetooth/sco.c4
10 files changed, 737 insertions, 188 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 6fc0692abf05..c3f7828bf9d5 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -922,10 +922,12 @@ static int hci_conn_hash_alloc_unset(struct hci_dev *hdev)
U16_MAX, GFP_ATOMIC);
}
-static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
+static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
+ bdaddr_t *dst, u8 dst_type,
u8 role, u16 handle)
{
struct hci_conn *conn;
+ struct smp_irk *irk = NULL;
switch (type) {
case ACL_LINK:
@@ -937,12 +939,14 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
case PA_LINK:
if (!hdev->iso_mtu)
return ERR_PTR(-ECONNREFUSED);
+ irk = hci_get_irk(hdev, dst, dst_type);
break;
case LE_LINK:
if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
+ irk = hci_get_irk(hdev, dst, dst_type);
break;
case SCO_LINK:
case ESCO_LINK:
@@ -960,7 +964,15 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
if (!conn)
return ERR_PTR(-ENOMEM);
- bacpy(&conn->dst, dst);
+ /* If and IRK exists use its identity address */
+ if (!irk) {
+ bacpy(&conn->dst, dst);
+ conn->dst_type = dst_type;
+ } else {
+ bacpy(&conn->dst, &irk->bdaddr);
+ conn->dst_type = irk->addr_type;
+ }
+
bacpy(&conn->src, &hdev->bdaddr);
conn->handle = handle;
conn->hdev = hdev;
@@ -1059,7 +1071,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
}
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
- bdaddr_t *dst, u8 role)
+ bdaddr_t *dst, u8 dst_type, u8 role)
{
int handle;
@@ -1069,16 +1081,16 @@ struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
if (unlikely(handle < 0))
return ERR_PTR(-ECONNREFUSED);
- return __hci_conn_add(hdev, type, dst, role, handle);
+ return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
- u8 role, u16 handle)
+ u8 dst_type, u8 role, u16 handle)
{
if (handle > HCI_CONN_HANDLE_MAX)
return ERR_PTR(-EINVAL);
- return __hci_conn_add(hdev, type, dst, role, handle);
+ return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
}
static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
@@ -1410,14 +1422,13 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
if (conn) {
bacpy(&conn->dst, dst);
} else {
- conn = hci_conn_add_unset(hdev, LE_LINK, dst, role);
+ conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type, role);
if (IS_ERR(conn))
return conn;
hci_conn_hold(conn);
conn->pending_sec_level = sec_level;
}
- conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout;
conn->le_adv_phy = phy;
@@ -1587,7 +1598,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
memcmp(conn->le_per_adv_data, base, base_len)))
return ERR_PTR(-EADDRINUSE);
- conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
+ conn = hci_conn_add_unset(hdev, BIS_LINK, dst, 0, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@@ -1633,7 +1644,8 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
BT_DBG("requesting refresh of dst_addr");
- conn = hci_conn_add_unset(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
+ conn = hci_conn_add_unset(hdev, LE_LINK, dst, dst_type,
+ HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@@ -1644,7 +1656,6 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
- conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
@@ -1681,7 +1692,8 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
- acl = hci_conn_add_unset(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
+ acl = hci_conn_add_unset(hdev, ACL_LINK, dst, 0,
+ HCI_ROLE_MASTER);
if (IS_ERR(acl))
return acl;
}
@@ -1750,7 +1762,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
if (!sco) {
- sco = hci_conn_add_unset(hdev, type, dst, HCI_ROLE_MASTER);
+ sco = hci_conn_add_unset(hdev, type, dst, 0, HCI_ROLE_MASTER);
if (IS_ERR(sco)) {
hci_conn_drop(acl);
return sco;
@@ -1942,7 +1954,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
qos->ucast.cis);
if (!cis) {
- cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
+ cis = hci_conn_add_unset(hdev, CIS_LINK, dst, dst_type,
HCI_ROLE_MASTER);
if (IS_ERR(cis))
return cis;
@@ -2133,12 +2145,11 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);
- conn = hci_conn_add_unset(hdev, PA_LINK, dst, HCI_ROLE_SLAVE);
+ conn = hci_conn_add_unset(hdev, PA_LINK, dst, dst_type, HCI_ROLE_SLAVE);
if (IS_ERR(conn))
return conn;
conn->iso_qos = *qos;
- conn->dst_type = dst_type;
conn->sid = sid;
conn->state = BT_LISTEN;
conn->conn_timeout = msecs_to_jiffies(qos->bcast.sync_timeout * 10);
@@ -2245,6 +2256,18 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
return conn;
}
+int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type)
+{
+ struct hci_conn *le;
+
+ /* Lookup existing LE connection to rebind to */
+ le = hci_conn_hash_lookup_le(conn->hdev, dst, dst_type);
+ if (!le)
+ return -EINVAL;
+
+ return hci_past_sync(conn, le);
+}
+
static void bis_mark_per_adv(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3838b90343d9..a9868f17ef40 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2267,7 +2267,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
} else {
if (!conn) {
conn = hci_conn_add_unset(hdev, ACL_LINK, &cp->bdaddr,
- HCI_ROLE_MASTER);
+ 0, HCI_ROLE_MASTER);
if (IS_ERR(conn))
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
}
@@ -2886,12 +2886,8 @@ static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
- if (conn) {
- if (conn->state == BT_CONFIG) {
- hci_connect_cfm(conn, status);
- hci_conn_drop(conn);
- }
- }
+ if (conn && conn->state == BT_CONFIG)
+ hci_connect_cfm(conn, status);
hci_dev_unlock(hdev);
}
@@ -3123,7 +3119,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
&ev->bdaddr,
BDADDR_BREDR)) {
conn = hci_conn_add_unset(hdev, ev->link_type,
- &ev->bdaddr, HCI_ROLE_SLAVE);
+ &ev->bdaddr, 0,
+ HCI_ROLE_SLAVE);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
goto unlock;
@@ -3299,7 +3296,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
&ev->bdaddr);
if (!conn) {
- conn = hci_conn_add_unset(hdev, ev->link_type, &ev->bdaddr,
+ conn = hci_conn_add_unset(hdev, ev->link_type, &ev->bdaddr, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
@@ -3914,11 +3911,49 @@ unlock:
return rp->status;
}
+static u8 hci_cc_le_read_all_local_features(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_all_local_features *rp = data;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+ if (rp->status)
+ return rp->status;
+
+ memcpy(hdev->le_features, rp->features, 248);
+
+ return rp->status;
+}
+
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
{
bt_dev_dbg(hdev, "status 0x%2.2x", status);
}
+static void hci_cs_le_read_all_remote_features(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_read_remote_features *cp;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn && conn->state == BT_CONFIG)
+ hci_connect_cfm(conn, status);
+
+ hci_dev_unlock(hdev);
+}
+
static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -4170,6 +4205,9 @@ static const struct hci_cc {
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
sizeof(struct hci_rp_le_setup_iso_path)),
+ HCI_CC(HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
+ hci_cc_le_read_all_local_features,
+ sizeof(struct hci_rp_le_read_all_local_features)),
};
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
@@ -4324,6 +4362,8 @@ static const struct hci_cs {
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
+ HCI_CS(HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
+ hci_cs_le_read_all_remote_features),
};
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
@@ -5644,6 +5684,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
struct hci_conn *conn;
struct smp_irk *irk;
u8 addr_type;
+ int err;
hci_dev_lock(hdev);
@@ -5670,14 +5711,13 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
if (status)
goto unlock;
- conn = hci_conn_add_unset(hdev, LE_LINK, bdaddr, role);
+ conn = hci_conn_add_unset(hdev, LE_LINK, bdaddr, bdaddr_type,
+ role);
if (IS_ERR(conn)) {
bt_dev_err(hdev, "connection err: %ld", PTR_ERR(conn));
goto unlock;
}
- conn->dst_type = bdaddr_type;
-
/* If we didn't have a hci_conn object previously
* but we're in central role this must be something
* initiated using an accept list. Since accept list based
@@ -5775,26 +5815,8 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
- /* The remote features procedure is defined for central
- * role only. So only in case of an initiated connection
- * request the remote features.
- *
- * If the local controller supports peripheral-initiated features
- * exchange, then requesting the remote features in peripheral
- * role is possible. Otherwise just transition into the
- * connected state without requesting the remote features.
- */
- if (conn->out ||
- (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
- struct hci_cp_le_read_remote_features cp;
-
- cp.handle = __cpu_to_le16(conn->handle);
-
- hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
- sizeof(cp), &cp);
-
- hci_conn_hold(conn);
- } else {
+ err = hci_le_read_remote_features(conn);
+ if (err) {
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
}
@@ -5936,6 +5958,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, 0,
+ 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 +6499,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)
{
@@ -6460,7 +6537,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
goto unlock;
/* Add connection to indicate PA sync event */
- pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
+ pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
@@ -6553,7 +6630,6 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
- hci_conn_drop(conn);
}
}
@@ -6901,7 +6977,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
if (!cis) {
- cis = hci_conn_add(hdev, CIS_LINK, &acl->dst,
+ cis = hci_conn_add(hdev, CIS_LINK, &acl->dst, acl->dst_type,
HCI_ROLE_SLAVE, cis_handle);
if (IS_ERR(cis)) {
hci_le_reject_cis(hdev, ev->cis_handle);
@@ -7018,7 +7094,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "ignore too large handle %u", handle);
continue;
}
- bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY,
+ bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE, handle);
if (IS_ERR(bis))
continue;
@@ -7131,6 +7207,50 @@ unlock:
hci_dev_unlock(hdev);
}
+static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
+ void *data, struct sk_buff *skb)
+{
+ struct hci_evt_le_read_all_remote_features_complete *ev = data;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (!conn)
+ goto unlock;
+
+ if (!ev->status)
+ memcpy(conn->le_features, ev->features, 248);
+
+ if (conn->state == BT_CONFIG) {
+ __u8 status;
+
+ /* If the local controller supports peripheral-initiated
+ * features exchange, but the remote controller does
+ * not, then it is possible that the error code 0x1a
+ * for unsupported remote feature gets returned.
+ *
+ * In this specific case, allow the connection to
+ * transition into connected state and mark it as
+ * successful.
+ */
+ if (!conn->out &&
+ ev->status == HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE &&
+ (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
+ status = 0x00;
+ else
+ status = ev->status;
+
+ conn->state = BT_CONNECTED;
+ hci_connect_cfm(conn, status);
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
[_op] = { \
.func = _func, \
@@ -7206,6 +7326,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)),
@@ -7232,6 +7356,12 @@ static const struct hci_le_ev {
hci_le_big_info_adv_report_evt,
sizeof(struct hci_evt_le_big_info_adv_report),
HCI_MAX_EVENT_SIZE),
+ /* [0x2b = HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE] */
+ HCI_LE_EV_VL(HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
+ hci_le_read_all_remote_features_evt,
+ sizeof(struct
+ hci_evt_le_read_all_remote_features_complete),
+ HCI_MAX_EVENT_SIZE),
};
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index ad19022ae127..4e7bf63af9c5 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -1185,7 +1185,7 @@ static int hci_sock_compat_ioctl(struct socket *sock, unsigned int cmd,
}
#endif
-static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
+static int hci_sock_bind(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len)
{
struct sockaddr_hci haddr;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 6e76798ec786..a9f5b1a68356 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -4011,8 +4011,19 @@ static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
/* Read LE Local Supported Features */
static int hci_le_read_local_features_sync(struct hci_dev *hdev)
{
- return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
- 0, NULL, HCI_CMD_TIMEOUT);
+ int err;
+
+ err = __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
+ 0, NULL, HCI_CMD_TIMEOUT);
+ if (err)
+ return err;
+
+ if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(2))
+ return __hci_cmd_sync_status(hdev,
+ HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
+ 0, NULL, HCI_CMD_TIMEOUT);
+
+ return err;
}
/* Read LE Supported States */
@@ -4324,6 +4335,10 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (ll_privacy_capable(hdev))
hdev->conn_flags |= HCI_CONN_FLAG_ADDRESS_RESOLUTION;
+ /* Mark PAST if supported */
+ if (past_capable(hdev))
+ hdev->conn_flags |= HCI_CONN_FLAG_PAST;
+
/* If the controller supports Extended Scanner Filter
* Policies, enable the corresponding event.
*/
@@ -4393,6 +4408,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))
@@ -7006,7 +7024,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
goto unlock;
/* Add connection to indicate PA sync error */
- pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
+ pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, 0,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
@@ -7021,10 +7039,41 @@ unlock:
hci_dev_unlock(hdev);
}
+static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
+ struct hci_conn *acl, struct bt_iso_qos *qos)
+{
+ struct hci_cp_le_past_params cp;
+ int err;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(acl->handle);
+ /* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent
+ * to the Host. HCI_LE_Periodic_Advertising_Report events will be
+ * enabled with duplicate filtering enabled.
+ */
+ cp.mode = 0x03;
+ cp.skip = cpu_to_le16(qos->bcast.skip);
+ cp.sync_timeout = cpu_to_le16(qos->bcast.sync_timeout);
+ cp.cte_type = qos->bcast.sync_cte_type;
+
+ /* HCI_LE_PAST_PARAMS command returns a command complete event so it
+ * cannot wait for HCI_EV_LE_PAST_RECEIVED.
+ */
+ err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST_PARAMS,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+ if (err)
+ return err;
+
+ /* Wait for HCI_EV_LE_PAST_RECEIVED event */
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, 0, NULL,
+ HCI_EV_LE_PAST_RECEIVED,
+ conn->conn_timeout, NULL);
+}
+
static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
{
struct hci_cp_le_pa_create_sync cp;
- struct hci_conn *conn = data;
+ struct hci_conn *conn = data, *le;
struct bt_iso_qos *qos = &conn->iso_qos;
int err;
@@ -7056,6 +7105,24 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
hci_update_passive_scan_sync(hdev);
+ /* Check if PAST is possible:
+ *
+ * 1. Check if an ACL connection with the destination address exists
+ * 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that
+ * user really intended to use PAST.
+ */
+ le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type);
+ if (le) {
+ struct hci_conn_params *params;
+
+ params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type);
+ if (params && params->flags & HCI_CONN_FLAG_PAST) {
+ err = hci_le_past_params_sync(hdev, conn, le, qos);
+ if (!err)
+ goto done;
+ }
+ }
+
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
* it.
*/
@@ -7172,3 +7239,182 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
create_big_complete);
}
+
+struct past_data {
+ struct hci_conn *conn;
+ struct hci_conn *le;
+};
+
+static void past_complete(struct hci_dev *hdev, void *data, int err)
+{
+ struct past_data *past = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(past);
+}
+
+static int hci_le_past_set_info_sync(struct hci_dev *hdev, void *data)
+{
+ struct past_data *past = data;
+ struct hci_cp_le_past_set_info cp;
+
+ hci_dev_lock(hdev);
+
+ if (!hci_conn_valid(hdev, past->conn) ||
+ !hci_conn_valid(hdev, past->le)) {
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(past->le->handle);
+ cp.adv_handle = past->conn->iso_qos.bcast.bis;
+
+ hci_dev_unlock(hdev);
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST_SET_INFO,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
+static int hci_le_past_sync(struct hci_dev *hdev, void *data)
+{
+ struct past_data *past = data;
+ struct hci_cp_le_past cp;
+
+ hci_dev_lock(hdev);
+
+ if (!hci_conn_valid(hdev, past->conn) ||
+ !hci_conn_valid(hdev, past->le)) {
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(past->le->handle);
+ cp.sync_handle = cpu_to_le16(past->conn->sync_handle);
+
+ hci_dev_unlock(hdev);
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
+int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
+{
+ struct past_data *data;
+ int err;
+
+ if (conn->type != BIS_LINK && conn->type != PA_LINK)
+ return -EINVAL;
+
+ if (!past_sender_capable(conn->hdev))
+ return -EOPNOTSUPP;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->conn = conn;
+ data->le = le;
+
+ if (conn->role == HCI_ROLE_MASTER)
+ err = hci_cmd_sync_queue_once(conn->hdev,
+ hci_le_past_set_info_sync, data,
+ past_complete);
+ else
+ err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
+ data, past_complete);
+
+ if (err)
+ kfree(data);
+
+ return err;
+}
+
+static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_conn *conn = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ if (err == -ECANCELED)
+ return;
+
+ hci_conn_drop(conn);
+}
+
+static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
+ void *data)
+{
+ struct hci_conn *conn = data;
+ struct hci_cp_le_read_all_remote_features cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.pages = 10; /* Attempt to read all pages */
+
+ /* Wait for HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE event otherwise
+ * hci_conn_drop may run prematurely causing a disconnection.
+ */
+ return __hci_cmd_sync_status_sk(hdev,
+ HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
+ sizeof(cp), &cp,
+ HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
+ HCI_CMD_TIMEOUT, NULL);
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
+static int hci_le_read_remote_features_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_conn *conn = data;
+ struct hci_cp_le_read_remote_features cp;
+
+ if (!hci_conn_valid(hdev, conn))
+ return -ECANCELED;
+
+ /* Check if LL Extended Feature Set is supported and
+ * HCI_OP_LE_READ_ALL_REMOTE_FEATURES is supported then use that to read
+ * all features.
+ */
+ if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(3))
+ return hci_le_read_all_remote_features_sync(hdev, data);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cpu_to_le16(conn->handle);
+
+ /* Wait for HCI_EV_LE_REMOTE_FEAT_COMPLETE event otherwise
+ * hci_conn_drop may run prematurely causing a disconnection.
+ */
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
+ sizeof(cp), &cp,
+ HCI_EV_LE_REMOTE_FEAT_COMPLETE,
+ HCI_CMD_TIMEOUT, NULL);
+}
+
+int hci_le_read_remote_features(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ int err;
+
+ /* The remote features procedure is defined for central
+ * role only. So only in case of an initiated connection
+ * request the remote features.
+ *
+ * If the local controller supports peripheral-initiated features
+ * exchange, then requesting the remote features in peripheral
+ * role is possible. Otherwise just transition into the
+ * connected state without requesting the remote features.
+ */
+ if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
+ err = hci_cmd_sync_queue_once(hdev,
+ hci_le_read_remote_features_sync,
+ hci_conn_hold(conn),
+ le_read_features_complete);
+ else
+ err = -EOPNOTSUPP;
+
+ return err;
+}
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 616c2fef91d2..e36d24a9098b 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -80,14 +80,15 @@ 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);
typedef bool (*iso_sock_match_t)(struct sock *sk, void *data);
-static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
- enum bt_sock_state state,
+static struct sock *iso_get_sock(struct hci_dev *hdev, bdaddr_t *src,
+ bdaddr_t *dst, enum bt_sock_state state,
iso_sock_match_t match, void *data);
/* ---- ISO timers ---- */
@@ -637,8 +638,8 @@ static struct sock *__iso_get_sock_listen_by_sid(bdaddr_t *ba, bdaddr_t *bc,
* match func data - pass -1 to ignore
* Returns closest match.
*/
-static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
- enum bt_sock_state state,
+static struct sock *iso_get_sock(struct hci_dev *hdev, bdaddr_t *src,
+ bdaddr_t *dst, enum bt_sock_state state,
iso_sock_match_t match, void *data)
{
struct sock *sk = NULL, *sk1 = NULL;
@@ -650,8 +651,25 @@ static struct sock *iso_get_sock(bdaddr_t *src, bdaddr_t *dst,
continue;
/* Match Broadcast destination */
- if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst))
- continue;
+ if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst)) {
+ struct smp_irk *irk1, *irk2;
+
+ /* Check if destination is an RPA that we can resolve */
+ irk1 = hci_find_irk_by_rpa(hdev, dst);
+ if (!irk1)
+ continue;
+
+ /* Match with identity address */
+ if (bacmp(&iso_pi(sk)->dst, &irk1->bdaddr)) {
+ /* Check if socket destination address is also
+ * an RPA and if the IRK matches.
+ */
+ irk2 = hci_find_irk_by_rpa(hdev,
+ &iso_pi(sk)->dst);
+ if (!irk2 || irk1 != irk2)
+ continue;
+ }
+ }
/* Use Match function if provided */
if (match && !match(sk, data))
@@ -944,7 +962,7 @@ static int iso_sock_create(struct net *net, struct socket *sock, int protocol,
return 0;
}
-static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
+static int iso_sock_bind_bc(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len)
{
struct sockaddr_iso *sa = (struct sockaddr_iso *)addr;
@@ -986,20 +1004,14 @@ static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
return 0;
}
-static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
+/* Must be called on the locked socket. */
+static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
int addr_len)
{
int err = 0;
- if (sk->sk_type != SOCK_SEQPACKET) {
- err = -EINVAL;
- goto done;
- }
-
- if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc)) {
- err = -EINVAL;
- goto done;
- }
+ if (!test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
+ return -EBADFD;
if (sa->iso_bc->bc_num_bis > ISO_MAX_NUM_BIS) {
err = -EINVAL;
@@ -1022,7 +1034,78 @@ done:
return err;
}
-static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
+static struct hci_dev *iso_conn_get_hdev(struct iso_conn *conn)
+{
+ struct hci_dev *hdev = NULL;
+
+ iso_conn_lock(conn);
+ if (conn->hcon)
+ hdev = hci_dev_hold(conn->hcon->hdev);
+ iso_conn_unlock(conn);
+
+ return hdev;
+}
+
+/* Must be called on the locked socket. */
+static int iso_sock_rebind_bc(struct sock *sk, struct sockaddr_iso *sa,
+ int addr_len)
+{
+ struct hci_dev *hdev;
+ struct hci_conn *bis;
+ int err;
+
+ if (sk->sk_type != SOCK_SEQPACKET || !iso_pi(sk)->conn)
+ return -EINVAL;
+
+ /* Check if it is really a Broadcast address being requested */
+ if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc))
+ return -EINVAL;
+
+ /* Check if the address hasn't changed then perhaps only the number of
+ * bis has changed.
+ */
+ if (!bacmp(&iso_pi(sk)->dst, &sa->iso_bc->bc_bdaddr) ||
+ !bacmp(&sa->iso_bc->bc_bdaddr, BDADDR_ANY))
+ return iso_sock_rebind_bis(sk, sa, addr_len);
+
+ /* Check if the address type is of LE type */
+ if (!bdaddr_type_is_le(sa->iso_bc->bc_bdaddr_type))
+ return -EINVAL;
+
+ hdev = iso_conn_get_hdev(iso_pi(sk)->conn);
+ if (!hdev)
+ return -EINVAL;
+
+ bis = iso_pi(sk)->conn->hcon;
+
+ /* Release the socket before lookups since that requires hci_dev_lock
+ * which shall not be acquired while holding sock_lock for proper
+ * ordering.
+ */
+ release_sock(sk);
+ hci_dev_lock(bis->hdev);
+ lock_sock(sk);
+
+ if (!iso_pi(sk)->conn || iso_pi(sk)->conn->hcon != bis) {
+ /* raced with iso_conn_del() or iso_disconn_sock() */
+ err = -ENOTCONN;
+ goto unlock;
+ }
+
+ BT_DBG("sk %p %pMR type %u", sk, &sa->iso_bc->bc_bdaddr,
+ sa->iso_bc->bc_bdaddr_type);
+
+ err = hci_past_bis(bis, &sa->iso_bc->bc_bdaddr,
+ le_addr_type(sa->iso_bc->bc_bdaddr_type));
+
+unlock:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int iso_sock_bind(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len)
{
struct sockaddr_iso *sa = (struct sockaddr_iso *)addr;
@@ -1037,13 +1120,12 @@ static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
lock_sock(sk);
- /* Allow the user to bind a PA sync socket to a number
- * of BISes to sync to.
- */
- if ((sk->sk_state == BT_CONNECT2 ||
- sk->sk_state == BT_CONNECTED) &&
- test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
- err = iso_sock_bind_pa_sk(sk, sa, addr_len);
+ if ((sk->sk_state == BT_CONNECT2 || sk->sk_state == BT_CONNECTED) &&
+ addr_len > sizeof(*sa)) {
+ /* Allow the user to rebind to a different address using
+ * PAST procedures.
+ */
+ err = iso_sock_rebind_bc(sk, sa, addr_len);
goto done;
}
@@ -1080,7 +1162,7 @@ done:
return err;
}
-static int iso_sock_connect(struct socket *sock, struct sockaddr *addr,
+static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
int alen, int flags)
{
struct sockaddr_iso *sa = (struct sockaddr_iso *)addr;
@@ -1939,6 +2021,11 @@ static bool iso_match_pa_sync_flag(struct sock *sk, void *data)
return test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
}
+static bool iso_match_dst(struct sock *sk, void *data)
+{
+ return !bacmp(&iso_pi(sk)->dst, (bdaddr_t *)data);
+}
+
static void iso_conn_ready(struct iso_conn *conn)
{
struct sock *parent = NULL;
@@ -1947,23 +2034,45 @@ static void iso_conn_ready(struct iso_conn *conn)
struct hci_ev_le_pa_sync_established *ev2 = NULL;
struct hci_ev_le_per_adv_report *ev3 = NULL;
struct hci_conn *hcon;
+ struct hci_dev *hdev;
BT_DBG("conn %p", conn);
if (sk) {
+ /* Attempt to update source address in case of BIS Sender if
+ * the advertisement is using a random address.
+ */
+ if (conn->hcon->type == BIS_LINK &&
+ conn->hcon->role == HCI_ROLE_MASTER &&
+ !bacmp(&conn->hcon->dst, BDADDR_ANY)) {
+ struct hci_conn *bis = conn->hcon;
+ struct adv_info *adv;
+
+ adv = hci_find_adv_instance(bis->hdev,
+ bis->iso_qos.bcast.bis);
+ if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
+ lock_sock(sk);
+ iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
+ bacpy(&iso_pi(sk)->src, &adv->random_addr);
+ release_sock(sk);
+ }
+ }
+
iso_sock_ready(conn->sk);
} else {
hcon = conn->hcon;
if (!hcon)
return;
+ hdev = hcon->hdev;
+
if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags)) {
/* A BIS slave hcon is notified to the ISO layer
* after the Command Complete for the LE Setup
* ISO Data Path command is received. Get the
* parent socket that matches the hcon BIG handle.
*/
- parent = iso_get_sock(&hcon->src, &hcon->dst,
+ parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN, iso_match_big_hcon,
hcon);
} else if (test_bit(HCI_CONN_BIG_SYNC_FAILED, &hcon->flags)) {
@@ -1971,12 +2080,12 @@ static void iso_conn_ready(struct iso_conn *conn)
HCI_EVT_LE_BIG_SYNC_ESTABLISHED);
/* Get reference to PA sync parent socket, if it exists */
- parent = iso_get_sock(&hcon->src, &hcon->dst,
+ parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN,
iso_match_pa_sync_flag,
NULL);
if (!parent && ev)
- parent = iso_get_sock(&hcon->src,
+ parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_big, ev);
@@ -1984,7 +2093,7 @@ static void iso_conn_ready(struct iso_conn *conn)
ev2 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev2)
- parent = iso_get_sock(&hcon->src,
+ parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_sid, ev2);
@@ -1992,7 +2101,7 @@ static void iso_conn_ready(struct iso_conn *conn)
ev3 = hci_recv_event_data(hcon->hdev,
HCI_EV_LE_PER_ADV_REPORT);
if (ev3)
- parent = iso_get_sock(&hcon->src,
+ parent = iso_get_sock(hdev, &hcon->src,
&hcon->dst,
BT_LISTEN,
iso_match_sync_handle_pa_report,
@@ -2000,8 +2109,8 @@ static void iso_conn_ready(struct iso_conn *conn)
}
if (!parent)
- parent = iso_get_sock(&hcon->src, BDADDR_ANY,
- BT_LISTEN, NULL, NULL);
+ parent = iso_get_sock(hdev, &hcon->src, BDADDR_ANY,
+ BT_LISTEN, iso_match_dst, BDADDR_ANY);
if (!parent)
return;
@@ -2090,6 +2199,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 +2228,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 +2242,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.
@@ -2131,7 +2252,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
*/
ev1 = hci_recv_event_data(hdev, HCI_EV_LE_PA_SYNC_ESTABLISHED);
if (ev1) {
- sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
+ sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sid, ev1);
if (sk && !ev1->status) {
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
@@ -2141,10 +2262,22 @@ 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, &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 */
- sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_CONNECTED,
+ sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_CONNECTED,
iso_match_sync_handle, ev2);
if (sk) {
sock_put(sk);
@@ -2153,10 +2286,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
}
/* Try to get PA sync socket, if it exists */
- sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_CONNECT2,
+ sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_CONNECT2,
iso_match_sync_handle, ev2);
if (!sk)
- sk = iso_get_sock(&hdev->bdaddr, bdaddr,
+ sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr,
BT_LISTEN,
iso_match_sync_handle,
ev2);
@@ -2195,7 +2328,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
u8 *base;
struct hci_conn *hcon;
- sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
+ sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sync_handle_pa_report, ev3);
if (!sk)
goto done;
@@ -2245,8 +2378,8 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
hcon->le_per_adv_data_len = 0;
}
} else {
- sk = iso_get_sock(&hdev->bdaddr, BDADDR_ANY,
- BT_LISTEN, NULL, NULL);
+ sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
+ BT_LISTEN, iso_match_dst, BDADDR_ANY);
}
done:
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 814fb8610ac4..9ee189c815d4 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -80,7 +80,7 @@ static int l2cap_validate_le_psm(u16 psm)
return 0;
}
-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
+static int l2cap_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int alen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -178,7 +178,7 @@ done:
return err;
}
-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
+static int l2cap_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
int alen, int flags)
{
struct sock *sk = sock->sk;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 262bf984d2aa..c11cdef42b6f 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -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;
}
@@ -5110,6 +5122,69 @@ static void device_flags_changed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_FLAGS_CHANGED, hdev, &ev, sizeof(ev), sk);
}
+static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
+{
+ struct hci_conn *conn;
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+ if (!conn)
+ return false;
+
+ if (conn->dst_type != type)
+ return false;
+
+ if (conn->state != BT_CONNECTED)
+ return false;
+
+ return true;
+}
+
+/* This function requires the caller holds hdev->lock */
+static struct hci_conn_params *hci_conn_params_set(struct hci_dev *hdev,
+ bdaddr_t *addr, u8 addr_type,
+ u8 auto_connect)
+{
+ struct hci_conn_params *params;
+
+ params = hci_conn_params_add(hdev, addr, addr_type);
+ if (!params)
+ return NULL;
+
+ if (params->auto_connect == auto_connect)
+ return params;
+
+ hci_pend_le_list_del_init(params);
+
+ switch (auto_connect) {
+ case HCI_AUTO_CONN_DISABLED:
+ case HCI_AUTO_CONN_LINK_LOSS:
+ /* If auto connect is being disabled when we're trying to
+ * connect to device, keep connecting.
+ */
+ if (params->explicit_connect)
+ hci_pend_le_list_add(params, &hdev->pend_le_conns);
+ break;
+ case HCI_AUTO_CONN_REPORT:
+ if (params->explicit_connect)
+ hci_pend_le_list_add(params, &hdev->pend_le_conns);
+ else
+ hci_pend_le_list_add(params, &hdev->pend_le_reports);
+ break;
+ case HCI_AUTO_CONN_DIRECT:
+ case HCI_AUTO_CONN_ALWAYS:
+ if (!is_connected(hdev, addr, addr_type))
+ hci_pend_le_list_add(params, &hdev->pend_le_conns);
+ break;
+ }
+
+ params->auto_connect = auto_connect;
+
+ bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u",
+ addr, addr_type, auto_connect);
+
+ return params;
+}
+
static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
@@ -5153,9 +5228,16 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
if (!params) {
- bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
- &cp->addr.bdaddr, le_addr_type(cp->addr.type));
- goto unlock;
+ /* Create a new hci_conn_params if it doesn't exist */
+ params = hci_conn_params_set(hdev, &cp->addr.bdaddr,
+ le_addr_type(cp->addr.type),
+ HCI_AUTO_CONN_DISABLED);
+ if (!params) {
+ bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
+ &cp->addr.bdaddr,
+ le_addr_type(cp->addr.type));
+ goto unlock;
+ }
}
supported_flags = hdev->conn_flags;
@@ -7542,68 +7624,6 @@ unlock:
return err;
}
-static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
-{
- struct hci_conn *conn;
-
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
- if (!conn)
- return false;
-
- if (conn->dst_type != type)
- return false;
-
- if (conn->state != BT_CONNECTED)
- return false;
-
- return true;
-}
-
-/* This function requires the caller holds hdev->lock */
-static int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr,
- u8 addr_type, u8 auto_connect)
-{
- struct hci_conn_params *params;
-
- params = hci_conn_params_add(hdev, addr, addr_type);
- if (!params)
- return -EIO;
-
- if (params->auto_connect == auto_connect)
- return 0;
-
- hci_pend_le_list_del_init(params);
-
- switch (auto_connect) {
- case HCI_AUTO_CONN_DISABLED:
- case HCI_AUTO_CONN_LINK_LOSS:
- /* If auto connect is being disabled when we're trying to
- * connect to device, keep connecting.
- */
- if (params->explicit_connect)
- hci_pend_le_list_add(params, &hdev->pend_le_conns);
- break;
- case HCI_AUTO_CONN_REPORT:
- if (params->explicit_connect)
- hci_pend_le_list_add(params, &hdev->pend_le_conns);
- else
- hci_pend_le_list_add(params, &hdev->pend_le_reports);
- break;
- case HCI_AUTO_CONN_DIRECT:
- case HCI_AUTO_CONN_ALWAYS:
- if (!is_connected(hdev, addr, addr_type))
- hci_pend_le_list_add(params, &hdev->pend_le_conns);
- break;
- }
-
- params->auto_connect = auto_connect;
-
- bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u",
- addr, addr_type, auto_connect);
-
- return 0;
-}
-
static void device_added(struct sock *sk, struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type, u8 action)
{
@@ -7715,17 +7735,13 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
- if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
- auto_conn) < 0) {
+ params = hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
+ auto_conn);
+ if (!params) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_FAILED, &cp->addr,
sizeof(cp->addr));
goto unlock;
- } else {
- params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
- addr_type);
- if (params)
- current_flags = params->flags;
}
cmd = mgmt_pending_new(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 96250807b32b..57b1dca8141f 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -781,7 +781,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
addr.l2_psm = 0;
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
- *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+ *err = kernel_bind(sock, (struct sockaddr_unsized *)&addr, sizeof(addr));
if (*err < 0)
goto failed;
@@ -808,7 +808,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
- *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
+ *err = kernel_connect(sock, (struct sockaddr_unsized *)&addr, sizeof(addr), O_NONBLOCK);
if (*err == 0 || *err == -EINPROGRESS)
return s;
@@ -2068,7 +2068,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
- err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+ err = kernel_bind(sock, (struct sockaddr_unsized *)&addr, sizeof(addr));
if (err < 0) {
BT_ERR("Bind failed %d", err);
goto failed;
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 913402806fa0..be6639cd6f59 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -324,7 +324,7 @@ static int rfcomm_sock_create(struct net *net, struct socket *sock,
return 0;
}
-static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+static int rfcomm_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len)
{
struct sockaddr_rc sa;
struct sock *sk = sock->sk;
@@ -371,7 +371,8 @@ done:
return err;
}
-static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+static int rfcomm_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
+ int alen, int flags)
{
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
struct sock *sk = sock->sk;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 298c2a9ab4df..87ba90336e80 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -605,7 +605,7 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol,
return 0;
}
-static int sco_sock_bind(struct socket *sock, struct sockaddr *addr,
+static int sco_sock_bind(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len)
{
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
@@ -639,7 +639,7 @@ done:
return err;
}
-static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, int alen, int flags)
{
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
struct sock *sk = sock->sk;