struct work_struct      bg_scan_update;
        struct work_struct      scan_update;
        struct work_struct      connectable_update;
-       struct work_struct      discoverable_update;
        struct delayed_work     le_scan_disable;
        struct delayed_work     le_scan_restart;
 
 void mgmt_smp_complete(struct hci_conn *conn, bool complete);
 bool mgmt_get_connectable(struct hci_dev *hdev);
 void mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status);
-void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status);
 u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev);
 void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
                            u8 instance);
 
 int hci_powered_update_sync(struct hci_dev *hdev);
 int hci_set_powered_sync(struct hci_dev *hdev, u8 val);
 
+int hci_update_discoverable_sync(struct hci_dev *hdev);
+int hci_update_discoverable(struct hci_dev *hdev);
+
 int hci_start_discovery_sync(struct hci_dev *hdev);
 int hci_stop_discovery_sync(struct hci_dev *hdev);
 
 
        return 0;
 }
 
-static void discoverable_update_work(struct work_struct *work)
-{
-       struct hci_dev *hdev = container_of(work, struct hci_dev,
-                                           discoverable_update);
-       u8 status;
-
-       hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, &status);
-       mgmt_set_discoverable_complete(hdev, status);
-}
-
 void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
                      u8 reason)
 {
        INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
        INIT_WORK(&hdev->scan_update, scan_update_work);
        INIT_WORK(&hdev->connectable_update, connectable_update_work);
-       INIT_WORK(&hdev->discoverable_update, discoverable_update_work);
        INIT_DELAYED_WORK(&hdev->discov_off, discov_off);
        INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
        INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
        cancel_work_sync(&hdev->bg_scan_update);
        cancel_work_sync(&hdev->scan_update);
        cancel_work_sync(&hdev->connectable_update);
-       cancel_work_sync(&hdev->discoverable_update);
        cancel_delayed_work_sync(&hdev->discov_off);
        cancel_delayed_work_sync(&hdev->le_scan_disable);
        cancel_delayed_work_sync(&hdev->le_scan_restart);
 
        hdev->advertising_paused = false;
        if (hdev->advertising_old_state) {
                hci_dev_set_flag(hdev, HCI_ADVERTISING);
-               queue_work(hdev->req_workqueue, &hdev->discoverable_update);
                hdev->advertising_old_state = 0;
        }
 
        return hci_power_off_sync(hdev);
 }
 
+static int hci_write_iac_sync(struct hci_dev *hdev)
+{
+       struct hci_cp_write_current_iac_lap cp;
+
+       if (!hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
+               return 0;
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
+               /* Limited discoverable mode */
+               cp.num_iac = min_t(u8, hdev->num_iac, 2);
+               cp.iac_lap[0] = 0x00;   /* LIAC */
+               cp.iac_lap[1] = 0x8b;
+               cp.iac_lap[2] = 0x9e;
+               cp.iac_lap[3] = 0x33;   /* GIAC */
+               cp.iac_lap[4] = 0x8b;
+               cp.iac_lap[5] = 0x9e;
+       } else {
+               /* General discoverable mode */
+               cp.num_iac = 1;
+               cp.iac_lap[0] = 0x33;   /* GIAC */
+               cp.iac_lap[1] = 0x8b;
+               cp.iac_lap[2] = 0x9e;
+       }
+
+       return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CURRENT_IAC_LAP,
+                                    (cp.num_iac * 3) + 1, &cp,
+                                    HCI_CMD_TIMEOUT);
+}
+
+int hci_update_discoverable_sync(struct hci_dev *hdev)
+{
+       int err = 0;
+
+       if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
+               err = hci_write_iac_sync(hdev);
+               if (err)
+                       return err;
+
+               err = hci_update_scan_sync(hdev);
+               if (err)
+                       return err;
+
+               err = hci_update_class_sync(hdev);
+               if (err)
+                       return err;
+       }
+
+       /* Advertising instances don't use the global discoverable setting, so
+        * only update AD if advertising was enabled using Set Advertising.
+        */
+       if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
+               err = hci_update_adv_data_sync(hdev, 0x00);
+               if (err)
+                       return err;
+
+               /* Discoverable mode affects the local advertising
+                * address in limited privacy mode.
+                */
+               if (hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY)) {
+                       if (ext_adv_capable(hdev))
+                               err = hci_start_ext_adv_sync(hdev, 0x00);
+                       else
+                               err = hci_enable_advertising_sync(hdev);
+               }
+       }
+
+       return err;
+}
+
+static int update_discoverable_sync(struct hci_dev *hdev, void *data)
+{
+       return hci_update_discoverable_sync(hdev);
+}
+
+int hci_update_discoverable(struct hci_dev *hdev)
+{
+       /* Only queue if it would have any effect */
+       if (hdev_is_powered(hdev) &&
+           hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+           hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
+           hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
+               return hci_cmd_sync_queue(hdev, update_discoverable_sync, NULL,
+                                         NULL);
+
+       return 0;
+}
+
 static int hci_inquiry_sync(struct hci_dev *hdev, u8 length)
 {
        const u8 giac[3] = { 0x33, 0x8b, 0x9e };
 
                return MGMT_STATUS_SUCCESS;
 }
 
-void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status)
+static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
+                                          int err)
 {
-       struct mgmt_pending_cmd *cmd;
+       struct mgmt_pending_cmd *cmd = data;
 
-       bt_dev_dbg(hdev, "status 0x%02x", status);
+       bt_dev_dbg(hdev, "err %d", err);
 
        hci_dev_lock(hdev);
 
-       cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
-       if (!cmd)
-               goto unlock;
-
-       if (status) {
-               u8 mgmt_err = mgmt_status(status);
+       if (err) {
+               u8 mgmt_err = mgmt_status(err);
                mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
                hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
-               goto remove_cmd;
+               goto done;
        }
 
        if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
        send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
        new_settings(hdev, cmd->sk);
 
-remove_cmd:
-       mgmt_pending_remove(cmd);
-
-unlock:
+done:
+       mgmt_pending_free(cmd);
        hci_dev_unlock(hdev);
 }
 
+static int set_discoverable_sync(struct hci_dev *hdev, void *data)
+{
+       BT_DBG("%s", hdev->name);
+
+       return hci_update_discoverable_sync(hdev);
+}
+
 static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                            u16 len)
 {
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
+       cmd = mgmt_pending_new(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        else
                hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
 
-       queue_work(hdev->req_workqueue, &hdev->discoverable_update);
-       err = 0;
+       err = hci_cmd_sync_queue(hdev, set_discoverable_sync, cmd,
+                                mgmt_set_discoverable_complete);
 
 failed:
        hci_dev_unlock(hdev);
                /* In limited privacy mode the change of bondable mode
                 * may affect the local advertising address.
                 */
-               if (hdev_is_powered(hdev) &&
-                   hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
-                   hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
-                   hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
-                       queue_work(hdev->req_workqueue,
-                                  &hdev->discoverable_update);
+               hci_update_discoverable(hdev);
 
                err = new_settings(hdev, sk);
        }