blob: dbbb8634dd0480ade34889fc05c103d5766bf208 [file] [log] [blame]
From 9faaa506a4dff388c7100c6c2a62744789dc1f85 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.
BUG=b:181843241
TEST=update_kernel on hatch. Observed the role switch 3 seconds after
the connection using a Bose headset and observe the connection remains
in central role after the kernel is updated.
Signed-off-by: Alain Michaud <alainm@chromium.org>
Change-Id: I67136706ba39a9f09b3285017b62a13a920bfffd
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2818461
Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---
net/bluetooth/hci_conn.c | 3 +--
net/bluetooth/hci_event.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fe803bee419a..754b92e4d19e 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -226,8 +226,6 @@ static void hci_acl_create_connection(struct hci_conn *conn)
conn->attempt++;
- conn->link_policy = hdev->link_policy;
-
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
@@ -704,6 +702,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
conn->rssi = HCI_RSSI_INVALID;
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_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 5ef357b63c68..19725446366e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3125,6 +3125,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);
+
conn->handle = __le16_to_cpu(ev->handle);
if (conn->handle > HCI_CONN_HANDLE_MAX) {
bt_dev_err(hdev, "Invalid handle: 0x%4.4x > 0x%4.4x",
@@ -3154,6 +3160,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);
+ }
+
/* Get remote features */
if (conn->type == ACL_LINK) {
struct hci_cp_read_remote_features cp;
--
2.36.0.512.ge40c2bad7a-goog