| 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 |
| |