#include <linux/interrupt.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static bool enable_le;
-
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_RESET, status);
- /* Reset all flags, except persistent ones */
- hdev->dev_flags &= BIT(HCI_MGMT) | BIT(HCI_SETUP) | BIT(HCI_AUTO_OFF) |
- BIT(HCI_LINK_KEYS) | BIT(HCI_DEBUG_KEYS);
+ /* Reset all non-persistent flags */
+ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS));
+
+ hdev->discovery.state = DISCOVERY_STOPPED;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_set_local_name_complete(hdev, sent, status);
-
- if (status == 0)
+ else if (!status)
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
hci_dev_unlock(hdev);
+
+ hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status);
}
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (rp->status)
return;
- memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
+ if (test_bit(HCI_SETUP, &hdev->dev_flags))
+ memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
}
static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV);
if (!sent)
return;
- memcpy(hdev->dev_class, sent, 3);
+ hci_dev_lock(hdev);
+
+ if (status == 0)
+ memcpy(hdev->dev_class, sent, 3);
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_set_class_of_dev_complete(hdev, sent, status);
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
}
-static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
-
- BT_DBG("%s status 0x%x", hdev->name, rp->status);
-
- if (rp->status)
- return;
-
- if (rp->mode)
- set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
- else
- clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-}
-
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- goto done;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
if (!sent)
return;
- if (*((u8 *) sent))
- set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
- else
- clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-
-done:
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_ssp_enable_complete(hdev, status);
+ mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status);
+ else if (!status) {
+ if (*((u8 *) sent))
+ set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ }
}
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-static void hci_set_le_support(struct hci_dev *hdev)
-{
- struct hci_cp_write_le_host_supported cp;
-
- memset(&cp, 0, sizeof(cp));
-
- if (enable_le) {
- cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
- }
-
- hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp);
-}
-
static void hci_setup(struct hci_dev *hdev)
{
if (hdev->dev_type != HCI_BREDR)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
- u8 mode = 0x01;
- hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ u8 mode = 0x01;
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
+ sizeof(mode), &mode);
+ } else {
+ struct hci_cp_write_eir cp;
+
+ memset(hdev->eir, 0, sizeof(hdev->eir));
+ memset(&cp, 0, sizeof(cp));
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+ }
}
if (hdev->features[3] & LMP_RSSI_INQ)
struct hci_cp_read_local_ext_features cp;
cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp),
+ &cp);
}
- if (hdev->features[4] & LMP_LE)
- hci_set_le_support(hdev);
+ if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
+ u8 enable = 1;
+ hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
+ &enable);
+ }
}
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
hdev->hci_ver = rp->hci_ver;
hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
if (test_bit(HCI_INIT, &hdev->flags))
hci_setup(hdev);
+
+done:
+ hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status);
}
static void hci_setup_link_policy(struct hci_dev *hdev)
link_policy |= HCI_LP_PARK;
link_policy = cpu_to_le16(link_policy);
- hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY,
- sizeof(link_policy), &link_policy);
+ hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(link_policy),
+ &link_policy);
}
static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb)
hdev->features[6], hdev->features[7]);
}
+static void hci_set_le_support(struct hci_dev *hdev)
+{
+ struct hci_cp_write_le_host_supported cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ cp.le = 1;
+ cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ }
+
+ if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE))
+ hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
+ &cp);
+}
+
static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
struct sk_buff *skb)
{
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
switch (rp->page) {
case 0:
break;
}
+ if (test_bit(HCI_INIT, &hdev->flags) && hdev->features[4] & LMP_LE)
+ hci_set_le_support(hdev);
+
+done:
hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
}
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
- 0, rp->status);
+ mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0,
+ rp->status);
hci_dev_unlock(hdev);
}
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
- ACL_LINK, 0,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
- 0, rp->status);
+ 0, rp->status);
hci_dev_unlock(hdev);
}
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
- ACL_LINK, 0,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev,
struct sk_buff *skb)
{
- struct hci_cp_read_local_ext_features cp;
+ struct hci_cp_write_le_host_supported *sent;
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED);
+ if (!sent)
return;
- cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp);
+ if (!status) {
+ if (sent->le)
+ hdev->host_features[0] |= LMP_HOST_LE;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_LE;
+ }
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+ !test_bit(HCI_INIT, &hdev->flags))
+ mgmt_le_enable_complete(hdev, sent->le, status);
+
+ hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
}
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
return 1;
}
-static inline int hci_resolve_name(struct hci_dev *hdev, struct inquiry_entry *e)
+static inline int hci_resolve_name(struct hci_dev *hdev,
+ struct inquiry_entry *e)
{
struct hci_cp_remote_name_req cp;
}
static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
- bdaddr_t *bdaddr, u8 *name, u8 name_len)
+ bdaddr_t *bdaddr, u8 *name, u8 name_len)
{
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,
- name, name_len, conn->dev_class);
+ mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
+ name_len, conn->dev_class);
if (discov->state == DISCOVERY_STOPPED)
return;
list_del(&e->list);
if (name)
mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
- e->data.rssi, name, name_len);
+ e->data.rssi, name, name_len);
}
if (hci_resolve_next_name(hdev))
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn)
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
- conn->dst_type, status);
+ conn->dst_type, status);
hci_dev_unlock(hdev);
}
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
- bool name_known;
+ bool name_known, ssp;
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.rssi = 0x00;
data.ssp_mode = 0x00;
- name_known = hci_inquiry_cache_update(hdev, &data, false);
+ name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, !name_known,
- NULL, 0);
+ info->dev_class, 0, !name_known, ssp, NULL,
+ 0);
}
hci_dev_unlock(hdev);
struct hci_cp_read_remote_features cp;
cp.handle = ev->handle;
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
/* Set packet type for incoming connection */
struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle;
cp.pkt_type = cpu_to_le16(conn->pkt_type);
- hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp),
+ &cp);
}
} else {
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
- conn->dst_type, ev->status);
+ conn->dst_type, ev->status);
}
if (conn->type == ACL_LINK)
else
cp.role = 0x01; /* Remain slave */
- hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
+ &cp);
} else {
struct hci_cp_accept_sync_conn_req cp;
cp.retrans_effort = 0xff;
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
} else {
/* Connection rejected */
conn->dst_type, ev->status);
else
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type);
+ conn->dst_type);
}
if (ev->status == 0) {
+ if (conn->type == ACL_LINK && conn->flush_key)
+ hci_remove_link_key(hdev, &conn->dst);
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
}
}
} else {
mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ ev->status);
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
if (ev->status == 0)
hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name,
- strnlen(ev->name, HCI_MAX_NAME_LENGTH));
+ strnlen(ev->name, HCI_MAX_NAME_LENGTH));
else
hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0);
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+ if (ev->status && conn->state == BT_CONNECTED) {
+ hci_acl_disconn(conn, 0x13);
+ hci_conn_put(conn);
+ goto unlock;
+ }
+
if (conn->state == BT_CONFIG) {
if (!ev->status)
conn->state = BT_CONNECTED;
hci_encrypt_cfm(conn, ev->status, ev->encrypt);
}
+unlock:
hci_dev_unlock(hdev);
}
goto unlock;
}
- if (!ev->status) {
+ if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
struct hci_cp_remote_name_req cp;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
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, NULL, 0,
- conn->dev_class);
+ conn->dst_type, 0, NULL, 0,
+ conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
hci_cc_host_buffer_size(hdev, skb);
break;
- case HCI_OP_READ_SSP_MODE:
- hci_cc_read_ssp_mode(hdev, skb);
- break;
-
case HCI_OP_WRITE_SSP_MODE:
hci_cc_write_ssp_mode(hdev, skb);
break;
case HCI_OP_USER_PASSKEY_NEG_REPLY:
hci_cc_user_passkey_neg_reply(hdev, skb);
+ break;
case HCI_OP_LE_SET_SCAN_PARAM:
hci_cc_le_set_scan_param(hdev, skb);
}
static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
int i;
{
struct inquiry_data data;
int num_rsp = *((__u8 *) skb->data);
- bool name_known;
+ bool name_known, ssp;
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
data.ssp_mode = 0x00;
name_known = hci_inquiry_cache_update(hdev, &data,
- false);
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- !name_known, NULL, 0);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
data.rssi = info->rssi;
data.ssp_mode = 0x00;
name_known = hci_inquiry_cache_update(hdev, &data,
- false);
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- !name_known, NULL, 0);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
}
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
- if (ev->features[0] & 0x01)
+ if (ev->features[0] & LMP_HOST_SSP)
set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
if (conn->state != BT_CONFIG)
goto unlock;
- if (!ev->status) {
+ if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
struct hci_cp_remote_name_req cp;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
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, NULL, 0,
- conn->dev_class);
+ conn->dst_type, 0, NULL, 0,
+ conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
- bool name_known;
+ bool name_known, ssp;
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
if (test_bit(HCI_MGMT, &hdev->dev_flags))
name_known = eir_has_data_type(info->data,
- sizeof(info->data),
- EIR_NAME_COMPLETE);
+ sizeof(info->data),
+ EIR_NAME_COMPLETE);
else
name_known = true;
- name_known = hci_inquiry_cache_update(hdev, &data, name_known);
+ name_known = hci_inquiry_cache_update(hdev, &data, name_known,
+ &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- !name_known, info->data,
- sizeof(info->data));
+ info->dev_class, info->rssi, !name_known,
+ ssp, info->data, sizeof(info->data));
}
hci_dev_unlock(hdev);
confirm:
mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey,
- confirm_hint);
+ confirm_hint);
unlock:
hci_dev_unlock(hdev);
* the mgmt_auth_failed event */
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0)
mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ ev->status);
hci_conn_put(conn);
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
hci_dev_unlock(hdev);
}
static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
struct oob_data *data;
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
mgmt_device_connected(hdev, &ev->bdaddr, conn->type,
- conn->dst_type, NULL, 0, 0);
+ conn->dst_type, 0, NULL, 0, NULL);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
rssi = ev->data[ev->length];
mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
- NULL, rssi, 0, ev->data, ev->length);
+ NULL, rssi, 0, 1, ev->data, ev->length);
ptr += sizeof(*ev) + ev->length + 1;
}
kfree_skb(skb);
hdev->stat.evt_rx++;
}
-
-module_param(enable_le, bool, 0644);
-MODULE_PARM_DESC(enable_le, "Enable LE support");