Commit 6ac59344 authored by Marcel Holtmann's avatar Marcel Holtmann Committed by David S. Miller

[Bluetooth] Support create connection cancel command

In case of non-blocking connects it is possible that the last user
of an ACL link quits before the connection has been fully established.
This will lead to a race condition where the internal state of a
connection is closed, but the actual link has been established and is
active. In case of Bluetooth 1.2 and later devices it is possible to
call create connection cancel to abort the connect. For older devices
the disconnect timer will be used to trigger the needed disconnect.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 1143e5a6
...@@ -297,6 +297,7 @@ struct hci_cp_host_buffer_size { ...@@ -297,6 +297,7 @@ struct hci_cp_host_buffer_size {
/* Link Control */ /* Link Control */
#define OGF_LINK_CTL 0x01 #define OGF_LINK_CTL 0x01
#define OCF_CREATE_CONN 0x0005 #define OCF_CREATE_CONN 0x0005
struct hci_cp_create_conn { struct hci_cp_create_conn {
bdaddr_t bdaddr; bdaddr_t bdaddr;
...@@ -307,6 +308,11 @@ struct hci_cp_create_conn { ...@@ -307,6 +308,11 @@ struct hci_cp_create_conn {
__u8 role_switch; __u8 role_switch;
} __attribute__ ((packed)); } __attribute__ ((packed));
#define OCF_CREATE_CONN_CANCEL 0x0008
struct hci_cp_create_conn_cancel {
bdaddr_t bdaddr;
} __attribute__ ((packed));
#define OCF_ACCEPT_CONN_REQ 0x0009 #define OCF_ACCEPT_CONN_REQ 0x0009
struct hci_cp_accept_conn_req { struct hci_cp_accept_conn_req {
bdaddr_t bdaddr; bdaddr_t bdaddr;
......
...@@ -316,10 +316,13 @@ static inline void hci_conn_put(struct hci_conn *conn) ...@@ -316,10 +316,13 @@ static inline void hci_conn_put(struct hci_conn *conn)
if (atomic_dec_and_test(&conn->refcnt)) { if (atomic_dec_and_test(&conn->refcnt)) {
unsigned long timeo; unsigned long timeo;
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
if (!conn->out)
timeo *= 2;
del_timer(&conn->idle_timer); del_timer(&conn->idle_timer);
if (conn->state == BT_CONNECTED) {
timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
if (!conn->out)
timeo *= 2;
} else
timeo = msecs_to_jiffies(10);
} else } else
timeo = msecs_to_jiffies(10); timeo = msecs_to_jiffies(10);
mod_timer(&conn->disc_timer, jiffies + timeo); mod_timer(&conn->disc_timer, jiffies + timeo);
......
...@@ -84,6 +84,20 @@ static void hci_acl_connect(struct hci_conn *conn) ...@@ -84,6 +84,20 @@ static void hci_acl_connect(struct hci_conn *conn)
hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, sizeof(cp), &cp); hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, sizeof(cp), &cp);
} }
static void hci_acl_connect_cancel(struct hci_conn *conn)
{
struct hci_cp_create_conn_cancel cp;
BT_DBG("%p", conn);
if (conn->hdev->hci_ver < 2)
return;
bacpy(&cp.bdaddr, &conn->dst);
hci_send_cmd(conn->hdev, OGF_LINK_CTL,
OCF_CREATE_CONN_CANCEL, sizeof(cp), &cp);
}
void hci_acl_disconn(struct hci_conn *conn, __u8 reason) void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
{ {
struct hci_cp_disconnect cp; struct hci_cp_disconnect cp;
...@@ -94,7 +108,8 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) ...@@ -94,7 +108,8 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
cp.handle = __cpu_to_le16(conn->handle); cp.handle = __cpu_to_le16(conn->handle);
cp.reason = reason; cp.reason = reason;
hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, sizeof(cp), &cp); hci_send_cmd(conn->hdev, OGF_LINK_CTL,
OCF_DISCONNECT, sizeof(cp), &cp);
} }
void hci_add_sco(struct hci_conn *conn, __u16 handle) void hci_add_sco(struct hci_conn *conn, __u16 handle)
...@@ -124,12 +139,20 @@ static void hci_conn_timeout(unsigned long arg) ...@@ -124,12 +139,20 @@ static void hci_conn_timeout(unsigned long arg)
return; return;
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (conn->state == BT_CONNECTED)
switch (conn->state) {
case BT_CONNECT:
hci_acl_connect_cancel(conn);
break;
case BT_CONNECTED:
hci_acl_disconn(conn, 0x13); hci_acl_disconn(conn, 0x13);
else break;
default:
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
break;
}
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return;
} }
static void hci_conn_idle(unsigned long arg) static void hci_conn_idle(unsigned long arg)
......
...@@ -750,6 +750,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -750,6 +750,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
if (test_bit(HCI_ENCRYPT, &hdev->flags)) if (test_bit(HCI_ENCRYPT, &hdev->flags))
conn->link_mode |= HCI_LM_ENCRYPT; conn->link_mode |= HCI_LM_ENCRYPT;
hci_conn_hold(conn);
/* Get remote features */ /* Get remote features */
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
struct hci_cp_read_remote_features cp; struct hci_cp_read_remote_features cp;
...@@ -778,6 +780,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -778,6 +780,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_send_cmd(hdev, OGF_LINK_CTL, hci_send_cmd(hdev, OGF_LINK_CTL,
OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
} }
hci_conn_put(conn);
} else } else
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
......
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