Bluetooth: hci_sync: Attempt to dequeue connection attempt

If connection is still queued/pending in the cmd_sync queue it means no
command has been generated and it should be safe to just dequeue the
callback when it is being aborted.
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 505ea2b2
...@@ -1083,6 +1083,24 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev) ...@@ -1083,6 +1083,24 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev)
return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num; return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num;
} }
static inline bool hci_conn_valid(struct hci_dev *hdev, struct hci_conn *conn)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c == conn) {
rcu_read_unlock();
return true;
}
}
rcu_read_unlock();
return false;
}
static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle) static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
...@@ -1493,6 +1511,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -1493,6 +1511,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, bool dst_resolved, u8 sec_level, u8 dst_type, bool dst_resolved, u8 sec_level,
u16 conn_timeout, u8 role); u16 conn_timeout, u8 role);
void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status);
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
u8 sec_level, u8 auth_type, u8 sec_level, u8 auth_type,
enum conn_reasons conn_reason, u16 timeout); enum conn_reasons conn_reason, u16 timeout);
......
...@@ -48,11 +48,11 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, ...@@ -48,11 +48,11 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy); void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy); void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
struct hci_cmd_sync_work_entry * struct hci_cmd_sync_work_entry *
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy); void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev, void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
struct hci_cmd_sync_work_entry *entry); struct hci_cmd_sync_work_entry *entry);
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
...@@ -139,8 +139,6 @@ struct hci_conn; ...@@ -139,8 +139,6 @@ struct hci_conn;
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason); int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
int hci_le_create_cis_sync(struct hci_dev *hdev); int hci_le_create_cis_sync(struct hci_dev *hdev);
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle); int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
...@@ -152,3 +150,7 @@ int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle); ...@@ -152,3 +150,7 @@ int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle);
int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle); int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle);
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn);
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn);
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn);
...@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = { ...@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
}; };
/* This function requires the caller holds hdev->lock */ /* This function requires the caller holds hdev->lock */
static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status) void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
{ {
struct hci_conn_params *params; struct hci_conn_params *params;
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
...@@ -1124,6 +1124,9 @@ void hci_conn_del(struct hci_conn *conn) ...@@ -1124,6 +1124,9 @@ void hci_conn_del(struct hci_conn *conn)
* rest of hci_conn_del. * rest of hci_conn_del.
*/ */
hci_conn_cleanup(conn); hci_conn_cleanup(conn);
/* Dequeue callbacks using connection pointer as data */
hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
} }
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type) struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
...@@ -1258,53 +1261,6 @@ u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle) ...@@ -1258,53 +1261,6 @@ u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle)
return 0; return 0;
} }
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
{
struct hci_conn *conn;
u16 handle = PTR_UINT(data);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn)
return;
bt_dev_dbg(hdev, "err %d", err);
hci_dev_lock(hdev);
if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00);
goto done;
}
/* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev))
goto done;
/* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err));
done:
hci_dev_unlock(hdev);
}
static int hci_connect_le_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn;
u16 handle = PTR_UINT(data);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn)
return 0;
bt_dev_dbg(hdev, "conn %p", conn);
clear_bit(HCI_CONN_SCANNING, &conn->flags);
conn->state = BT_CONNECT;
return hci_le_create_conn_sync(hdev, conn);
}
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, bool dst_resolved, u8 sec_level, u8 dst_type, bool dst_resolved, u8 sec_level,
u16 conn_timeout, u8 role) u16 conn_timeout, u8 role)
...@@ -1371,9 +1327,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -1371,9 +1327,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
conn->sec_level = BT_SECURITY_LOW; conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout; conn->conn_timeout = conn_timeout;
err = hci_cmd_sync_queue(hdev, hci_connect_le_sync, err = hci_connect_le_sync(hdev, conn);
UINT_PTR(conn->handle),
create_le_conn_complete);
if (err) { if (err) {
hci_conn_del(conn); hci_conn_del(conn);
return ERR_PTR(err); return ERR_PTR(err);
...@@ -2909,12 +2863,10 @@ u32 hci_conn_get_phy(struct hci_conn *conn) ...@@ -2909,12 +2863,10 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
static int abort_conn_sync(struct hci_dev *hdev, void *data) static int abort_conn_sync(struct hci_dev *hdev, void *data)
{ {
struct hci_conn *conn; struct hci_conn *conn = data;
u16 handle = PTR_UINT(data);
conn = hci_conn_hash_lookup_handle(hdev, handle); if (!hci_conn_valid(hdev, conn))
if (!conn) return -ECANCELED;
return 0;
return hci_abort_conn_sync(hdev, conn, conn->abort_reason); return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
} }
...@@ -2949,8 +2901,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) ...@@ -2949,8 +2901,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
hci_cmd_sync_cancel(hdev, -ECANCELED); hci_cmd_sync_cancel(hdev, -ECANCELED);
break; break;
} }
/* Cancel connect attempt if still queued/pending */
} else if (!hci_cancel_connect_sync(hdev, conn)) {
return 0;
} }
return hci_cmd_sync_queue(hdev, abort_conn_sync, UINT_PTR(conn->handle), return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
NULL);
} }
...@@ -6285,12 +6285,21 @@ static int hci_le_ext_create_conn_sync(struct hci_dev *hdev, ...@@ -6285,12 +6285,21 @@ static int hci_le_ext_create_conn_sync(struct hci_dev *hdev,
conn->conn_timeout, NULL); conn->conn_timeout, NULL);
} }
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn) static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
{ {
struct hci_cp_le_create_conn cp; struct hci_cp_le_create_conn cp;
struct hci_conn_params *params; struct hci_conn_params *params;
u8 own_addr_type; u8 own_addr_type;
int err; int err;
struct hci_conn *conn = data;
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
bt_dev_dbg(hdev, "conn %p", conn);
clear_bit(HCI_CONN_SCANNING, &conn->flags);
conn->state = BT_CONNECT;
/* If requested to connect as peripheral use directed advertising */ /* If requested to connect as peripheral use directed advertising */
if (conn->role == HCI_ROLE_SLAVE) { if (conn->role == HCI_ROLE_SLAVE) {
...@@ -6611,16 +6620,11 @@ int hci_update_adv_data(struct hci_dev *hdev, u8 instance) ...@@ -6611,16 +6620,11 @@ int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
{ {
struct hci_conn *conn; struct hci_conn *conn = data;
u16 handle = PTR_UINT(data);
struct inquiry_entry *ie; struct inquiry_entry *ie;
struct hci_cp_create_conn cp; struct hci_cp_create_conn cp;
int err; int err;
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn)
return 0;
/* Many controllers disallow HCI Create Connection while it is doing /* Many controllers disallow HCI Create Connection while it is doing
* HCI Inquiry. So we cancel the Inquiry first before issuing HCI Create * HCI Inquiry. So we cancel the Inquiry first before issuing HCI Create
* Connection. This may cause the MGMT discovering state to become false * Connection. This may cause the MGMT discovering state to become false
...@@ -6679,6 +6683,58 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) ...@@ -6679,6 +6683,58 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{ {
return hci_cmd_sync_queue(hdev, hci_acl_create_conn_sync, return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
UINT_PTR(conn->handle), NULL); NULL);
}
static void create_le_conn_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_dev_lock(hdev);
if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00);
goto done;
}
/* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev))
goto done;
/* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err));
done:
hci_dev_unlock(hdev);
}
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
create_le_conn_complete);
}
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
if (conn->state != BT_OPEN)
return -EINVAL;
switch (conn->type) {
case ACL_LINK:
return !hci_cmd_sync_dequeue_once(hdev,
hci_acl_create_conn_sync,
conn, NULL);
case LE_LINK:
return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
conn, create_le_conn_complete);
}
return -ENOENT;
} }
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