Bluetooth: Add LE SecMgr and mgmtops support
Enabled ECB Block encoding for Low Energy pairing
Implemented missing components of MGMTOPS interface
Differentiated as needed between BR/EDR pairing and LE pairing
Signed-off-by: Brian Gix <bgix@codeaurora.org>
Conflicts:
net/bluetooth/mgmt.c
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 310e17e..16033bb 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;
@@ -139,7 +156,7 @@
return -ENOMEM;
}
- put_unaligned_le16(count, &rp->num_controllers);
+ put_unaligned_le16(0, &rp->num_controllers);
i = 0;
list_for_each(p, &hci_dev_list) {
@@ -153,6 +170,7 @@
continue;
put_unaligned_le16(d->id, &rp->index[i++]);
+ put_unaligned_le16((u16)i, &rp->num_controllers);
BT_DBG("Added hci%u", d->id);
}
@@ -216,6 +234,8 @@
static void mgmt_pending_free(struct pending_cmd *cmd)
{
+ BT_DBG("%d", cmd->opcode);
+
sock_put(cmd->sk);
kfree(cmd->param);
kfree(cmd);
@@ -226,6 +246,8 @@
{
struct pending_cmd *cmd;
+ BT_DBG("%d", opcode);
+
cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
if (!cmd)
return NULL;
@@ -256,6 +278,8 @@
{
struct list_head *p, *n;
+ BT_DBG(" %d", opcode);
+
list_for_each_safe(p, n, &cmd_list) {
struct pending_cmd *cmd;
@@ -275,6 +299,8 @@
{
struct list_head *p;
+ BT_DBG(" %d", opcode);
+
list_for_each(p, &cmd_list) {
struct pending_cmd *cmd;
@@ -294,6 +320,8 @@
static void mgmt_pending_remove(struct pending_cmd *cmd)
{
+ BT_DBG(" %d", cmd->opcode);
+
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
@@ -475,6 +503,8 @@
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;
@@ -948,7 +978,7 @@
while (i < len) {
struct mgmt_key_info *key = (void *) cp->keys + i;
- i += sizeof(*key) + key->dlen;
+ i += sizeof(*key);
if (key->type == KEY_TYPE_LTK) {
struct key_master_id *id = (void *) key->data;
@@ -957,7 +987,7 @@
continue;
hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len,
- id->ediv, id->rand, key->val);
+ key->auth, id->ediv, id->rand, key->val);
continue;
}
@@ -1288,6 +1318,8 @@
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;
@@ -1307,7 +1339,7 @@
{
struct pending_cmd *cmd;
- BT_DBG("status %u", status);
+ BT_DBG(" %u", status);
cmd = find_pairing(conn);
if (!cmd) {
@@ -1318,23 +1350,68 @@
pairing_complete(cmd, status);
}
+static void 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 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;
+ }
+
+ 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;
- u8 sec_level, auth_type;
+ u8 sec_level, auth_type, io_cap;
struct hci_conn *conn;
int err;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- 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);
@@ -1355,8 +1432,19 @@
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
}
- conn = hci_connect(hdev, ACL_LINK, 0, &cp->bdaddr, sec_level,
- auth_type);
+ if (hci_find_adv_entry(hdev, &cp->bdaddr)) {
+ conn = hci_connect(hdev, LE_LINK, 0, &cp->bdaddr, sec_level,
+ auth_type);
+ if (conn && !IS_ERR(conn))
+ hci_conn_hold(conn);
+ } else {
+ /* ACL-SSP does not support io_cap 0x04 (KeyboadDisplay) */
+ if (io_cap == 0x04)
+ io_cap = 0x01;
+ conn = hci_connect(hdev, ACL_LINK, 0, &cp->bdaddr, sec_level,
+ auth_type);
+ }
+
if (IS_ERR(conn)) {
err = PTR_ERR(conn);
goto unlock;
@@ -1375,10 +1463,10 @@
goto unlock;
}
- conn->connect_cfm_cb = pairing_complete_cb;
- conn->security_cfm_cb = pairing_complete_cb;
+ conn->connect_cfm_cb = connect_complete_cb;
+ conn->security_cfm_cb = 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 &&
@@ -1395,25 +1483,23 @@
}
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);
@@ -1424,16 +1510,64 @@
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 = le_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 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;
}
- err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr);
+ 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);
@@ -1483,6 +1617,293 @@
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 || !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);
+ return;
+ }
+
+ hci_dev_lock(hdev);
+
+ 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);
+
+ 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);
+ }
+}
+
+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);
+ del_timer_sync(&ilp->le_timer);
+
+ 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);
+ }
+}
+
+static int start_discovery(struct sock *sk, u16 index)
+{
+ struct hci_cp_inquiry cp = {{0x33, 0x8b, 0x9e}, 8, 0};
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ /* 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(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+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 = NULL;
+ int err = -EPERM;
+
+ BT_DBG("");
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (lmp_le_capable(hdev)) {
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ ilp = cmd->param;
+ }
+
+ 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(hdev);
+ hci_dev_put(hdev);
+
+ if (err < 0)
+ return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, -err);
+ else
+ return err;
+}
+
static int read_local_oob_data(struct sock *sk, u16 index)
{
struct hci_dev *hdev;
@@ -1631,6 +2052,7 @@
goto done;
}
+ BT_DBG("got opcode %x", opcode);
switch (opcode) {
case MGMT_OP_READ_VERSION:
err = read_version(sk);
@@ -1690,14 +2112,23 @@
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_START_DISCOVERY:
+ err = start_discovery(sk, index);
+ break;
+ case MGMT_OP_STOP_DISCOVERY:
+ err = stop_discovery(sk, index);
+ 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;
@@ -1727,11 +2158,13 @@
int mgmt_index_added(u16 index)
{
+ BT_DBG("%d", index);
return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL);
}
int mgmt_index_removed(u16 index)
{
+ BT_DBG("%d", index);
return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL);
}
@@ -1766,6 +2199,8 @@
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);
ev.val = powered;
@@ -1815,7 +2250,7 @@
return ret;
}
-int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type)
+int mgmt_new_key(u16 index, struct link_key *key, u8 bonded)
{
struct mgmt_ev_new_key *ev;
int err, total;
@@ -1829,7 +2264,8 @@
ev->key.type = key->type;
memcpy(ev->key.val, key->val, 16);
ev->key.pin_len = key->pin_len;
- ev->old_key_type = old_key_type;
+ ev->key.auth = key->auth;
+ ev->store_hint = bonded;
ev->key.dlen = key->dlen;
memcpy(ev->key.data, key->data, key->dlen);
@@ -1914,7 +2350,10 @@
{
struct mgmt_ev_pin_code_request ev;
+ BT_DBG("hci%u", index);
+
bacpy(&ev.bdaddr, bdaddr);
+ ev.secure = 0;
return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev),
NULL);
@@ -1962,16 +2401,56 @@
return err;
}
-int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value)
+int mgmt_user_confirm_request(u16 index, u8 event,
+ bdaddr_t *bdaddr, __le32 value)
{
struct mgmt_ev_user_confirm_request ev;
+ struct hci_conn *conn = NULL;
+ struct hci_dev *hdev;
+ u8 loc_cap, rem_cap, loc_mitm, rem_mitm;
+
+ BT_DBG("hci%u", index);
+
+ hdev = hci_dev_get(index);
+
+ if (hdev)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+
+ ev.auto_confirm = 0;
+
+ if (!conn || event != HCI_EV_USER_CONFIRM_REQUEST)
+ goto no_auto_confirm;
+
+ loc_cap = (conn->io_capability == 0x04) ? 0x01 : conn->io_capability;
+ rem_cap = conn->remote_cap;
+ loc_mitm = conn->auth_type & 0x01;
+ rem_mitm = conn->remote_auth & 0x01;
+
+ if (loc_cap == 0x01 && (rem_cap == 0x00 || rem_cap == 0x03))
+ goto no_auto_confirm;
+
+
+ if ((!loc_mitm || rem_cap == 0x03) && (!rem_mitm || loc_cap == 0x03))
+ ev.auto_confirm = 1;
+
+no_auto_confirm:
+ bacpy(&ev.bdaddr, bdaddr);
+ ev.event = event;
+ put_unaligned_le32(value, &ev.value);
+
+ return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev),
+ NULL);
+}
+
+int mgmt_user_passkey_request(u16 index, bdaddr_t *bdaddr)
+{
+ struct mgmt_ev_user_passkey_request ev;
BT_DBG("hci%u", index);
bacpy(&ev.bdaddr, bdaddr);
- put_unaligned_le32(value, &ev.value);
- return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev),
+ return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, index, &ev, sizeof(ev),
NULL);
}
@@ -2089,30 +2568,71 @@
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 hci_dev *hdev = hci_dev_get(index);
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("le: %d", le);
memset(&ev, 0, sizeof(ev));
bacpy(&ev.bdaddr, bdaddr);
- memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
ev.rssi = rssi;
+ ev.type = type;
+ ev.le = le;
- if (eir)
- memcpy(ev.eir, eir, sizeof(ev.eir));
+ 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;
+
+ 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);
+ }
+ }
+ }
+
+ 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);