| From b2bf7ca133a909feacab7bd050fbfa0c3a858512 Mon Sep 17 00:00:00 2001 |
| From: Daniel Winkler <danielwinkler@google.com> |
| Date: Tue, 15 Dec 2020 11:00:30 -0800 |
| Subject: [PATCH] CHROMIUM: Bluetooth: Improve adv/scan use around random |
| address update |
| |
| Currently, mis-management of both scanning and advertising can cause |
| random address updates to fail. The two known failures are as follows: |
| |
| 1. Advertising starts immediately at login. No scanning is possible |
| until the advertisement is cancelled, since active advertising causes |
| random address updates to be deferred indefinitely. Without a random |
| address, scanning cannot start. |
| |
| 2. Scanning starts, and then a broadcast advertisement is added. Random |
| address updates fail if there is an active scan in session, so the |
| advertisement registration fails. |
| |
| This patch addresses both issues: |
| |
| 1. It removes active advertising as a cause for random address deferral, |
| and instead pauses active advertisements, sets the random address, |
| and resumes active advertisements again. This allows us to avoid the |
| conflict in state, and prevents the indefinite lock to scanning. |
| |
| 2. It adds active LE scan as a cause for random address deferral, which |
| prevents a new advertisement from attempting to set the random |
| address. Because a random address must have been set to perform an LE |
| scan, this can not cause a failure due to an unset random address (as |
| in problem 1, above). |
| |
| 3. Additionally, it was discovered that after an LE connect complete |
| event, kernel marks advertising as stopped in all cases, even though |
| some controllers do not do so. This change re-enables previously |
| configured advertisements if the connection is in SLAVE mode, where |
| advertising is expected to have stopped. |
| |
| This patch has been tested on hatch and atlas to solve problems 1 and 2 |
| above, and to result in successful use of both Nearby features, PaaSK, |
| and passes QuickHealth automated bluetooth tests. |
| |
| BUG=b:175069101, b:159995178 |
| TEST=Test Nearby startup flow on hatch |
| |
| Signed-off-by: Daniel Winkler <danielwinkler@google.com> |
| Signed-off-by: Archie Pusaka <apusaka@chromium.org> |
| Change-Id: I6d9f2c1d94b50baeea14a725ad52e113de244401 |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3507075 |
| Reviewed-by: Alain Michaud <alainm@chromium.org> |
| (cherry picked from commit 19d63b02e6936505797c0480e879b6674b1b6e73) |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3524282 |
| Reviewed-by: Yun-Hao Chung <howardchung@chromium.org> |
| --- |
| net/bluetooth/hci_event.c | 8 ++-- |
| net/bluetooth/hci_request.c | 81 ++++++++++++++++++++++++++++++++----- |
| net/bluetooth/hci_request.h | 2 +- |
| 3 files changed, 77 insertions(+), 14 deletions(-) |
| |
| diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c |
| index 43b4a7734b7c..520cc85c0cf3 100644 |
| --- a/net/bluetooth/hci_event.c |
| +++ b/net/bluetooth/hci_event.c |
| @@ -5530,10 +5530,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, |
| |
| hci_dev_lock(hdev); |
| |
| - /* All controllers implicitly stop advertising in the event of a |
| - * connection, so ensure that the state bit is cleared. |
| + /* When entering a connection in the slave role, the controller will |
| + * disable advertising. To avoid a lapse in service, we restart any |
| + * previously active advertising instances. |
| */ |
| - hci_dev_clear_flag(hdev, HCI_LE_ADV); |
| + if (role == HCI_ROLE_SLAVE) |
| + hci_req_enable_paused_adv(hdev); |
| |
| conn = hci_lookup_le_connect(hdev); |
| if (!conn) { |
| diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c |
| index 0f86655cc76c..235e44b7d15a 100644 |
| --- a/net/bluetooth/hci_request.c |
| +++ b/net/bluetooth/hci_request.c |
| @@ -835,6 +835,53 @@ void __hci_req_disable_advertising(struct hci_request *req) |
| } |
| } |
| |
| +static bool has_pending_adv(struct hci_dev *hdev) |
| +{ |
| + struct adv_info *adv_instance; |
| + |
| + list_for_each_entry(adv_instance, &hdev->adv_instances, list) { |
| + if (adv_instance->pending) |
| + return true; |
| + } |
| + |
| + return false; |
| +} |
| + |
| +static void __hci_req_enable_paused_adv(struct hci_request *req) |
| +{ |
| + struct hci_dev *hdev = req->hdev; |
| + struct adv_info *adv; |
| + u8 enable = 0x01; |
| + |
| + /* Determine if any of our advertising instances are in the middle of |
| + * registration. This is important, because we do not want to enable |
| + * advertising if an enable will be provided at the end of registration. |
| + * Such a double call may result in an HCI error on some platforms. |
| + */ |
| + |
| + if (ext_adv_capable(hdev)) { |
| + /* Call for each tracked instance to be re-enabled */ |
| + list_for_each_entry(adv, &hdev->adv_instances, list) { |
| + if (!adv->pending) |
| + __hci_req_enable_ext_advertising(req, |
| + adv->instance); |
| + } |
| + } else if (!has_pending_adv(hdev)) { |
| + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, |
| + sizeof(enable), &enable); |
| + } |
| +} |
| + |
| +int hci_req_enable_paused_adv(struct hci_dev *hdev) |
| +{ |
| + struct hci_request req; |
| + |
| + hci_req_init(&req, hdev); |
| + __hci_req_enable_paused_adv(&req); |
| + |
| + return hci_req_run(&req, NULL); |
| +} |
| + |
| static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags) |
| { |
| /* If privacy is not enabled don't use RPA */ |
| @@ -1304,25 +1351,39 @@ void __hci_req_clear_ext_adv_sets(struct hci_request *req) |
| static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) |
| { |
| struct hci_dev *hdev = req->hdev; |
| + bool adv_enabled = hci_dev_test_flag(hdev, HCI_LE_ADV); |
| |
| - /* If we're advertising or initiating an LE connection we can't |
| - * go ahead and change the random address at this time. This is |
| - * because the eventual initiator address used for the |
| - * subsequently created connection will be undefined (some |
| - * controllers use the new address and others the one we had |
| - * when the operation started). |
| + /* If we're initiating an LE connection or actively scanning, we can't |
| + * go ahead and change the random address at this time. This is because |
| + * the eventual initiator address used for the subsequently created |
| + * connection will be undefined (some controllers use the new address |
| + * and others the one we had when the operation started). |
| * |
| - * In this kind of scenario skip the update and let the random |
| - * address be updated at the next cycle. |
| + * In this kind of scenario skip the update and let the random address |
| + * be updated at the next cycle. |
| */ |
| - if (hci_dev_test_flag(hdev, HCI_LE_ADV) || |
| - hci_lookup_le_connect(hdev)) { |
| + |
| + if (hci_lookup_le_connect(hdev) || |
| + (hci_dev_test_flag(hdev, HCI_LE_SCAN) && |
| + hdev->le_scan_type == LE_SCAN_ACTIVE)) { |
| bt_dev_dbg(hdev, "Deferring random address update"); |
| hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); |
| return; |
| } |
| |
| + /* Because any random address update will fail while advertising is in |
| + * progress, we pause any active instances to update the random address. |
| + */ |
| + if (adv_enabled) { |
| + __hci_req_disable_advertising(req); |
| + hci_dev_clear_flag(hdev, HCI_LE_ADV); |
| + } |
| + |
| hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa); |
| + |
| + /* Re-enable previously-active advertisements */ |
| + if (adv_enabled) |
| + __hci_req_enable_paused_adv(req); |
| } |
| |
| int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) |
| diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h |
| index 7f8df258e295..1b69b381563b 100644 |
| --- a/net/bluetooth/hci_request.h |
| +++ b/net/bluetooth/hci_request.h |
| @@ -92,7 +92,7 @@ int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance, |
| void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk, |
| struct hci_request *req, u8 instance, |
| bool force); |
| - |
| +int hci_req_enable_paused_adv(struct hci_dev *hdev); |
| int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance); |
| int __hci_req_start_ext_adv(struct hci_request *req, u8 instance); |
| int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance); |
| -- |
| 2.35.1.894.gb6a874cedc-goog |
| |