Bluetooth: Simplify usage of the enable_advertising function
[pandora-kernel.git] / net / bluetooth / mgmt.c
index fb1aa0c..68c0698 100644 (file)
@@ -89,6 +89,10 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_ADD_DEVICE,
        MGMT_OP_REMOVE_DEVICE,
        MGMT_OP_LOAD_CONN_PARAM,
+       MGMT_OP_READ_UNCONF_INDEX_LIST,
+       MGMT_OP_READ_CONFIG_INFO,
+       MGMT_OP_SET_EXTERNAL_CONFIG,
+       MGMT_OP_SET_PUBLIC_ADDRESS,
 };
 
 static const u16 mgmt_events[] = {
@@ -118,6 +122,9 @@ static const u16 mgmt_events[] = {
        MGMT_EV_DEVICE_ADDED,
        MGMT_EV_DEVICE_REMOVED,
        MGMT_EV_NEW_CONN_PARAM,
+       MGMT_EV_UNCONF_INDEX_ADDED,
+       MGMT_EV_UNCONF_INDEX_REMOVED,
+       MGMT_EV_NEW_CONFIG_OPTIONS,
 };
 
 #define CACHE_TIMEOUT  msecs_to_jiffies(2 * 1000)
@@ -207,6 +214,36 @@ static u8 mgmt_status(u8 hci_status)
        return MGMT_STATUS_FAILED;
 }
 
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
+                     struct sock *skip_sk)
+{
+       struct sk_buff *skb;
+       struct mgmt_hdr *hdr;
+
+       skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = (void *) skb_put(skb, sizeof(*hdr));
+       hdr->opcode = cpu_to_le16(event);
+       if (hdev)
+               hdr->index = cpu_to_le16(hdev->id);
+       else
+               hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+       hdr->len = cpu_to_le16(data_len);
+
+       if (data)
+               memcpy(skb_put(skb, data_len), data, data_len);
+
+       /* Time stamp */
+       __net_timestamp(skb);
+
+       hci_send_to_control(skb, skip_sk);
+       kfree_skb(skb);
+
+       return 0;
+}
+
 static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
 {
        struct sk_buff *skb;
@@ -334,7 +371,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (d->dev_type == HCI_BREDR)
+               if (d->dev_type == HCI_BREDR &&
+                   !test_bit(HCI_UNCONFIGURED, &d->dev_flags))
                        count++;
        }
 
@@ -347,16 +385,19 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (test_bit(HCI_SETUP, &d->dev_flags))
-                       continue;
-
-               if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+               if (test_bit(HCI_SETUP, &d->dev_flags) ||
+                   test_bit(HCI_CONFIG, &d->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &d->dev_flags))
                        continue;
 
+               /* Devices marked as raw-only are neither configured
+                * nor unconfigured controllers.
+                */
                if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
                        continue;
 
-               if (d->dev_type == HCI_BREDR) {
+               if (d->dev_type == HCI_BREDR &&
+                   !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
                        rp->index[count++] = cpu_to_le16(d->id);
                        BT_DBG("Added hci%u", d->id);
                }
@@ -375,6 +416,138 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
        return err;
 }
 
+static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev,
+                                 void *data, u16 data_len)
+{
+       struct mgmt_rp_read_unconf_index_list *rp;
+       struct hci_dev *d;
+       size_t rp_len;
+       u16 count;
+       int err;
+
+       BT_DBG("sock %p", sk);
+
+       read_lock(&hci_dev_list_lock);
+
+       count = 0;
+       list_for_each_entry(d, &hci_dev_list, list) {
+               if (d->dev_type == HCI_BREDR &&
+                   test_bit(HCI_UNCONFIGURED, &d->dev_flags))
+                       count++;
+       }
+
+       rp_len = sizeof(*rp) + (2 * count);
+       rp = kmalloc(rp_len, GFP_ATOMIC);
+       if (!rp) {
+               read_unlock(&hci_dev_list_lock);
+               return -ENOMEM;
+       }
+
+       count = 0;
+       list_for_each_entry(d, &hci_dev_list, list) {
+               if (test_bit(HCI_SETUP, &d->dev_flags) ||
+                   test_bit(HCI_CONFIG, &d->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+                       continue;
+
+               /* Devices marked as raw-only are neither configured
+                * nor unconfigured controllers.
+                */
+               if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
+                       continue;
+
+               if (d->dev_type == HCI_BREDR &&
+                   test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
+                       rp->index[count++] = cpu_to_le16(d->id);
+                       BT_DBG("Added hci%u", d->id);
+               }
+       }
+
+       rp->num_controllers = cpu_to_le16(count);
+       rp_len = sizeof(*rp) + (2 * count);
+
+       read_unlock(&hci_dev_list_lock);
+
+       err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_UNCONF_INDEX_LIST,
+                          0, rp, rp_len);
+
+       kfree(rp);
+
+       return err;
+}
+
+static bool is_configured(struct hci_dev *hdev)
+{
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
+           !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+               return false;
+
+       if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
+           !bacmp(&hdev->public_addr, BDADDR_ANY))
+               return false;
+
+       return true;
+}
+
+static __le32 get_missing_options(struct hci_dev *hdev)
+{
+       u32 options = 0;
+
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
+           !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+               options |= MGMT_OPTION_EXTERNAL_CONFIG;
+
+       if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
+           !bacmp(&hdev->public_addr, BDADDR_ANY))
+               options |= MGMT_OPTION_PUBLIC_ADDRESS;
+
+       return cpu_to_le32(options);
+}
+
+static int new_options(struct hci_dev *hdev, struct sock *skip)
+{
+       __le32 options = get_missing_options(hdev);
+
+       return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
+                         sizeof(options), skip);
+}
+
+static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
+{
+       __le32 options = get_missing_options(hdev);
+
+       return cmd_complete(sk, hdev->id, opcode, 0, &options,
+                           sizeof(options));
+}
+
+static int read_config_info(struct sock *sk, struct hci_dev *hdev,
+                           void *data, u16 data_len)
+{
+       struct mgmt_rp_read_config_info rp;
+       u32 options = 0;
+
+       BT_DBG("sock %p %s", sk, hdev->name);
+
+       hci_dev_lock(hdev);
+
+       memset(&rp, 0, sizeof(rp));
+       rp.manufacturer = cpu_to_le16(hdev->manufacturer);
+
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks))
+               options |= MGMT_OPTION_EXTERNAL_CONFIG;
+
+       if (hdev->set_bdaddr)
+               options |= MGMT_OPTION_PUBLIC_ADDRESS;
+
+       rp.supported_options = cpu_to_le32(options);
+       rp.missing_options = get_missing_options(hdev);
+
+       hci_dev_unlock(hdev);
+
+       return cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, &rp,
+                           sizeof(rp));
+}
+
 static u32 get_supported_settings(struct hci_dev *hdev)
 {
        u32 settings = 0;
@@ -407,6 +580,10 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                settings |= MGMT_SETTING_PRIVACY;
        }
 
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
+           hdev->set_bdaddr)
+               settings |= MGMT_SETTING_CONFIGURATION;
+
        return settings;
 }
 
@@ -862,6 +1039,13 @@ static bool get_connectable(struct hci_dev *hdev)
        return test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
 }
 
+static void disable_advertising(struct hci_request *req)
+{
+       u8 enable = 0x00;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
 static void enable_advertising(struct hci_request *req)
 {
        struct hci_dev *hdev = req->hdev;
@@ -869,12 +1053,18 @@ static void enable_advertising(struct hci_request *req)
        u8 own_addr_type, enable = 0x01;
        bool connectable;
 
-       /* Clear the HCI_ADVERTISING bit temporarily so that the
+       if (hci_conn_num(hdev, LE_LINK) > 0)
+               return;
+
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+               disable_advertising(req);
+
+       /* Clear the HCI_LE_ADV bit temporarily so that the
         * hci_update_random_address knows that it's safe to go ahead
         * and write a new random address. The flag will be set back on
         * as soon as the SET_ADV_ENABLE HCI command completes.
         */
-       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+       clear_bit(HCI_LE_ADV, &hdev->dev_flags);
 
        connectable = get_connectable(hdev);
 
@@ -897,13 +1087,6 @@ static void enable_advertising(struct hci_request *req)
        hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
 }
 
-static void disable_advertising(struct hci_request *req)
-{
-       u8 enable = 0x00;
-
-       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
 static void service_cache_off(struct work_struct *work)
 {
        struct hci_dev *hdev = container_of(work, struct hci_dev,
@@ -935,19 +1118,14 @@ static void rpa_expired(struct work_struct *work)
 
        set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
 
-       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags) ||
-           hci_conn_num(hdev, LE_LINK) > 0)
+       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                return;
 
        /* The generation of a new RPA and programming it into the
         * controller happens in the enable_advertising() function.
         */
-
        hci_req_init(&req, hdev);
-
-       disable_advertising(&req);
        enable_advertising(&req);
-
        hci_req_run(&req, NULL);
 }
 
@@ -1229,36 +1407,6 @@ failed:
        return err;
 }
 
-static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
-                     struct sock *skip_sk)
-{
-       struct sk_buff *skb;
-       struct mgmt_hdr *hdr;
-
-       skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
-       if (!skb)
-               return -ENOMEM;
-
-       hdr = (void *) skb_put(skb, sizeof(*hdr));
-       hdr->opcode = cpu_to_le16(event);
-       if (hdev)
-               hdr->index = cpu_to_le16(hdev->id);
-       else
-               hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
-       hdr->len = cpu_to_le16(data_len);
-
-       if (data)
-               memcpy(skb_put(skb, data_len), data, data_len);
-
-       /* Time stamp */
-       __net_timestamp(skb);
-
-       hci_send_to_control(skb, skip_sk);
-       kfree_skb(skb);
-
-       return 0;
-}
-
 static int new_settings(struct hci_dev *hdev, struct sock *skip)
 {
        __le32 ev;
@@ -1603,8 +1751,10 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
 
        send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
 
-       if (changed)
+       if (changed) {
                new_settings(hdev, cmd->sk);
+               hci_update_background_scan(hdev);
+       }
 
 remove_cmd:
        mgmt_pending_remove(cmd);
@@ -1715,10 +1865,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                write_fast_connectable(&req, false);
 
        if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) &&
-           hci_conn_num(hdev, LE_LINK) == 0) {
-               disable_advertising(&req);
+           !test_bit(HCI_LE_ADV, &hdev->dev_flags))
                enable_advertising(&req);
-       }
 
        err = hci_req_run(&req, set_connectable_complete);
        if (err < 0) {
@@ -2003,6 +2151,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
                update_scan_rsp_data(&req);
                hci_req_run(&req, NULL);
 
+               hci_update_background_scan(hdev);
+
                hci_dev_unlock(hdev);
        }
 }
@@ -2403,6 +2553,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                          u16 len)
 {
        struct mgmt_cp_load_link_keys *cp = data;
+       const u16 max_key_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_link_key_info));
        u16 key_count, expected_len;
        bool changed;
        int i;
@@ -2414,6 +2566,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        key_count = __le16_to_cpu(cp->key_count);
+       if (key_count > max_key_count) {
+               BT_ERR("load_link_keys: too big key_count value %u",
+                      key_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + key_count *
                                        sizeof(struct mgmt_link_key_info);
@@ -2960,8 +3118,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                 */
                hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
 
+               /* Request a connection with master = true role */
                conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
-                                     sec_level, auth_type);
+                                     sec_level, HCI_LE_CONN_TIMEOUT, true);
        }
 
        if (IS_ERR(conn)) {
@@ -3874,6 +4033,11 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status)
                return;
        }
 
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+               set_bit(HCI_ADVERTISING, &hdev->dev_flags);
+       else
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
        mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
                             &match);
 
@@ -4503,6 +4667,8 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                     u16 len)
 {
        struct mgmt_cp_load_irks *cp = cp_data;
+       const u16 max_irk_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_irk_info));
        u16 irk_count, expected_len;
        int i, err;
 
@@ -4513,6 +4679,11 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        irk_count = __le16_to_cpu(cp->irk_count);
+       if (irk_count > max_irk_count) {
+               BT_ERR("load_irks: too big irk_count value %u", irk_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
        if (expected_len != len) {
@@ -4582,6 +4753,8 @@ 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;
 
@@ -4592,6 +4765,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        key_count = __le16_to_cpu(cp->key_count);
+       if (key_count > max_key_count) {
+               BT_ERR("load_ltks: too big key_count value %u", key_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + key_count *
                                        sizeof(struct mgmt_ltk_info);
@@ -5111,12 +5289,15 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
                        goto unlock;
                }
 
-               hci_pend_le_conn_del(hdev, &cp->addr.bdaddr, addr_type);
+               list_del(&params->action);
                list_del(&params->list);
                kfree(params);
+               hci_update_background_scan(hdev);
 
                device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
        } else {
+               struct hci_conn_params *p, *tmp;
+
                if (cp->addr.type) {
                        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
                                           MGMT_STATUS_INVALID_PARAMS,
@@ -5124,7 +5305,18 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
                        goto unlock;
                }
 
-               hci_conn_params_clear_enabled(hdev);
+               list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
+                       if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
+                               continue;
+                       device_removed(sk, hdev, &p->addr, p->addr_type);
+                       list_del(&p->action);
+                       list_del(&p->list);
+                       kfree(p);
+               }
+
+               BT_DBG("All LE connection parameters were removed");
+
+               hci_update_background_scan(hdev);
        }
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
@@ -5139,6 +5331,8 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                           u16 len)
 {
        struct mgmt_cp_load_conn_param *cp = data;
+       const u16 max_param_count = ((U16_MAX - sizeof(*cp)) /
+                                    sizeof(struct mgmt_conn_param));
        u16 param_count, expected_len;
        int i;
 
@@ -5147,6 +5341,12 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        param_count = __le16_to_cpu(cp->param_count);
+       if (param_count > max_param_count) {
+               BT_ERR("load_conn_param: too big param_count value %u",
+                      param_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + param_count *
                                        sizeof(struct mgmt_conn_param);
@@ -5212,6 +5412,116 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
        return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0);
 }
 
+static int set_external_config(struct sock *sk, struct hci_dev *hdev,
+                              void *data, u16 len)
+{
+       struct mgmt_cp_set_external_config *cp = data;
+       bool changed;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                 MGMT_STATUS_REJECTED);
+
+       if (cp->config != 0x00 && cp->config != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                   MGMT_STATUS_INVALID_PARAMS);
+
+       if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       if (cp->config)
+               changed = !test_and_set_bit(HCI_EXT_CONFIGURED,
+                                           &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_EXT_CONFIGURED,
+                                            &hdev->dev_flags);
+
+       err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (!changed)
+               goto unlock;
+
+       err = new_options(hdev, sk);
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) {
+               mgmt_index_removed(hdev);
+
+               if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+                       set_bit(HCI_CONFIG, &hdev->dev_flags);
+                       set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+
+                       queue_work(hdev->req_workqueue, &hdev->power_on);
+               } else {
+                       set_bit(HCI_RAW, &hdev->flags);
+                       mgmt_index_added(hdev);
+               }
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int set_public_address(struct sock *sk, struct hci_dev *hdev,
+                             void *data, u16 len)
+{
+       struct mgmt_cp_set_public_address *cp = data;
+       bool changed;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_REJECTED);
+
+       if (!bacmp(&cp->bdaddr, BDADDR_ANY))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       if (!hdev->set_bdaddr)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       changed = !!bacmp(&hdev->public_addr, &cp->bdaddr);
+       bacpy(&hdev->public_addr, &cp->bdaddr);
+
+       err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (!changed)
+               goto unlock;
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               err = new_options(hdev, sk);
+
+       if (is_configured(hdev)) {
+               mgmt_index_removed(hdev);
+
+               clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+
+               set_bit(HCI_CONFIG, &hdev->dev_flags);
+               set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+
+               queue_work(hdev->req_workqueue, &hdev->power_on);
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static const struct mgmt_handler {
        int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
                     u16 data_len);
@@ -5271,7 +5581,11 @@ static const struct mgmt_handler {
        { get_clock_info,         false, MGMT_GET_CLOCK_INFO_SIZE },
        { add_device,             false, MGMT_ADD_DEVICE_SIZE },
        { remove_device,          false, MGMT_REMOVE_DEVICE_SIZE },
-       { load_conn_param,        true, MGMT_LOAD_CONN_PARAM_SIZE },
+       { load_conn_param,        true,  MGMT_LOAD_CONN_PARAM_SIZE },
+       { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE },
+       { read_config_info,       false, MGMT_READ_CONFIG_INFO_SIZE },
+       { set_external_config,    false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
+       { set_public_address,     false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
 };
 
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
@@ -5317,8 +5631,17 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                }
 
                if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
-                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) ||
-                   test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) {
+                   test_bit(HCI_CONFIG, &hdev->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+                       err = cmd_status(sk, index, opcode,
+                                        MGMT_STATUS_INVALID_INDEX);
+                       goto done;
+               }
+
+               if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
+                   opcode != MGMT_OP_READ_CONFIG_INFO &&
+                   opcode != MGMT_OP_SET_EXTERNAL_CONFIG &&
+                   opcode != MGMT_OP_SET_PUBLIC_ADDRESS) {
                        err = cmd_status(sk, index, opcode,
                                         MGMT_STATUS_INVALID_INDEX);
                        goto done;
@@ -5333,8 +5656,15 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                goto done;
        }
 
-       if ((hdev && opcode < MGMT_OP_READ_INFO) ||
-           (!hdev && opcode >= MGMT_OP_READ_INFO)) {
+       if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST ||
+                    opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) {
+               err = cmd_status(sk, index, opcode,
+                                MGMT_STATUS_INVALID_INDEX);
+               goto done;
+       }
+
+       if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST &&
+                     opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) {
                err = cmd_status(sk, index, opcode,
                                 MGMT_STATUS_INVALID_INDEX);
                goto done;
@@ -5373,7 +5703,13 @@ void mgmt_index_added(struct hci_dev *hdev)
        if (hdev->dev_type != HCI_BREDR)
                return;
 
-       mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               return;
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL);
+       else
+               mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
 }
 
 void mgmt_index_removed(struct hci_dev *hdev)
@@ -5383,35 +5719,40 @@ void mgmt_index_removed(struct hci_dev *hdev)
        if (hdev->dev_type != HCI_BREDR)
                return;
 
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               return;
+
        mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
 
-       mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
+       else
+               mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
 /* This function requires the caller holds hdev->lock */
-static void restart_le_auto_conns(struct hci_dev *hdev)
+static void restart_le_actions(struct hci_dev *hdev)
 {
        struct hci_conn_params *p;
-       bool added = false;
 
        list_for_each_entry(p, &hdev->le_conn_params, list) {
-               if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) {
-                       hci_pend_le_conn_add(hdev, &p->addr, p->addr_type);
-                       added = true;
+               /* Needed for AUTO_OFF case where might not "really"
+                * have been powered off.
+                */
+               list_del_init(&p->action);
+
+               switch (p->auto_connect) {
+               case HCI_AUTO_CONN_ALWAYS:
+                       list_add(&p->action, &hdev->pend_le_conns);
+                       break;
+               case HCI_AUTO_CONN_REPORT:
+                       list_add(&p->action, &hdev->pend_le_reports);
+                       break;
+               default:
+                       break;
                }
        }
 
-       /* Calling hci_pend_le_conn_add will actually already trigger
-        * background scanning when needed. So no need to trigger it
-        * just another time.
-        *
-        * This check is here to avoid an unneeded restart of the
-        * passive scanning. Since this is during the controller
-        * power up phase the duplicate filtering is not an issue.
-        */
-       if (added)
-               return;
-
        hci_update_background_scan(hdev);
 }
 
@@ -5423,7 +5764,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
 
        hci_dev_lock(hdev);
 
-       restart_le_auto_conns(hdev);
+       restart_le_actions(hdev);
 
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
@@ -5641,18 +5982,6 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
                new_settings(hdev, NULL);
 }
 
-void mgmt_advertising(struct hci_dev *hdev, u8 advertising)
-{
-       /* Powering off may stop advertising - don't let that interfere */
-       if (!advertising && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-               return;
-
-       if (advertising)
-               set_bit(HCI_ADVERTISING, &hdev->dev_flags);
-       else
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-}
-
 void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
 {
        u8 mgmt_err = mgmt_status(status);
@@ -5795,6 +6124,9 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
 {
        struct mgmt_ev_new_conn_param ev;
 
+       if (!hci_is_identity_address(bdaddr, bdaddr_type))
+               return;
+
        memset(&ev, 0, sizeof(ev));
        bacpy(&ev.addr.bdaddr, bdaddr);
        ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type);
@@ -6381,11 +6713,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
-       struct smp_irk *irk;
        size_t ev_size;
 
-       if (!hci_discovery_active(hdev))
-               return;
+       /* Don't send events for a non-kernel initiated discovery. With
+        * LE one exception is if we have pend_le_reports > 0 in which
+        * case we're doing passive scanning and want these events.
+        */
+       if (!hci_discovery_active(hdev)) {
+               if (link_type == ACL_LINK)
+                       return;
+               if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports))
+                       return;
+       }
 
        /* Make sure that the buffer is big enough. The 5 extra bytes
         * are for the potential CoD field.
@@ -6395,15 +6734,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        memset(buf, 0, sizeof(buf));
 
-       irk = hci_get_irk(hdev, bdaddr, addr_type);
-       if (irk) {
-               bacpy(&ev->addr.bdaddr, &irk->bdaddr);
-               ev->addr.type = link_to_bdaddr(link_type, irk->addr_type);
-       } else {
-               bacpy(&ev->addr.bdaddr, bdaddr);
-               ev->addr.type = link_to_bdaddr(link_type, addr_type);
-       }
-
+       bacpy(&ev->addr.bdaddr, bdaddr);
+       ev->addr.type = link_to_bdaddr(link_type, addr_type);
        ev->rssi = rssi;
        ev->flags = cpu_to_le32(flags);
 
@@ -6476,32 +6808,16 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
 static void adv_enable_complete(struct hci_dev *hdev, u8 status)
 {
        BT_DBG("%s status %u", hdev->name, status);
-
-       /* Clear the advertising mgmt setting if we failed to re-enable it */
-       if (status) {
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-               new_settings(hdev, NULL);
-       }
 }
 
 void mgmt_reenable_advertising(struct hci_dev *hdev)
 {
        struct hci_request req;
 
-       if (hci_conn_num(hdev, LE_LINK) > 0)
-               return;
-
        if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                return;
 
        hci_req_init(&req, hdev);
        enable_advertising(&req);
-
-       /* If this fails we have no option but to let user space know
-        * that we've disabled advertising.
-        */
-       if (hci_req_run(&req, adv_enable_complete) < 0) {
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-               new_settings(hdev, NULL);
-       }
+       hci_req_run(&req, adv_enable_complete);
 }