aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2015-11-25 16:15:44 +0200
committerMarcel Holtmann <marcel@holtmann.org>2015-12-10 00:51:49 +0100
commit2ff13894cfb877cb3d02d96a8402202f0a6f3efd (patch)
treefeb1092db11f30427f75af9694909fbdf6178c9a /net/bluetooth/mgmt.c
parentBluetooth: Move fast connectable code to hci_request.c (diff)
downloadlinux-dev-2ff13894cfb877cb3d02d96a8402202f0a6f3efd.tar.xz
linux-dev-2ff13894cfb877cb3d02d96a8402202f0a6f3efd.zip
Bluetooth: Perform HCI update for power on synchronously
The request to update HCI during power on is always coming either from hdev->req_workqueue or through an ioctl, so it's safe to use hci_req_sync for it. This way we also eliminate potential races with incoming mgmt commands or other actions while powering on. Part of this refactoring is the splitting of mgmt_powered() into mgmt_power_on() and __mgmt_power_off() functions. The main reason is the different requirements as far as hdev locking is concerned, as highlighted with the __ prefix of the power off API. Since the power on in the case of clearing the AUTO_OFF flag cannot be done synchronously in the set_powered mgmt handler, the hci_power_on work callback is extended to cover this (which also simplifies the set_powered helper a lot). Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r--net/bluetooth/mgmt.c136
1 files changed, 8 insertions, 128 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0a7e6f4de383..468402ad933c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -961,17 +961,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
- cancel_delayed_work(&hdev->power_off);
-
- if (cp->val) {
- mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
- data, len);
- err = mgmt_powered(hdev, 1);
- goto failed;
- }
- }
-
if (!!cp->val == hdev_is_powered(hdev)) {
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
goto failed;
@@ -6434,139 +6423,33 @@ static void restart_le_actions(struct hci_dev *hdev)
}
}
-static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_power_on(struct hci_dev *hdev, int err)
{
struct cmd_lookup match = { NULL, hdev };
- BT_DBG("status 0x%02x", status);
+ BT_DBG("err %d", err);
- if (!status) {
+ hci_dev_lock(hdev);
+
+ if (!err) {
restart_le_actions(hdev);
hci_update_background_scan(hdev);
}
- hci_dev_lock(hdev);
-
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
new_settings(hdev, match.sk);
- hci_dev_unlock(hdev);
-
if (match.sk)
sock_put(match.sk);
-}
-static int powered_update_hci(struct hci_dev *hdev)
-{
- struct hci_request req;
- struct adv_info *adv_instance;
- u8 link_sec;
-
- hci_req_init(&req, 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);
- }
-
- return hci_req_run(&req, powered_complete);
+ hci_dev_unlock(hdev);
}
-int mgmt_powered(struct hci_dev *hdev, u8 powered)
+void __mgmt_power_off(struct hci_dev *hdev)
{
struct cmd_lookup match = { NULL, hdev };
u8 status, zero_cod[] = { 0, 0, 0 };
- int err;
-
- if (!hci_dev_test_flag(hdev, HCI_MGMT))
- return 0;
-
- if (powered) {
- /* 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);
-
- if (powered_update_hci(hdev) == 0)
- return 0;
-
- mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp,
- &match);
- goto new_settings;
- }
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
@@ -6588,13 +6471,10 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod), NULL);
-new_settings:
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
void mgmt_set_powered_failed(struct hci_dev *hdev, int err)