Commit 052b30b0 authored by Marcel Holtmann's avatar Marcel Holtmann

Bluetooth: Add different pairing timeout for Legacy Pairing

The Bluetooth stack uses a reference counting for all established ACL
links and if no user (L2CAP connection) is present, the link will be
terminated to save power. The problem part is the dedicated pairing
when using Legacy Pairing (Bluetooth 2.0 and before). At that point
no user is present and pairing attempts will be disconnected within
10 seconds or less. In previous kernel version this was not a problem
since the disconnect timeout wasn't triggered on incoming connections
for the first time. However this caused issues with broken host stacks
that kept the connections around after dedicated pairing. When the
support for Simple Pairing got added, the link establishment procedure
needed to be changed and now causes issues when using Legacy Pairing

When using Simple Pairing it is possible to do a proper reference
counting of ACL link users. With Legacy Pairing this is not possible
since the specification is unclear in some areas and too many broken
Bluetooth devices have already been deployed. So instead of trying to
deal with all the broken devices, a special pairing timeout will be
introduced that increases the timeout to 60 seconds when pairing is
triggered.

If a broken devices now puts the stack into an unforeseen state, the
worst that happens is the disconnect timeout triggers after 120 seconds
instead of 4 seconds. This allows successful pairings with legacy and
broken devices now.

Based on a report by Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent f3784d83
...@@ -101,6 +101,7 @@ enum { ...@@ -101,6 +101,7 @@ enum {
/* HCI timeouts */ /* HCI timeouts */
#define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */ #define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */
#define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */ #define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */
#define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */
#define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ #define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */
#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ #define HCI_INIT_TIMEOUT (10000) /* 10 seconds */
......
...@@ -171,6 +171,7 @@ struct hci_conn { ...@@ -171,6 +171,7 @@ struct hci_conn {
__u8 auth_type; __u8 auth_type;
__u8 sec_level; __u8 sec_level;
__u8 power_save; __u8 power_save;
__u16 disc_timeout;
unsigned long pend; unsigned long pend;
unsigned int sent; unsigned int sent;
...@@ -349,9 +350,9 @@ static inline void hci_conn_put(struct hci_conn *conn) ...@@ -349,9 +350,9 @@ static inline void hci_conn_put(struct hci_conn *conn)
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
del_timer(&conn->idle_timer); del_timer(&conn->idle_timer);
if (conn->state == BT_CONNECTED) { if (conn->state == BT_CONNECTED) {
timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT); timeo = msecs_to_jiffies(conn->disc_timeout);
if (!conn->out) if (!conn->out)
timeo *= 5; timeo *= 2;
} else } else
timeo = msecs_to_jiffies(10); timeo = msecs_to_jiffies(10);
} else } else
......
...@@ -215,6 +215,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ...@@ -215,6 +215,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->state = BT_OPEN; conn->state = BT_OPEN;
conn->power_save = 1; conn->power_save = 1;
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
switch (type) { switch (type) {
case ACL_LINK: case ACL_LINK:
......
...@@ -883,6 +883,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -883,6 +883,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
conn->state = BT_CONFIG; conn->state = BT_CONFIG;
hci_conn_hold(conn); hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
} else } else
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
...@@ -1063,9 +1064,14 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -1063,9 +1064,14 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_proto_connect_cfm(conn, ev->status); hci_proto_connect_cfm(conn, ev->status);
hci_conn_put(conn); hci_conn_put(conn);
} }
} else } else {
hci_auth_cfm(conn, ev->status); hci_auth_cfm(conn, ev->status);
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_put(conn);
}
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
if (!ev->status) { if (!ev->status) {
struct hci_cp_set_conn_encrypt cp; struct hci_cp_set_conn_encrypt cp;
...@@ -1479,7 +1485,21 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb ...@@ -1479,7 +1485,21 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb
static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct hci_ev_pin_code_req *ev = (void *) skb->data;
struct hci_conn *conn;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
hci_conn_hold(conn);
conn->disc_timeout = HCI_PAIRING_TIMEOUT;
hci_conn_put(conn);
}
hci_dev_unlock(hdev);
} }
static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
...@@ -1489,7 +1509,21 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff ...@@ -1489,7 +1509,21 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct hci_ev_link_key_notify *ev = (void *) skb->data;
struct hci_conn *conn;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_put(conn);
}
hci_dev_unlock(hdev);
} }
static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment