summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/bluetooth/hci.h2
-rw-r--r--include/net/bluetooth/hci_core.h3
-rw-r--r--net/bluetooth/hci_conn.c2
-rw-r--r--net/bluetooth/hci_core.c7
-rw-r--r--net/bluetooth/hci_event.c127
5 files changed, 118 insertions, 23 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 5ac0a18db63..55576e84882 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -180,6 +180,8 @@ enum {
#define LMP_SNIFF_SUBR 0x02
+#define LMP_SIMPLE_PAIR 0x08
+
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
#define HCI_CM_HOLD 0x0001
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index f73cc294570..28fbd0caa53 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -348,7 +348,7 @@ static inline void hci_conn_put(struct hci_conn *conn)
if (conn->state == BT_CONNECTED) {
timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
if (!conn->out)
- timeo *= 2;
+ timeo *= 5;
} else
timeo = msecs_to_jiffies(10);
} else
@@ -463,6 +463,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF)
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
+#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
/* ----- HCI protocols ----- */
struct hci_proto {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 41351ba692e..6f22533e765 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -170,11 +170,13 @@ static void hci_conn_timeout(unsigned long arg)
switch (conn->state) {
case BT_CONNECT:
+ case BT_CONNECT2:
if (conn->type == ACL_LINK)
hci_acl_connect_cancel(conn);
else
hci_acl_disconn(conn, 0x13);
break;
+ case BT_CONFIG:
case BT_CONNECTED:
hci_acl_disconn(conn, 0x13);
break;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 69b2c1aac08..f5b21cb9369 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1283,9 +1283,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
- if (c->type != type || c->state != BT_CONNECTED
- || skb_queue_empty(&c->data_q))
+ if (c->type != type || skb_queue_empty(&c->data_q))
continue;
+
+ if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
+ continue;
+
num++;
if (c->sent < min) {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index c8fda7dc298..e3e360c3c53 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -624,6 +624,62 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
BT_DBG("%s status 0x%x", hdev->name, status);
}
+static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_read_remote_features *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_read_remote_ext_features *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_put(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_setup_sync_conn *cp;
@@ -759,7 +815,12 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (!ev->status) {
conn->handle = __le16_to_cpu(ev->handle);
- conn->state = BT_CONNECTED;
+
+ if (conn->type == ACL_LINK) {
+ conn->state = BT_CONFIG;
+ hci_conn_hold(conn);
+ } else
+ conn->state = BT_CONNECTED;
if (test_bit(HCI_AUTH, &hdev->flags))
conn->link_mode |= HCI_LM_AUTH;
@@ -771,7 +832,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (conn->type == ACL_LINK) {
struct hci_cp_read_remote_features cp;
cp.handle = ev->handle;
- hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
+ sizeof(cp), &cp);
}
/* Set packet type for incoming connection */
@@ -781,10 +843,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
cp.pkt_type = cpu_to_le16(conn->pkt_type);
hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
sizeof(cp), &cp);
- } else {
- /* Update disconnect timer */
- hci_conn_hold(conn);
- hci_conn_put(conn);
}
} else
conn->state = BT_CLOSED;
@@ -804,9 +862,10 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
}
- hci_proto_connect_cfm(conn, ev->status);
- if (ev->status)
+ if (ev->status) {
+ hci_proto_connect_cfm(conn, ev->status);
hci_conn_del(conn);
+ }
unlock:
hci_dev_unlock(hdev);
@@ -1006,14 +1065,29 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
- if (ev->status)
- return;
-
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
- if (conn)
- memcpy(conn->features, ev->features, 8);
+ if (conn) {
+ if (!ev->status)
+ memcpy(conn->features, ev->features, 8);
+
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status && lmp_ssp_capable(hdev) &&
+ lmp_ssp_capable(conn)) {
+ struct hci_cp_read_remote_ext_features cp;
+ cp.handle = ev->handle;
+ cp.page = 0x01;
+ hci_send_cmd(hdev,
+ HCI_OP_READ_REMOTE_EXT_FEATURES,
+ sizeof(cp), &cp);
+ } else {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ }
+ }
+ }
hci_dev_unlock(hdev);
}
@@ -1180,6 +1254,14 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_remote_name_req(hdev, ev->status);
break;
+ case HCI_OP_READ_REMOTE_FEATURES:
+ hci_cs_read_remote_features(hdev, ev->status);
+ break;
+
+ case HCI_OP_READ_REMOTE_EXT_FEATURES:
+ hci_cs_read_remote_ext_features(hdev, ev->status);
+ break;
+
case HCI_OP_SETUP_SYNC_CONN:
hci_cs_setup_sync_conn(hdev, ev->status);
break;
@@ -1422,19 +1504,24 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
BT_DBG("%s", hdev->name);
- if (ev->status || ev->page != 0x01)
- return;
-
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
- struct inquiry_entry *ie;
+ if (!ev->status && ev->page == 0x01) {
+ struct inquiry_entry *ie;
- if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)))
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst)))
+ ie->data.ssp_mode = (ev->features[0] & 0x01);
- conn->ssp_mode = (ev->features[0] & 0x01);
+ conn->ssp_mode = (ev->features[0] & 0x01);
+ }
+
+ if (conn->state == BT_CONFIG) {
+ conn->state = BT_CONNECTED;
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_put(conn);
+ }
}
hci_dev_unlock(hdev);