blob: e589c8780d64e8bf81b77d7c3d167694460358e9 [file] [log] [blame]
From 7430e29686439b1ec93126ca47a0bb03d091bdbb Mon Sep 17 00:00:00 2001
From: Joseph Hwang <josephsih@chromium.org>
Date: Fri, 12 Nov 2021 23:21:46 +0800
Subject: [PATCH] CHROMIUM: Bluetooth: surface Intel telemetry events through
mgmt
When receiving a HCI vendor event, the kernel checks if it is an
Intel telemetry event. If yes, the event is sent to bluez user
space through the mgmt socket.
BUG=b:203035611,b:208922349,b:242124116
TEST=Run an audio a2dp or hfp test and check logs.
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3299654
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3554314
Change-Id: I63681490281b2392aa1ac05dff91a126394ab649
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3823821
[rebase61(tzungbi):
Squashed:
FIXUP: CHROMIUM: Bluetooth: surface Intel telemetry events through mgmt
FIXUP: CHROMIUM: Bluetooth: surface Intel telemetry events through mgmt
]
Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org>
---
drivers/bluetooth/btintel.c | 43 +++++++++++++++++++++++++++++++-
drivers/bluetooth/btintel.h | 12 +++++++++
include/net/bluetooth/hci_core.h | 2 ++
net/bluetooth/hci_event.c | 12 ++++++---
4 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index d9349ba48281e2918bc42f677f2356d85648e619..7124534d75bdfdedecdffca12b5370508681742b 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -1527,6 +1527,45 @@ static int btintel_register_devcoredump_support(struct hci_dev *hdev)
return err;
}
+#define INTEL_PREFIX 0x8087
+#define TELEMETRY_CODE 0x03
+
+struct intel_prefix_evt_data {
+ __le16 vendor_prefix;
+ __u8 code;
+ __u8 data[0]; /* a number of struct intel_tlv subevents */
+} __packed;
+
+bool btintel_is_quality_report_evt(struct sk_buff *skb)
+{
+ struct intel_prefix_evt_data *ev;
+ u16 vendor_prefix;
+
+ if (skb->len < sizeof(struct intel_prefix_evt_data))
+ return false;
+
+ ev = (struct intel_prefix_evt_data *)skb->data;
+ vendor_prefix = __le16_to_cpu(ev->vendor_prefix);
+
+ return vendor_prefix == INTEL_PREFIX && ev->code == TELEMETRY_CODE;
+}
+EXPORT_SYMBOL_GPL(btintel_is_quality_report_evt);
+
+bool btintel_pull_quality_report_data(struct sk_buff *skb)
+{
+ skb_pull(skb, sizeof(struct intel_prefix_evt_data));
+
+ /* A telemetry event contains at least one intel_tlv subevent. */
+ if (skb->len < sizeof(struct intel_tlv)) {
+ BT_ERR("Telemetry event length %u too short (at least %zu)",
+ skb->len, sizeof(struct intel_tlv));
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(btintel_pull_quality_report_data);
+
static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
@@ -2599,8 +2638,10 @@ static int btintel_setup_combined(struct hci_dev *hdev)
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
- /* Set up the quality report callback for Intel devices */
+ /* Set up the quality report callbacks for Intel devices */
hdev->set_quality_report = btintel_set_quality_report;
+ hdev->is_quality_report_evt = btintel_is_quality_report_evt;
+ hdev->pull_quality_report_data = btintel_pull_quality_report_data;
/* For Legacy device, check the HW platform value and size */
if (skb->len == sizeof(ver) && skb->data[1] == 0x37) {
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index d6a1dc8d8a828732ed4cf70b1481b8c63017cd74..59c34fbb2543dae4e7143236e6644b3d0dfb76d2 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -224,6 +224,8 @@ void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
void btintel_secure_send_result(struct hci_dev *hdev,
const void *ptr, unsigned int len);
int btintel_set_quality_report(struct hci_dev *hdev, bool enable);
+bool btintel_is_quality_report_evt(struct sk_buff *skb);
+bool btintel_pull_quality_report_data(struct sk_buff *skb);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@@ -320,4 +322,14 @@ static inline int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
{
return -ENODEV;
}
+
+static inline bool btintel_is_quality_report_evt(struct sk_buff *skb)
+{
+ return false;
+}
+
+static inline bool btintel_pull_quality_report_data(struct sk_buff *skb)
+{
+ return false;
+}
#endif
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4c85aaeb1a9d836fbc5affed3cad8d751d6a48c0..d71a9fb568336ad2d75c101d2f666b43fbd89ba0 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -675,6 +675,8 @@ struct hci_dev {
int (*get_codec_config_data)(struct hci_dev *hdev, __u8 type,
struct bt_codec *codec, __u8 *vnd_len,
__u8 **vnd_data);
+ bool (*is_quality_report_evt)(struct sk_buff *skb);
+ bool (*pull_quality_report_data)(struct sk_buff *skb);
};
#define HCI_PHY_HANDLE(handle) (handle & 0xff)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1fa98b149226122f8a3394aff40e29b4b84cdb41..d070c5018c74332c7fa66fceddb735b979af1330 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5764,11 +5764,17 @@ static bool quality_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (aosp_has_quality_report(hdev) &&
aosp_pull_quality_report_data(skb))
mgmt_quality_report(hdev, skb, QUALITY_SPEC_AOSP_BQR);
-
- return true;
+ } else if (hdev->is_quality_report_evt &&
+ hdev->is_quality_report_evt(skb)) {
+ if (hdev->set_quality_report &&
+ hdev->pull_quality_report_data(skb))
+ mgmt_quality_report(hdev, skb,
+ QUALITY_SPEC_INTEL_TELEMETRY);
+ } else {
+ return false;
}
- return false;
+ return true;
}
static void hci_vendor_evt(struct hci_dev *hdev, void *data,
--
2.38.3