clear_bit(HCI_RESET, &hdev->flags);
+ if (status)
+ return;
+
/* Reset all non-persistent flags */
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
hdev->le_scan_type = LE_SCAN_PASSIVE;
hdev->ssp_debug_mode = 0;
+
+ hci_bdaddr_list_clear(&hdev->le_white_list);
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
- NULL, NULL, rp->status);
+ mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+ rp->status);
hci_dev_unlock(hdev);
}
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
- rp->hash256, rp->randomizer256,
+ mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+ rp->hash256, rp->rand256,
rp->status);
hci_dev_unlock(hdev);
}
hci_dev_lock(hdev);
- /* If we're doing connection initation as peripheral. Set a
+ /* If we're doing connection initiation as peripheral. Set a
* timeout in case something goes wrong.
*/
if (*sent) {
struct discovery_state *discov = &hdev->discovery;
struct inquiry_entry *e;
- if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
- name_len, conn->dev_class);
+ /* Update the mgmt connected state if necessary. Be careful with
+ * conn objects that exist but are not (yet) connected however.
+ * Only those in BT_CONFIG or BT_CONNECTED states can be
+ * considered connected.
+ */
+ if (conn &&
+ (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+ !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, conn, 0, name, name_len);
if (discov->state == DISCOVERY_STOPPED)
return;
hci_dev_unlock(hdev);
}
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_switch_role *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (conn)
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
cp.handle = ev->handle;
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
+
+ hci_update_page_scan(hdev, NULL);
}
/* Set packet type for incoming connection */
mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
reason, mgmt_connected);
- if (conn->type == ACL_LINK &&
- test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
- hci_remove_link_key(hdev, &conn->dst);
+ if (conn->type == ACL_LINK) {
+ if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
+ hci_remove_link_key(hdev, &conn->dst);
+
+ hci_update_page_scan(hdev, NULL);
+ }
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
conn->sec_level = conn->pending_sec_level;
}
} else {
- mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ mgmt_auth_failed(conn, ev->status);
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
}
}
+ /* We should disregard the current RPA and generate a new one
+ * whenever the encryption procedure fails.
+ */
+ if (ev->status && conn->type == LE_LINK)
+ set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
if (ev->status && conn->state == BT_CONNECTED) {
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
hci_cs_create_conn(hdev, ev->status);
break;
+ case HCI_OP_DISCONNECT:
+ hci_cs_disconnect(hdev, ev->status);
+ break;
+
case HCI_OP_ADD_SCO:
hci_cs_add_sco(hdev, ev->status);
break;
hci_cs_setup_sync_conn(hdev, ev->status);
break;
- case HCI_OP_SNIFF_MODE:
- hci_cs_sniff_mode(hdev, ev->status);
+ case HCI_OP_CREATE_PHY_LINK:
+ hci_cs_create_phylink(hdev, ev->status);
break;
- case HCI_OP_EXIT_SNIFF_MODE:
- hci_cs_exit_sniff_mode(hdev, ev->status);
+ case HCI_OP_ACCEPT_PHY_LINK:
+ hci_cs_accept_phylink(hdev, ev->status);
break;
- case HCI_OP_DISCONNECT:
- hci_cs_disconnect(hdev, ev->status);
+ case HCI_OP_SNIFF_MODE:
+ hci_cs_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_CREATE_PHY_LINK:
- hci_cs_create_phylink(hdev, ev->status);
+ case HCI_OP_EXIT_SNIFF_MODE:
+ hci_cs_exit_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_ACCEPT_PHY_LINK:
- hci_cs_accept_phylink(hdev, ev->status);
+ case HCI_OP_SWITCH_ROLE:
+ hci_cs_switch_role(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
}
}
+static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+ BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+}
+
static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_role_change *ev = (void *) skb->data;
hci_dev_unlock(hdev);
}
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+ if (key_type == HCI_LK_CHANGED_COMBINATION)
+ return;
+
+ conn->pin_length = pin_len;
+ conn->key_type = key_type;
+
+ switch (key_type) {
+ case HCI_LK_LOCAL_UNIT:
+ case HCI_LK_REMOTE_UNIT:
+ case HCI_LK_DEBUG_COMBINATION:
+ return;
+ case HCI_LK_COMBINATION:
+ if (pin_len == 16)
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ else
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_UNAUTH_COMBINATION_P192:
+ case HCI_LK_UNAUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P192:
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_FIPS;
+ break;
+ }
+}
+
static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_link_key_req *ev = (void *) skb->data;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
+ clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+
if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
goto not_found;
}
- conn->key_type = key->type;
- conn->pin_length = key->pin_len;
+ conn_set_key(conn, key->type, key->pin_len);
}
bacpy(&cp.bdaddr, &ev->bdaddr);
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;
- pin_len = conn->pin_length;
+ if (!conn)
+ goto unlock;
- if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
- conn->key_type = ev->key_type;
+ hci_conn_hold(conn);
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ hci_conn_drop(conn);
- hci_conn_drop(conn);
- }
+ set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+ conn_set_key(conn, ev->key_type, conn->pin_length);
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
if (!key)
goto unlock;
+ /* Update connection information since adding the key will have
+ * fixed up the type in the case of changed combination keys.
+ */
+ if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+ conn_set_key(conn, key->type, key->pin_len);
+
mgmt_new_link_key(hdev, key, persistent);
/* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
*/
if (key->type == HCI_LK_DEBUG_COMBINATION &&
!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
- list_del(&key->list);
- kfree(key);
- } else if (conn) {
- if (persistent)
- clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
- else
- set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
+ goto unlock;
}
+ if (persistent)
+ clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ else
+ set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+
unlock:
hci_dev_unlock(hdev);
}
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
- mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ mgmt_auth_failed(conn, ev->status);
hci_conn_drop(conn);
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
if (data) {
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ if (bredr_sc_enabled(hdev)) {
struct hci_cp_remote_oob_ext_data_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
- memcpy(cp.randomizer192, data->randomizer192,
- sizeof(cp.randomizer192));
+ memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
- memcpy(cp.randomizer256, data->randomizer256,
- sizeof(cp.randomizer256));
+ memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
sizeof(cp), &cp);
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash, data->hash192, sizeof(cp.hash));
- memcpy(cp.randomizer, data->randomizer192,
- sizeof(cp.randomizer));
+ memcpy(cp.rand, data->rand192, sizeof(cp.rand));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
sizeof(cp), &cp);
conn->dst_type = irk->addr_type;
}
- if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
- addr_type = BDADDR_LE_PUBLIC;
- else
- addr_type = BDADDR_LE_RANDOM;
-
if (ev->status) {
hci_le_conn_failed(conn, ev->status);
goto unlock;
}
+ if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
+ addr_type = BDADDR_LE_PUBLIC;
+ else
+ addr_type = BDADDR_LE_RANDOM;
+
/* Drop the connection if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
hci_conn_drop(conn);
}
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0, NULL);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
hci_proto_connect_cfm(conn, ev->status);
- params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+ params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
+ conn->dst_type);
if (params) {
list_del_init(¶ms->action);
if (params->conn) {
hci_conn_drop(params->conn);
+ hci_conn_put(params->conn);
params->conn = NULL;
}
}
}
/* This function requires the caller holds hdev->lock */
-static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
- u8 addr_type, u8 adv_type)
+static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
+ bdaddr_t *addr,
+ u8 addr_type, u8 adv_type)
{
struct hci_conn *conn;
struct hci_conn_params *params;
/* If the event is not connectable don't proceed further */
if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
/* Ignore if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
- return;
+ return NULL;
/* Most controller will fail if we try to create new connections
* while we have an existing one in slave role.
*/
if (hdev->conn_hash.le_num_slave > 0)
- return;
+ return NULL;
/* If we're not connectable only connect devices that we have in
* our pend_le_conns list.
params = hci_pend_le_action_lookup(&hdev->pend_le_conns,
addr, addr_type);
if (!params)
- return;
+ return NULL;
switch (params->auto_connect) {
case HCI_AUTO_CONN_DIRECT:
* incoming connections from slave devices.
*/
if (adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
break;
case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
*/
break;
default:
- return;
+ return NULL;
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
* the parameters get removed and keep the reference
* count consistent once the connection is established.
*/
- params->conn = conn;
- return;
+ params->conn = hci_conn_get(conn);
+ return conn;
}
switch (PTR_ERR(conn)) {
break;
default:
BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
+ return NULL;
}
+
+ return NULL;
}
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
{
struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk;
+ struct hci_conn *conn;
bool match;
u32 flags;
}
/* Check if we have been requested to connect to this device */
- check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ if (conn && type == LE_ADV_IND) {
+ /* Store report for later inclusion by
+ * mgmt_device_connected
+ */
+ memcpy(conn->le_adv_data, data, len);
+ conn->le_adv_data_len = len;
+ }
/* Passive scanning shouldn't trigger any device found events,
* except for devices marked as CONN_REPORT for which we do send
if (conn == NULL)
goto not_found;
- ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
- if (ltk == NULL)
+ ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role);
+ if (!ltk)
goto not_found;
+ if (smp_ltk_is_sc(ltk)) {
+ /* With SC both EDiv and Rand are set to zero */
+ if (ev->ediv || ev->rand)
+ goto not_found;
+ } else {
+ /* For non-SC keys check that EDiv and Rand match */
+ if (ev->ediv != ltk->ediv || ev->rand != ltk->rand)
+ goto not_found;
+ }
+
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
- if (ltk->authenticated)
- conn->pending_sec_level = BT_SECURITY_HIGH;
- else
- conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ conn->pending_sec_level = smp_ltk_sec_level(ltk);
conn->enc_key_size = ltk->enc_size;
*/
if (ltk->type == SMP_STK) {
set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
- list_del(<k->list);
- kfree(ltk);
+ list_del_rcu(<k->list);
+ kfree_rcu(ltk, rcu);
} else {
clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
}
hci_cmd_status_evt(hdev, skb);
break;
+ case HCI_EV_HARDWARE_ERROR:
+ hci_hardware_error_evt(hdev, skb);
+ break;
+
case HCI_EV_ROLE_CHANGE:
hci_role_change_evt(hdev, skb);
break;