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