blob: a865d4c9b27b9a6fc6ccd7f5e5479509b9f681e3 [file] [log] [blame]
From 1e256e7da62280095366110452d03fad480d79f9 Mon Sep 17 00:00:00 2001
From: Abhishek Pandit-Subedi <abhishekpandit@google.com>
Date: Mon, 7 Mar 2022 12:52:06 -0800
Subject: [PATCH] CHROMIUM: bluetooth: Track usermode connections
Track usermode connections so that we can notify drivers when new ACL
and SCO connections are made. This is necessary for drivers like btusb
which need to switch altsettings when sco connections are active.
This patch is not suitable for upstreaming because peeking at the HCI
stream for usermode is not a good design. Eventually, connection
notifications need to come from userspace via ioctls on the socket
interface itself.
BUG=b:221497057
TEST=HFP working with Floss
Change-Id: I208b7c21974d202690228ff23bfe9ab917fec8d2
Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3514513
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_core.c | 10 ++++-
net/bluetooth/hci_event.c | 66 ++++++++++++++++++++++++++++++++
3 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ecb38ae6b231..8cf13def63d8 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1406,6 +1406,7 @@ bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err);
bool hci_is_adv_monitoring(struct hci_dev *hdev);
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);
+void hci_handle_userchannel_packet(struct hci_dev *hdev, struct sk_buff *skb);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
void hci_init_sysfs(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6447555cba12..3989594ed54e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3800,7 +3800,15 @@ static void hci_rx_work(struct work_struct *work)
*/
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
!test_bit(HCI_INIT, &hdev->flags)) {
- kfree_skb(skb);
+ /* If the device has been opened in HCI_USER_CHANNEL,
+ * we still want to process event packets for connection
+ * management. We need to keep track of how many
+ * connections are up and notify the driver.
+ */
+ if (hci_skb_pkt_type(skb) == HCI_EVENT_PKT)
+ hci_handle_userchannel_packet(hdev, skb);
+ else
+ kfree_skb(skb);
continue;
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 266b1521a9a6..d540b80d2a5b 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -6871,6 +6871,72 @@ static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb,
ev->func(hdev, data, skb);
}
+void hci_handle_userchannel_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *)skb->data;
+ struct hci_ev_conn_complete *cc_ev;
+ struct hci_ev_sync_conn_complete *scc_ev;
+ struct hci_ev_disconn_complete *dcc_ev;
+
+ struct hci_conn *conn;
+ u8 event = hdr->evt;
+ int conn_type;
+
+ skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
+ switch (event) {
+ case HCI_EV_CONN_COMPLETE:
+ cc_ev = (void *)skb->data;
+ if (!cc_ev->status) {
+ conn_type = (cc_ev->link_type == ACL_LINK) ? ACL_LINK :
+ SCO_LINK;
+
+ conn = hci_conn_hash_lookup_ba(hdev, conn_type,
+ &cc_ev->bdaddr);
+ if (!conn) {
+ conn = hci_conn_add(hdev, conn_type,
+ &cc_ev->bdaddr, 0);
+ }
+
+ if (conn) {
+ conn->handle = __le16_to_cpu(cc_ev->handle);
+ conn->type = conn_type;
+ bt_dev_dbg(hdev, "%d handle(%d) type (%d)",
+ event, conn->handle, conn->type);
+
+ if (conn->type == SCO_LINK && hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+ }
+ }
+ break;
+ case HCI_EV_SYNC_CONN_COMPLETE:
+ scc_ev = (void *)skb->data;
+ if (!scc_ev->status) {
+ conn = hci_conn_hash_lookup_ba(hdev, SCO_LINK,
+ &scc_ev->bdaddr);
+ if (!conn) {
+ conn = hci_conn_add(hdev, SCO_LINK,
+ &scc_ev->bdaddr, 0);
+ }
+
+ if (conn && hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+ }
+ break;
+ case HCI_EV_DISCONN_COMPLETE:
+ dcc_ev = (void *)skb->data;
+ conn = hci_conn_hash_lookup_handle(hdev,
+ __le16_to_cpu(dcc_ev->handle));
+ if (conn)
+ hci_conn_del(conn);
+ break;
+ default:
+ break;
+ }
+
+ kfree_skb(skb);
+}
+
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
--
2.35.1.723.g4982287a31-goog