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);