Bluetooth: Perform HCI update for power on synchronously
[deliverable/linux.git] / net / bluetooth / hci_request.c
index 0abd83ddd4fb19c21ddbbfe488556b195d0f4787..7cc24f1448bd3d902aef81fa1f8d9352710ac325 100644 (file)
@@ -2181,6 +2181,106 @@ static void discov_off(struct work_struct *work)
        mgmt_new_settings(hdev);
 }
 
+static int powered_update_hci(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct adv_info *adv_instance;
+       u8 link_sec;
+
+       hci_dev_lock(hdev);
+
+       if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
+           !lmp_host_ssp_capable(hdev)) {
+               u8 mode = 0x01;
+
+               hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+
+               if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+                       u8 support = 0x01;
+
+                       hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
+                                   sizeof(support), &support);
+               }
+       }
+
+       if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+           lmp_bredr_capable(hdev)) {
+               struct hci_cp_write_le_host_supported cp;
+
+               cp.le = 0x01;
+               cp.simul = 0x00;
+
+               /* Check first if we already have the right
+                * host state (host features set)
+                */
+               if (cp.le != lmp_host_le_capable(hdev) ||
+                   cp.simul != lmp_host_le_br_capable(hdev))
+                       hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+                                   sizeof(cp), &cp);
+       }
+
+       if (lmp_le_capable(hdev)) {
+               /* Make sure the controller has a good default for
+                * advertising data. This also applies to the case
+                * where BR/EDR was toggled during the AUTO_OFF phase.
+                */
+               if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+                   (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+                    !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) {
+                       __hci_req_update_adv_data(req, HCI_ADV_CURRENT);
+                       __hci_req_update_scan_rsp_data(req, HCI_ADV_CURRENT);
+               }
+
+               if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
+                   hdev->cur_adv_instance == 0x00 &&
+                   !list_empty(&hdev->adv_instances)) {
+                       adv_instance = list_first_entry(&hdev->adv_instances,
+                                                       struct adv_info, list);
+                       hdev->cur_adv_instance = adv_instance->instance;
+               }
+
+               if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+                       __hci_req_enable_advertising(req);
+               else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
+                        hdev->cur_adv_instance)
+                       __hci_req_schedule_adv_instance(req,
+                                                       hdev->cur_adv_instance,
+                                                       true);
+       }
+
+       link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
+       if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
+               hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE,
+                           sizeof(link_sec), &link_sec);
+
+       if (lmp_bredr_capable(hdev)) {
+               if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
+                       __hci_req_write_fast_connectable(req, true);
+               else
+                       __hci_req_write_fast_connectable(req, false);
+               __hci_req_update_scan(req);
+               __hci_req_update_class(req);
+               __hci_req_update_name(req);
+               __hci_req_update_eir(req);
+       }
+
+       hci_dev_unlock(hdev);
+       return 0;
+}
+
+int __hci_req_hci_power_on(struct hci_dev *hdev)
+{
+       /* Register the available SMP channels (BR/EDR and LE) only when
+        * successfully powering on the controller. This late
+        * registration is required so that LE SMP can clearly decide if
+        * the public address or static address is used.
+        */
+       smp_register(hdev);
+
+       return __hci_req_sync(hdev, powered_update_hci, 0, HCI_CMD_TIMEOUT,
+                             NULL);
+}
+
 void hci_request_setup(struct hci_dev *hdev)
 {
        INIT_WORK(&hdev->discov_update, discov_update);
This page took 0.025206 seconds and 5 git commands to generate.