Bluetooth: Host: Fix create connection fails to stop scanner

Fix race condition in bt_conn_create_le for the state of the scanner in
the Host. This leads to the host issuing a create connection command
without stopping the scanner first. This leads to command disallowed and
failing to establish connection. As well as inconsistent state in the
host which does not allow to stop the running scanner.

The race condition exists because the processing of le_adv_report
handler is done before the thread that called bt_conn_create_le was
woken up to continue after the command_complete event.

Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
Joakim Andersson 2019-11-21 17:49:55 +01:00 committed by Carles Cufí
commit 0bf9931c2c
3 changed files with 54 additions and 12 deletions

View file

@ -2054,13 +2054,13 @@ int bt_conn_create_auto_le(const struct bt_le_conn_param *param)
return err;
}
atomic_set_bit(bt_dev.flags, BT_DEV_AUTO_CONN);
return 0;
}
int bt_conn_create_auto_stop(void)
{
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
return -EINVAL;
}
@ -2069,11 +2069,7 @@ int bt_conn_create_auto_stop(void)
return -EINVAL;
}
int err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL,
NULL);
atomic_clear_bit(bt_dev.flags, BT_DEV_AUTO_CONN);
err = bt_le_auto_conn_cancel();
if (err) {
BT_ERR("Failed to stop initiator");
return err;

View file

@ -100,6 +100,20 @@ static size_t discovery_results_size;
static size_t discovery_results_count;
#endif /* CONFIG_BT_BREDR */
struct cmd_state_set {
atomic_t *target;
int bit;
bool val;
};
void cmd_state_set_init(struct cmd_state_set *state, atomic_t *target, int bit,
bool val)
{
state->target = target;
state->bit = bit;
state->val = val;
}
struct cmd_data {
/** BT_BUF_CMD */
u8_t type;
@ -110,6 +124,9 @@ struct cmd_data {
/** The command OpCode that the buffer contains */
u16_t opcode;
/** The state to update when command completes with success. */
struct cmd_state_set *state;
/** Used by bt_hci_cmd_send_sync. */
struct k_sem *sync;
};
@ -274,6 +291,7 @@ struct net_buf *bt_hci_cmd_create(u16_t opcode, u8_t param_len)
cmd(buf)->type = BT_BUF_CMD;
cmd(buf)->opcode = opcode;
cmd(buf)->sync = NULL;
cmd(buf)->state = NULL;
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->opcode = sys_cpu_to_le16(opcode);
@ -386,6 +404,7 @@ const bt_addr_le_t *bt_lookup_id_addr(u8_t id, const bt_addr_le_t *addr)
static int set_advertise_enable(bool enable)
{
struct net_buf *buf;
struct cmd_state_set state;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
@ -399,13 +418,14 @@ static int set_advertise_enable(bool enable)
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
}
cmd_state_set_init(&state, bt_dev.flags, BT_DEV_ADVERTISING, enable);
cmd(buf)->state = &state;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING, enable);
return 0;
}
@ -575,6 +595,7 @@ static int set_le_scan_enable(u8_t enable)
{
struct bt_hci_cp_le_set_scan_enable *cp;
struct net_buf *buf;
struct cmd_state_set state;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*cp));
@ -593,14 +614,15 @@ static int set_le_scan_enable(u8_t enable)
cp->enable = enable;
cmd_state_set_init(&state, bt_dev.flags, BT_DEV_SCANNING,
enable == BT_HCI_LE_SCAN_ENABLE);
cmd(buf)->state = &state;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, NULL);
if (err) {
return err;
}
atomic_set_bit_to(bt_dev.flags, BT_DEV_SCANNING,
enable == BT_HCI_LE_SCAN_ENABLE);
return 0;
}
#endif /* CONFIG_BT_OBSERVER */
@ -706,6 +728,7 @@ static void hci_num_completed_packets(struct net_buf *buf)
int bt_le_auto_conn(const struct bt_le_conn_param *conn_param)
{
struct net_buf *buf;
struct cmd_state_set state;
struct bt_hci_cp_le_create_conn *cp;
u8_t own_addr_type;
int err;
@ -764,8 +787,24 @@ int bt_le_auto_conn(const struct bt_le_conn_param *conn_param)
cp->conn_latency = sys_cpu_to_le16(conn_param->latency);
cp->supervision_timeout = sys_cpu_to_le16(conn_param->timeout);
cmd_state_set_init(&state, bt_dev.flags, BT_DEV_AUTO_CONN, true);
cmd(buf)->state = &state;
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
}
int bt_le_auto_conn_cancel(void)
{
struct net_buf *buf;
struct cmd_state_set state;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN_CANCEL, 0);
cmd_state_set_init(&state, bt_dev.flags, BT_DEV_AUTO_CONN, false);
cmd(buf)->state = &state;
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, buf, NULL);
}
#endif /* defined(CONFIG_BT_WHITELIST) */
static int hci_le_create_conn(const struct bt_conn *conn)
@ -3343,6 +3382,12 @@ static void hci_cmd_done(u16_t opcode, u8_t status, struct net_buf *buf)
opcode, cmd(buf)->opcode);
}
if (cmd(buf)->state && !status) {
struct cmd_state_set *update = cmd(buf)->state;
atomic_set_bit_to(update->target, update->bit, update->val);
}
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
if (cmd(buf)->sync) {
cmd(buf)->status = status;

View file

@ -195,6 +195,7 @@ bool bt_le_conn_params_valid(const struct bt_le_conn_param *param);
int bt_le_scan_update(bool fast_scan);
int bt_le_auto_conn(const struct bt_le_conn_param *conn_param);
int bt_le_auto_conn_cancel(void);
bool bt_addr_le_is_bonded(u8_t id, const bt_addr_le_t *addr);
const bt_addr_le_t *bt_lookup_id_addr(u8_t id, const bt_addr_le_t *addr);