Bluetooth: Set le data length command and event
[platform/kernel/linux-starfive.git] / net / bluetooth / mgmt.c
index a92e7e4..a674d9c 100644 (file)
 #include <net/bluetooth/hci_sock.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
+#ifdef TIZEN_BT
+#include <net/bluetooth/mgmt_tizen.h>
+#include <net/bluetooth/sco.h>
+#endif
 
 #include "hci_request.h"
 #include "smp.h"
@@ -859,6 +863,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
            hdev->set_bdaddr)
                settings |= MGMT_SETTING_CONFIGURATION;
 
+       if (cis_central_capable(hdev))
+               settings |= MGMT_SETTING_CIS_CENTRAL;
+
+       if (cis_peripheral_capable(hdev))
+               settings |= MGMT_SETTING_CIS_PERIPHERAL;
+
        settings |= MGMT_SETTING_PHY_CONFIGURATION;
 
        return settings;
@@ -932,6 +942,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED))
                settings |= MGMT_SETTING_WIDEBAND_SPEECH;
 
+       if (cis_central_capable(hdev))
+               settings |= MGMT_SETTING_CIS_CENTRAL;
+
+       if (cis_peripheral_capable(hdev))
+               settings |= MGMT_SETTING_CIS_PERIPHERAL;
+
        return settings;
 }
 
@@ -4627,12 +4643,6 @@ static int set_mgmt_mesh_func(struct sock *sk, struct hci_dev *hdev,
                                       MGMT_OP_SET_EXP_FEATURE,
                                       MGMT_STATUS_INVALID_INDEX);
 
-       /* Changes can only be made when controller is powered down */
-       if (hdev_is_powered(hdev))
-               return mgmt_cmd_status(sk, hdev->id,
-                                      MGMT_OP_SET_EXP_FEATURE,
-                                      MGMT_STATUS_REJECTED);
-
        /* Parameters are limited to a single octet */
        if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
                return mgmt_cmd_status(sk, hdev->id,
@@ -7019,9 +7029,14 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
                                       MGMT_STATUS_INVALID_PARAMS);
 
+#ifndef TIZEN_BT
+       /* commenting out since set privacy command is always rejected
+        * if this condition is enabled.
+        */
        if (hdev_is_powered(hdev))
                return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
                                       MGMT_STATUS_REJECTED);
+#endif
 
        hci_dev_lock(hdev);
 
@@ -7146,222 +7161,2522 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
        return err;
 }
 
-static bool ltk_is_valid(struct mgmt_ltk_info *key)
+#ifdef TIZEN_BT
+static int set_advertising_params(struct sock *sk, struct hci_dev *hdev,
+                       void *data, u16 len)
 {
-       if (key->initiator != 0x00 && key->initiator != 0x01)
-               return false;
+       struct mgmt_cp_set_advertising_params *cp = data;
+       __u16 min_interval;
+       __u16 max_interval;
+       int err;
 
-       switch (key->addr.type) {
-       case BDADDR_LE_PUBLIC:
-               return true;
+       BT_DBG("%s", hdev->name);
 
-       case BDADDR_LE_RANDOM:
-               /* Two most significant bits shall be set */
-               if ((key->addr.bdaddr.b[5] & 0xc0) != 0xc0)
-                       return false;
-               return true;
-       }
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_PARAMS,
+                               MGMT_STATUS_NOT_SUPPORTED);
 
-       return false;
+       if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_PARAMS,
+                               MGMT_STATUS_BUSY);
+
+       min_interval = __le16_to_cpu(cp->interval_min);
+       max_interval = __le16_to_cpu(cp->interval_max);
+
+       if (min_interval > max_interval ||
+           min_interval < 0x0020 || max_interval > 0x4000)
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_PARAMS,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       hdev->le_adv_min_interval = min_interval;
+       hdev->le_adv_max_interval = max_interval;
+       hdev->adv_filter_policy = cp->filter_policy;
+       hdev->adv_type = cp->type;
+
+       err = mgmt_cmd_complete(sk, hdev->id,
+                       MGMT_OP_SET_ADVERTISING_PARAMS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
 }
 
-static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
-                              void *cp_data, u16 len)
+static void set_advertising_data_complete(struct hci_dev *hdev,
+                       u8 status, u16 opcode)
 {
-       struct mgmt_cp_load_long_term_keys *cp = cp_data;
-       const u16 max_key_count = ((U16_MAX - sizeof(*cp)) /
-                                  sizeof(struct mgmt_ltk_info));
-       u16 key_count, expected_len;
-       int i, err;
+       struct mgmt_cp_set_advertising_data *cp;
+       struct mgmt_pending_cmd *cmd;
 
-       bt_dev_dbg(hdev, "sock %p", sk);
+       BT_DBG("status 0x%02x", status);
 
-       if (!lmp_le_capable(hdev))
-               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                      MGMT_STATUS_NOT_SUPPORTED);
+       hci_dev_lock(hdev);
 
-       key_count = __le16_to_cpu(cp->key_count);
-       if (key_count > max_key_count) {
-               bt_dev_err(hdev, "load_ltks: too big key_count value %u",
-                          key_count);
-               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                      MGMT_STATUS_INVALID_PARAMS);
-       }
+       cmd = pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev);
+       if (!cmd)
+               goto unlock;
 
-       expected_len = struct_size(cp, keys, key_count);
-       if (expected_len != len) {
-               bt_dev_err(hdev, "load_keys: expected %u bytes, got %u bytes",
-                          expected_len, len);
-               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                      MGMT_STATUS_INVALID_PARAMS);
-       }
+       cp = cmd->param;
 
-       bt_dev_dbg(hdev, "key_count %u", key_count);
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_DATA,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_DATA, 0,
+                               cp, sizeof(*cp));
 
-       for (i = 0; i < key_count; i++) {
-               struct mgmt_ltk_info *key = &cp->keys[i];
+       mgmt_pending_remove(cmd);
 
-               if (!ltk_is_valid(key))
-                       return mgmt_cmd_status(sk, hdev->id,
-                                              MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                              MGMT_STATUS_INVALID_PARAMS);
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int set_advertising_data(struct sock *sk, struct hci_dev *hdev,
+                       void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct mgmt_cp_set_advertising_data *cp = data;
+       struct hci_cp_le_set_adv_data adv;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev)) {
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_DATA,
+                               MGMT_STATUS_NOT_SUPPORTED);
        }
 
        hci_dev_lock(hdev);
 
-       hci_smp_ltks_clear(hdev);
+       if (pending_find(MGMT_OP_SET_ADVERTISING_DATA, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_DATA,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
 
-       for (i = 0; i < key_count; i++) {
-               struct mgmt_ltk_info *key = &cp->keys[i];
-               u8 type, authenticated;
+       if (len > HCI_MAX_AD_LENGTH) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_ADVERTISING_DATA,
+                               MGMT_STATUS_INVALID_PARAMS);
+               goto unlocked;
+       }
 
-               if (hci_is_blocked_key(hdev,
-                                      HCI_BLOCKED_KEY_TYPE_LTK,
-                                      key->val)) {
-                       bt_dev_warn(hdev, "Skipping blocked LTK for %pMR",
-                                   &key->addr.bdaddr);
-                       continue;
-               }
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING_DATA,
+                              hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
 
-               switch (key->type) {
-               case MGMT_LTK_UNAUTHENTICATED:
-                       authenticated = 0x00;
-                       type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER;
-                       break;
-               case MGMT_LTK_AUTHENTICATED:
-                       authenticated = 0x01;
-                       type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER;
-                       break;
-               case MGMT_LTK_P256_UNAUTH:
-                       authenticated = 0x00;
-                       type = SMP_LTK_P256;
-                       break;
-               case MGMT_LTK_P256_AUTH:
-                       authenticated = 0x01;
-                       type = SMP_LTK_P256;
-                       break;
-               case MGMT_LTK_P256_DEBUG:
-                       authenticated = 0x00;
-                       type = SMP_LTK_P256_DEBUG;
-                       fallthrough;
-               default:
-                       continue;
-               }
+       hci_req_init(&req, hdev);
 
-               hci_add_ltk(hdev, &key->addr.bdaddr,
-                           le_addr_type(key->addr.type), type, authenticated,
-                           key->val, key->enc_size, key->ediv, key->rand);
-       }
+       memset(&adv, 0, sizeof(adv));
+       memcpy(adv.data, cp->data, len);
+       adv.length = len;
 
-       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
-                          NULL, 0);
+       hci_req_add(&req, HCI_OP_LE_SET_ADV_DATA, sizeof(adv), &adv);
+
+       err = hci_req_run(&req, set_advertising_data_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
 
+unlocked:
        hci_dev_unlock(hdev);
 
        return err;
 }
 
-static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
+/* Adv White List feature */
+static void add_white_list_complete(struct hci_dev *hdev, u8 status, u16 opcode)
 {
-       struct mgmt_pending_cmd *cmd = data;
-       struct hci_conn *conn = cmd->user_data;
-       struct mgmt_cp_get_conn_info *cp = cmd->param;
-       struct mgmt_rp_get_conn_info rp;
-       u8 status;
+       struct mgmt_cp_add_dev_white_list *cp;
+       struct mgmt_pending_cmd *cmd;
 
-       bt_dev_dbg(hdev, "err %d", err);
+       BT_DBG("status 0x%02x", status);
 
-       memcpy(&rp.addr, &cp->addr.bdaddr, sizeof(rp.addr));
+       hci_dev_lock(hdev);
 
-       status = mgmt_status(err);
-       if (status == MGMT_STATUS_SUCCESS) {
-               rp.rssi = conn->rssi;
-               rp.tx_power = conn->tx_power;
-               rp.max_tx_power = conn->max_tx_power;
-       } else {
-               rp.rssi = HCI_RSSI_INVALID;
-               rp.tx_power = HCI_TX_POWER_INVALID;
-               rp.max_tx_power = HCI_TX_POWER_INVALID;
-       }
+       cmd = pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev);
+       if (!cmd)
+               goto unlock;
 
-       mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
-                         &rp, sizeof(rp));
+       cp = cmd->param;
 
-       mgmt_pending_free(cmd);
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST,
+                          mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                               MGMT_OP_ADD_DEV_WHITE_LIST, 0, cp, sizeof(*cp));
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
 }
 
-static int get_conn_info_sync(struct hci_dev *hdev, void *data)
+static int add_white_list(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
 {
-       struct mgmt_pending_cmd *cmd = data;
-       struct mgmt_cp_get_conn_info *cp = cmd->param;
-       struct hci_conn *conn;
+       struct mgmt_pending_cmd *cmd;
+       struct mgmt_cp_add_dev_white_list *cp = data;
+       struct hci_request req;
        int err;
-       __le16   handle;
 
-       /* Make sure we are still connected */
-       if (cp->addr.type == BDADDR_BREDR)
-               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
-                                              &cp->addr.bdaddr);
-       else
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+       BT_DBG("%s", hdev->name);
 
-       if (!conn || conn->state != BT_CONNECTED)
-               return MGMT_STATUS_NOT_CONNECTED;
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       cmd->user_data = conn;
-       handle = cpu_to_le16(conn->handle);
+       if (!hdev_is_powered(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST,
+                                 MGMT_STATUS_REJECTED);
 
-       /* Refresh RSSI each time */
-       err = hci_read_rssi_sync(hdev, handle);
+       hci_dev_lock(hdev);
 
-       /* For LE links TX power does not change thus we don't need to
-        * query for it once value is known.
-        */
-       if (!err && (!bdaddr_type_is_le(cp->addr.type) ||
-                    conn->tx_power == HCI_TX_POWER_INVALID))
-               err = hci_read_tx_power_sync(hdev, handle, 0x00);
+       if (pending_find(MGMT_OP_ADD_DEV_WHITE_LIST, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_DEV_WHITE_LIST,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
 
-       /* Max TX power needs to be read only once per connection */
-       if (!err && conn->max_tx_power == HCI_TX_POWER_INVALID)
-               err = hci_read_tx_power_sync(hdev, handle, 0x01);
+       cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEV_WHITE_LIST, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       hci_req_add(&req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(*cp), cp);
+
+       err = hci_req_run(&req, add_white_list_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
 
        return err;
 }
 
-static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
-                        u16 len)
+static void remove_from_white_list_complete(struct hci_dev *hdev,
+                       u8 status, u16 opcode)
 {
-       struct mgmt_cp_get_conn_info *cp = data;
-       struct mgmt_rp_get_conn_info rp;
-       struct hci_conn *conn;
-       unsigned long conn_info_age;
-       int err = 0;
-
-       bt_dev_dbg(hdev, "sock %p", sk);
-
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
+       struct mgmt_cp_remove_dev_from_white_list *cp;
+       struct mgmt_pending_cmd *cmd;
 
-       if (!bdaddr_type_is_valid(cp->addr.type))
-               return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
-                                        MGMT_STATUS_INVALID_PARAMS,
-                                        &rp, sizeof(rp));
+       BT_DBG("status 0x%02x", status);
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
-               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
-                                       MGMT_STATUS_NOT_POWERED, &rp,
-                                       sizeof(rp));
+       cmd = pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev);
+       if (!cmd)
                goto unlock;
-       }
 
-       if (cp->addr.type == BDADDR_BREDR)
-               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
-                                              &cp->addr.bdaddr);
-       else
-               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+       cp = cmd->param;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                       MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                       mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                       MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, 0,
+                       cp, sizeof(*cp));
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int remove_from_white_list(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct mgmt_cp_remove_dev_from_white_list *cp = data;
+       struct hci_request req;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       if (!hdev_is_powered(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                               MGMT_STATUS_REJECTED);
+
+       hci_dev_lock(hdev);
+
+       if (pending_find(MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+                               hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       hci_req_add(&req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(*cp), cp);
+
+       err = hci_req_run(&req, remove_from_white_list_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void clear_white_list_complete(struct hci_dev *hdev, u8 status,
+                       u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                          mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                               MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                               0, NULL, 0);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int clear_white_list(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       if (!hdev_is_powered(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                               MGMT_STATUS_REJECTED);
+
+       hci_dev_lock(hdev);
+
+       if (pending_find(MGMT_OP_CLEAR_DEV_WHITE_LIST, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_CLEAR_DEV_WHITE_LIST,
+                               hdev, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       hci_req_add(&req, HCI_OP_LE_CLEAR_WHITE_LIST, 0, NULL);
+
+       err = hci_req_run(&req, clear_white_list_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void set_scan_rsp_data_complete(struct hci_dev *hdev, u8 status,
+                       u16 opcode)
+{
+       struct mgmt_cp_set_scan_rsp_data *cp;
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev);
+       if (!cmd)
+               goto unlock;
+
+       cp = cmd->param;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                               MGMT_OP_SET_SCAN_RSP_DATA, 0,
+                               cp, sizeof(*cp));
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int set_scan_rsp_data(struct sock *sk, struct hci_dev *hdev, void *data,
+                       u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct mgmt_cp_set_scan_rsp_data *cp = data;
+       struct hci_cp_le_set_scan_rsp_data rsp;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_SCAN_RSP_DATA,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       if (pending_find(MGMT_OP_SET_SCAN_RSP_DATA, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       if (len > HCI_MAX_AD_LENGTH) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_RSP_DATA,
+                               MGMT_STATUS_INVALID_PARAMS);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_SCAN_RSP_DATA, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       memset(&rsp, 0, sizeof(rsp));
+       memcpy(rsp.data, cp->data, len);
+       rsp.length = len;
+
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(rsp), &rsp);
+
+       err = hci_req_run(&req, set_scan_rsp_data_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void set_rssi_threshold_complete(struct hci_dev *hdev,
+                       u8 status, u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0,
+                               NULL, 0);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static void set_rssi_disable_complete(struct hci_dev *hdev,
+                       u8 status, u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               0, NULL, 0);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+int mgmt_set_rssi_threshold(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       int err = 0;
+       struct hci_cp_set_rssi_threshold th = { 0, };
+       struct mgmt_cp_set_enable_rssi *cp = data;
+       struct hci_conn *conn;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       __u8 dest_type;
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev);
+       if (!cmd) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_FAILED);
+               goto unlocked;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               BT_DBG("%s", hdev->name);
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlocked;
+       }
+
+       if (cp->link_type == 0x01)
+               dest_type = LE_LINK;
+       else
+               dest_type = ACL_LINK;
+
+       /* Get LE/ACL link handle info */
+       conn = hci_conn_hash_lookup_ba(hdev,
+                       dest_type, &cp->bdaddr);
+
+       if (!conn) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                               MGMT_OP_SET_RSSI_ENABLE, 1, NULL, 0);
+               mgmt_pending_remove(cmd);
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       th.hci_le_ext_opcode = 0x0B;
+       th.mode = 0x01;
+       th.conn_handle = conn->handle;
+       th.alert_mask = 0x07;
+       th.low_th = cp->low_th;
+       th.in_range_th = cp->in_range_th;
+       th.high_th = cp->high_th;
+
+       hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th);
+       err = hci_req_run(&req, set_rssi_threshold_complete);
+
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               BT_ERR("Error in requesting hci_req_run");
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+void mgmt_rssi_enable_success(struct sock *sk, struct hci_dev *hdev,
+               void *data, struct hci_cc_rsp_enable_rssi *rp, int success)
+{
+       struct mgmt_cc_rsp_enable_rssi mgmt_rp = { 0, };
+       struct mgmt_cp_set_enable_rssi *cp = data;
+       struct mgmt_pending_cmd *cmd;
+
+       if (!cp || !rp)
+               goto remove_cmd;
+
+       mgmt_rp.status = rp->status;
+       mgmt_rp.le_ext_opcode = rp->le_ext_opcode;
+       mgmt_rp.bt_address = cp->bdaddr;
+       mgmt_rp.link_type = cp->link_type;
+
+       mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                       MGMT_STATUS_SUCCESS, &mgmt_rp,
+                       sizeof(struct mgmt_cc_rsp_enable_rssi));
+
+       mgmt_event(MGMT_EV_RSSI_ENABLED, hdev, &mgmt_rp,
+                       sizeof(struct mgmt_cc_rsp_enable_rssi), NULL);
+
+       hci_conn_rssi_unset_all(hdev, mgmt_rp.link_type);
+       hci_conn_rssi_state_set(hdev, mgmt_rp.link_type,
+                       &mgmt_rp.bt_address, true);
+
+remove_cmd:
+       hci_dev_lock(hdev);
+       cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev);
+       if (cmd)
+               mgmt_pending_remove(cmd);
+
+       hci_dev_unlock(hdev);
+}
+
+void mgmt_rssi_disable_success(struct sock *sk, struct hci_dev *hdev,
+               void *data, struct hci_cc_rsp_enable_rssi *rp, int success)
+{
+       struct mgmt_cc_rp_disable_rssi mgmt_rp = { 0, };
+       struct mgmt_cp_disable_rssi *cp = data;
+       struct mgmt_pending_cmd *cmd;
+
+       if (!cp || !rp)
+               goto remove_cmd;
+
+       mgmt_rp.status = rp->status;
+       mgmt_rp.le_ext_opcode = rp->le_ext_opcode;
+       mgmt_rp.bt_address = cp->bdaddr;
+       mgmt_rp.link_type = cp->link_type;
+
+       mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                       MGMT_STATUS_SUCCESS, &mgmt_rp,
+                       sizeof(struct mgmt_cc_rsp_enable_rssi));
+
+       mgmt_event(MGMT_EV_RSSI_DISABLED, hdev, &mgmt_rp,
+                       sizeof(struct mgmt_cc_rsp_enable_rssi), NULL);
+
+       hci_conn_rssi_state_set(hdev, mgmt_rp.link_type,
+                       &mgmt_rp.bt_address, false);
+
+remove_cmd:
+       hci_dev_lock(hdev);
+       cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev);
+       if (cmd)
+               mgmt_pending_remove(cmd);
+
+       hci_dev_unlock(hdev);
+}
+
+static int mgmt_set_disable_rssi(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct hci_cp_set_enable_rssi cp_en = { 0, };
+       int err;
+
+       BT_DBG("Set Disable RSSI.");
+
+       cp_en.hci_le_ext_opcode = 0x01;
+       cp_en.le_enable_cs_Features = 0x00;
+       cp_en.data[0] = 0x00;
+       cp_en.data[1] = 0x00;
+       cp_en.data[2] = 0x00;
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev);
+       if (!cmd) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               MGMT_STATUS_FAILED);
+               goto unlocked;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               BT_DBG("%s", hdev->name);
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]",
+                       sizeof(struct hci_cp_set_enable_rssi),
+                       cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features,
+                       cp_en.data[0], cp_en.data[1], cp_en.data[2]);
+
+       hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en);
+       err = hci_req_run(&req, set_rssi_disable_complete);
+
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               BT_ERR("Error in requesting hci_req_run");
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+void mgmt_enable_rssi_cc(struct hci_dev *hdev, void *response, u8 status)
+{
+       struct hci_cc_rsp_enable_rssi *rp = response;
+       struct mgmt_pending_cmd *cmd_enable = NULL;
+       struct mgmt_pending_cmd *cmd_disable = NULL;
+       struct mgmt_cp_set_enable_rssi *cp_en;
+       struct mgmt_cp_disable_rssi *cp_dis;
+
+       hci_dev_lock(hdev);
+       cmd_enable = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev);
+       cmd_disable = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev);
+       hci_dev_unlock(hdev);
+
+       if (cmd_enable)
+               BT_DBG("Enable Request");
+
+       if (cmd_disable)
+               BT_DBG("Disable Request");
+
+       if (cmd_enable) {
+               cp_en = cmd_enable->param;
+
+               if (status != 0x00)
+                       return;
+
+               switch (rp->le_ext_opcode) {
+               case 0x01:
+                       BT_DBG("RSSI enabled.. Setting Threshold...");
+                       mgmt_set_rssi_threshold(cmd_enable->sk, hdev,
+                                       cp_en, sizeof(*cp_en));
+                       break;
+
+               case 0x0B:
+                       BT_DBG("Sending RSSI enable success");
+                       mgmt_rssi_enable_success(cmd_enable->sk, hdev,
+                                       cp_en, rp, rp->status);
+                       break;
+               }
+
+       } else if (cmd_disable) {
+               cp_dis = cmd_disable->param;
+
+               if (status != 0x00)
+                       return;
+
+               switch (rp->le_ext_opcode) {
+               case 0x01:
+                       BT_DBG("Sending RSSI disable success");
+                       mgmt_rssi_disable_success(cmd_disable->sk, hdev,
+                                       cp_dis, rp, rp->status);
+                       break;
+
+               case 0x0B:
+                       /*
+                        * Only unset RSSI Threshold values for the Link if
+                        * RSSI is monitored for other BREDR or LE Links
+                        */
+                       if (hci_conn_hash_lookup_rssi_count(hdev) > 1) {
+                               BT_DBG("Unset Threshold. Other links being monitored");
+                               mgmt_rssi_disable_success(cmd_disable->sk, hdev,
+                                               cp_dis, rp, rp->status);
+                       } else {
+                               BT_DBG("Unset Threshold. Disabling...");
+                               mgmt_set_disable_rssi(cmd_disable->sk, hdev,
+                                               cp_dis, sizeof(*cp_dis));
+                       }
+                       break;
+               }
+       }
+}
+
+static void set_rssi_enable_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE, 0,
+                               NULL, 0);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int set_enable_rssi(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct mgmt_cp_set_enable_rssi *cp = data;
+       struct hci_cp_set_enable_rssi cp_en = { 0, };
+       int err;
+
+       BT_DBG("Set Enable RSSI.");
+
+       cp_en.hci_le_ext_opcode = 0x01;
+       cp_en.le_enable_cs_Features = 0x04;
+       cp_en.data[0] = 0x00;
+       cp_en.data[1] = 0x00;
+       cp_en.data[2] = 0x00;
+
+       hci_dev_lock(hdev);
+
+       if (!lmp_le_capable(hdev)) {
+               err =  mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlocked;
+       }
+
+       if (pending_find(MGMT_OP_SET_RSSI_ENABLE, hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_ENABLE,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_ENABLE, hdev, cp,
+                       sizeof(*cp));
+       if (!cmd) {
+               BT_DBG("%s", hdev->name);
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       /* If RSSI is already enabled directly set Threshold values */
+       if (hci_conn_hash_lookup_rssi_count(hdev) > 0) {
+               hci_dev_unlock(hdev);
+               BT_DBG("RSSI Enabled. Directly set Threshold");
+               err = mgmt_set_rssi_threshold(sk, hdev, cp, sizeof(*cp));
+               return err;
+       }
+
+       hci_req_init(&req, hdev);
+
+       BT_DBG("Enable Len: %zu [%2.2X %2.2X %2.2X %2.2X %2.2X]",
+                       sizeof(struct hci_cp_set_enable_rssi),
+                       cp_en.hci_le_ext_opcode, cp_en.le_enable_cs_Features,
+                       cp_en.data[0], cp_en.data[1], cp_en.data[2]);
+
+       hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(cp_en), &cp_en);
+       err = hci_req_run(&req, set_rssi_enable_complete);
+
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               BT_ERR("Error in requesting hci_req_run");
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void get_raw_rssi_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_GET_RAW_RSSI, hdev);
+       if (!cmd)
+               goto unlock;
+
+       mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_GET_RAW_RSSI,
+                       MGMT_STATUS_SUCCESS, &status, 1);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int get_raw_rssi(struct sock *sk, struct hci_dev *hdev, void *data,
+                       u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct mgmt_cp_get_raw_rssi *cp = data;
+       struct hci_cp_get_raw_rssi hci_cp;
+
+       struct hci_conn *conn;
+       int err;
+       __u8 dest_type;
+
+       BT_DBG("Get Raw RSSI.");
+
+       hci_dev_lock(hdev);
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (cp->link_type == 0x01)
+               dest_type = LE_LINK;
+       else
+               dest_type = ACL_LINK;
+
+       /* Get LE/BREDR link handle info */
+       conn = hci_conn_hash_lookup_ba(hdev,
+                       dest_type, &cp->bt_address);
+       if (!conn) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI,
+                                                  MGMT_STATUS_NOT_CONNECTED);
+               goto unlocked;
+       }
+       hci_cp.conn_handle = conn->handle;
+
+       if (!hdev_is_powered(hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlocked;
+       }
+
+       if (pending_find(MGMT_OP_GET_RAW_RSSI, hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_RAW_RSSI,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_GET_RAW_RSSI, hdev, data, len);
+       if (!cmd) {
+               BT_DBG("%s", hdev->name);
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       BT_DBG("Connection Handle [%d]", hci_cp.conn_handle);
+       hci_req_add(&req, HCI_OP_GET_RAW_RSSI, sizeof(hci_cp), &hci_cp);
+       err = hci_req_run(&req, get_raw_rssi_complete);
+
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               BT_ERR("Error in requesting hci_req_run");
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+void mgmt_raw_rssi_response(struct hci_dev *hdev,
+               struct hci_cc_rp_get_raw_rssi *rp, int success)
+{
+       struct mgmt_cc_rp_get_raw_rssi mgmt_rp = { 0, };
+       struct hci_conn *conn;
+
+       mgmt_rp.status = rp->status;
+       mgmt_rp.rssi_dbm = rp->rssi_dbm;
+
+       conn = hci_conn_hash_lookup_handle(hdev, rp->conn_handle);
+       if (!conn)
+               return;
+
+       bacpy(&mgmt_rp.bt_address, &conn->dst);
+       if (conn->type == LE_LINK)
+               mgmt_rp.link_type = 0x01;
+       else
+               mgmt_rp.link_type = 0x00;
+
+       mgmt_event(MGMT_EV_RAW_RSSI, hdev, &mgmt_rp,
+                       sizeof(struct mgmt_cc_rp_get_raw_rssi), NULL);
+}
+
+static void set_disable_threshold_complete(struct hci_dev *hdev,
+                       u8 status, u16 opcode)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev);
+       if (!cmd)
+               goto unlock;
+
+       mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                       MGMT_STATUS_SUCCESS, &status, 1);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+/** Removes monitoring for a link*/
+static int set_disable_threshold(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       int err = 0;
+       struct hci_cp_set_rssi_threshold th = { 0, };
+       struct mgmt_cp_disable_rssi *cp = data;
+       struct hci_conn *conn;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       __u8 dest_type;
+
+       BT_DBG("Set Disable RSSI.");
+
+       hci_dev_lock(hdev);
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       /* Get LE/ACL link handle info*/
+       if (cp->link_type == 0x01)
+               dest_type = LE_LINK;
+       else
+               dest_type = ACL_LINK;
+
+       conn = hci_conn_hash_lookup_ba(hdev, dest_type, &cp->bdaddr);
+       if (!conn) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                               MGMT_OP_SET_RSSI_DISABLE, 1, NULL, 0);
+               goto unlocked;
+       }
+
+       th.hci_le_ext_opcode = 0x0B;
+       th.mode = 0x01;
+       th.conn_handle = conn->handle;
+       th.alert_mask = 0x00;
+       th.low_th = 0x00;
+       th.in_range_th = 0x00;
+       th.high_th = 0x00;
+
+       if (!hdev_is_powered(hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               0, data, len);
+               goto unlocked;
+       }
+
+       if (pending_find(MGMT_OP_SET_RSSI_DISABLE, hdev)) {
+               BT_DBG("%s", hdev->name);
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_RSSI_DISABLE,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_RSSI_DISABLE, hdev, cp,
+                       sizeof(*cp));
+       if (!cmd) {
+               BT_DBG("%s", hdev->name);
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       hci_req_add(&req, HCI_OP_ENABLE_RSSI, sizeof(th), &th);
+       err = hci_req_run(&req, set_disable_threshold_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               BT_ERR("Error in requesting hci_req_run");
+               goto unlocked;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+void mgmt_rssi_alert_evt(struct hci_dev *hdev, u16 conn_handle,
+               s8 alert_type, s8 rssi_dbm)
+{
+       struct mgmt_ev_vendor_specific_rssi_alert mgmt_ev;
+       struct hci_conn *conn;
+
+       BT_DBG("RSSI alert [%2.2X %2.2X %2.2X]",
+                       conn_handle, alert_type, rssi_dbm);
+
+       conn = hci_conn_hash_lookup_handle(hdev, conn_handle);
+
+       if (!conn) {
+               BT_ERR("RSSI alert Error: Device not found for handle");
+               return;
+       }
+       bacpy(&mgmt_ev.bdaddr, &conn->dst);
+
+       if (conn->type == LE_LINK)
+               mgmt_ev.link_type = 0x01;
+       else
+               mgmt_ev.link_type = 0x00;
+
+       mgmt_ev.alert_type = alert_type;
+       mgmt_ev.rssi_dbm = rssi_dbm;
+
+       mgmt_event(MGMT_EV_RSSI_ALERT, hdev, &mgmt_ev,
+                       sizeof(struct mgmt_ev_vendor_specific_rssi_alert),
+                       NULL);
+}
+
+static int mgmt_start_le_discovery_failed(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       u8 type;
+       int err;
+
+       hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+       cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       type = hdev->le_discovery.type;
+
+       err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode,
+                               mgmt_status(status), &type, sizeof(type));
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+static void start_le_discovery_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       unsigned long timeout = 0;
+
+       BT_DBG("status %d", status);
+
+       if (status) {
+               hci_dev_lock(hdev);
+               mgmt_start_le_discovery_failed(hdev, status);
+               hci_dev_unlock(hdev);
+               return;
+       }
+
+       hci_dev_lock(hdev);
+       hci_le_discovery_set_state(hdev, DISCOVERY_FINDING);
+       hci_dev_unlock(hdev);
+
+       if (hdev->le_discovery.type != DISCOV_TYPE_LE)
+               BT_ERR("Invalid discovery type %d", hdev->le_discovery.type);
+
+       if (!timeout)
+               return;
+
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
+}
+
+static int start_le_discovery(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_start_le_discovery *cp = data;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       struct hci_request req;
+       u8 status, own_addr_type;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (hdev->le_discovery.state != DISCOVERY_STOPPED) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       if (cp->type != DISCOV_TYPE_LE) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_INVALID_PARAMS);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_LE_DISCOVERY, hdev, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hdev->le_discovery.type = cp->type;
+
+       hci_req_init(&req, hdev);
+
+       status = mgmt_le_support(hdev);
+       if (status) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               status);
+               mgmt_pending_remove(cmd);
+               goto unlock;
+       }
+
+       /* If controller is scanning, it means the background scanning
+        * is running. Thus, we should temporarily stop it in order to
+        * set the discovery scanning parameters.
+        */
+       if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+               hci_req_add_le_scan_disable(&req, false);
+
+       memset(&param_cp, 0, sizeof(param_cp));
+
+       /* All active scans will be done with either a resolvable
+        * private address (when privacy feature has been enabled)
+        * or unresolvable private address.
+        */
+       err = hci_update_random_address_sync(hdev, true, hci_dev_test_flag(hdev, HCI_PRIVACY), &own_addr_type);
+       if (err < 0) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_START_LE_DISCOVERY,
+                               MGMT_STATUS_FAILED);
+               mgmt_pending_remove(cmd);
+               goto unlock;
+       }
+
+       param_cp.type = hdev->le_scan_type;
+       param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
+       param_cp.window = cpu_to_le16(hdev->le_scan_window);
+       param_cp.own_address_type = own_addr_type;
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+                   &param_cp);
+
+       memset(&enable_cp, 0, sizeof(enable_cp));
+       enable_cp.enable = LE_SCAN_ENABLE;
+       enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
+
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+                   &enable_cp);
+
+       err = hci_req_run(&req, start_le_discovery_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+       else
+               hci_le_discovery_set_state(hdev, DISCOVERY_STARTING);
+
+unlock:
+       return err;
+}
+
+static int mgmt_stop_le_discovery_failed(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       int err;
+
+       cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       err = mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode,
+                               mgmt_status(status), &hdev->le_discovery.type,
+                               sizeof(hdev->le_discovery.type));
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+static void stop_le_discovery_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       BT_DBG("status %d", status);
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               mgmt_stop_le_discovery_failed(hdev, status);
+               goto unlock;
+       }
+
+       hci_le_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int stop_le_discovery(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_stop_le_discovery *mgmt_cp = data;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hci_le_discovery_active(hdev)) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_REJECTED, &mgmt_cp->type,
+                                       sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       if (hdev->le_discovery.type != mgmt_cp->type) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_INVALID_PARAMS,
+                                       &mgmt_cp->type, sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_LE_DISCOVERY, hdev, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_req_init(&req, hdev);
+
+       if (hdev->le_discovery.state  != DISCOVERY_FINDING) {
+               BT_DBG("unknown le discovery state %u",
+                                       hdev->le_discovery.state);
+
+               mgmt_pending_remove(cmd);
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_LE_DISCOVERY,
+                                       MGMT_STATUS_FAILED, &mgmt_cp->type,
+                                       sizeof(mgmt_cp->type));
+               goto unlock;
+       }
+
+       cancel_delayed_work(&hdev->le_scan_disable);
+       hci_req_add_le_scan_disable(&req, false);
+
+       err = hci_req_run(&req, stop_le_discovery_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+       else
+               hci_le_discovery_set_state(hdev, DISCOVERY_STOPPING);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+/* Separate LE discovery */
+void mgmt_le_discovering(struct hci_dev *hdev, u8 discovering)
+{
+       struct mgmt_ev_discovering ev;
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("%s le discovering %u", hdev->name, discovering);
+
+       if (discovering)
+               cmd = pending_find(MGMT_OP_START_LE_DISCOVERY, hdev);
+       else
+               cmd = pending_find(MGMT_OP_STOP_LE_DISCOVERY, hdev);
+
+       if (cmd) {
+               u8 type = hdev->le_discovery.type;
+
+               mgmt_cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
+                                 sizeof(type));
+               mgmt_pending_remove(cmd);
+       }
+
+       memset(&ev, 0, sizeof(ev));
+       ev.type = hdev->le_discovery.type;
+       ev.discovering = discovering;
+
+       mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
+}
+
+static int disable_le_auto_connect(struct sock *sk, struct hci_dev *hdev,
+                       void *data, u16 len)
+{
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       err = hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
+       if (err < 0)
+               BT_ERR("HCI_OP_LE_CREATE_CONN_CANCEL is failed");
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static inline int check_le_conn_update_param(u16 min, u16 max, u16 latency,
+               u16 to_multiplier)
+{
+       u16 max_latency;
+
+       if (min > max || min < 6 || max > 3200)
+               return -EINVAL;
+
+       if (to_multiplier < 10 || to_multiplier > 3200)
+               return -EINVAL;
+
+       if (max >= to_multiplier * 8)
+               return -EINVAL;
+
+       max_latency = (to_multiplier * 8 / max) - 1;
+
+       if (latency > 499 || latency > max_latency)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int le_conn_update(struct sock *sk, struct hci_dev *hdev, void *data,
+               u16 len)
+{
+       struct mgmt_cp_le_conn_update *cp = data;
+
+       struct hci_conn *conn;
+       u16 min, max, latency, supervision_timeout;
+       int err = -1;
+
+       if (!hdev_is_powered(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE,
+                               MGMT_STATUS_NOT_POWERED);
+
+       min = __le16_to_cpu(cp->conn_interval_min);
+       max = __le16_to_cpu(cp->conn_interval_max);
+       latency = __le16_to_cpu(cp->conn_latency);
+       supervision_timeout = __le16_to_cpu(cp->supervision_timeout);
+
+       BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x supervision_timeout: 0x%4.4x",
+                       min, max, latency, supervision_timeout);
+
+       err = check_le_conn_update_param(min, max, latency,
+                       supervision_timeout);
+
+       if (err < 0)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+       if (!conn) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE,
+                               MGMT_STATUS_NOT_CONNECTED);
+               hci_dev_unlock(hdev);
+               return err;
+       }
+
+       hci_dev_unlock(hdev);
+
+       hci_le_conn_update(conn, min, max, latency, supervision_timeout);
+
+       return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_CONN_UPDATE, 0,
+                                NULL, 0);
+}
+
+static void set_manufacturer_data_complete(struct hci_dev *hdev, u8 status,
+               u16 opcode)
+{
+       struct mgmt_cp_set_manufacturer_data *cp;
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_SET_MANUFACTURER_DATA, hdev);
+       if (!cmd)
+               goto unlock;
+
+       cp = cmd->param;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                               MGMT_OP_SET_MANUFACTURER_DATA,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                                 MGMT_OP_SET_MANUFACTURER_DATA, 0,
+                                 cp, sizeof(*cp));
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int set_manufacturer_data(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       struct mgmt_cp_set_manufacturer_data *cp = data;
+       u8 old_data[HCI_MAX_EIR_LENGTH] = {0, };
+       u8 old_len;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_bredr_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_MANUFACTURER_DATA,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       if (cp->data[0] == 0 ||
+                       cp->data[0] - 1 > sizeof(hdev->manufacturer_data))
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_MANUFACTURER_DATA,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       if (cp->data[1] != 0xFF)
+               return mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_MANUFACTURER_DATA,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       if (pending_find(MGMT_OP_SET_MANUFACTURER_DATA, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_SET_MANUFACTURER_DATA,
+                               MGMT_STATUS_BUSY);
+               goto unlocked;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_MANUFACTURER_DATA, hdev, data,
+                       len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlocked;
+       }
+
+       hci_req_init(&req, hdev);
+
+       /* if new data is same as previous data then return command
+        * complete event
+        */
+       if (hdev->manufacturer_len == cp->data[0] - 1 &&
+           !memcmp(hdev->manufacturer_data, cp->data + 2, cp->data[0] - 1)) {
+               mgmt_pending_remove(cmd);
+               mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_MANUFACTURER_DATA,
+                                 0, cp, sizeof(*cp));
+               err = 0;
+               goto unlocked;
+       }
+
+       old_len = hdev->manufacturer_len;
+       if (old_len > 0)
+               memcpy(old_data, hdev->manufacturer_data, old_len);
+
+       hdev->manufacturer_len = cp->data[0] - 1;
+       if (hdev->manufacturer_len > 0)
+               memcpy(hdev->manufacturer_data, cp->data + 2,
+                               hdev->manufacturer_len);
+
+       hci_update_eir_sync(hdev);
+
+       err = hci_req_run(&req, set_manufacturer_data_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto failed;
+       }
+
+unlocked:
+       hci_dev_unlock(hdev);
+
+       return err;
+
+failed:
+       memset(hdev->manufacturer_data, 0x00, sizeof(hdev->manufacturer_data));
+       hdev->manufacturer_len = old_len;
+       if (hdev->manufacturer_len > 0)
+               memcpy(hdev->manufacturer_data, old_data,
+                      hdev->manufacturer_len);
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int le_set_scan_params(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_le_set_scan_params *cp = data;
+       __u16 interval, window;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS,
+                               MGMT_STATUS_NOT_SUPPORTED);
+
+       interval = __le16_to_cpu(cp->interval);
+
+       if (interval < 0x0004 || interval > 0x4000)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       window = __le16_to_cpu(cp->window);
+
+       if (window < 0x0004 || window > 0x4000)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       if (window > interval)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS,
+                               MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       hdev->le_scan_type = cp->type;
+       hdev->le_scan_interval = interval;
+       hdev->le_scan_window = window;
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_SET_SCAN_PARAMS, 0,
+                               NULL, 0);
+
+       /* If background scan is running, restart it so new parameters are
+        * loaded.
+        */
+       if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
+           hdev->discovery.state == DISCOVERY_STOPPED) {
+               struct hci_request req;
+
+               hci_req_init(&req, hdev);
+
+               hci_req_add_le_scan_disable(&req, false);
+               hci_req_add_le_passive_scan(&req);
+
+               hci_req_run(&req, NULL);
+       }
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static int set_voice_setting(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_set_voice_setting *cp = data;
+       struct hci_conn *conn;
+       struct hci_conn *sco_conn;
+
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_bredr_capable(hdev)) {
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING,
+                               MGMT_STATUS_NOT_SUPPORTED);
+       }
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+       if (!conn) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                               MGMT_OP_SET_VOICE_SETTING, 0, NULL, 0);
+               goto unlock;
+       }
+
+       conn->voice_setting = cp->voice_setting;
+       conn->sco_role = cp->sco_role;
+
+       sco_conn = hci_conn_hash_lookup_sco(hdev);
+       if (sco_conn && bacmp(&sco_conn->dst, &cp->bdaddr) != 0) {
+               BT_ERR("There is other SCO connection.");
+               goto done;
+       }
+
+       if (conn->sco_role == MGMT_SCO_ROLE_HANDSFREE) {
+               if (conn->voice_setting == 0x0063)
+                       sco_connect_set_wbc(hdev);
+               else
+                       sco_connect_set_nbc(hdev);
+       } else {
+               if (conn->voice_setting == 0x0063)
+                       sco_connect_set_gw_wbc(hdev);
+               else
+                       sco_connect_set_gw_nbc(hdev);
+       }
+
+done:
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_VOICE_SETTING, 0,
+                       cp, sizeof(cp));
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int get_adv_tx_power(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_rp_get_adv_tx_power *rp;
+       size_t rp_len;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       rp_len = sizeof(*rp);
+       rp = kmalloc(rp_len, GFP_KERNEL);
+       if (!rp) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       rp->adv_tx_power = hdev->adv_tx_power;
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_TX_POWER, 0, rp,
+                               rp_len);
+
+       kfree(rp);
+
+unlock:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+void mgmt_hardware_error(struct hci_dev *hdev, u8 err_code)
+{
+       struct mgmt_ev_hardware_error ev;
+
+       ev.error_code = err_code;
+       mgmt_event(MGMT_EV_HARDWARE_ERROR, hdev, &ev, sizeof(ev), NULL);
+}
+
+void mgmt_tx_timeout_error(struct hci_dev *hdev)
+{
+       mgmt_event(MGMT_EV_TX_TIMEOUT_ERROR, hdev, NULL, 0, NULL);
+}
+
+void mgmt_multi_adv_state_change_evt(struct hci_dev *hdev, u8 adv_instance,
+               u8 state_change_reason, u16 connection_handle)
+{
+       struct mgmt_ev_vendor_specific_multi_adv_state_changed mgmt_ev;
+
+       BT_DBG("Multi adv state changed [%2.2X %2.2X %2.2X]",
+              adv_instance, state_change_reason, connection_handle);
+
+       mgmt_ev.adv_instance = adv_instance;
+       mgmt_ev.state_change_reason = state_change_reason;
+       mgmt_ev.connection_handle = connection_handle;
+
+       mgmt_event(MGMT_EV_MULTI_ADV_STATE_CHANGED, hdev, &mgmt_ev,
+               sizeof(struct mgmt_ev_vendor_specific_multi_adv_state_changed),
+               NULL);
+}
+
+static int enable_bt_6lowpan(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       int err;
+       struct mgmt_cp_enable_6lowpan *cp = data;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN,
+                                     MGMT_STATUS_NOT_POWERED);
+               goto unlocked;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN,
+                                     MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (cp->enable_6lowpan)
+               bt_6lowpan_enable();
+       else
+               bt_6lowpan_disable();
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ENABLE_6LOWPAN,
+                               MGMT_STATUS_SUCCESS, NULL, 0);
+unlocked:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int connect_bt_6lowpan(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_connect_6lowpan *cp = data;
+       __u8 addr_type = ADDR_LE_DEV_PUBLIC;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN,
+                                     MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN,
+                                     MGMT_STATUS_REJECTED);
+               goto unlocked;
+       }
+
+       if (bdaddr_type_is_le(cp->addr.type)) {
+               if (cp->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+       } else {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN,
+                                       MGMT_STATUS_INVALID_PARAMS, NULL, 0);
+               goto unlocked;
+       }
+
+       hci_dev_unlock(hdev);
+
+       /* 6lowpan Connect */
+       err = _bt_6lowpan_connect(&cp->addr.bdaddr, cp->addr.type);
+
+       hci_dev_lock(hdev);
+
+       if (err < 0) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN,
+                                       MGMT_STATUS_REJECTED, NULL, 0);
+
+               goto unlocked;
+       }
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, 0,
+                               NULL, 0);
+unlocked:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int disconnect_bt_6lowpan(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_disconnect_6lowpan *cp = data;
+       struct hci_conn *conn = NULL;
+       __u8 addr_type = ADDR_LE_DEV_PUBLIC;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT_6LOWPAN,
+                                     MGMT_STATUS_NOT_SUPPORTED);
+               goto unlocked;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT_6LOWPAN,
+                                     MGMT_STATUS_REJECTED);
+               goto unlocked;
+       }
+
+       if (bdaddr_type_is_le(cp->addr.type)) {
+               if (cp->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+       } else {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                                       MGMT_OP_DISCONNECT_6LOWPAN,
+                                       MGMT_STATUS_INVALID_PARAMS, NULL, 0);
+               goto unlocked;
+       }
+
+       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+       if (!conn) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                                       MGMT_OP_DISCONNECT_6LOWPAN,
+                                       MGMT_STATUS_NOT_CONNECTED, NULL, 0);
+               goto unlocked;
+       }
+
+       if (conn->dst_type != addr_type) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                                       MGMT_OP_DISCONNECT_6LOWPAN,
+                                       MGMT_STATUS_INVALID_PARAMS, NULL, 0);
+               goto unlocked;
+       }
+
+       if (conn->state != BT_CONNECTED) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                                       MGMT_OP_DISCONNECT_6LOWPAN,
+                                       MGMT_STATUS_NOT_CONNECTED, NULL, 0);
+               goto unlocked;
+       }
+
+       /* 6lowpan Disconnect */
+       err = _bt_6lowpan_disconnect(conn->l2cap_data, cp->addr.type);
+       if (err < 0) {
+               err = mgmt_cmd_complete(sk, hdev->id,
+                                       MGMT_OP_DISCONNECT_6LOWPAN,
+                                       MGMT_STATUS_REJECTED, NULL, 0);
+               goto unlocked;
+       }
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONNECT_6LOWPAN, 0,
+                               NULL, 0);
+
+unlocked:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+void mgmt_6lowpan_conn_changed(struct hci_dev *hdev, char if_name[16],
+               bdaddr_t *bdaddr, u8 addr_type, bool connected)
+{
+       char buf[512];
+       struct mgmt_ev_6lowpan_conn_state_changed *ev = (void *)buf;
+       size_t ev_size;
+
+       memset(buf, 0, sizeof(buf));
+       bacpy(&ev->addr.bdaddr, bdaddr);
+       ev->addr.type = addr_type;
+       ev->connected = connected;
+       memcpy(ev->ifname, (__u8 *)if_name, 16);
+
+       ev_size = sizeof(*ev);
+
+       mgmt_event(MGMT_EV_6LOWPAN_CONN_STATE_CHANGED, hdev, ev, ev_size, NULL);
+}
+
+void mgmt_le_read_maximum_data_length_complete(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct mgmt_rp_le_read_maximum_data_length rp;
+
+       BT_DBG("%s status %u", hdev->name, status);
+
+       cmd = pending_find(MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, hdev);
+       if (!cmd)
+               return;
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                               MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                               mgmt_status(status));
+
+       memset(&rp, 0, sizeof(rp));
+
+       rp.max_tx_octets = cpu_to_le16(hdev->le_max_tx_len);
+       rp.max_tx_time = cpu_to_le16(hdev->le_max_tx_time);
+       rp.max_rx_octets = cpu_to_le16(hdev->le_max_rx_len);
+       rp.max_rx_time = cpu_to_le16(hdev->le_max_rx_time);
+
+       mgmt_cmd_complete(cmd->sk, hdev->id,
+                         MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, 0,
+                         &rp, sizeof(rp));
+
+       mgmt_pending_remove(cmd);
+}
+
+static int read_maximum_le_data_length(struct sock *sk,
+               struct hci_dev *hdev, void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       int err;
+
+       BT_DBG("read_maximum_le_data_length  %s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                                     MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                                     MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                                     MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                                     MGMT_STATUS_NOT_SUPPORTED);
+               goto unlock;
+       }
+
+       if (pending_find(MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                                     MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                                     MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+                              hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       err = hci_send_cmd(hdev, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+void mgmt_le_write_host_suggested_data_length_complete(struct hci_dev *hdev,
+               u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       cmd = pending_find(MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, hdev);
+       if (!cmd) {
+               BT_ERR("cmd not found in the pending list");
+               goto unlock;
+       }
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                               MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                               mgmt_status(status));
+       else
+               mgmt_cmd_complete(cmd->sk, hdev->id,
+                                 MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                                 0, NULL, 0);
+
+       mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int write_host_suggested_le_data_length(struct sock *sk,
+               struct hci_dev *hdev, void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct mgmt_cp_le_write_host_suggested_data_length *cp = data;
+       struct hci_cp_le_write_def_data_len hci_data;
+       int err = 0;
+
+       BT_DBG("Write host suggested data length request for %s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlock;
+       }
+
+       if (pending_find(MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+                              hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_data.tx_len = cp->def_tx_octets;
+       hci_data.tx_time = cp->def_tx_time;
+
+       err = hci_send_cmd(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN,
+                          sizeof(hci_data), &hci_data);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+void mgmt_le_read_host_suggested_data_length_complete(struct hci_dev *hdev,
+               u8 status)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct mgmt_rp_le_read_host_suggested_data_length rp;
+
+       BT_DBG("%s status %u", hdev->name, status);
+
+       cmd = pending_find(MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH, hdev);
+       if (!cmd) {
+               BT_ERR("cmd not found in the pending list");
+               return;
+       }
+
+       if (status)
+               mgmt_cmd_status(cmd->sk, hdev->id,
+                       MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                       mgmt_status(status));
+
+       memset(&rp, 0, sizeof(rp));
+
+       rp.def_tx_octets = cpu_to_le16(hdev->le_def_tx_len);
+       rp.def_tx_time = cpu_to_le16(hdev->le_def_tx_time);
+
+       mgmt_cmd_complete(cmd->sk, hdev->id,
+                         MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH, 0,
+                         &rp, sizeof(rp));
+
+       mgmt_pending_remove(cmd);
+}
+
+static int read_host_suggested_data_length(struct sock *sk,
+               struct hci_dev *hdev, void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+       int err;
+
+       BT_DBG("read_host_suggested_data_length %s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_NOT_SUPPORTED);
+               goto unlock;
+       }
+
+       if (pending_find(MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id,
+                               MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                               MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+                              hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       err = hci_send_cmd(hdev, HCI_OP_LE_READ_DEF_DATA_LEN, 0, NULL);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static int set_le_data_length_params(struct sock *sk, struct hci_dev *hdev,
+               void *data, u16 len)
+{
+       struct mgmt_cp_le_set_data_length *cp = data;
+       struct mgmt_rp_le_set_data_length *rp;
+       struct mgmt_pending_cmd *cmd;
+       struct hci_conn *conn;
+       int err = 0;
+       u16 max_tx_octets, max_tx_time;
+       size_t rp_len;
+
+       BT_INFO("Set Data length for the device %s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       rp_len = sizeof(*rp);
+       rp = kmalloc(rp_len, GFP_KERNEL);
+       if (!rp) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_DATA_LENGTH,
+                                     MGMT_STATUS_NOT_POWERED);
+               goto unlock;
+       }
+
+       if (!lmp_le_capable(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_DATA_LENGTH,
+                                     MGMT_STATUS_NOT_SUPPORTED);
+               goto unlock;
+       }
+
+       if (pending_find(MGMT_OP_LE_SET_DATA_LENGTH, hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_DATA_LENGTH,
+                                     MGMT_STATUS_BUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_LE_SET_DATA_LENGTH, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       max_tx_octets = __le16_to_cpu(cp->max_tx_octets);
+       max_tx_time = __le16_to_cpu(cp->max_tx_time);
+
+       BT_DBG("max_tx_octets 0x%4.4x max_tx_time 0x%4.4x latency",
+              max_tx_octets, max_tx_time);
+
+       conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+       if (!conn) {
+               mgmt_cmd_status(sk, hdev->id, MGMT_OP_LE_SET_DATA_LENGTH,
+                               MGMT_STATUS_NOT_CONNECTED);
+               goto unlock;
+       }
+
+       hci_dev_unlock(hdev);
+
+       err = hci_le_set_data_length(conn, max_tx_octets, max_tx_time);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+       rp->handle = conn->handle;
+       rp->status = 0;
+
+       hci_dev_lock(hdev);
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LE_SET_DATA_LENGTH, 0,
+                               rp, rp_len);
+unlock:
+       kfree(rp);
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+void mgmt_le_data_length_change_complete(struct hci_dev *hdev,
+               bdaddr_t *bdaddr, u16 tx_octets, u16 tx_time,
+               u16 rx_octets, u16 rx_time)
+{
+       struct mgmt_ev_le_data_length_changed ev;
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.max_tx_octets = tx_octets;
+       ev.max_tx_time = tx_time;
+       ev.max_rx_octets = rx_octets;
+       ev.max_rx_time = rx_time;
+
+       mgmt_event(MGMT_EV_LE_DATA_LENGTH_CHANGED, hdev, &ev, sizeof(ev), NULL);
+}
+#endif /* TIZEN_BT */
+
+static bool ltk_is_valid(struct mgmt_ltk_info *key)
+{
+       if (key->initiator != 0x00 && key->initiator != 0x01)
+               return false;
+
+       switch (key->addr.type) {
+       case BDADDR_LE_PUBLIC:
+               return true;
+
+       case BDADDR_LE_RANDOM:
+               /* Two most significant bits shall be set */
+               if ((key->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+                       return false;
+               return true;
+       }
+
+       return false;
+}
+
+static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
+                              void *cp_data, u16 len)
+{
+       struct mgmt_cp_load_long_term_keys *cp = cp_data;
+       const u16 max_key_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_ltk_info));
+       u16 key_count, expected_len;
+       int i, err;
+
+       bt_dev_dbg(hdev, "sock %p", sk);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                      MGMT_STATUS_NOT_SUPPORTED);
+
+       key_count = __le16_to_cpu(cp->key_count);
+       if (key_count > max_key_count) {
+               bt_dev_err(hdev, "load_ltks: too big key_count value %u",
+                          key_count);
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                      MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       expected_len = struct_size(cp, keys, key_count);
+       if (expected_len != len) {
+               bt_dev_err(hdev, "load_keys: expected %u bytes, got %u bytes",
+                          expected_len, len);
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                      MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       bt_dev_dbg(hdev, "key_count %u", key_count);
+
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_ltk_info *key = &cp->keys[i];
+
+               if (!ltk_is_valid(key))
+                       return mgmt_cmd_status(sk, hdev->id,
+                                              MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                              MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       hci_dev_lock(hdev);
+
+       hci_smp_ltks_clear(hdev);
+
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_ltk_info *key = &cp->keys[i];
+               u8 type, authenticated;
+
+               if (hci_is_blocked_key(hdev,
+                                      HCI_BLOCKED_KEY_TYPE_LTK,
+                                      key->val)) {
+                       bt_dev_warn(hdev, "Skipping blocked LTK for %pMR",
+                                   &key->addr.bdaddr);
+                       continue;
+               }
+
+               switch (key->type) {
+               case MGMT_LTK_UNAUTHENTICATED:
+                       authenticated = 0x00;
+                       type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER;
+                       break;
+               case MGMT_LTK_AUTHENTICATED:
+                       authenticated = 0x01;
+                       type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER;
+                       break;
+               case MGMT_LTK_P256_UNAUTH:
+                       authenticated = 0x00;
+                       type = SMP_LTK_P256;
+                       break;
+               case MGMT_LTK_P256_AUTH:
+                       authenticated = 0x01;
+                       type = SMP_LTK_P256;
+                       break;
+               case MGMT_LTK_P256_DEBUG:
+                       authenticated = 0x00;
+                       type = SMP_LTK_P256_DEBUG;
+                       fallthrough;
+               default:
+                       continue;
+               }
+
+               hci_add_ltk(hdev, &key->addr.bdaddr,
+                           le_addr_type(key->addr.type), type, authenticated,
+                           key->val, key->enc_size, key->ediv, key->rand);
+       }
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
+                          NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
+static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
+{
+       struct mgmt_pending_cmd *cmd = data;
+       struct hci_conn *conn = cmd->user_data;
+       struct mgmt_cp_get_conn_info *cp = cmd->param;
+       struct mgmt_rp_get_conn_info rp;
+       u8 status;
+
+       bt_dev_dbg(hdev, "err %d", err);
+
+       memcpy(&rp.addr, &cp->addr.bdaddr, sizeof(rp.addr));
+
+       status = mgmt_status(err);
+       if (status == MGMT_STATUS_SUCCESS) {
+               rp.rssi = conn->rssi;
+               rp.tx_power = conn->tx_power;
+               rp.max_tx_power = conn->max_tx_power;
+       } else {
+               rp.rssi = HCI_RSSI_INVALID;
+               rp.tx_power = HCI_TX_POWER_INVALID;
+               rp.max_tx_power = HCI_TX_POWER_INVALID;
+       }
+
+       mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
+                         &rp, sizeof(rp));
+
+       mgmt_pending_free(cmd);
+}
+
+static int get_conn_info_sync(struct hci_dev *hdev, void *data)
+{
+       struct mgmt_pending_cmd *cmd = data;
+       struct mgmt_cp_get_conn_info *cp = cmd->param;
+       struct hci_conn *conn;
+       int err;
+       __le16   handle;
+
+       /* Make sure we are still connected */
+       if (cp->addr.type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+       else
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+
+       if (!conn || conn->state != BT_CONNECTED)
+               return MGMT_STATUS_NOT_CONNECTED;
+
+       cmd->user_data = conn;
+       handle = cpu_to_le16(conn->handle);
+
+       /* Refresh RSSI each time */
+       err = hci_read_rssi_sync(hdev, handle);
+
+       /* For LE links TX power does not change thus we don't need to
+        * query for it once value is known.
+        */
+       if (!err && (!bdaddr_type_is_le(cp->addr.type) ||
+                    conn->tx_power == HCI_TX_POWER_INVALID))
+               err = hci_read_tx_power_sync(hdev, handle, 0x00);
+
+       /* Max TX power needs to be read only once per connection */
+       if (!err && conn->max_tx_power == HCI_TX_POWER_INVALID)
+               err = hci_read_tx_power_sync(hdev, handle, 0x01);
+
+       return err;
+}
+
+static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
+                        u16 len)
+{
+       struct mgmt_cp_get_conn_info *cp = data;
+       struct mgmt_rp_get_conn_info rp;
+       struct hci_conn *conn;
+       unsigned long conn_info_age;
+       int err = 0;
+
+       bt_dev_dbg(hdev, "sock %p", sk);
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                        MGMT_STATUS_INVALID_PARAMS,
+                                        &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                       MGMT_STATUS_NOT_POWERED, &rp,
+                                       sizeof(rp));
+               goto unlock;
+       }
+
+       if (cp->addr.type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+       else
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
 
        if (!conn || conn->state != BT_CONNECTED) {
                err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
@@ -8055,6 +10370,101 @@ unlock:
        return err;
 }
 
+#ifdef TIZEN_BT
+int mgmt_device_name_update(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name,
+                           u8 name_len)
+{
+       char buf[512];
+       struct mgmt_ev_device_name_update *ev = (void *)buf;
+       u16 eir_len = 0;
+
+       if (name_len <= 0)
+               return -EINVAL;
+
+       bacpy(&ev->addr.bdaddr, bdaddr);
+       ev->addr.type = BDADDR_BREDR;
+
+       eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
+                                 name_len);
+
+       ev->eir_len = cpu_to_le16(eir_len);
+
+       return mgmt_event(MGMT_EV_DEVICE_NAME_UPDATE, hdev, buf,
+                         sizeof(*ev) + eir_len, NULL);
+}
+
+int mgmt_le_conn_update_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                              u8 link_type, u8 addr_type, u8 status)
+{
+       struct mgmt_ev_conn_update_failed ev;
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_bdaddr(link_type, addr_type);
+       ev.status = status;
+
+       return mgmt_event(MGMT_EV_CONN_UPDATE_FAILED, hdev,
+                               &ev, sizeof(ev), NULL);
+}
+
+int mgmt_le_conn_updated(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                        u8 link_type, u8 addr_type, u16 conn_interval,
+                        u16 conn_latency, u16 supervision_timeout)
+{
+       struct mgmt_ev_conn_updated ev;
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_bdaddr(link_type, addr_type);
+       ev.conn_interval = cpu_to_le16(conn_interval);
+       ev.conn_latency = cpu_to_le16(conn_latency);
+       ev.supervision_timeout = cpu_to_le16(supervision_timeout);
+
+       return mgmt_event(MGMT_EV_CONN_UPDATED, hdev,
+                               &ev, sizeof(ev), NULL);
+}
+
+/* le device found event - Pass adv type */
+void mgmt_le_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+               u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, u8 *eir,
+               u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len, u8 adv_type)
+{
+       char buf[512];
+       struct mgmt_ev_le_device_found *ev = (void *)buf;
+       size_t ev_size;
+
+       if (!hci_discovery_active(hdev) && !hci_le_discovery_active(hdev))
+               return;
+
+       /* Make sure that the buffer is big enough. The 5 extra bytes
+        * are for the potential CoD field.
+        */
+       if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf))
+               return;
+
+       memset(buf, 0, sizeof(buf));
+
+       bacpy(&ev->addr.bdaddr, bdaddr);
+       ev->addr.type = link_to_bdaddr(link_type, addr_type);
+       ev->rssi = rssi;
+       ev->flags = cpu_to_le32(flags);
+       ev->adv_type = adv_type;
+
+       if (eir_len > 0)
+               memcpy(ev->eir, eir, eir_len);
+
+       if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, NULL))
+               eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
+                                         dev_class, 3);
+
+       if (scan_rsp_len > 0)
+               memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+
+       ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
+       ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
+
+       mgmt_event(MGMT_EV_LE_DEVICE_FOUND, hdev, ev, ev_size, NULL);
+}
+#endif
+
 static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
                                             int err)
 {
@@ -8859,7 +11269,7 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
         * extra parameters we don't know about will be ignored in this request.
         */
        if (data_len < MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE)
-               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
                                       MGMT_STATUS_INVALID_PARAMS);
 
        flags = __le32_to_cpu(cp->flags);
@@ -9352,7 +11762,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
        { add_ext_adv_data,        MGMT_ADD_EXT_ADV_DATA_SIZE,
                                                HCI_MGMT_VAR_LEN },
        { add_adv_patterns_monitor_rssi,
-                                  MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE },
+                                  MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE,
+                                               HCI_MGMT_VAR_LEN },
        { set_mesh,                MGMT_SET_MESH_RECEIVER_SIZE,
                                                HCI_MGMT_VAR_LEN },
        { mesh_features,           MGMT_MESH_READ_FEATURES_SIZE },
@@ -9361,6 +11772,42 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
        { mesh_send_cancel,        MGMT_MESH_SEND_CANCEL_SIZE },
 };
 
+#ifdef TIZEN_BT
+static const struct hci_mgmt_handler tizen_mgmt_handlers[] = {
+       { NULL }, /* 0x0000 (no command) */
+       { set_advertising_params,  MGMT_SET_ADVERTISING_PARAMS_SIZE },
+       { set_advertising_data,    MGMT_SET_ADV_MIN_APP_DATA_SIZE,
+                                               HCI_MGMT_VAR_LEN },
+       { set_scan_rsp_data,       MGMT_SET_SCAN_RSP_MIN_APP_DATA_SIZE,
+                                               HCI_MGMT_VAR_LEN },
+       { add_white_list,          MGMT_ADD_DEV_WHITE_LIST_SIZE },
+       { remove_from_white_list,  MGMT_REMOVE_DEV_FROM_WHITE_LIST_SIZE },
+       { clear_white_list,        MGMT_OP_CLEAR_DEV_WHITE_LIST_SIZE },
+       { set_enable_rssi,         MGMT_SET_RSSI_ENABLE_SIZE },
+       { get_raw_rssi,            MGMT_GET_RAW_RSSI_SIZE },
+       { set_disable_threshold,   MGMT_SET_RSSI_DISABLE_SIZE },
+       { start_le_discovery,      MGMT_START_LE_DISCOVERY_SIZE },
+       { stop_le_discovery,       MGMT_STOP_LE_DISCOVERY_SIZE },
+       { disable_le_auto_connect, MGMT_DISABLE_LE_AUTO_CONNECT_SIZE },
+       { le_conn_update,          MGMT_LE_CONN_UPDATE_SIZE },
+       { set_manufacturer_data,   MGMT_SET_MANUFACTURER_DATA_SIZE },
+       { le_set_scan_params,      MGMT_LE_SET_SCAN_PARAMS_SIZE },
+       { set_voice_setting,       MGMT_SET_VOICE_SETTING_SIZE },
+       { get_adv_tx_power,        MGMT_GET_ADV_TX_POWER_SIZE },
+       { enable_bt_6lowpan,       MGMT_ENABLE_BT_6LOWPAN_SIZE },
+       { connect_bt_6lowpan,      MGMT_CONNECT_6LOWPAN_SIZE },
+       { disconnect_bt_6lowpan,   MGMT_DISCONNECT_6LOWPAN_SIZE },
+       { read_maximum_le_data_length,
+                                  MGMT_LE_READ_MAXIMUM_DATA_LENGTH_SIZE },
+       { write_host_suggested_le_data_length,
+                                  MGMT_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_SIZE },
+       { read_host_suggested_data_length,
+                                  MGMT_LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE },
+       { set_le_data_length_params,
+                                  MGMT_LE_SET_DATA_LENGTH_SIZE },
+};
+#endif
+
 void mgmt_index_added(struct hci_dev *hdev)
 {
        struct mgmt_ev_ext_index ev;
@@ -10524,6 +12971,10 @@ static struct hci_mgmt_chan chan = {
        .channel        = HCI_CHANNEL_CONTROL,
        .handler_count  = ARRAY_SIZE(mgmt_handlers),
        .handlers       = mgmt_handlers,
+#ifdef TIZEN_BT
+       .tizen_handler_count    = ARRAY_SIZE(tizen_mgmt_handlers),
+       .tizen_handlers = tizen_mgmt_handlers,
+#endif
        .hdev_init      = mgmt_init_hdev,
 };