blob: c03a67d42764143f6fcdcd3a042224bbc3bab93a [file] [log] [blame]
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