diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2015-11-25 09:15:44 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2015-12-09 18:51:49 -0500 |
commit | 2ff13894cfb877cb3d02d96a8402202f0a6f3efd (patch) | |
tree | feb1092db11f30427f75af9694909fbdf6178c9a /net/bluetooth | |
parent | bf943cbf76ecd3b9838a80d5e08777b0f4ccc665 (diff) |
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')
-rw-r--r-- | net/bluetooth/hci_core.c | 21 | ||||
-rw-r--r-- | net/bluetooth/hci_request.c | 100 | ||||
-rw-r--r-- | net/bluetooth/hci_request.h | 2 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 136 |
4 files changed, 126 insertions, 133 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 484c75f3332c..eac3f6fa1272 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
@@ -1399,10 +1399,10 @@ static int hci_dev_do_open(struct hci_dev *hdev) | |||
1399 | !hci_dev_test_flag(hdev, HCI_CONFIG) && | 1399 | !hci_dev_test_flag(hdev, HCI_CONFIG) && |
1400 | !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && | 1400 | !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && |
1401 | !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && | 1401 | !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) && |
1402 | hci_dev_test_flag(hdev, HCI_MGMT) && | ||
1402 | hdev->dev_type == HCI_BREDR) { | 1403 | hdev->dev_type == HCI_BREDR) { |
1403 | hci_dev_lock(hdev); | 1404 | ret = __hci_req_hci_power_on(hdev); |
1404 | mgmt_powered(hdev, 1); | 1405 | mgmt_power_on(hdev, ret); |
1405 | hci_dev_unlock(hdev); | ||
1406 | } | 1406 | } |
1407 | } else { | 1407 | } else { |
1408 | /* Init failed, cleanup */ | 1408 | /* Init failed, cleanup */ |
@@ -1559,8 +1559,9 @@ int hci_dev_do_close(struct hci_dev *hdev) | |||
1559 | 1559 | ||
1560 | auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF); | 1560 | auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF); |
1561 | 1561 | ||
1562 | if (!auto_off && hdev->dev_type == HCI_BREDR) | 1562 | if (!auto_off && hdev->dev_type == HCI_BREDR && |
1563 | mgmt_powered(hdev, 0); | 1563 | hci_dev_test_flag(hdev, HCI_MGMT)) |
1564 | __mgmt_power_off(hdev); | ||
1564 | 1565 | ||
1565 | hci_inquiry_cache_flush(hdev); | 1566 | hci_inquiry_cache_flush(hdev); |
1566 | hci_pend_le_actions_clear(hdev); | 1567 | hci_pend_le_actions_clear(hdev); |
@@ -2013,6 +2014,16 @@ static void hci_power_on(struct work_struct *work) | |||
2013 | 2014 | ||
2014 | BT_DBG("%s", hdev->name); | 2015 | BT_DBG("%s", hdev->name); |
2015 | 2016 | ||
2017 | if (test_bit(HCI_UP, &hdev->flags) && | ||
2018 | hci_dev_test_flag(hdev, HCI_MGMT) && | ||
2019 | hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { | ||
2020 | hci_req_sync_lock(hdev); | ||
2021 | err = __hci_req_hci_power_on(hdev); | ||
2022 | hci_req_sync_unlock(hdev); | ||
2023 | mgmt_power_on(hdev, err); | ||
2024 | return; | ||
2025 | } | ||
2026 | |||
2016 | err = hci_dev_do_open(hdev); | 2027 | err = hci_dev_do_open(hdev); |
2017 | if (err < 0) { | 2028 | if (err < 0) { |
2018 | hci_dev_lock(hdev); | 2029 | hci_dev_lock(hdev); |
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 0abd83ddd4fb..7cc24f1448bd 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c | |||
@@ -2181,6 +2181,106 @@ static void discov_off(struct work_struct *work) | |||
2181 | mgmt_new_settings(hdev); | 2181 | mgmt_new_settings(hdev); |
2182 | } | 2182 | } |
2183 | 2183 | ||
2184 | static int powered_update_hci(struct hci_request *req, unsigned long opt) | ||
2185 | { | ||
2186 | struct hci_dev *hdev = req->hdev; | ||
2187 | struct adv_info *adv_instance; | ||
2188 | u8 link_sec; | ||
2189 | |||
2190 | hci_dev_lock(hdev); | ||
2191 | |||
2192 | if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) && | ||
2193 | !lmp_host_ssp_capable(hdev)) { | ||
2194 | u8 mode = 0x01; | ||
2195 | |||
2196 | hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); | ||
2197 | |||
2198 | if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) { | ||
2199 | u8 support = 0x01; | ||
2200 | |||
2201 | hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, | ||
2202 | sizeof(support), &support); | ||
2203 | } | ||
2204 | } | ||
2205 | |||
2206 | if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) && | ||
2207 | lmp_bredr_capable(hdev)) { | ||
2208 | struct hci_cp_write_le_host_supported cp; | ||
2209 | |||
2210 | cp.le = 0x01; | ||
2211 | cp.simul = 0x00; | ||
2212 | |||
2213 | /* Check first if we already have the right | ||
2214 | * host state (host features set) | ||
2215 | */ | ||
2216 | if (cp.le != lmp_host_le_capable(hdev) || | ||
2217 | cp.simul != lmp_host_le_br_capable(hdev)) | ||
2218 | hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED, | ||
2219 | sizeof(cp), &cp); | ||
2220 | } | ||
2221 | |||
2222 | if (lmp_le_capable(hdev)) { | ||
2223 | /* Make sure the controller has a good default for | ||
2224 | * advertising data. This also applies to the case | ||
2225 | * where BR/EDR was toggled during the AUTO_OFF phase. | ||
2226 | */ | ||
2227 | if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) && | ||
2228 | (hci_dev_test_flag(hdev, HCI_ADVERTISING) || | ||
2229 | !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) { | ||
2230 | __hci_req_update_adv_data(req, HCI_ADV_CURRENT); | ||
2231 | __hci_req_update_scan_rsp_data(req, HCI_ADV_CURRENT); | ||
2232 | } | ||
2233 | |||
2234 | if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && | ||
2235 | hdev->cur_adv_instance == 0x00 && | ||
2236 | !list_empty(&hdev->adv_instances)) { | ||
2237 | adv_instance = list_first_entry(&hdev->adv_instances, | ||
2238 | struct adv_info, list); | ||
2239 | hdev->cur_adv_instance = adv_instance->instance; | ||
2240 | } | ||
2241 | |||
2242 | if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) | ||
2243 | __hci_req_enable_advertising(req); | ||
2244 | else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && | ||
2245 | hdev->cur_adv_instance) | ||
2246 | __hci_req_schedule_adv_instance(req, | ||
2247 | hdev->cur_adv_instance, | ||
2248 | true); | ||
2249 | } | ||
2250 | |||
2251 | link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY); | ||
2252 | if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) | ||
2253 | hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, | ||
2254 | sizeof(link_sec), &link_sec); | ||
2255 | |||
2256 | if (lmp_bredr_capable(hdev)) { | ||
2257 | if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) | ||
2258 | __hci_req_write_fast_connectable(req, true); | ||
2259 | else | ||
2260 | __hci_req_write_fast_connectable(req, false); | ||
2261 | __hci_req_update_scan(req); | ||
2262 | __hci_req_update_class(req); | ||
2263 | __hci_req_update_name(req); | ||
2264 | __hci_req_update_eir(req); | ||
2265 | } | ||
2266 | |||
2267 | hci_dev_unlock(hdev); | ||
2268 | return 0; | ||
2269 | } | ||
2270 | |||
2271 | int __hci_req_hci_power_on(struct hci_dev *hdev) | ||
2272 | { | ||
2273 | /* Register the available SMP channels (BR/EDR and LE) only when | ||
2274 | * successfully powering on the controller. This late | ||
2275 | * registration is required so that LE SMP can clearly decide if | ||
2276 | * the public address or static address is used. | ||
2277 | */ | ||
2278 | smp_register(hdev); | ||
2279 | |||
2280 | return __hci_req_sync(hdev, powered_update_hci, 0, HCI_CMD_TIMEOUT, | ||
2281 | NULL); | ||
2282 | } | ||
2283 | |||
2184 | void hci_request_setup(struct hci_dev *hdev) | 2284 | void hci_request_setup(struct hci_dev *hdev) |
2185 | { | 2285 | { |
2186 | INIT_WORK(&hdev->discov_update, discov_update); | 2286 | INIT_WORK(&hdev->discov_update, discov_update); |
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index d3dd24deca74..a24d3b55094c 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h | |||
@@ -55,6 +55,8 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err); | |||
55 | struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, | 55 | struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, |
56 | const void *param); | 56 | const void *param); |
57 | 57 | ||
58 | int __hci_req_hci_power_on(struct hci_dev *hdev); | ||
59 | |||
58 | void __hci_req_write_fast_connectable(struct hci_request *req, bool enable); | 60 | void __hci_req_write_fast_connectable(struct hci_request *req, bool enable); |
59 | void __hci_req_update_name(struct hci_request *req); | 61 | void __hci_req_update_name(struct hci_request *req); |
60 | void __hci_req_update_eir(struct hci_request *req); | 62 | void __hci_req_update_eir(struct hci_request *req); |
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, | |||
961 | goto failed; | 961 | goto failed; |
962 | } | 962 | } |
963 | 963 | ||
964 | if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { | ||
965 | cancel_delayed_work(&hdev->power_off); | ||
966 | |||
967 | if (cp->val) { | ||
968 | mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, | ||
969 | data, len); | ||
970 | err = mgmt_powered(hdev, 1); | ||
971 | goto failed; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | if (!!cp->val == hdev_is_powered(hdev)) { | 964 | if (!!cp->val == hdev_is_powered(hdev)) { |
976 | err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); | 965 | err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); |
977 | goto failed; | 966 | goto failed; |
@@ -6434,139 +6423,33 @@ static void restart_le_actions(struct hci_dev *hdev) | |||
6434 | } | 6423 | } |
6435 | } | 6424 | } |
6436 | 6425 | ||
6437 | static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode) | 6426 | void mgmt_power_on(struct hci_dev *hdev, int err) |
6438 | { | 6427 | { |
6439 | struct cmd_lookup match = { NULL, hdev }; | 6428 | struct cmd_lookup match = { NULL, hdev }; |
6440 | 6429 | ||
6441 | BT_DBG("status 0x%02x", status); | 6430 | BT_DBG("err %d", err); |
6442 | 6431 | ||
6443 | if (!status) { | 6432 | hci_dev_lock(hdev); |
6433 | |||
6434 | if (!err) { | ||
6444 | restart_le_actions(hdev); | 6435 | restart_le_actions(hdev); |
6445 | hci_update_background_scan(hdev); | 6436 | hci_update_background_scan(hdev); |
6446 | } | 6437 | } |
6447 | 6438 | ||
6448 | hci_dev_lock(hdev); | ||
6449 | |||
6450 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); | 6439 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); |
6451 | 6440 | ||
6452 | new_settings(hdev, match.sk); | 6441 | new_settings(hdev, match.sk); |
6453 | 6442 | ||
6454 | hci_dev_unlock(hdev); | ||
6455 | |||
6456 | if (match.sk) | 6443 | if (match.sk) |
6457 | sock_put(match.sk); | 6444 | sock_put(match.sk); |
6458 | } | ||
6459 | 6445 | ||
6460 | static int powered_update_hci(struct hci_dev *hdev) | 6446 | hci_dev_unlock(hdev); |
6461 | { | ||
6462 | struct hci_request req; | ||
6463 | struct adv_info *adv_instance; | ||
6464 | u8 link_sec; | ||
6465 | |||
6466 | hci_req_init(&req, hdev); | ||
6467 | |||
6468 | if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) && | ||
6469 | !lmp_host_ssp_capable(hdev)) { | ||
6470 | u8 mode = 0x01; | ||
6471 | |||
6472 | hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); | ||
6473 | |||
6474 | if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) { | ||
6475 | u8 support = 0x01; | ||
6476 | |||
6477 | hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, | ||
6478 | sizeof(support), &support); | ||
6479 | } | ||
6480 | } | ||
6481 | |||
6482 | if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) && | ||
6483 | lmp_bredr_capable(hdev)) { | ||
6484 | struct hci_cp_write_le_host_supported cp; | ||
6485 | |||
6486 | cp.le = 0x01; | ||
6487 | cp.simul = 0x00; | ||
6488 | |||
6489 | /* Check first if we already have the right | ||
6490 | * host state (host features set) | ||
6491 | */ | ||
6492 | if (cp.le != lmp_host_le_capable(hdev) || | ||
6493 | cp.simul != lmp_host_le_br_capable(hdev)) | ||
6494 | hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, | ||
6495 | sizeof(cp), &cp); | ||
6496 | } | ||
6497 | |||
6498 | if (lmp_le_capable(hdev)) { | ||
6499 | /* Make sure the controller has a good default for | ||
6500 | * advertising data. This also applies to the case | ||
6501 | * where BR/EDR was toggled during the AUTO_OFF phase. | ||
6502 | */ | ||
6503 | if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) && | ||
6504 | (hci_dev_test_flag(hdev, HCI_ADVERTISING) || | ||
6505 | !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) { | ||
6506 | __hci_req_update_adv_data(&req, HCI_ADV_CURRENT); | ||
6507 | __hci_req_update_scan_rsp_data(&req, HCI_ADV_CURRENT); | ||
6508 | } | ||
6509 | |||
6510 | if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && | ||
6511 | hdev->cur_adv_instance == 0x00 && | ||
6512 | !list_empty(&hdev->adv_instances)) { | ||
6513 | adv_instance = list_first_entry(&hdev->adv_instances, | ||
6514 | struct adv_info, list); | ||
6515 | hdev->cur_adv_instance = adv_instance->instance; | ||
6516 | } | ||
6517 | |||
6518 | if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) | ||
6519 | __hci_req_enable_advertising(&req); | ||
6520 | else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && | ||
6521 | hdev->cur_adv_instance) | ||
6522 | __hci_req_schedule_adv_instance(&req, | ||
6523 | hdev->cur_adv_instance, | ||
6524 | true); | ||
6525 | } | ||
6526 | |||
6527 | link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY); | ||
6528 | if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) | ||
6529 | hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE, | ||
6530 | sizeof(link_sec), &link_sec); | ||
6531 | |||
6532 | if (lmp_bredr_capable(hdev)) { | ||
6533 | if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) | ||
6534 | __hci_req_write_fast_connectable(&req, true); | ||
6535 | else | ||
6536 | __hci_req_write_fast_connectable(&req, false); | ||
6537 | __hci_req_update_scan(&req); | ||
6538 | __hci_req_update_class(&req); | ||
6539 | __hci_req_update_name(&req); | ||
6540 | __hci_req_update_eir(&req); | ||
6541 | } | ||
6542 | |||
6543 | return hci_req_run(&req, powered_complete); | ||
6544 | } | 6447 | } |
6545 | 6448 | ||
6546 | int mgmt_powered(struct hci_dev *hdev, u8 powered) | 6449 | void __mgmt_power_off(struct hci_dev *hdev) |
6547 | { | 6450 | { |
6548 | struct cmd_lookup match = { NULL, hdev }; | 6451 | struct cmd_lookup match = { NULL, hdev }; |
6549 | u8 status, zero_cod[] = { 0, 0, 0 }; | 6452 | u8 status, zero_cod[] = { 0, 0, 0 }; |
6550 | int err; | ||
6551 | |||
6552 | if (!hci_dev_test_flag(hdev, HCI_MGMT)) | ||
6553 | return 0; | ||
6554 | |||
6555 | if (powered) { | ||
6556 | /* Register the available SMP channels (BR/EDR and LE) only | ||
6557 | * when successfully powering on the controller. This late | ||
6558 | * registration is required so that LE SMP can clearly | ||
6559 | * decide if the public address or static address is used. | ||
6560 | */ | ||
6561 | smp_register(hdev); | ||
6562 | |||
6563 | if (powered_update_hci(hdev) == 0) | ||
6564 | return 0; | ||
6565 | |||
6566 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, | ||
6567 | &match); | ||
6568 | goto new_settings; | ||
6569 | } | ||
6570 | 6453 | ||
6571 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); | 6454 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); |
6572 | 6455 | ||
@@ -6588,13 +6471,10 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) | |||
6588 | mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, | 6471 | mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, |
6589 | zero_cod, sizeof(zero_cod), NULL); | 6472 | zero_cod, sizeof(zero_cod), NULL); |
6590 | 6473 | ||
6591 | new_settings: | 6474 | new_settings(hdev, match.sk); |
6592 | err = new_settings(hdev, match.sk); | ||
6593 | 6475 | ||
6594 | if (match.sk) | 6476 | if (match.sk) |
6595 | sock_put(match.sk); | 6477 | sock_put(match.sk); |
6596 | |||
6597 | return err; | ||
6598 | } | 6478 | } |
6599 | 6479 | ||
6600 | void mgmt_set_powered_failed(struct hci_dev *hdev, int err) | 6480 | void mgmt_set_powered_failed(struct hci_dev *hdev, int err) |