blob: 49cf06e64d5f34d4d85bfa7ce439b7e8d924a673 [file] [log] [blame]
From bd76287003539353e7442baadfb2aff8f2827d8c Mon Sep 17 00:00:00 2001
From: Alain Michaud <alainm@chromium.org>
Date: Fri, 9 Apr 2021 15:21:27 +0000
Subject: [PATCH] CHROMIUM: Use link policies to disallow role switches
Some peripherals will attempt to role switch the host device into a
peripheral role mode. When combined with other peripherals such as mice
and keyboards, this forces the controller to maintain a complicated
topology which results in audio glitches or mouse movement glitches.
The solution implemented by this patch is to leverage the link_policy to
attempt to disallow the peripheral from switching roles once the host
settles into the central role.
This is submitted as a chromium patch to gain some feedback on
improvement before proposing upstream.
Signed-off-by: Alain Michaud <alainm@chromium.org>
BUG=none
TEST=build
Change-Id: I8209b0b1073c0b2d03a2631c64423e67ff96184f
Signed-off-by: Zhengping Jiang <jiangzp@google.com>
Kcr-patch: 2651373bd21928a45d21c88602ed1535423fe062aff4c93be8d231e5.patch
---
net/bluetooth/hci_conn.c | 1 +
net/bluetooth/hci_event.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index ebdd7f83eb476c142005417ee68a8f64b78c0ca6..0c44c5d9d6746afe99c4dbf1d0a3879d675d00a3 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -931,6 +931,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_INVALID;
conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
+ conn->link_policy = hdev->link_policy;
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index a8b8cfebe0180cce2fb661e8e5f21a79bf7a7656..b44b1cf1431584bf3a4433fc6a83c3c75152faab 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3183,6 +3183,12 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
}
if (!status) {
+ int mask = hdev->link_mode;
+ __u8 flags = 0;
+
+ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type,
+ &flags);
+
status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle));
if (status)
goto done;
@@ -3208,6 +3214,19 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
if (test_bit(HCI_ENCRYPT, &hdev->flags))
set_bit(HCI_CONN_ENCRYPT, &conn->flags);
+ /* Attempt to remain in the central role if preferred */
+ if ((conn->mode == HCI_ROLE_MASTER && (mask & HCI_LM_MASTER)) &&
+ (conn->link_policy & HCI_LP_RSWITCH)) {
+ struct hci_cp_write_link_policy cp;
+
+ conn->link_policy &= ~HCI_LP_RSWITCH;
+
+ cp.handle = ev->handle;
+ cp.policy = conn->link_policy;
+ hci_send_cmd(hdev, HCI_OP_WRITE_LINK_POLICY,
+ sizeof(cp), &cp);
+ }
+
/* "Link key request" completed ahead of "connect request" completes */
if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
ev->link_type == ACL_LINK) {
--
2.44.0.478.gd926399ef9-goog