Implemented missing components of MGMTOPS interface
Differentiated as needed between BR/EDR pairing and LE pairing
These are a combination of changes we made to get make MGMTOPS more
robust, and provide MITM pairing options for LE. Much work has been put
into
merging these changes into the current master branch of Bluetooth Next, but
they have not been extensively tested on that build.
This is anticipated to be the first step towards upstreaming these changes,
Signed-off-by: Brian Gix <[email protected]>
---
include/net/bluetooth/hci.h | 36 ++
include/net/bluetooth/hci_core.h | 32 ++-
include/net/bluetooth/l2cap.h | 5 +
include/net/bluetooth/mgmt.h | 24 ++-
include/net/bluetooth/smp.h | 14 +-
net/bluetooth/hci_conn.c | 58 +++-
net/bluetooth/hci_core.c | 64 +++-
net/bluetooth/hci_event.c | 166 ++++++---
net/bluetooth/hci_sock.c | 9 +-
net/bluetooth/l2cap_core.c | 15 +-
net/bluetooth/mgmt.c | 743
++++++++++++++++++++++++++++++++------
net/bluetooth/smp.c | 661 ++++++++++++++++++++++++---------
12 files changed, 1441 insertions(+), 386 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index c5fcd13..8ae8dca 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -632,6 +632,12 @@ struct hci_cp_host_buffer_size {
__le16 sco_max_pkt;
} __packed;
+#define HCI_OP_WRITE_CURRENT_IAC_LAP 0x0c3a
+struct hci_cp_write_current_iac_lap {
+ __u8 num_current_iac;
+ __u8 lap[6];
+} __packed;
+
#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45
#define HCI_MAX_EIR_LENGTH 240
@@ -753,6 +759,15 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt;
} __packed;
+#define HCI_OP_LE_SET_SCAN_PARAMETERS 0x200b
+struct hci_cp_le_set_scan_parameters {
+ __u8 type;
+ __le16 interval;
+ __le16 window;
+ __u8 own_bdaddr_type;
+ __u8 filter;
+} __packed;
+
#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c
struct hci_cp_le_set_scan_enable {
__u8 enable;
@@ -788,6 +803,16 @@ struct hci_cp_le_conn_update {
__le16 max_ce_len;
} __packed;
+#define HCI_OP_LE_ENCRYPT 0x2017
+struct hci_cp_le_encrypt {
+ __u8 key[16];
+ __u8 data[16];
+} __packed;
+struct hci_cp_le_encrypt_reply {
+ __u8 status;
+ __u8 encrypted[16];
+} __packed;
+
#define HCI_OP_LE_START_ENC 0x2019
struct hci_cp_le_start_enc {
__le16 handle;
@@ -1069,6 +1094,11 @@ struct hci_ev_user_confirm_req {
__le32 passkey;
} __packed;
+#define HCI_EV_USER_PASSKEY_REQUEST 0x34
+struct hci_ev_user_passkey_request {
+ bdaddr_t bdaddr;
+} __packed;
+
#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
struct hci_ev_remote_oob_data_request {
bdaddr_t bdaddr;
@@ -1080,6 +1110,12 @@ struct hci_ev_simple_pair_complete {
bdaddr_t bdaddr;
} __packed;
+#define HCI_EV_USER_PASSKEY_NOTIFICATION 0x3b
+struct hci_ev_user_passkey_notification {
+ bdaddr_t bdaddr;
+ __le32 passkey;
+} __packed;
+
#define HCI_EV_REMOTE_HOST_FEATURES 0x3d
struct hci_ev_remote_host_features {
bdaddr_t bdaddr;
diff --git a/include/net/bluetooth/hci_core.h
b/include/net/bluetooth/hci_core.h
index f97792c..9cadb41 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -89,11 +89,17 @@ struct key_master_id {
u8 rand[8];
} __packed;
+#define KEY_TYPE_LE_BASE 0x11
+#define KEY_TYPE_LTK 0x11
+#define KEY_TYPE_IRK 0x12
+#define KEY_TYPE_CSRK 0x13
+
struct link_key_data {
bdaddr_t bdaddr;
u8 type;
u8 val[16];
u8 pin_len;
+ u8 auth;
u8 dlen;
u8 data[0];
} __packed;
@@ -101,9 +107,11 @@ struct link_key_data {
struct link_key {
struct list_head list;
bdaddr_t bdaddr;
- u8 type;
+ u8 addr_type;
+ u8 key_type;
u8 val[16];
u8 pin_len;
+ u8 auth;
u8 dlen;
u8 data[0];
};
@@ -119,6 +127,7 @@ struct adv_entry {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
+ u8 flags;
};
#define NUM_REASSEMBLY 4
@@ -215,6 +224,8 @@ struct hci_dev {
__u16 init_last_cmd;
+ struct crypto_blkcipher *tfm;
+
struct inquiry_cache inq_cache;
struct hci_conn_hash conn_hash;
struct list_head blacklist;
@@ -282,6 +293,7 @@ struct hci_conn {
__u8 pin_length;
__u8 enc_key_size;
__u8 io_capability;
+ __u8 auth_initiator;
__u8 power_save;
__u16 disc_timeout;
unsigned long pend;
@@ -311,6 +323,9 @@ struct hci_conn {
struct hci_conn *link;
+ /* Low Energy SMP Connection */
+ void *smp_conn;
+
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
@@ -627,8 +642,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct
hci_conn *conn, int new_key,
struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8
rand[8]);
struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type);
-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
+int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, u8
type,
+ u8 auth, u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
int hci_remote_oob_data_clear(struct hci_dev *hdev);
@@ -918,6 +933,7 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t
*bdaddr, u8 status);
int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8
status);
int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
u8 confirm_hint);
+int mgmt_user_oob_request(u16 index, bdaddr_t *bdaddr);
int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8
status);
int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
u8 status);
@@ -925,14 +941,18 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr,
u8 status);
int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8
*randomizer,
u8 status);
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
- u8 *eir);
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
+int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
+ u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir);
+int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name);
int mgmt_inquiry_failed(u16 index, u8 status);
int mgmt_discovering(u16 index, u8 discovering);
+void mgmt_inquiry_complete_evt(u16 index, u8 status);
int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr);
int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr);
+/* LE SMP Management interface */
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, void *cp);
+
/* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index fdb2b78..a411288 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -538,6 +538,11 @@ struct l2cap_conn {
rwlock_t chan_lock;
};
+struct sock_del_list {
+ struct sock *sk;
+ struct list_head list;
+};
+
#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04
#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 3062fd3..b0191d2 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -90,6 +90,8 @@ struct mgmt_cp_set_dev_class {
__u8 major;
__u8 minor;
} __packed;
+#define MGMT_MAJOR_CLASS_MASK 0x1F
+#define MGMT_MAJOR_CLASS_LIMITED 0x20
#define MGMT_OP_SET_SERVICE_CACHE 0x000C
struct mgmt_cp_set_service_cache {
@@ -98,9 +100,13 @@ struct mgmt_cp_set_service_cache {
struct mgmt_key_info {
bdaddr_t bdaddr;
- u8 type;
+ u8 addr_type;
+ u8 key_type;
u8 val[16];
u8 pin_len;
+ u8 auth;
+ u8 dlen;
+ u8 data[10];
} __packed;
#define MGMT_OP_LOAD_KEYS 0x000D
@@ -214,6 +220,19 @@ struct mgmt_cp_set_fast_connectable {
__u8 enable;
} __packed;
+#define MGMT_OP_USER_PASSKEY_REPLY 0x0020
+struct mgmt_cp_user_passkey_reply {
+ bdaddr_t bdaddr;
+ __le32 passkey;
+} __packed;
+
+#define MGMT_OP_RESOLVE_NAME 0x0021
+struct mgmt_cp_resolve_name {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_SET_LIMIT_DISCOVERABLE 0x0022
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -295,12 +314,15 @@ struct mgmt_ev_device_found {
bdaddr_t bdaddr;
__u8 dev_class[3];
__s8 rssi;
+ __u8 le;
+ __u8 type;
__u8 eir[HCI_MAX_EIR_LENGTH];
} __packed;
#define MGMT_EV_REMOTE_NAME 0x0013
struct mgmt_ev_remote_name {
bdaddr_t bdaddr;
+ __u8 status;
__u8 name[MGMT_MAX_NAME_LENGTH];
} __packed;
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
index 15b97d5..8fc0c84 100644
--- a/include/net/bluetooth/smp.h
+++ b/include/net/bluetooth/smp.h
@@ -55,6 +55,13 @@ struct smp_cmd_pairing {
#define SMP_AUTH_BONDING 0x01
#define SMP_AUTH_MITM 0x04
+#define SMP_JUST_WORKS 0x00
+#define SMP_JUST_CFM 0x01
+#define SMP_REQ_PASSKEY 0x02
+#define SMP_CFM_PASSKEY 0x03
+#define SMP_REQ_OOB 0x04
+#define SMP_OVERLAP 0xFF
+
#define SMP_CMD_PAIRING_CONFIRM 0x03
struct smp_cmd_pairing_confirm {
__u8 confirm_val[16];
@@ -123,7 +130,11 @@ struct smp_chan {
u8 rrnd[16]; /* SMP Pairing Random (remote) */
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
+ u8 smp_tk_valid;
+ u8 smp_cfm_pending;
u8 smp_key_size;
+ u8 smp_sec_req;
+ u8 smp_auth;
struct crypto_blkcipher *tfm;
struct work_struct confirm;
struct work_struct random;
@@ -133,7 +144,8 @@ struct smp_chan {
/* SMP Commands */
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_link_encrypt_cmplt(struct l2cap_conn *conn, __u8 status, __u8
encrypt);
+void smp_timeout(struct l2cap_conn *conn);
void smp_chan_destroy(struct l2cap_conn *conn);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e545376..b543434 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -50,10 +50,13 @@ static void hci_le_connect(struct hci_conn *conn)
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_create_conn cp;
+ BT_DBG("%p", conn);
+
conn->state = BT_CONNECT;
conn->out = 1;
conn->link_mode |= HCI_LM_MASTER;
conn->sec_level = BT_SECURITY_LOW;
+ conn->type = LE_LINK;
memset(&cp, 0, sizeof(cp));
cp.scan_interval = cpu_to_le16(0x0060);
@@ -283,9 +286,6 @@ static void hci_conn_timeout(unsigned long arg)
BT_DBG("conn %p state %d", conn, conn->state);
- if (atomic_read(&conn->refcnt))
- return;
-
hci_dev_lock(hdev);
switch (conn->state) {
@@ -300,11 +300,14 @@ static void hci_conn_timeout(unsigned long arg)
break;
case BT_CONFIG:
case BT_CONNECTED:
- reason = hci_proto_disconn_ind(conn);
- hci_acl_disconn(conn, reason);
+ if (!atomic_read(&conn->refcnt)) {
+ reason = hci_proto_disconn_ind(conn);
+ hci_acl_disconn(conn, reason);
+ }
break;
default:
- conn->state = BT_CLOSED;
+ if (!atomic_read(&conn->refcnt))
+ conn->state = BT_CLOSED;
break;
}
@@ -493,7 +496,8 @@ EXPORT_SYMBOL(hci_get_route);
/* Create SCO, ACL or LE connection.
* Device _must_ be locked */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t
*dst, __u8 sec_level, __u8 auth_type)
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
+ __u8 sec_level, __u8 auth_type)
{
struct hci_conn *acl;
struct hci_conn *sco;
@@ -503,20 +507,31 @@ struct hci_conn *hci_connect(struct hci_dev *hdev,
int type, bdaddr_t *dst, __u8
if (type == LE_LINK) {
struct adv_entry *entry;
+ struct link_key *key;
+ u8 bdaddr_type;
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (le)
- return ERR_PTR(-EBUSY);
+ if (le) {
+ hci_conn_hold(le);
+ return le;
+ }
- entry = hci_find_adv_entry(hdev, dst);
- if (!entry)
- return ERR_PTR(-EHOSTUNREACH);
+ key = hci_find_link_key_type(hdev, dst, KEY_TYPE_LTK);
+ if (!key) {
+ entry = hci_find_adv_entry(hdev, dst);
+ if (entry)
+ bdaddr_type = entry->bdaddr_type;
+ else
+ bdaddr_type = 0;
+ } else {
+ bdaddr_type = key->addr_type;
+ }
le = hci_conn_add(hdev, LE_LINK, dst);
if (!le)
return ERR_PTR(-ENOMEM);
- le->dst_type = entry->bdaddr_type;
+ le->dst_type = bdaddr_type;
hci_le_connect(le);
@@ -604,8 +619,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8
sec_level, __u8 auth_type)
/* Make sure we preserve an existing MITM requirement*/
auth_type |= (conn->auth_type & 0x01);
-
conn->auth_type = auth_type;
+ conn->auth_initiator = 1;
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
@@ -636,7 +651,7 @@ static void hci_conn_encrypt(struct hci_conn *conn)
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8
auth_type)
{
- BT_DBG("conn %p", conn);
+ BT_DBG("conn %p %d %d", conn, sec_level, auth_type);
/* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
@@ -674,8 +689,19 @@ int hci_conn_security(struct hci_conn *conn, __u8
sec_level, __u8 auth_type)
goto encrypt;
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ if (conn->type == LE_LINK) {
+ if (conn->pending_sec_level > sec_level)
+ sec_level = conn->pending_sec_level;
+
+ if (sec_level > conn->sec_level)
+ conn->pending_sec_level = sec_level;
+ hci_proto_connect_cfm(conn, 0);
+ return 0;
+ } else if (conn->link_mode & HCI_LM_ENCRYPT) {
+ return hci_conn_auth(conn, sec_level, auth_type);
+ } else if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
return 0;
+ }
if (!hci_conn_auth(conn, sec_level, auth_type))
return 0;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b7f6b5b..dbfe890 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -549,7 +549,8 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
- if (!test_bit(HCI_SETUP, &hdev->flags))
+ if (!test_bit(HCI_SETUP, &hdev->flags) &&
+ hdev->dev_type == HCI_BREDR)
mgmt_powered(hdev->id, 1);
} else {
/* Init failed, cleanup */
@@ -634,7 +635,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */
hdev->close(hdev);
- mgmt_powered(hdev->id, 0);
+ if (hdev->dev_type == HCI_BREDR)
+ mgmt_powered(hdev->id, 0);
/* Clear flags */
hdev->flags = 0;
@@ -929,14 +931,16 @@ static void hci_power_on(struct work_struct *work)
BT_DBG("%s", hdev->name);
- if (hci_dev_open(hdev->id) < 0)
+ if (hci_dev_open(hdev->id) < 0 && !test_bit(HCI_UP, &hdev->flags))
return;
- if (test_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_bit(HCI_AUTO_OFF, &hdev->flags) &&
+ hdev->dev_type == HCI_BREDR)
mod_timer(&hdev->off_timer,
jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
- if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
+ if (test_and_clear_bit(HCI_SETUP, &hdev->flags) &&
+ hdev->dev_type == HCI_BREDR)
mgmt_index_added(hdev->id);
}
@@ -1054,7 +1058,7 @@ struct link_key *hci_find_ltk(struct hci_dev
*hdev, __le16 ediv, u8 rand[8])
list_for_each_entry(k, &hdev->link_keys, list) {
struct key_master_id *id;
- if (k->type != HCI_LK_SMP_LTK)
+ if (k->key_type != HCI_LK_SMP_LTK)
continue;
if (k->dlen != sizeof(*id))
@@ -1071,12 +1075,12 @@ struct link_key *hci_find_ltk(struct hci_dev
*hdev, __le16 ediv, u8 rand[8])
EXPORT_SYMBOL(hci_find_ltk);
struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 type)
+ bdaddr_t *bdaddr, u8 key_type)
{
struct link_key *k;
list_for_each_entry(k, &hdev->link_keys, list)
- if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0)
+ if (k->key_type == key_type && bacmp(bdaddr, &k->bdaddr) == 0)
return k;
return NULL;
@@ -1091,7 +1095,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct
hci_conn *conn, int new_key,
old_key = hci_find_link_key(hdev, bdaddr);
if (old_key) {
- old_key_type = old_key->type;
+ old_key_type = old_key->key_type;
key = old_key;
} else {
old_key_type = conn ? conn->key_type : 0xff;
@@ -1119,9 +1123,9 @@ int hci_add_link_key(struct hci_dev *hdev, struct
hci_conn *conn, int new_key,
key->pin_len = pin_len;
if (type == HCI_LK_CHANGED_COMBINATION)
- key->type = old_key_type;
+ key->key_type = old_key_type;
else
- key->type = type;
+ key->key_type = type;
if (!new_key)
return 0;
@@ -1139,39 +1143,39 @@ int hci_add_link_key(struct hci_dev *hdev,
struct hci_conn *conn, int new_key,
}
int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16])
+ u8 addr_type, u8 key_size, u8 auth,
+ __le16 ediv, u8 rand[8], u8 ltk[16])
{
struct link_key *key, *old_key;
struct key_master_id *id;
- u8 old_key_type;
- BT_DBG("%s addr %s", hdev->name, batostr(bdaddr));
+ BT_DBG("%s Auth: %2.2X addr %s", hdev->name, auth, batostr(bdaddr));
old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK);
if (old_key) {
key = old_key;
- old_key_type = old_key->type;
} else {
key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC);
if (!key)
return -ENOMEM;
list_add(&key->list, &hdev->link_keys);
- old_key_type = 0xff;
}
key->dlen = sizeof(*id);
bacpy(&key->bdaddr, bdaddr);
+ key->addr_type = addr_type;
memcpy(key->val, ltk, sizeof(key->val));
- key->type = HCI_LK_SMP_LTK;
+ key->key_type = HCI_LK_SMP_LTK;
key->pin_len = key_size;
+ key->auth = auth;
id = (void *) &key->data;
id->ediv = ediv;
memcpy(id->rand, rand, sizeof(id->rand));
if (new_key)
- mgmt_new_key(hdev->id, key, old_key_type);
+ mgmt_new_key(hdev->id, key, auth & 0x01);
return 0;
}
@@ -1382,14 +1386,32 @@ int hci_add_adv_entry(struct hci_dev *hdev,
struct hci_ev_le_advertising_info *ev)
{
struct adv_entry *entry;
+ u8 flags = 0;
+ int i;
+
+ BT_DBG("");
if (!is_connectable_adv(ev->evt_type))
return -EINVAL;
+ if (ev->data && ev->length) {
+ for (i = 0; (i + 2) < ev->length; i++)
+ if (ev->data[i+1] == 0x01) {
+ flags = ev->data[i+2];
+ BT_DBG("flags: %2.2x", flags);
+ break;
+ } else {
+ i += ev->data[i];
+ }
+ }
+
+ entry = hci_find_adv_entry(hdev, &ev->bdaddr);
/* Only new entries should be added to adv_entries. So, if
* bdaddr was found, don't add it. */
- if (hci_find_adv_entry(hdev, &ev->bdaddr))
+ if (entry) {
+ entry->flags = flags;
return 0;
+ }
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
@@ -1397,6 +1419,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
bacpy(&entry->bdaddr, &ev->bdaddr);
entry->bdaddr_type = ev->bdaddr_type;
+ entry->flags = flags;
list_add(&entry->list, &hdev->adv_entries);
@@ -1546,7 +1569,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
kfree_skb(hdev->reassembly[i]);
if (!test_bit(HCI_INIT, &hdev->flags) &&
- !test_bit(HCI_SETUP, &hdev->flags))
+ !test_bit(HCI_SETUP, &hdev->flags) &&
+ hdev->dev_type == HCI_BREDR)
mgmt_index_removed(hdev->id);
hci_notify(hdev, HCI_DEV_UNREG);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d8fa657..5eec6be 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -641,6 +641,23 @@ static void hci_cc_read_local_features(struct
hci_dev *hdev, struct sk_buff *skb
memcpy(hdev->features, rp->features, 8);
+ if (hdev->dev_type == HCI_BREDR && test_bit(HCI_INIT, &hdev->flags)) {
+ if (hdev->features[6] & LMP_SIMPLE_PAIR) {
+ u8 mode = 0x01;
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
+ sizeof(mode), &mode);
+ }
+
+ if (hdev->features[3] & LMP_RSSI_INQ)
+ hci_setup_inquiry_mode(hdev);
+
+ if (hdev->features[7] & LMP_INQ_TX_PWR)
+ hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER,
+ 0, NULL);
+
+ hci_setup_event_mask(hdev);
+ }
+
/* Adjust default settings according to features
* supported by device. */
@@ -1071,9 +1088,6 @@ static void hci_cs_auth_requested(struct hci_dev
*hdev, __u8 status)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (!status)
- return;
-
cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED);
if (!cp)
return;
@@ -1082,10 +1096,11 @@ static void hci_cs_auth_requested(struct hci_dev
*hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- if (conn->state == BT_CONFIG) {
+ if (status && conn->state == BT_CONFIG) {
hci_proto_connect_cfm(conn, status);
hci_conn_put(conn);
}
+ conn->auth_initiator = 1;
}
hci_dev_unlock(hdev);
@@ -1349,6 +1364,10 @@ static void hci_cs_le_create_conn(struct hci_dev
*hdev, __u8 status)
BT_ERR("No memory for new connection");
}
}
+
+ if (conn)
+ mod_timer(&conn->disc_timer,
+ jiffies + msecs_to_jiffies(5000));
}
hci_dev_unlock(hdev);
@@ -1365,13 +1384,19 @@ static inline void
hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, status);
- if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
- test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 0);
+ if (!lmp_le_capable(hdev)) {
+ clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
+ }
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
- hci_conn_check_pending(hdev);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_inquiry_complete_evt(hdev->id, status);
+
+ if (!lmp_le_capable(hdev))
+ hci_conn_check_pending(hdev);
}
static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct
sk_buff *skb)
@@ -1403,8 +1428,8 @@ static inline void hci_inquiry_result_evt(struct
hci_dev *hdev, struct sk_buff *
data.rssi = 0x00;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
- NULL);
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
+ info->dev_class, 0, 0, NULL);
}
hci_dev_unlock(hdev);
@@ -1438,6 +1463,10 @@ static inline void hci_conn_complete_evt(struct
hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ mgmt_connected(hdev->id, &ev->bdaddr, 0);
+ } else if (conn->type == LE_LINK) {
+ conn->state = BT_CONNECTED;
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
} else
conn->state = BT_CONNECTED;
@@ -1469,7 +1498,7 @@ static inline void hci_conn_complete_evt(struct
hci_dev *hdev, struct sk_buff *s
}
} else {
conn->state = BT_CLOSED;
- if (conn->type == ACL_LINK)
+ if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
}
@@ -1668,8 +1697,8 @@ static inline void hci_remote_name_evt(struct
hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
- if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
- mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_remote_name(hdev->id, &ev->bdaddr, ev->status, ev->name);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (!conn)
@@ -2252,32 +2281,32 @@ static inline void
hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
goto not_found;
}
- BT_DBG("%s found key type %u for %s", hdev->name, key->type,
+ BT_DBG("%s found key type %u for %s", hdev->name, key->key_type,
batostr(&ev->bdaddr));
if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) &&
- key->type == HCI_LK_DEBUG_COMBINATION) {
+ key->key_type == HCI_LK_DEBUG_COMBINATION) {
BT_DBG("%s ignoring debug key", hdev->name);
goto not_found;
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
- if (key->type == HCI_LK_UNAUTH_COMBINATION &&
+ if (key->key_type == HCI_LK_UNAUTH_COMBINATION &&
conn->auth_type != 0xff &&
(conn->auth_type & 0x01)) {
BT_DBG("%s ignoring unauthenticated key", hdev->name);
goto not_found;
}
- if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 &&
+ if (key->key_type == HCI_LK_COMBINATION && key->pin_len < 16 &&
conn->pending_sec_level == BT_SECURITY_HIGH) {
BT_DBG("%s ignoring key unauthenticated for high \
security", hdev->name);
goto not_found;
}
- conn->key_type = key->type;
+ conn->key_type = key->key_type;
conn->pin_length = key->pin_len;
}
@@ -2413,9 +2442,9 @@ static inline void
hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
info->dev_class, info->rssi,
- NULL);
+ 0, NULL);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -2430,9 +2459,9 @@ static inline void
hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
info->dev_class, info->rssi,
- NULL);
+ 0, NULL);
}
}
@@ -2579,8 +2608,9 @@ static inline void
hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
- info->rssi, info->data);
+ mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
+ info->dev_class, info->rssi,
+ HCI_MAX_EIR_LENGTH, info->data);
}
hci_dev_unlock(hdev);
@@ -2588,19 +2618,23 @@ static inline void
hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
static inline u8 hci_get_auth_req(struct hci_conn *conn)
{
+ BT_DBG("%p", conn);
+
/* If remote requests dedicated bonding follow that lead */
if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
/* If both remote and local IO capabilities allow MITM
* protection then require it, otherwise don't */
- if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
+ if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) {
return 0x02;
- else
+ } else {
+ conn->auth_type |= 0x01;
return 0x03;
+ }
}
/* If remote requests no-bonding follow that lead */
- if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
- return conn->remote_auth | (conn->auth_type & 0x01);
+ if (conn->remote_auth <= 0x01)
+ return 0x00;
return conn->auth_type;
}
@@ -2626,11 +2660,15 @@ static inline void
hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
struct hci_cp_io_capability_reply cp;
+ u8 io_cap = conn->io_capability;
+ /* ACL-SSP does not support IO CAP 0x04 */
+ cp.capability = (io_cap == 0x04) ? 0x01 : io_cap;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.capability = conn->io_capability;
- conn->auth_type = hci_get_auth_req(conn);
- cp.authentication = conn->auth_type;
+ if (conn->auth_initiator)
+ cp.authentication = conn->auth_type;
+ else
+ cp.authentication = hci_get_auth_req(conn);
if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
hci_find_remote_oob_data(hdev, &conn->dst))
@@ -2675,24 +2713,28 @@ unlock:
hci_dev_unlock(hdev);
}
-static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
+static inline void hci_user_ssp_confirmation_evt(struct hci_dev *hdev,
+ u8 event, struct sk_buff *skb)
{
struct hci_ev_user_confirm_req *ev = (void *) skb->data;
int loc_mitm, rem_mitm, confirm_hint = 0;
struct hci_conn *conn;
+ __le32 passkey = 0;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- goto unlock;
-
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (!conn)
goto unlock;
+ if (event != HCI_EV_USER_PASSKEY_REQUEST)
+ passkey = ev->passkey;
+
+ if (event != HCI_EV_USER_CONFIRM_REQUEST)
+ goto confirm;
+
loc_mitm = (conn->auth_type & 0x01);
rem_mitm = (conn->remote_auth & 0x01);
@@ -2736,7 +2778,8 @@ static inline void
hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_user_confirm_request(hdev->id, &ev->bdaddr, passkey,
confirm_hint);
unlock:
@@ -2855,7 +2898,10 @@ static inline void
hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ mgmt_connected(hdev->id, &ev->bdaddr, 1);
+ hci_conn_hold(conn);
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
@@ -2865,25 +2911,6 @@ unlock:
hci_dev_unlock(hdev);
}
-static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
-{
- u8 num_reports = skb->data[0];
- void *ptr = &skb->data[1];
-
- hci_dev_lock(hdev);
-
- while (num_reports--) {
- struct hci_ev_le_advertising_info *ev = ptr;
-
- hci_add_adv_entry(hdev, ev);
-
- ptr += sizeof(*ev) + ev->length + 1;
- }
-
- hci_dev_unlock(hdev);
-}
-
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2921,6 +2948,27 @@ not_found:
hci_dev_unlock(hdev);
}
+static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_le_advertising_info *ev;
+ u8 num_reports;
+
+ num_reports = skb->data[0];
+ ev = (void *) &skb->data[1];
+
+ hci_dev_lock(hdev);
+
+ while (num_reports--) {
+ mgmt_device_found(hdev->id, &ev->bdaddr, ev->bdaddr_type,
+ 1, NULL, 0, ev->length, ev->data);
+ hci_add_adv_entry(hdev, ev);
+ ev = (void *) (ev->data + ev->length + 1);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct
sk_buff *skb)
{
struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -2950,6 +2998,8 @@ void hci_event_packet(struct hci_dev *hdev, struct
sk_buff *skb)
struct hci_event_hdr *hdr = (void *) skb->data;
__u8 event = hdr->evt;
+ BT_DBG("");
+
skb_pull(skb, HCI_EVENT_HDR_SIZE);
switch (event) {
@@ -3077,8 +3127,10 @@ void hci_event_packet(struct hci_dev *hdev,
struct sk_buff *skb)
hci_io_capa_reply_evt(hdev, skb);
break;
+ case HCI_EV_USER_PASSKEY_REQUEST:
+ case HCI_EV_USER_PASSKEY_NOTIFICATION:
case HCI_EV_USER_CONFIRM_REQUEST:
- hci_user_confirm_request_evt(hdev, skb);
+ hci_user_ssp_confirmation_evt(hdev, event, skb);
break;
case HCI_EV_SIMPLE_PAIR_COMPLETE:
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index f6afe3d..0f3918c 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -49,7 +49,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static int enable_mgmt;
+static int enable_mgmt = 1;
/* ----- HCI socket interface ----- */
@@ -281,7 +281,12 @@ static int hci_sock_ioctl(struct socket *sock,
unsigned int cmd, unsigned long a
case HCIDEVUP:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
- return hci_dev_open(arg);
+
+ err = hci_dev_open(arg);
+ if (!err || err == -EALREADY)
+ return 0;
+ else
+ return err;
case HCIDEVDOWN:
if (!capable(CAP_NET_ADMIN))
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 6e0d4ce..0f80c7e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -947,7 +947,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
- if (smp_conn_security(conn, chan->sec_level))
+ u8 sec_level = chan->sec_level;
+ u8 pending_sec = conn->hcon->pending_sec_level;
+
+ if (pending_sec > sec_level)
+ sec_level = pending_sec;
+
+ if (smp_conn_security(conn, sec_level))
l2cap_chan_ready(sk);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -958,6 +964,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
} else if (chan->state == BT_CONNECT)
l2cap_do_start(chan);
+
bh_unlock_sock(sk);
}
@@ -1033,6 +1040,7 @@ static void security_timeout(unsigned long arg)
{
struct l2cap_conn *conn = (void *) arg;
+ smp_timeout(conn);
l2cap_conn_del(conn->hcon, ETIMEDOUT);
}
@@ -4456,7 +4464,6 @@ static int l2cap_security_cfm(struct hci_conn
*hcon, u8 status, u8 encrypt)
BT_DBG("conn %p", conn);
if (hcon->type == LE_LINK) {
- smp_distribute_keys(conn, 0);
del_timer(&conn->security_timer);
}
@@ -4475,6 +4482,10 @@ static int l2cap_security_cfm(struct hci_conn
*hcon, u8 status, u8 encrypt)
l2cap_chan_ready(sk);
}
+ del_timer(&conn->security_timer);
+ l2cap_chan_ready(sk);
+ smp_link_encrypt_cmplt(conn, status, encrypt);
+
bh_unlock_sock(sk);
continue;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 747366a..766660a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -27,11 +27,28 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h>
+#include <net/bluetooth/smp.h>
#define MGMT_VERSION 0
#define MGMT_REVISION 1
+enum scan_mode {
+ SCAN_IDLE,
+ SCAN_LE,
+ SCAN_BR
+};
+
+struct disco_interleave {
+ struct timer_list timer;
+ struct timer_list le_timer;
+ u16 index;
+ enum scan_mode mode;
+ int int_phase;
+ int int_count;
+};
+
struct pending_cmd {
struct list_head list;
__u16 opcode;
@@ -134,6 +151,9 @@ static int read_index_list(struct sock *sk)
count = 0;
list_for_each(p, &hci_dev_list) {
+ struct hci_dev *d = list_entry(p, struct hci_dev, list);
+ if (d->dev_type != HCI_BREDR)
+ continue;
count++;
}
@@ -144,16 +164,20 @@ static int read_index_list(struct sock *sk)
return -ENOMEM;
}
- put_unaligned_le16(count, &rp->num_controllers);
+ put_unaligned_le16(0, &rp->num_controllers);
i = 0;
list_for_each_entry(d, &hci_dev_list, list) {
hci_del_off_timer(d);
+ if (d->dev_type != HCI_BREDR)
+ continue;
+
if (test_bit(HCI_SETUP, &d->flags))
continue;
put_unaligned_le16(d->id, &rp->index[i++]);
+ put_unaligned_le16((u16)i, &rp->num_controllers);
BT_DBG("Added hci%u", d->id);
}
@@ -217,6 +241,8 @@ static int read_controller_info(struct sock *sk, u16
index)
static void mgmt_pending_free(struct pending_cmd *cmd)
{
+ BT_DBG("%d", cmd->opcode);
+
sock_put(cmd->sk);
kfree(cmd->param);
kfree(cmd);
@@ -227,6 +253,8 @@ static struct pending_cmd *mgmt_pending_add(struct
sock *sk, u16 opcode,
{
struct pending_cmd *cmd;
+ BT_DBG("%d", opcode);
+
cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
if (!cmd)
return NULL;
@@ -257,6 +285,8 @@ static void mgmt_pending_foreach(u16 opcode, int index,
{
struct list_head *p, *n;
+ BT_DBG(" %d", opcode);
+
list_for_each_safe(p, n, &cmd_list) {
struct pending_cmd *cmd;
@@ -291,6 +321,8 @@ static struct pending_cmd *mgmt_pending_find(u16
opcode, int index)
static void mgmt_pending_remove(struct pending_cmd *cmd)
{
+ BT_DBG(" %d", cmd->opcode);
+
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
@@ -345,6 +377,124 @@ failed:
return err;
}
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct list_head *p;
+ u8 val = 0;
+
+ list_for_each(p, &hdev->uuids) {
+ struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+
+ val |= uuid->svc_hint;
+ }
+
+ return val;
+}
+
+static int update_class(struct hci_dev *hdev)
+{
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ return 0;
+
+ cod[0] = hdev->minor_class;
+ cod[1] = hdev->major_class;
+ cod[2] = get_service_classes(hdev);
+
+ if (memcmp(cod, hdev->dev_class, 3) == 0)
+ return 0;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
+
+static int set_limited_discoverable(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_mode *cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ struct hci_cp_write_current_iac_lap dcp;
+ int update_cod;
+ int err = 0;
+ /* General Inquiry LAP: 0x9E8B33, Limited Inquiry LAP: 0x9E8B00 */
+ u8 lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+
+ cp = (void *) data;
+
+ BT_DBG("hci%u discoverable: %d", index, cp->val);
+
+ if (!cp || len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENETDOWN);
+ goto failed;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_LIMIT_DISCOVERABLE, index)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EBUSY);
+ goto failed;
+ }
+
+ if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
+ test_bit(HCI_PSCAN, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EALREADY);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LIMIT_DISCOVERABLE, index, data,
+ len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&dcp, 0, sizeof(dcp));
+ dcp.num_current_iac = cp->val ? 2 : 1;
+ memcpy(&dcp.lap, lap, dcp.num_current_iac * 3);
+ update_cod = 1;
+
+ if (cp->val) {
+ if (hdev->major_class & MGMT_MAJOR_CLASS_LIMITED)
+ update_cod = 0;
+ hdev->major_class |= MGMT_MAJOR_CLASS_LIMITED;
+ } else {
+ if (!(hdev->major_class & MGMT_MAJOR_CLASS_LIMITED))
+ update_cod = 0;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_LIMITED;
+ }
+
+ if (update_cod)
+ err = update_class(hdev);
+
+ if (err >= 0)
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ sizeof(dcp), &dcp);
+
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_discoverable(struct sock *sk, u16 index, unsigned char
*data,
u16 len)
{
@@ -472,6 +622,8 @@ static int mgmt_event(u16 event, u16 index, void
*data, u16 data_len,
struct sk_buff *skb;
struct mgmt_hdr *hdr;
+ BT_DBG("hci%d %d", index, event);
+
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
@@ -684,36 +836,6 @@ static int update_eir(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct bt_uuid *uuid;
- u8 val = 0;
-
- list_for_each_entry(uuid, &hdev->uuids, list)
- val |= uuid->svc_hint;
-
- return val;
-}
-
-static int update_class(struct hci_dev *hdev)
-{
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
- return 0;
-
- cod[0] = hdev->minor_class;
- cod[1] = hdev->major_class;
- cod[2] = get_service_classes(hdev);
-
- if (memcmp(cod, hdev->dev_class, 3) == 0)
- return 0;
-
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
static int add_uuid(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct mgmt_cp_add_uuid *cp;
@@ -842,7 +964,8 @@ static int set_dev_class(struct sock *sk, u16 index,
unsigned char *data,
hci_dev_lock_bh(hdev);
- hdev->major_class = cp->major;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_MASK;
+ hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK;
hdev->minor_class = cp->minor;
err = update_class(hdev);
@@ -914,9 +1037,9 @@ static int load_keys(struct sock *sk, u16 index,
unsigned char *data, u16 len)
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
- if (expected_len != len) {
+ if (expected_len > len) {
BT_ERR("load_keys: expected %u bytes, got %u bytes",
- len, expected_len);
+ expected_len, len);
return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL);
}
@@ -938,11 +1061,31 @@ static int load_keys(struct sock *sk, u16 index,
unsigned char *data, u16 len)
else
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
- for (i = 0; i < key_count; i++) {
- struct mgmt_key_info *key = &cp->keys[i];
+ len -= sizeof(*cp);
+ i = 0;
+
+ while (i < len) {
+ struct mgmt_key_info *key = (void *) cp->keys + i;
+
+ i += sizeof(*key);
+
+ if (key->key_type == KEY_TYPE_LTK) {
+ struct key_master_id *id = (void *) key->data;
- hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
- key->pin_len);
+ i += key->dlen;
+
+ if (key->dlen != sizeof(struct key_master_id))
+ continue;
+
+ hci_add_ltk(hdev, 0, &key->bdaddr, key->addr_type,
+ key->pin_len, key->auth, id->ediv,
+ id->rand, key->val);
+
+ continue;
+ }
+
+ hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val,
+ key->key_type, key->pin_len);
}
hci_dev_unlock_bh(hdev);
@@ -1283,6 +1426,8 @@ static void pairing_complete(struct pending_cmd
*cmd, u8 status)
struct mgmt_rp_pair_device rp;
struct hci_conn *conn = cmd->user_data;
+ BT_DBG(" %u", status);
+
bacpy(&rp.bdaddr, &conn->dst);
rp.status = status;
@@ -1293,8 +1438,6 @@ static void pairing_complete(struct pending_cmd
*cmd, u8 status)
conn->security_cfm_cb = NULL;
conn->disconn_cfm_cb = NULL;
- hci_conn_put(conn);
-
mgmt_pending_remove(cmd);
}
@@ -1302,7 +1445,7 @@ static void pairing_complete_cb(struct hci_conn
*conn, u8 status)
{
struct pending_cmd *cmd;
- BT_DBG("status %u", status);
+ BT_DBG(" %u", status);
cmd = find_pairing(conn);
if (!cmd) {
@@ -1313,13 +1456,65 @@ static void pairing_complete_cb(struct hci_conn
*conn, u8 status)
pairing_complete(cmd, status);
}
+static void pairing_security_complete_cb(struct hci_conn *conn, u8 status)
+{
+ struct pending_cmd *cmd;
+
+ BT_DBG(" %u", status);
+
+ cmd = find_pairing(conn);
+ if (!cmd) {
+ BT_DBG("Unable to find a pending command");
+ return;
+ }
+
+ if (conn->type == LE_LINK)
+ smp_link_encrypt_cmplt(conn->l2cap_data, status,
+ status ? 0 : 1);
+ else
+ pairing_complete(cmd, status);
+}
+
+static void pairing_connect_complete_cb(struct hci_conn *conn, u8 status)
+{
+ struct pending_cmd *cmd;
+
+ BT_DBG("conn: %p %u", conn, status);
+
+ cmd = find_pairing(conn);
+ if (!cmd) {
+ BT_DBG("Unable to find a pending command");
+ return;
+ }
+
+ if (status)
+ pairing_complete(cmd, status);
+
+ hci_conn_put(conn);
+}
+
+static void discovery_terminated(struct pending_cmd *cmd, void *data)
+{
+ struct mgmt_mode ev = {0};
+ struct disco_interleave *ilp = cmd->param;
+
+ BT_DBG("");
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ mgmt_event(MGMT_EV_DISCOVERING, cmd->index, &ev, sizeof(ev), NULL);
+
+ list_del(&cmd->list);
+
+ mgmt_pending_free(cmd);
+}
+
static int pair_device(struct sock *sk, u16 index, unsigned char
*data, u16 len)
{
struct hci_dev *hdev;
struct mgmt_cp_pair_device *cp;
struct pending_cmd *cmd;
struct adv_entry *entry;
- u8 sec_level, auth_type;
+ u8 sec_level, auth_type, io_cap;
struct hci_conn *conn;
int err;
@@ -1331,24 +1526,30 @@ static int pair_device(struct sock *sk, u16
index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL);
hdev = hci_dev_get(index);
+
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
hci_dev_lock_bh(hdev);
sec_level = BT_SECURITY_MEDIUM;
- if (cp->io_cap == 0x03)
+ io_cap = cp->io_cap;
+ if ((io_cap == 0) || (io_cap == 0x03))
auth_type = HCI_AT_DEDICATED_BONDING;
else
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
entry = hci_find_adv_entry(hdev, &cp->bdaddr);
- if (entry)
+ if (entry && entry->flags & 0x04) {
conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level,
auth_type);
- else
+ } else {
+ /* ACL-SSP does not support io_cap 0x04 (KeyboadDisplay) */
+ if (io_cap == 0x04)
+ io_cap = 0x01;
conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level,
auth_type);
+ }
if (IS_ERR(conn)) {
err = PTR_ERR(conn);
@@ -1369,12 +1570,10 @@ static int pair_device(struct sock *sk, u16
index, unsigned char *data, u16 len)
}
/* For LE, just connecting isn't a proof that the pairing finished */
- if (!entry)
- conn->connect_cfm_cb = pairing_complete_cb;
-
- conn->security_cfm_cb = pairing_complete_cb;
+ conn->connect_cfm_cb = pairing_connect_complete_cb;
+ conn->security_cfm_cb = pairing_security_complete_cb;
conn->disconn_cfm_cb = pairing_complete_cb;
- conn->io_capability = cp->io_cap;
+ conn->io_capability = io_cap;
cmd->user_data = conn;
if (conn->state == BT_CONNECTED &&
@@ -1391,25 +1590,23 @@ unlock:
}
static int user_confirm_reply(struct sock *sk, u16 index, unsigned
char *data,
- u16 len, int success)
+ u16 len, u16 opcode)
{
struct mgmt_cp_user_confirm_reply *cp = (void *) data;
- u16 mgmt_op, hci_op;
+ u16 mgmt_op = opcode, hci_op;
struct pending_cmd *cmd;
struct hci_dev *hdev;
+ struct hci_conn *le_conn;
int err;
- BT_DBG("");
+ BT_DBG("%d", mgmt_op);
- if (success) {
- mgmt_op = MGMT_OP_USER_CONFIRM_REPLY;
- hci_op = HCI_OP_USER_CONFIRM_REPLY;
- } else {
- mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+ if (mgmt_op == MGMT_OP_USER_CONFIRM_NEG_REPLY)
hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY;
- }
+ else
+ hci_op = HCI_OP_USER_CONFIRM_REPLY;
- if (len != sizeof(*cp))
+ if (len < sizeof(*cp))
return cmd_status(sk, index, mgmt_op, EINVAL);
hdev = hci_dev_get(index);
@@ -1420,19 +1617,67 @@ static int user_confirm_reply(struct sock *sk,
u16 index, unsigned char *data,
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
- goto failed;
+ goto done;
+ }
+
+ le_conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+ if (le_conn) {
+ err = smp_user_confirm_reply(le_conn, mgmt_op, (void *) cp);
+ goto done;
}
+ BT_DBG("BR/EDR: %s", mgmt_op == MGMT_OP_USER_CONFIRM_NEG_REPLY ?
+ "Reject" : "Accept");
cmd = mgmt_pending_add(sk, mgmt_op, index, data, len);
if (!cmd) {
err = -ENOMEM;
- goto failed;
+ goto done;
}
err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr);
if (err < 0)
mgmt_pending_remove(cmd);
+done:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int resolve_name(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct mgmt_cp_resolve_name *mgmt_cp = (void *) data;
+ struct hci_cp_remote_name_req hci_cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("");
+
+ if (len != sizeof(*mgmt_cp))
+ return cmd_status(sk, index, MGMT_OP_RESOLVE_NAME, EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_RESOLVE_NAME, ENODEV);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_RESOLVE_NAME, index, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&hci_cp, 0, sizeof(hci_cp));
+ bacpy(&hci_cp.bdaddr, &mgmt_cp->bdaddr);
+ err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(hci_cp),
+ &hci_cp);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1479,6 +1724,169 @@ failed:
return err;
}
+static void discovery_rsp(struct pending_cmd *cmd, void *data)
+{
+ struct mgmt_mode ev;
+
+ BT_DBG("");
+ if (cmd->opcode == MGMT_OP_START_DISCOVERY) {
+ ev.val = 1;
+ cmd_status(cmd->sk, cmd->index, MGMT_OP_START_DISCOVERY, 0);
+ } else {
+ ev.val = 0;
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_STOP_DISCOVERY,
+ NULL, 0);
+ if (cmd->opcode == MGMT_OP_STOP_DISCOVERY) {
+ struct disco_interleave *ilp = cmd->param;
+
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ }
+ }
+
+ mgmt_event(MGMT_EV_DISCOVERING, cmd->index, &ev, sizeof(ev), NULL);
+
+ list_del(&cmd->list);
+
+ mgmt_pending_free(cmd);
+}
+
+void mgmt_inquiry_started(u16 index)
+{
+ BT_DBG("");
+ mgmt_pending_foreach(MGMT_OP_START_DISCOVERY, index,
+ discovery_rsp, NULL);
+}
+
+void mgmt_inquiry_complete_evt(u16 index, u8 status)
+{
+ struct hci_dev *hdev;
+ struct hci_cp_le_set_scan_enable le_cp = {1, 0};
+ struct pending_cmd *cmd;
+ int err = -1;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+
+ if (hdev)
+ hci_dev_lock(hdev);
+
+ if (!hdev || !lmp_le_capable(hdev)) {
+ struct mgmt_mode cp = {0};
+
+ mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
+ discovery_terminated, NULL);
+
+ mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL);
+
+ if (hdev)
+ goto done;
+ else
+ return;
+ }
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd && cmd->param) {
+ struct disco_interleave *ilp = cmd->param;
+
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ if (err >= 0) {
+ mod_timer(&ilp->le_timer, jiffies +
+ msecs_to_jiffies(ilp->int_phase * 1000));
+ ilp->mode = SCAN_LE;
+ } else
+ ilp->mode = SCAN_IDLE;
+ }
+
+ if (err < 0)
+ mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
+ discovery_terminated, NULL);
+
+done:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+}
+
+static void disco_to(unsigned long data)
+{
+ struct disco_interleave *ilp = (void *)data;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+
+ BT_DBG("hci%d", ilp->index);
+
+ del_timer_sync(&ilp->le_timer);
+ hdev = hci_dev_get(ilp->index);
+
+ if (hdev) {
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
+
+ if (ilp->mode != SCAN_IDLE) {
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ if (ilp->mode == SCAN_LE)
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ else
+ hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
+ 0, NULL);
+
+ ilp->mode = SCAN_IDLE;
+ }
+
+ if (cmd) {
+ struct mgmt_mode cp = {0};
+
+ mgmt_event(MGMT_EV_DISCOVERING, ilp->index, &cp,
+ sizeof(cp), NULL);
+ mgmt_pending_remove(cmd);
+ }
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ }
+}
+
+static void disco_le_to(unsigned long data)
+{
+ struct disco_interleave *ilp = (void *)data;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ BT_DBG("hci%d", ilp->index);
+
+ hdev = hci_dev_get(ilp->index);
+
+ if (hdev) {
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, ilp->index);
+
+ if (ilp->mode == SCAN_LE)
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+
+ /* re-start BR scan */
+ if (cmd) {
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ ilp->int_phase *= 2;
+ ilp->int_count = 0;
+ cp.num_rsp = (u8) ilp->int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+ ilp->mode = SCAN_BR;
+ } else
+ ilp->mode = SCAN_IDLE;
+
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ }
+}
+
static int read_local_oob_data(struct sock *sk, u16 index)
{
struct hci_dev *hdev;
@@ -1598,19 +2006,18 @@ static int remove_remote_oob_data(struct sock
*sk, u16 index,
static int start_discovery(struct sock *sk, u16 index)
{
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- struct hci_cp_inquiry cp;
- struct pending_cmd *cmd;
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 8, 0};
struct hci_dev *hdev;
+ struct pending_cmd *cmd;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("");
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
if (!cmd) {
@@ -1618,17 +2025,54 @@ static int start_discovery(struct sock *sk, u16
index)
goto failed;
}
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, 3);
- cp.length = 0x08;
- cp.num_rsp = 0x00;
+ /* If LE Capable, we will alternate between BR/EDR and LE */
+ if (lmp_le_capable(hdev)) {
+ struct hci_cp_le_set_scan_parameters le_cp;
+
+ /* Shorten BR scan params */
+ cp.num_rsp = 1;
+ cp.length /= 2;
+
+ /* Setup LE scan params */
+ memset(&le_cp, 0, sizeof(le_cp));
+ le_cp.type = 0x01; /* Active scanning */
+ /* The recommended value for scan interval and window is
+ * 11.25 msec. It is calculated by: time = n * 0.625 msec */
+ le_cp.interval = cpu_to_le16(0x0012);
+ le_cp.window = cpu_to_le16(0x0012);
+ le_cp.own_bdaddr_type = 0; /* Public address */
+ le_cp.filter = 0; /* Accept all adv packets */
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAMETERS,
+ sizeof(le_cp), &le_cp);
+ }
err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
if (err < 0)
mgmt_pending_remove(cmd);
+ else if (lmp_le_capable(hdev)) {
+ struct disco_interleave il, *ilp;
+
+ il.int_phase = 1;
+ il.int_count = 0;
+ il.index = index;
+ il.mode = SCAN_BR;
+ mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, &il,
+ sizeof(struct disco_interleave));
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd) {
+ ilp = cmd->param;
+ setup_timer(&ilp->le_timer, disco_le_to,
+ (unsigned long) ilp);
+ setup_timer(&ilp->timer, disco_to, (unsigned long) ilp);
+ mod_timer(&ilp->timer,
+ jiffies + msecs_to_jiffies(20000));
+ }
+ }
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1636,33 +2080,60 @@ failed:
static int stop_discovery(struct sock *sk, u16 index)
{
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+ struct mgmt_mode mode_cp = {0};
+ struct disco_interleave *ilp = NULL;
struct hci_dev *hdev;
- struct pending_cmd *cmd;
- int err;
+ struct pending_cmd *cmd = NULL;
+ int err = -EPERM;
- BT_DBG("hci%u", index);
+ BT_DBG("");
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
- hci_dev_lock_bh(hdev);
+ hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
+ if (lmp_le_capable(hdev)) {
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ ilp = cmd->param;
}
- err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
- if (err < 0)
+ if (lmp_le_capable(hdev) && ilp && (ilp->mode == SCAN_LE))
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+
+ if (err < 0) {
+ if (!ilp || (ilp->mode == SCAN_BR))
+ err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL,
+ 0, NULL);
+ }
+
+ if (ilp) {
+ ilp->mode = SCAN_IDLE;
+ del_timer_sync(&ilp->le_timer);
+ del_timer_sync(&ilp->timer);
+ }
+
+ if (err < 0 && cmd)
mgmt_pending_remove(cmd);
+ mgmt_event(MGMT_EV_DISCOVERING, index, &mode_cp, sizeof(mode_cp), NULL);
+
failed:
- hci_dev_unlock_bh(hdev);
+ hci_dev_unlock(hdev);
hci_dev_put(hdev);
- return err;
+ if (err < 0)
+ return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, -err);
+ else
+ return err;
}
static int block_device(struct sock *sk, u16 index, unsigned char *data,
@@ -1840,6 +2311,7 @@ int mgmt_control(struct sock *sk, struct msghdr
*msg, size_t msglen)
goto done;
}
+ BT_DBG("got opcode %x", opcode);
switch (opcode) {
case MGMT_OP_READ_VERSION:
err = read_version(sk);
@@ -1856,6 +2328,10 @@ int mgmt_control(struct sock *sk, struct msghdr
*msg, size_t msglen)
case MGMT_OP_SET_DISCOVERABLE:
err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_LIMIT_DISCOVERABLE:
+ err = set_limited_discoverable(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
case MGMT_OP_SET_CONNECTABLE:
err = set_connectable(sk, index, buf + sizeof(*hdr), len);
break;
@@ -1899,14 +2375,17 @@ int mgmt_control(struct sock *sk, struct msghdr
*msg, size_t msglen)
err = pair_device(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_USER_CONFIRM_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1);
- break;
+ case MGMT_OP_USER_PASSKEY_REPLY:
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
+ err = user_confirm_reply(sk, index, buf + sizeof(*hdr),
+ len, opcode);
break;
case MGMT_OP_SET_LOCAL_NAME:
err = set_local_name(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_RESOLVE_NAME:
+ err = resolve_name(sk, index, buf + sizeof(*hdr), len);
+ break;
case MGMT_OP_READ_LOCAL_OOB_DATA:
err = read_local_oob_data(sk, index);
break;
@@ -1959,6 +2438,7 @@ static void cmd_status_rsp(struct pending_cmd
*cmd, void *data)
int mgmt_index_added(u16 index)
{
+ BT_DBG("%d", index);
return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL);
}
@@ -2002,6 +2482,8 @@ int mgmt_powered(u16 index, u8 powered)
struct cmd_lookup match = { powered, NULL };
int ret;
+ BT_DBG("hci%u %d", index, powered);
+
mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
if (!powered) {
@@ -2058,17 +2540,29 @@ int mgmt_connectable(u16 index, u8 connectable)
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
{
- struct mgmt_ev_new_key ev;
+ struct mgmt_ev_new_key *ev;
+ int err, total;
- memset(&ev, 0, sizeof(ev));
+ total = sizeof(struct mgmt_ev_new_key) + key->dlen;
+ ev = kzalloc(total, GFP_ATOMIC);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->store_hint = persistent;
+ bacpy(&ev->key.bdaddr, &key->bdaddr);
+ ev->key.addr_type = key->addr_type;
+ ev->key.key_type = key->key_type;
+ memcpy(ev->key.val, key->val, 16);
+ ev->key.pin_len = key->pin_len;
+ ev->key.auth = key->auth;
+ ev->key.dlen = key->dlen;
+ memcpy(ev->key.data, key->data, key->dlen);
+
+ err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
- ev.store_hint = persistent;
- bacpy(&ev.key.bdaddr, &key->bdaddr);
- ev.key.type = key->type;
- memcpy(ev.key.val, key->val, 16);
- ev.key.pin_len = key->pin_len;
+ kfree(ev);
- return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
+ return err;
}
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
@@ -2145,6 +2639,8 @@ int mgmt_pin_code_request(u16 index, bdaddr_t
*bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
+ BT_DBG("hci%u", index);
+
bacpy(&ev.bdaddr, bdaddr);
ev.secure = secure;
@@ -2323,32 +2819,75 @@ int mgmt_read_local_oob_data_reply_complete(u16
index, u8 *hash, u8 *randomizer,
return err;
}
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
- u8 *eir)
+int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
+ u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir)
{
struct mgmt_ev_device_found ev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("le: %d", le);
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
ev.rssi = rssi;
-
- if (eir)
- memcpy(ev.eir, eir, sizeof(ev.eir));
+ ev.type = type;
+ ev.le = le;
if (dev_class)
memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
- return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+ if (eir && eir_len)
+ memcpy(ev.eir, eir, eir_len);
+
+ err = mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+
+ if (err < 0)
+ return err;
+
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (cmd) {
+ struct disco_interleave *ilp = cmd->param;
+ struct hci_dev *hdev = hci_dev_get(index);
+
+ ilp->int_count++;
+ if (hdev && ilp->int_count >= ilp->int_phase) {
+ /* Inquiry scan for General Discovery LAP */
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 4, 0};
+ struct hci_cp_le_set_scan_enable le_cp = {0, 0};
+
+ ilp->int_phase *= 2;
+ ilp->int_count = 0;
+ if (ilp->mode == SCAN_LE) {
+ /* cancel LE scan */
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(le_cp), &le_cp);
+ /* start BR scan */
+ cp.num_rsp = (u8) ilp->int_phase;
+ hci_send_cmd(hdev, HCI_OP_INQUIRY,
+ sizeof(cp), &cp);
+ ilp->mode = SCAN_BR;
+ del_timer_sync(&ilp->le_timer);
+ }
+ }
+
+ if (hdev)
+ hci_dev_put(hdev);
+ }
+
+ return 0;
}
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
+
+int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name)
{
struct mgmt_ev_remote_name ev;
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
+ ev.status = status;
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 94e94ca..b882783 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -23,13 +23,27 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/mgmt.h>
#include <net/bluetooth/smp.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <crypto/b128ops.h>
+#include <asm/unaligned.h>
#define SMP_TIMEOUT 30000 /* 30 seconds */
+#define SMP_MIN_CONN_INTERVAL 40 /* 50ms (40 * 1.25ms) */
+#define SMP_MAX_CONN_INTERVAL 56 /* 70ms (56 * 1.25ms) */
+#define SMP_MAX_CONN_LATENCY 0 /* 0ms (0 * 1.25ms) */
+#define SMP_SUPERVISION_TIMEOUT 500 /* 5 seconds (500 * 10ms) */
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE (!FALSE)
+#endif
+
+static int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+
static inline void swap128(u8 src[16], u8 dst[16])
{
int i;
@@ -183,9 +197,27 @@ static void smp_send_cmd(struct l2cap_conn *conn,
u8 code, u16 len, void *data)
skb->priority = HCI_PRIO_MAX;
hci_send_acl(conn->hchan, skb, 0);
+}
+
+static __u8 authreq_to_seclevel(__u8 authreq)
+{
+ if (authreq & SMP_AUTH_MITM)
+ return BT_SECURITY_HIGH;
+ else if (authreq & SMP_AUTH_BONDING)
+ return BT_SECURITY_MEDIUM;
+ else
+ return BT_SECURITY_LOW;
+}
+
+static __u8 seclevel_to_authreq(__u8 level)
+{
+ switch (level) {
+ case BT_SECURITY_HIGH:
+ return SMP_AUTH_MITM | SMP_AUTH_BONDING;
- mod_timer(&conn->security_timer, jiffies +
- msecs_to_jiffies(SMP_TIMEOUT));
+ default:
+ return SMP_AUTH_NONE;
+ }
}
static void build_pairing_cmd(struct l2cap_conn *conn,
@@ -193,30 +225,40 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
struct smp_cmd_pairing *rsp,
__u8 authreq)
{
- u8 dist_keys;
+ u8 all_keys = 0;
+ u8 dist_keys = 0;
- dist_keys = 0;
- if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
- dist_keys = SMP_DIST_ENC_KEY;
- authreq |= SMP_AUTH_BONDING;
- }
+ dist_keys = SMP_DIST_ENC_KEY;
+ authreq |= SMP_AUTH_BONDING;
+
+ BT_DBG("conn->hcon->io_capability:%d", conn->hcon->io_capability);
if (rsp == NULL) {
req->io_capability = conn->hcon->io_capability;
req->oob_flag = SMP_OOB_NOT_PRESENT;
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
- req->init_key_dist = dist_keys;
+ req->init_key_dist = all_keys;
req->resp_key_dist = dist_keys;
req->auth_req = authreq;
+ BT_DBG("SMP_CMD_PAIRING_REQ %d %d %d %d %2.2x %2.2x",
+ req->io_capability, req->oob_flag,
+ req->auth_req, req->max_key_size,
+ req->init_key_dist, req->resp_key_dist);
return;
}
- rsp->io_capability = conn->hcon->io_capability;
+ /* Don't request OOB */
rsp->oob_flag = SMP_OOB_NOT_PRESENT;
+
+ rsp->io_capability = conn->hcon->io_capability;
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
rsp->init_key_dist = req->init_key_dist & dist_keys;
rsp->resp_key_dist = req->resp_key_dist & dist_keys;
rsp->auth_req = authreq;
+ BT_DBG("SMP_CMD_PAIRING_RSP %d %d %d %d %2.2x %2.2x",
+ req->io_capability, req->oob_flag, req->auth_req,
+ req->max_key_size, req->init_key_dist,
+ req->resp_key_dist);
}
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
@@ -232,162 +274,220 @@ static u8 check_enc_key_size(struct l2cap_conn
*conn, __u8 max_key_size)
return 0;
}
-static void confirm_work(struct work_struct *work)
+#define JUST_WORKS SMP_JUST_WORKS
+#define REQ_PASSKEY SMP_REQ_PASSKEY
+#define CFM_PASSKEY SMP_CFM_PASSKEY
+#define JUST_CFM SMP_JUST_CFM
+#define OVERLAP SMP_OVERLAP
+static const u8 gen_method[5][5] = {
+ {JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY},
+ {JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY},
+ {CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY},
+ {JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM},
+ {CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP}
+};
+
+static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
+ u8 local_io, u8 remote_io)
{
- struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
- struct l2cap_conn *conn = smp->conn;
- struct crypto_blkcipher *tfm;
- struct smp_cmd_pairing_confirm cp;
- int ret;
- u8 res[16], reason;
-
- BT_DBG("conn %p", conn);
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = conn->smp_chan;
+ u8 method;
+ u32 passkey = 0;
+ int ret = 0;
- tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm)) {
- reason = SMP_UNSPECIFIED;
- goto error;
+ /* Initialize key to JUST WORKS */
+ memset(smp->tk, 0, sizeof(smp->tk));
+ smp->smp_tk_valid = FALSE;
+ smp->smp_auth = auth;
+
+ BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
+
+ /* If neither side wants MITM, use JUST WORKS */
+ /* If either side has unknown io_caps, use JUST_WORKS */
+ if (!(auth & SMP_AUTH_MITM) ||
+ local_io > SMP_IO_KEYBOARD_DISPLAY ||
+ remote_io > SMP_IO_KEYBOARD_DISPLAY) {
+ smp->smp_auth &= ~SMP_AUTH_MITM;
+ smp->smp_tk_valid = TRUE;
+ return 0;
}
- smp->tfm = tfm;
+ /* MITM is now officially requested, but not required */
+ /* Determine what we need (if anything) from the agent */
+ method = gen_method[local_io][remote_io];
- if (conn->hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
- conn->src, conn->hcon->dst_type, conn->dst,
- res);
- else
- ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
- conn->hcon->dst_type, conn->dst, 0, conn->src,
- res);
- if (ret) {
- reason = SMP_UNSPECIFIED;
- goto error;
+ if (method == SMP_JUST_WORKS || method == SMP_JUST_CFM)
+ smp->smp_auth &= ~SMP_AUTH_MITM;
+
+ /* Don't bother confirming unbonded JUST_WORKS */
+ if (!(auth & SMP_AUTH_BONDING) && method == SMP_JUST_CFM) {
+ smp->smp_tk_valid = TRUE;
+ return 0;
+ } else if (method == SMP_JUST_WORKS) {
+ smp->smp_tk_valid = TRUE;
+ return 0;
+ } else if (method == SMP_OVERLAP) {
+ if (hcon->link_mode & HCI_LM_MASTER)
+ method = SMP_CFM_PASSKEY;
+ else
+ method = SMP_REQ_PASSKEY;
}
- swap128(res, cp.confirm_val);
- smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+ if (method == SMP_CFM_PASSKEY) {
+ u8 key[16];
+ /* Generate a passkey for display. It is not valid until
+ * confirmed.
+ */
+ memset(key, 0, sizeof(key));
+ get_random_bytes(&passkey, sizeof(passkey));
+ passkey %= 1000000;
+ put_unaligned_le32(passkey, key);
+ swap128(key, smp->tk);
+ BT_DBG("PassKey: %d", passkey);
+ }
- return;
+ hci_dev_lock(hcon->hdev);
-error:
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
- smp_chan_destroy(conn);
+ switch (method) {
+ case SMP_REQ_PASSKEY:
+ ret = mgmt_user_confirm_request(hcon->hdev->id, conn->dst,
+ 0, 0);
+ break;
+ case SMP_CFM_PASSKEY:
+ default:
+ ret = mgmt_user_confirm_request(hcon->hdev->id, conn->dst,
+ passkey, 0);
+ break;
+ }
+
+ hci_dev_unlock(hcon->hdev);
+
+ return ret;
}
-static void random_work(struct work_struct *work)
+static int send_pairing_confirm(struct l2cap_conn *conn)
{
- struct smp_chan *smp = container_of(work, struct smp_chan, random);
- struct l2cap_conn *conn = smp->conn;
- struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = conn->smp_chan;
struct crypto_blkcipher *tfm = smp->tfm;
- u8 reason, confirm[16], res[16], key[16];
+ struct smp_cmd_pairing_confirm cp;
int ret;
+ u8 res[16];
- if (IS_ERR_OR_NULL(tfm)) {
- reason = SMP_UNSPECIFIED;
- goto error;
- }
-
- BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
-
- if (hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
- conn->src, hcon->dst_type, conn->dst,
+ if (conn->hcon->out)
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
+ conn->src, conn->hcon->dst_type, conn->dst,
res);
else
- ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
- hcon->dst_type, conn->dst, 0, conn->src,
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ conn->hcon->dst_type, conn->dst, 0, conn->src,
res);
- if (ret) {
- reason = SMP_UNSPECIFIED;
- goto error;
- }
-
- swap128(res, confirm);
-
- if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
- BT_ERR("Pairing failed (confirmation values mismatch)");
- reason = SMP_CONFIRM_FAILED;
- goto error;
- }
- if (hcon->out) {
- u8 stk[16], rand[8];
- __le16 ediv;
-
- memset(rand, 0, sizeof(rand));
- ediv = 0;
-
- smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key);
- swap128(key, stk);
-
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
-
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) {
- reason = SMP_UNSPECIFIED;
- goto error;
- }
-
- hci_le_start_enc(hcon, ediv, rand, stk);
- hcon->enc_key_size = smp->smp_key_size;
- } else {
- u8 stk[16], r[16], rand[8];
- __le16 ediv;
-
- memset(rand, 0, sizeof(rand));
- ediv = 0;
-
- swap128(smp->prnd, r);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
-
- smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key);
- swap128(key, stk);
+ if (ret)
+ return SMP_CONFIRM_FAILED;
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+ swap128(res, cp.confirm_val);
- hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size,
- ediv, rand, stk);
- }
+ smp->smp_cfm_pending = FALSE;
- return;
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
-error:
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
- smp_chan_destroy(conn);
+ return 0;
}
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
{
struct smp_chan *smp;
+ struct crypto_blkcipher *tfm;
+
+ if (conn->smp_chan)
+ return conn->smp_chan;
smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC);
if (!smp)
return NULL;
+
+ tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (!tfm || IS_ERR(tfm)) {
+ kfree(smp);
+ return NULL;
+ }
- INIT_WORK(&smp->confirm, confirm_work);
- INIT_WORK(&smp->random, random_work);
-
+ smp->tfm = tfm;
smp->conn = conn;
conn->smp_chan = smp;
-
+
hci_conn_hold(conn->hcon);
-
+
return smp;
}
void smp_chan_destroy(struct l2cap_conn *conn)
{
- kfree(conn->smp_chan);
+ struct smp_chan *smp = conn->smp_chan;
+
+ if (smp && !IS_ERR(smp->tfm))
+ crypto_free_blkcipher(smp->tfm);
+
+ kfree(smp);
+ conn->smp_chan = NULL;
hci_conn_put(conn->hcon);
}
+int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, void *cp)
+{
+ struct mgmt_cp_user_passkey_reply *psk_reply = cp;
+ struct l2cap_conn *conn = hcon->smp_conn;
+ struct smp_chan *smp = conn->smp_chan;
+ u8 key[16];
+ u8 reason = 0;
+ int ret = 0;
+
+ BT_DBG("");
+
+ smp->smp_tk_valid = TRUE;
+
+ switch (mgmt_op) {
+ case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+ reason = SMP_CONFIRM_FAILED;
+ break;
+ case MGMT_OP_USER_CONFIRM_REPLY:
+ break;
+ case MGMT_OP_USER_PASSKEY_REPLY:
+ memset(key, 0, sizeof(key));
+ BT_DBG("PassKey: %d", psk_reply->passkey);
+ put_unaligned_le32(psk_reply->passkey, key);
+ swap128(key, smp->tk);
+ break;
+ default:
+ reason = SMP_CONFIRM_FAILED;
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (reason) {
+ BT_DBG("smp_send_cmd: SMP_CMD_PAIRING_FAIL");
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
+ &reason);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+ mgmt_auth_failed(hcon->hdev->id, conn->dst, reason);
+ del_timer(&conn->security_timer);
+ if (smp)
+ smp_chan_destroy(conn);
+ } else if (smp->smp_cfm_pending) {
+ BT_DBG("send_pairing_confirm");
+ ret = send_pairing_confirm(conn);
+ }
+
+ return ret;
+}
+
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff
*skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
struct smp_chan *smp;
u8 key_size;
+ u8 auth = SMP_AUTH_NONE;
int ret;
BT_DBG("conn %p", conn);
@@ -401,26 +501,35 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn
*conn, struct sk_buff *skb)
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
- if (req->oob_flag)
- return SMP_OOB_NOT_AVAIL;
+ /* We didn't start the pairing, so match remote */
+ if (req->auth_req & SMP_AUTH_BONDING)
+ auth = req->auth_req;
- /* We didn't start the pairing, so no requirements */
- build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE);
+ build_pairing_cmd(conn, req, &rsp, auth);
key_size = min(req->max_key_size, rsp.max_key_size);
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
-
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
+ /* Request setup of TK */
+ ret = tk_request(conn, req->oob_flag, auth, rsp.io_capability,
+ req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
return 0;
@@ -430,8 +539,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn
*conn, struct sk_buff *skb)
{
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
- u8 key_size;
+ u8 key_size, auth = SMP_AUTH_NONE;
int ret;
BT_DBG("conn %p", conn);
@@ -444,20 +552,33 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn
*conn, struct sk_buff *skb)
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- if (rsp->oob_flag)
- return SMP_OOB_NOT_AVAIL;
-
- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
- smp->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
+ if ((req->auth_req & SMP_AUTH_BONDING) &&
+ (rsp->auth_req & SMP_AUTH_BONDING))
+ auth = SMP_AUTH_BONDING;
+
+ auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
+
+ ret = tk_request(conn, req->oob_flag, auth, rsp->io_capability,
+ req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ smp->smp_cfm_pending = TRUE;
- queue_work(hdev->workqueue, &smp->confirm);
+ /* Can't compose response until we have been confirmed */
+ if (!smp->smp_tk_valid)
+ return 0;
+
+ ret = send_pairing_confirm(conn);
+ if (ret)
+ return SMP_CONFIRM_FAILED;
return 0;
}
@@ -465,7 +586,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn
*conn, struct sk_buff *skb)
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct
sk_buff *skb)
{
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
+ int ret;
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
@@ -478,69 +599,154 @@ static u8 smp_cmd_pairing_confirm(struct
l2cap_conn *conn, struct sk_buff *skb)
swap128(smp->prnd, random);
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
random);
- } else {
- queue_work(hdev->workqueue, &smp->confirm);
- }
+ } else if (smp->smp_tk_valid) {
+ ret = send_pairing_confirm(conn);
+
+ if (ret)
+ return SMP_CONFIRM_FAILED;
+ } else
+ smp->smp_cfm_pending = TRUE;
+
+
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
return 0;
}
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct
sk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp = conn->smp_chan;
- struct hci_dev *hdev = conn->hcon->hdev;
+ struct crypto_blkcipher *tfm = smp->tfm;
+ int ret;
+ u8 key[16], res[16], random[16], confirm[16];
+ u8 stk[16], rand[8];
+ __le16 ediv;
- BT_DBG("conn %p", conn);
- swap128(skb->data, smp->rrnd);
- skb_pull(skb, sizeof(smp->rrnd));
+ swap128(skb->data, random);
+ skb_pull(skb, sizeof(random));
+
+ if (hcon->out)
+ ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp, 0,
+ conn->src, hcon->dst_type, conn->dst,
+ res);
+ else
+ ret = smp_c1(tfm, smp->tk, random, smp->preq, smp->prsp,
+ hcon->dst_type, conn->dst, 0, conn->src,
+ res);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ BT_DBG("conn %p %s", conn, hcon->out ? "master" : "slave");
- queue_work(hdev->workqueue, &smp->random);
+ swap128(res, confirm);
+
+ if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
+ BT_ERR("Pairing failed (confirmation values mismatch)");
+ return SMP_CONFIRM_FAILED;
+ }
+
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+
+ if (hcon->out) {
+ smp_s1(tfm, smp->tk, random, smp->prnd, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ hci_le_start_enc(hcon, ediv, rand, stk);
+ hcon->enc_key_size = smp->smp_key_size;
+ } else {
+ u8 r[16];
+
+ swap128(smp->prnd, r);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+
+ smp_s1(tfm, smp->tk, smp->prnd, random, key);
+ swap128(key, stk);
+
+ memset(stk + smp->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+
+ hci_add_ltk(hcon->hdev, 0, conn->dst, hcon->dst_type,
+ smp->smp_key_size, smp->smp_auth, ediv, rand, stk);
+ }
return 0;
}
-static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
+static int smp_encrypt_link(struct l2cap_conn *conn, struct link_key *key)
{
- struct link_key *key;
+ struct smp_chan *smp = conn->smp_chan;
struct key_master_id *master;
- struct hci_conn *hcon = conn->hcon;
+ u8 sec_level;
+ u8 zerobuf[8];
- key = hci_find_link_key_type(hcon->hdev, conn->dst,
- HCI_LK_SMP_LTK);
- if (!key)
- return 0;
+ if (!conn->hcon || !key || !key->data)
+ return -EINVAL;
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
- &hcon->pend))
- return 1;
+ memset(zerobuf, 0, sizeof(zerobuf));
master = (void *) key->data;
- hci_le_start_enc(hcon, master->ediv, master->rand,
- key->val);
- hcon->enc_key_size = key->pin_len;
- return 1;
+ if (!master->ediv && !memcmp(master->rand, zerobuf, sizeof(zerobuf)))
+ return -EINVAL;
+
+ conn->hcon->enc_key_size = key->pin_len;
+ smp->smp_sec_req = TRUE;
+ sec_level = authreq_to_seclevel(key->auth);
+
+ BT_DBG("cur %d, req: %d", conn->hcon->sec_level, sec_level);
+
+ if (sec_level > conn->hcon->sec_level)
+ conn->hcon->pending_sec_level = sec_level;
+
+ if (!(conn->hcon->link_mode & HCI_LM_ENCRYPT))
+ hci_conn_hold(conn->hcon);
+
+ hci_le_start_enc(conn->hcon, master->ediv, master->rand, key->val);
+
+ return 0;
}
+
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff
*skb)
{
- struct smp_cmd_security_req *rp = (void *) skb->data;
- struct smp_cmd_pairing cp;
struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp;
+ struct smp_cmd_security_req *rp = (void *) skb->data;
+ struct smp_cmd_pairing cp;
+ struct link_key *key;
BT_DBG("conn %p", conn);
- hcon->pending_sec_level = BT_SECURITY_MEDIUM;
-
- if (smp_ltk_encrypt(conn))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ key = hci_find_link_key_type(hcon->hdev, conn->dst, KEY_TYPE_LTK);
+ if (key && ((key->auth & SMP_AUTH_MITM) ||
+ !(rp->auth_req & SMP_AUTH_MITM))) {
+
+ if (smp_encrypt_link(conn, key) < 0)
+ goto invalid_key;
+
return 0;
+ }
+invalid_key:
smp = smp_chan_create(conn);
+ if (!smp)
+ return SMP_CMD_NOTSUPP;
+
+ smp->smp_sec_req = FALSE;
+
+ /* Switch to Pairing Connection Parameters */
+ hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL, SMP_MAX_CONN_INTERVAL,
+ SMP_MAX_CONN_LATENCY, SMP_SUPERVISION_TIMEOUT);
skb_pull(skb, sizeof(*rp));
@@ -552,6 +758,13 @@ static u8 smp_cmd_security_req(struct l2cap_conn
*conn, struct sk_buff *skb)
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ hci_conn_hold(hcon);
+
return 0;
}
@@ -559,6 +772,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8
sec_level)
{
struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp = conn->smp_chan;
+ u8 authreq;
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
@@ -571,22 +785,48 @@ int smp_conn_security(struct l2cap_conn *conn,
__u8 sec_level)
if (hcon->sec_level >= sec_level)
return 1;
- if (hcon->link_mode & HCI_LM_MASTER)
- if (smp_ltk_encrypt(conn))
- goto done;
-
if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
return 0;
+ if (hcon->sec_level >= sec_level)
+ return 1;
+
+ authreq = seclevel_to_authreq(sec_level);
+
smp = smp_chan_create(conn);
+ hcon->smp_conn = conn;
+ hcon->pending_sec_level = sec_level;
+
+ if ((hcon->link_mode & HCI_LM_MASTER) && !smp->smp_sec_req) {
+ struct link_key *key;
+
+ key = hci_find_link_key_type(hcon->hdev, conn->dst,
+ KEY_TYPE_LTK);
+
+ if (smp_encrypt_link(conn, key) == 0)
+ goto done;
+ }
+
+ smp->smp_sec_req = FALSE;
+
+ if (!smp)
+ return 1;
if (hcon->link_mode & HCI_LM_MASTER) {
struct smp_cmd_pairing cp;
- build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
+ /* Switch to Pairing Connection Parameters */
+ hci_le_conn_update(hcon, SMP_MIN_CONN_INTERVAL,
+ SMP_MAX_CONN_INTERVAL, SMP_MAX_CONN_LATENCY,
+ SMP_SUPERVISION_TIMEOUT);
+
+ build_pairing_cmd(conn, &cp, NULL, authreq);
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], &cp, sizeof(cp));
+ mod_timer(&conn->security_timer,
+ jiffies + msecs_to_jiffies(SMP_TIMEOUT));
+
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
} else {
struct smp_cmd_security_req cp;
@@ -595,7 +835,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8
sec_level)
}
done:
- hcon->pending_sec_level = sec_level;
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
return 0;
}
@@ -616,29 +856,45 @@ static int smp_cmd_master_ident(struct l2cap_conn
*conn, struct sk_buff *skb)
{
struct smp_cmd_master_ident *rp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
+ struct smp_cmd_pairing *paircmd = (void *) &smp->prsp[1];
+ u8 *keydist;
skb_pull(skb, sizeof(*rp));
- hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
- rp->ediv, rp->rand, smp->tk);
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->hcon->dst_type,
+ smp->smp_key_size, smp->smp_auth, rp->ediv,
+ rp->rand, smp->tk);
+
+ if (conn->hcon->out)
+ keydist = &paircmd->resp_key_dist;
+ else
+ keydist = &paircmd->init_key_dist;
+
+ BT_DBG("keydist 0x%x", *keydist);
- smp_distribute_keys(conn, 1);
+ *keydist &= ~SMP_DIST_ENC_KEY;
+ if (conn->hcon->out) {
+ if (!(*keydist))
+ smp_distribute_keys(conn, 1);
+ }
return 0;
}
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
__u8 code = skb->data[0];
__u8 reason;
int err = 0;
- if (!lmp_host_le_capable(conn->hcon->hdev)) {
+ if (!lmp_host_le_capable(hcon->hdev)) {
err = -ENOTSUPP;
reason = SMP_PAIRING_NOTSUPP;
goto done;
}
+ hcon->smp_conn = conn;
skb_pull(skb, sizeof(code));
switch (code) {
@@ -649,6 +905,11 @@ int smp_sig_channel(struct l2cap_conn *conn, struct
sk_buff *skb)
case SMP_CMD_PAIRING_FAIL:
reason = 0;
err = -EPERM;
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+ mgmt_auth_failed(hcon->hdev->id, conn->dst, skb->data[0]);
+ del_timer(&conn->security_timer);
+ if (conn->smp_chan)
+ smp_chan_destroy(conn);
break;
case SMP_CMD_PAIRING_RSP:
@@ -691,15 +952,21 @@ int smp_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
}
done:
- if (reason)
+ if (reason) {
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+ mgmt_auth_failed(hcon->hdev->id, conn->dst, reason);
+ del_timer(&conn->security_timer);
+ if (conn->smp_chan)
+ smp_chan_destroy(conn);
+ }
kfree_skb(skb);
return err;
}
-int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
+static int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
{
struct smp_cmd_pairing *req, *rsp;
struct smp_chan *smp = conn->smp_chan;
@@ -740,8 +1007,9 @@ int smp_distribute_keys(struct l2cap_conn *conn,
__u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- ediv, ident.rand, enc.ltk);
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst,
+ conn->hcon->dst_type, smp->smp_key_size,
+ smp->smp_auth, ediv, ident.rand, enc.ltk);
ident.ediv = cpu_to_le16(ediv);
@@ -780,11 +1048,46 @@ int smp_distribute_keys(struct l2cap_conn *conn,
__u8 force)
*keydist &= ~SMP_DIST_SIGN;
}
- if (conn->hcon->out || force) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ if (conn->hcon->out || rsp->resp_key_dist) {
+ if (conn->hcon->disconn_cfm_cb)
+ conn->hcon->disconn_cfm_cb(conn->hcon, 0);
+
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
del_timer(&conn->security_timer);
smp_chan_destroy(conn);
}
return 0;
}
+
+int smp_link_encrypt_cmplt(struct l2cap_conn *conn, u8 status, u8 encrypt)
+{
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = conn->smp_chan;
+
+ BT_DBG("smp: %d %d %d", status, encrypt, smp->smp_sec_req);
+
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ if (!status && encrypt && !smp->smp_sec_req)
+ smp_distribute_keys(conn, 0);
+
+ /* Fall back to Pairing request if failed a Link Security request */
+ else if (smp->smp_sec_req && (status || !encrypt))
+ smp_conn_security(conn, hcon->sec_level);
+ else
+ smp_chan_destroy(conn);
+
+ return 0;
+}
+
+void smp_timeout(struct l2cap_conn *conn)
+{
+ u8 reason = SMP_UNSPECIFIED;
+
+ BT_DBG("%p", conn);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
+ mgmt_auth_failed(conn->hcon->hdev->id, conn->dst, SMP_UNSPECIFIED);
+}
--
1.7.7.1
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Brian,
On Mon, Nov 07, 2011 at 02:00:16PM -0800, Brian Gix wrote:
...
> hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
>
> static inline u8 hci_get_auth_req(struct hci_conn *conn)
> {
> + BT_DBG("%p", conn);
> +
> /* If remote requests dedicated bonding follow that lead */
> if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
> /* If both remote and local IO capabilities allow MITM
> * protection then require it, otherwise don't */
> - if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
> + if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) {
> return 0x02;
> - else
> + } else {
> + conn->auth_type |= 0x01;
> return 0x03;
> + }
> }
Maybe you could also fix those magic numbers issues. We shall define
capabilities.
>
> /* If remote requests no-bonding follow that lead */
> - if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
> - return conn->remote_auth | (conn->auth_type & 0x01);
> + if (conn->remote_auth <= 0x01)
> + return 0x00;
>
> return conn->auth_type;
> }
> @@ -2626,11 +2660,15 @@ static inline void
> hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
> if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
> (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
> struct hci_cp_io_capability_reply cp;
> + u8 io_cap = conn->io_capability;
>
> + /* ACL-SSP does not support IO CAP 0x04 */
> + cp.capability = (io_cap == 0x04) ? 0x01 : io_cap;
> bacpy(&cp.bdaddr, &ev->bdaddr);
> - cp.capability = conn->io_capability;
> - conn->auth_type = hci_get_auth_req(conn);
> - cp.authentication = conn->auth_type;
> + if (conn->auth_initiator)
> + cp.authentication = conn->auth_type;
> + else
> + cp.authentication = hci_get_auth_req(conn);
>
> if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
> hci_find_remote_oob_data(hdev, &conn->dst))
> @@ -2675,24 +2713,28 @@ unlock:
> hci_dev_unlock(hdev);
...
Best regards
Andrei Emeltchenko
On 11/7/2011 2:58 PM, Gustavo Padovan wrote:
> Hi Brian,
>
> * Brian Gix<[email protected]> [2011-11-07 14:00:16 -0800]:
>
>> Implemented missing components of MGMTOPS interface
>> Differentiated as needed between BR/EDR pairing and LE pairing
>>
>> These are a combination of changes we made to get make MGMTOPS more
>> robust, and provide MITM pairing options for LE. Much work has been
>> put into
>> merging these changes into the current master branch of Bluetooth Next, but
>> they have not been extensively tested on that build.
>>
>> This is anticipated to be the first step towards upstreaming these changes,
>>
>> Signed-off-by: Brian Gix<[email protected]>
>> ---
>> include/net/bluetooth/hci.h | 36 ++
>> include/net/bluetooth/hci_core.h | 32 ++-
>> include/net/bluetooth/l2cap.h | 5 +
>> include/net/bluetooth/mgmt.h | 24 ++-
>> include/net/bluetooth/smp.h | 14 +-
>> net/bluetooth/hci_conn.c | 58 +++-
>> net/bluetooth/hci_core.c | 64 +++-
>> net/bluetooth/hci_event.c | 166 ++++++---
>> net/bluetooth/hci_sock.c | 9 +-
>> net/bluetooth/l2cap_core.c | 15 +-
>> net/bluetooth/mgmt.c | 743
>> ++++++++++++++++++++++++++++++++------
>> net/bluetooth/smp.c | 661 ++++++++++++++++++++++++---------
>> 12 files changed, 1441 insertions(+), 386 deletions(-)
>
> We really appreciate your contribution to upstream, but this need to be
> broken in more patches, otherwise its impossible to me review and accept it.
>
> Try to split this in logical and atomic changes, upstream the easiest things
> first, like small l2cap or smp changes. New MGMT commands need one commit
> for each because they need to be discussed and so on.
>
> The are many examples of this in the mailing list.
>
> Gustavo
I understand. This was a first rough pass, which I expect to undergo a
variety of changes, including but not limited to conformance to the
MGMTOPS api as agreed upon in Prague, and the splitting into many more
smaller patches. I did not intend for this single behemoth to be
accepted in whole or in part at this point, but rather as a delivery
mechanism for those who have been waiting for something that actually
could be used with mainline code, which was impossible in the form we
had published elsewhere. This is why I chose RFC rather than PATCH in
the subject header.
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Brian,
* Brian Gix <[email protected]> [2011-11-07 14:00:16 -0800]:
> Implemented missing components of MGMTOPS interface
> Differentiated as needed between BR/EDR pairing and LE pairing
>
> These are a combination of changes we made to get make MGMTOPS more
> robust, and provide MITM pairing options for LE. Much work has been
> put into
> merging these changes into the current master branch of Bluetooth Next, but
> they have not been extensively tested on that build.
>
> This is anticipated to be the first step towards upstreaming these changes,
>
> Signed-off-by: Brian Gix <[email protected]>
> ---
> include/net/bluetooth/hci.h | 36 ++
> include/net/bluetooth/hci_core.h | 32 ++-
> include/net/bluetooth/l2cap.h | 5 +
> include/net/bluetooth/mgmt.h | 24 ++-
> include/net/bluetooth/smp.h | 14 +-
> net/bluetooth/hci_conn.c | 58 +++-
> net/bluetooth/hci_core.c | 64 +++-
> net/bluetooth/hci_event.c | 166 ++++++---
> net/bluetooth/hci_sock.c | 9 +-
> net/bluetooth/l2cap_core.c | 15 +-
> net/bluetooth/mgmt.c | 743
> ++++++++++++++++++++++++++++++++------
> net/bluetooth/smp.c | 661 ++++++++++++++++++++++++---------
> 12 files changed, 1441 insertions(+), 386 deletions(-)
We really appreciate your contribution to upstream, but this need to be
broken in more patches, otherwise its impossible to me review and accept it.
Try to split this in logical and atomic changes, upstream the easiest things
first, like small l2cap or smp changes. New MGMT commands need one commit
for each because they need to be discussed and so on.
The are many examples of this in the mailing list.
Gustavo