Bluetooth: Split hci_request helpers to hci_request.[ch]
[cascardo/linux.git] / net / bluetooth / mgmt.c
index 81b2886..95473e9 100644 (file)
@@ -32,6 +32,7 @@
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "hci_request.h"
 #include "smp.h"
 
 #define MGMT_VERSION   1
@@ -1492,6 +1493,12 @@ static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
                     cmd->param_len);
 }
 
+static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+                    sizeof(struct mgmt_addr_info));
+}
+
 static u8 mgmt_bredr_support(struct hci_dev *hdev)
 {
        if (!lmp_bredr_capable(hdev))
@@ -1560,7 +1567,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
         * entries.
         */
        hci_req_init(&req, hdev);
-       hci_update_page_scan(hdev, &req);
+       __hci_update_page_scan(&req);
        update_class(&req);
        hci_req_run(&req, NULL);
 
@@ -1807,7 +1814,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
 
        if (conn_changed || discov_changed) {
                new_settings(hdev, cmd->sk);
-               hci_update_page_scan(hdev, NULL);
+               hci_update_page_scan(hdev);
                if (discov_changed)
                        mgmt_update_adv_data(hdev);
                hci_update_background_scan(hdev);
@@ -1841,7 +1848,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
                return err;
 
        if (changed) {
-               hci_update_page_scan(hdev, NULL);
+               hci_update_page_scan(hdev);
                hci_update_background_scan(hdev);
                return new_settings(hdev, sk);
        }
@@ -2193,12 +2200,14 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
 
+       hci_dev_lock(hdev);
+
        if (status) {
                u8 mgmt_err = mgmt_status(status);
 
                mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
                                     &mgmt_err);
-               return;
+               goto unlock;
        }
 
        mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
@@ -2216,17 +2225,16 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
        if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
                struct hci_request req;
 
-               hci_dev_lock(hdev);
-
                hci_req_init(&req, hdev);
                update_adv_data(&req);
                update_scan_rsp_data(&req);
                hci_req_run(&req, NULL);
 
                hci_update_background_scan(hdev);
-
-               hci_dev_unlock(hdev);
        }
+
+unlock:
+       hci_dev_unlock(hdev);
 }
 
 static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -2815,6 +2823,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        dc.handle = cpu_to_le16(conn->handle);
        dc.reason = 0x13; /* Remote User Terminated Connection */
        err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
@@ -3032,6 +3042,8 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        bacpy(&reply.bdaddr, &cp->addr.bdaddr);
        reply.pin_len = cp->pin_len;
        memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -3104,14 +3116,13 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
        conn->disconn_cfm_cb = NULL;
 
        hci_conn_drop(conn);
-       hci_conn_put(conn);
-
-       mgmt_pending_remove(cmd);
 
        /* The device is paired so there is no need to remove
         * its connection parameters anymore.
         */
        clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+       hci_conn_put(conn);
 }
 
 void mgmt_smp_complete(struct hci_conn *conn, bool complete)
@@ -3120,8 +3131,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
        struct pending_cmd *cmd;
 
        cmd = find_pairing(conn);
-       if (cmd)
-               pairing_complete(cmd, status);
+       if (cmd) {
+               cmd->cmd_complete(cmd, status);
+               mgmt_pending_remove(cmd);
+       }
 }
 
 static void pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3131,10 +3144,13 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
        BT_DBG("status %u", status);
 
        cmd = find_pairing(conn);
-       if (!cmd)
+       if (!cmd) {
                BT_DBG("Unable to find a pending command");
-       else
-               pairing_complete(cmd, mgmt_status(status));
+               return;
+       }
+
+       cmd->cmd_complete(cmd, mgmt_status(status));
+       mgmt_pending_remove(cmd);
 }
 
 static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3147,10 +3163,13 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
                return;
 
        cmd = find_pairing(conn);
-       if (!cmd)
+       if (!cmd) {
                BT_DBG("Unable to find a pending command");
-       else
-               pairing_complete(cmd, mgmt_status(status));
+               return;
+       }
+
+       cmd->cmd_complete(cmd, mgmt_status(status));
+       mgmt_pending_remove(cmd);
 }
 
 static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3247,6 +3266,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = pairing_complete;
+
        /* For LE, just connecting isn't a proof that the pairing finished */
        if (cp->addr.type == BDADDR_BREDR) {
                conn->connect_cfm_cb = pairing_complete_cb;
@@ -3262,8 +3283,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        cmd->user_data = hci_conn_get(conn);
 
        if ((conn->state == BT_CONNECTED || conn->state == BT_CONFIG) &&
-           hci_conn_security(conn, sec_level, auth_type, true))
-               pairing_complete(cmd, 0);
+           hci_conn_security(conn, sec_level, auth_type, true)) {
+               cmd->cmd_complete(cmd, 0);
+               mgmt_pending_remove(cmd);
+       }
 
        err = 0;
 
@@ -3305,7 +3328,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
-       pairing_complete(cmd, MGMT_STATUS_CANCELLED);
+       cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
+       mgmt_pending_remove(cmd);
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
                           addr, sizeof(*addr));
@@ -3363,6 +3387,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
                goto done;
        }
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        /* Continue with pairing via HCI */
        if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
                struct hci_cp_user_passkey_reply cp;
@@ -3777,7 +3803,7 @@ static bool trigger_discovery(struct hci_request *req, u8 *status)
 
                /* All active scans will be done with either a resolvable
                 * private address (when privacy feature has been enabled)
-                * or unresolvable private address.
+                * or non-resolvable private address.
                 */
                err = hci_update_random_address(req, true, &own_addr_type);
                if (err < 0) {
@@ -3821,10 +3847,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status)
                cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
 
        if (cmd) {
-               u8 type = hdev->discovery.type;
-
-               cmd_complete(cmd->sk, hdev->id, cmd->opcode,
-                            mgmt_status(status), &type, sizeof(type));
+               cmd->cmd_complete(cmd, mgmt_status(status));
                mgmt_pending_remove(cmd);
        }
 
@@ -3887,18 +3910,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
+       cmd->cmd_complete = generic_cmd_complete;
+
        /* Clear the discovery filter first to free any previously
         * allocated memory for the UUID list.
         */
        hci_discovery_filter_clear(hdev);
 
        hdev->discovery.type = cp->type;
+       hdev->discovery.report_invalid_rssi = false;
 
        hci_req_init(&req, hdev);
 
@@ -3922,6 +3948,11 @@ failed:
        return err;
 }
 
+static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
+}
+
 static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
                                   void *data, u16 len)
 {
@@ -3977,12 +4008,14 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
        }
 
        cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
-                              hdev, NULL, 0);
+                              hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
+       cmd->cmd_complete = service_discovery_cmd_complete;
+
        /* Clear the discovery filter first to free any previously
         * allocated memory for the UUID list.
         */
@@ -4038,10 +4071,7 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
 
        cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
        if (cmd) {
-               u8 type = hdev->discovery.type;
-
-               cmd_complete(cmd->sk, hdev->id, cmd->opcode,
-                            mgmt_status(status), &type, sizeof(type));
+               cmd->cmd_complete(cmd, mgmt_status(status));
                mgmt_pending_remove(cmd);
        }
 
@@ -4077,12 +4107,14 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
        }
 
+       cmd->cmd_complete = generic_cmd_complete;
+
        hci_req_init(&req, hdev);
 
        hci_stop_discovery(&req);
@@ -4259,12 +4291,14 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status)
 {
        struct cmd_lookup match = { NULL, hdev };
 
+       hci_dev_lock(hdev);
+
        if (status) {
                u8 mgmt_err = mgmt_status(status);
 
                mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
                                     cmd_status_rsp, &mgmt_err);
-               return;
+               goto unlock;
        }
 
        if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
@@ -4279,6 +4313,9 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status)
 
        if (match.sk)
                sock_put(match.sk);
+
+unlock:
+       hci_dev_unlock(hdev);
 }
 
 static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -4661,7 +4698,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
        hci_req_init(&req, hdev);
 
        write_fast_connectable(&req, false);
-       hci_update_page_scan(hdev, &req);
+       __hci_update_page_scan(&req);
 
        /* Since only the advertising data flags will change, there
         * is no need to update the scan response data.
@@ -5055,67 +5092,42 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
-struct cmd_conn_lookup {
-       struct hci_conn *conn;
-       bool valid_tx_power;
-       u8 mgmt_status;
-};
-
-static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
 {
-       struct cmd_conn_lookup *match = data;
-       struct mgmt_cp_get_conn_info *cp;
-       struct mgmt_rp_get_conn_info rp;
        struct hci_conn *conn = cmd->user_data;
+       struct mgmt_rp_get_conn_info rp;
 
-       if (conn != match->conn)
-               return;
-
-       cp = (struct mgmt_cp_get_conn_info *) cmd->param;
-
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
+       memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
 
-       if (!match->mgmt_status) {
+       if (status == MGMT_STATUS_SUCCESS) {
                rp.rssi = conn->rssi;
-
-               if (match->valid_tx_power) {
-                       rp.tx_power = conn->tx_power;
-                       rp.max_tx_power = conn->max_tx_power;
-               } else {
-                       rp.tx_power = HCI_TX_POWER_INVALID;
-                       rp.max_tx_power = HCI_TX_POWER_INVALID;
-               }
+               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_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
-                    match->mgmt_status, &rp, sizeof(rp));
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
+                    &rp, sizeof(rp));
 
        hci_conn_drop(conn);
        hci_conn_put(conn);
-
-       mgmt_pending_remove(cmd);
 }
 
-static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
 {
        struct hci_cp_read_rssi *cp;
+       struct pending_cmd *cmd;
        struct hci_conn *conn;
-       struct cmd_conn_lookup match;
        u16 handle;
+       u8 status;
 
-       BT_DBG("status 0x%02x", status);
+       BT_DBG("status 0x%02x", hci_status);
 
        hci_dev_lock(hdev);
 
-       /* TX power data is valid in case request completed successfully,
-        * otherwise we assume it's not valid. At the moment we assume that
-        * either both or none of current and max values are valid to keep code
-        * simple.
-        */
-       match.valid_tx_power = !status;
-
        /* Commands sent in request are either Read RSSI or Read Transmit Power
         * Level so we check which one was last sent to retrieve connection
         * handle.  Both commands have handle as first parameter so it's safe to
@@ -5128,29 +5140,29 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
        cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
        if (!cp) {
                cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
+       } else {
+               status = mgmt_status(hci_status);
        }
 
        if (!cp) {
-               BT_ERR("invalid sent_cmd in response");
+               BT_ERR("invalid sent_cmd in conn_info response");
                goto unlock;
        }
 
        handle = __le16_to_cpu(cp->handle);
        conn = hci_conn_hash_lookup_handle(hdev, handle);
        if (!conn) {
-               BT_ERR("unknown handle (%d) in response", handle);
+               BT_ERR("unknown handle (%d) in conn_info response", handle);
                goto unlock;
        }
 
-       match.conn = conn;
-       match.mgmt_status = mgmt_status(status);
+       cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
+       if (!cmd)
+               goto unlock;
 
-       /* Cache refresh is complete, now reply for mgmt request for given
-        * connection only.
-        */
-       mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
-                            get_conn_info_complete, &match);
+       cmd->cmd_complete(cmd, status);
+       mgmt_pending_remove(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
@@ -5196,6 +5208,12 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_BUSY, &rp, sizeof(rp));
+               goto unlock;
+       }
+
        /* To avoid client trying to guess when to poll again for information we
         * calculate conn info age as random value between min/max set in hdev.
         */
@@ -5251,6 +5269,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
 
                hci_conn_hold(conn);
                cmd->user_data = hci_conn_get(conn);
+               cmd->cmd_complete = conn_info_cmd_complete;
 
                conn->conn_info_timestamp = jiffies;
        } else {
@@ -5268,10 +5287,40 @@ unlock:
        return err;
 }
 
-static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
 {
-       struct mgmt_cp_get_clock_info *cp;
+       struct hci_conn *conn = cmd->user_data;
        struct mgmt_rp_get_clock_info rp;
+       struct hci_dev *hdev;
+
+       memset(&rp, 0, sizeof(rp));
+       memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
+
+       if (status)
+               goto complete;
+
+       hdev = hci_dev_get(cmd->index);
+       if (hdev) {
+               rp.local_clock = cpu_to_le32(hdev->clock);
+               hci_dev_put(hdev);
+       }
+
+       if (conn) {
+               rp.piconet_clock = cpu_to_le32(conn->clock);
+               rp.accuracy = cpu_to_le16(conn->clock_accuracy);
+       }
+
+complete:
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
+
+       if (conn) {
+               hci_conn_drop(conn);
+               hci_conn_put(conn);
+       }
+}
+
+static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+{
        struct hci_cp_read_clock *hci_cp;
        struct pending_cmd *cmd;
        struct hci_conn *conn;
@@ -5295,29 +5344,8 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
        if (!cmd)
                goto unlock;
 
-       cp = cmd->param;
-
-       memset(&rp, 0, sizeof(rp));
-       memcpy(&rp.addr, &cp->addr, sizeof(rp.addr));
-
-       if (status)
-               goto send_rsp;
-
-       rp.local_clock = cpu_to_le32(hdev->clock);
-
-       if (conn) {
-               rp.piconet_clock = cpu_to_le32(conn->clock);
-               rp.accuracy = cpu_to_le16(conn->clock_accuracy);
-       }
-
-send_rsp:
-       cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
-                    &rp, sizeof(rp));
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
-       if (conn) {
-               hci_conn_drop(conn);
-               hci_conn_put(conn);
-       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -5373,6 +5401,8 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       cmd->cmd_complete = clock_info_cmd_complete;
+
        hci_req_init(&req, hdev);
 
        memset(&hci_cp, 0, sizeof(hci_cp));
@@ -5444,7 +5474,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
                if (err)
                        goto unlock;
 
-               hci_update_page_scan(hdev, NULL);
+               hci_update_page_scan(hdev);
 
                goto added;
        }
@@ -5527,7 +5557,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
                                goto unlock;
                        }
 
-                       hci_update_page_scan(hdev, NULL);
+                       hci_update_page_scan(hdev);
 
                        device_removed(sk, hdev, &cp->addr.bdaddr,
                                       cp->addr.type);
@@ -5578,7 +5608,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
                        kfree(b);
                }
 
-               hci_update_page_scan(hdev, NULL);
+               hci_update_page_scan(hdev);
 
                list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
                        if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -5884,7 +5914,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        if (!buf)
                return -ENOMEM;
 
-       if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
+       if (memcpy_from_msg(buf, msg, msglen)) {
                err = -EFAULT;
                goto done;
        }
@@ -6068,6 +6098,11 @@ static int powered_update_hci(struct hci_dev *hdev)
                hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
        }
 
+       if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+               u8 sc = 0x01;
+               hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, sizeof(sc), &sc);
+       }
+
        if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
            lmp_bredr_capable(hdev)) {
                struct hci_cp_write_le_host_supported cp;
@@ -6105,7 +6140,7 @@ static int powered_update_hci(struct hci_dev *hdev)
 
        if (lmp_bredr_capable(hdev)) {
                write_fast_connectable(&req, false);
-               hci_update_page_scan(hdev, &req);
+               __hci_update_page_scan(&req);
                update_class(&req);
                update_name(&req);
                update_eir(&req);
@@ -6117,8 +6152,7 @@ static int powered_update_hci(struct hci_dev *hdev)
 int mgmt_powered(struct hci_dev *hdev, u8 powered)
 {
        struct cmd_lookup match = { NULL, hdev };
-       u8 status_not_powered = MGMT_STATUS_NOT_POWERED;
-       u8 zero_cod[] = { 0, 0, 0 };
+       u8 status, zero_cod[] = { 0, 0, 0 };
        int err;
 
        if (!test_bit(HCI_MGMT, &hdev->dev_flags))
@@ -6134,7 +6168,20 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
        }
 
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
-       mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status_not_powered);
+
+       /* If the power off is because of hdev unregistration let
+        * use the appropriate INVALID_INDEX status. Otherwise use
+        * NOT_POWERED. We cover both scenarios here since later in
+        * mgmt_index_removed() any hci_conn callbacks will have already
+        * been triggered, potentially causing misleading DISCONNECTED
+        * status responses.
+        */
+       if (test_bit(HCI_UNREGISTER, &hdev->dev_flags))
+               status = MGMT_STATUS_INVALID_INDEX;
+       else
+               status = MGMT_STATUS_NOT_POWERED;
+
+       mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
 
        if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
                mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
@@ -6418,16 +6465,10 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
 {
        struct hci_dev *hdev = data;
        struct mgmt_cp_unpair_device *cp = cmd->param;
-       struct mgmt_rp_unpair_device rp;
-
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
 
        device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
 
-       cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, 0);
        mgmt_pending_remove(cmd);
 }
 
@@ -6544,18 +6585,12 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                  u8 status)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_pin_code_reply rp;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
                return;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = BDADDR_BREDR;
-
-       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
-                    mgmt_status(status), &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 }
 
@@ -6563,18 +6598,12 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                      u8 status)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_pin_code_reply rp;
 
        cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
                return;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = BDADDR_BREDR;
-
-       cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
-                    mgmt_status(status), &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 }
 
@@ -6614,21 +6643,15 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                      u8 opcode)
 {
        struct pending_cmd *cmd;
-       struct mgmt_rp_user_confirm_reply rp;
-       int err;
 
        cmd = mgmt_pending_find(opcode, hdev);
        if (!cmd)
                return -ENOENT;
 
-       bacpy(&rp.addr.bdaddr, bdaddr);
-       rp.addr.type = link_to_bdaddr(link_type, addr_type);
-       err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
-                          &rp, sizeof(rp));
-
+       cmd->cmd_complete(cmd, mgmt_status(status));
        mgmt_pending_remove(cmd);
 
-       return err;
+       return 0;
 }
 
 int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -6692,8 +6715,10 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
        mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
                    cmd ? cmd->sk : NULL);
 
-       if (cmd)
-               pairing_complete(cmd, status);
+       if (cmd) {
+               cmd->cmd_complete(cmd, status);
+               mgmt_pending_remove(cmd);
+       }
 }
 
 void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
@@ -6927,14 +6952,6 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
        mgmt_pending_remove(cmd);
 }
 
-/* this is reversed hex representation of bluetooth base uuid. We need it for
- * service uuid parsing in eir.
- */
-static const u8 reverse_base_uuid[] = {
-                       0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
-                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
 static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
 {
        int i;
@@ -6966,7 +6983,7 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
                case EIR_UUID16_ALL:
                case EIR_UUID16_SOME:
                        for (i = 0; i + 3 <= field_len; i += 2) {
-                               memcpy(uuid, reverse_base_uuid, 16);
+                               memcpy(uuid, bluetooth_base_uuid, 16);
                                uuid[13] = eir[i + 3];
                                uuid[12] = eir[i + 2];
                                if (has_uuid(uuid, uuid_count, uuids))
@@ -6976,7 +6993,7 @@ static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
                case EIR_UUID32_ALL:
                case EIR_UUID32_SOME:
                        for (i = 0; i + 5 <= field_len; i += 4) {
-                               memcpy(uuid, reverse_base_uuid, 16);
+                               memcpy(uuid, bluetooth_base_uuid, 16);
                                uuid[15] = eir[i + 5];
                                uuid[14] = eir[i + 4];
                                uuid[13] = eir[i + 3];
@@ -7026,9 +7043,12 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
         * if such a RSSI threshold is specified. If a RSSI threshold has
         * been specified, then all results with a RSSI smaller than the
         * RSSI threshold will be dropped.
+        *
+        * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
+        * the results are also dropped.
         */
        if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
-           rssi < hdev->discovery.rssi)
+           (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
                return;
 
        /* Make sure that the buffer is big enough. The 5 extra bytes
@@ -7039,6 +7059,17 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        memset(buf, 0, sizeof(buf));
 
+       /* In case of device discovery with BR/EDR devices (pre 1.2), the
+        * RSSI value was reported as 0 when not available. This behavior
+        * is kept when using device discovery. This is required for full
+        * backwards compatibility with the API.
+        *
+        * However when using service discovery, the value 127 will be
+        * returned when the RSSI is not available.
+        */
+       if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+               rssi = 0;
+
        bacpy(&ev->addr.bdaddr, bdaddr);
        ev->addr.type = link_to_bdaddr(link_type, addr_type);
        ev->rssi = rssi;
@@ -7051,13 +7082,15 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                 * kept and checking possible scan response data
                 * will be skipped.
                 */
-               if (hdev->discovery.uuid_count > 0) {
+               if (hdev->discovery.uuid_count > 0)
                        match = eir_has_uuids(eir, eir_len,
                                              hdev->discovery.uuid_count,
                                              hdev->discovery.uuids);
-                       if (!match)
-                               return;
-               }
+               else
+                       match = true;
+
+               if (!match && !scan_rsp_len)
+                       return;
 
                /* Copy EIR or advertising data into event */
                memcpy(ev->eir, eir, eir_len);
@@ -7066,8 +7099,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                 * provided, results with empty EIR or advertising data
                 * should be dropped since they do not match any UUID.
                 */
-               if (hdev->discovery.uuid_count > 0)
+               if (hdev->discovery.uuid_count > 0 && !scan_rsp_len)
                        return;
+
+               match = false;
        }
 
        if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))