* [PATCH v3 2/3] Bluetooth: btintel: surface Intel telemetry events through mgmt
2022-02-09 9:22 [PATCH v3 1/3] Bluetooth: aosp: surface AOSP quality report through mgmt Joseph Hwang
@ 2022-02-09 9:22 ` Joseph Hwang
2022-02-09 9:23 ` [PATCH v3 3/3] Bluetooth: mgmt: add set_quality_report for MGMT_OP_SET_QUALITY_REPORT Joseph Hwang
2022-02-09 9:45 ` [v3,1/3] Bluetooth: aosp: surface AOSP quality report through mgmt bluez.test.bot
2 siblings, 0 replies; 4+ messages in thread
From: Joseph Hwang @ 2022-02-09 9:22 UTC (permalink / raw)
To: linux-bluetooth, marcel, luiz.dentz, pali
Cc: josephsih, chromeos-bluetooth-upstreaming, Joseph Hwang,
Archie Pusaka, David S. Miller, Jakub Kicinski, Johan Hedberg,
linux-kernel, netdev
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.
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
---
Changes in v3:
- Move intel_vendor_evt() from hci_event.c to the btintel driver.
Changes in v2:
- Drop the pull_quality_report_data function from hci_dev.
Do not bother hci_dev with it. Do not bleed the details
into the core.
drivers/bluetooth/btintel.c | 37 +++++++++++++++++++++++++++++++-
drivers/bluetooth/btintel.h | 7 ++++++
include/net/bluetooth/hci_core.h | 2 ++
net/bluetooth/hci_event.c | 12 +++++++++++
4 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 06514ed66022..c7732da2752f 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -2401,9 +2401,12 @@ 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;
+ /* Set up the vendor specific callback for Intel devices */
+ hdev->vendor_evt = btintel_vendor_evt;
+
/* For Legacy device, check the HW platform value and size */
if (skb->len == sizeof(ver) && skb->data[1] == 0x37) {
bt_dev_dbg(hdev, "Read the legacy Intel version information");
@@ -2650,6 +2653,38 @@ void btintel_secure_send_result(struct hci_dev *hdev,
}
EXPORT_SYMBOL_GPL(btintel_secure_send_result);
+#define INTEL_PREFIX 0x8087
+#define TELEMETRY_CODE 0x03
+
+struct intel_prefix_evt_data {
+ __le16 vendor_prefix;
+ __u8 code;
+ __u8 data[]; /* a number of struct intel_tlv subevents */
+} __packed;
+
+static bool 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;
+}
+
+void btintel_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
+{
+ /* Only interested in the telemetry event for now. */
+ if (hdev->set_quality_report && is_quality_report_evt(skb))
+ mgmt_quality_report(hdev, skb->data, skb->len,
+ QUALITY_SPEC_INTEL_TELEMETRY);
+}
+EXPORT_SYMBOL_GPL(btintel_vendor_evt);
+
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
MODULE_VERSION(VERSION);
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index e0060e58573c..82dc278b09eb 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -211,6 +211,7 @@ 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);
+void btintel_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@@ -306,4 +307,10 @@ static inline int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
{
return -ENODEV;
}
+
+static inline void btintel_vendor_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+}
+
#endif
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ea83619ac4de..3505ffe20779 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -635,6 +635,8 @@ struct hci_dev {
void (*cmd_timeout)(struct hci_dev *hdev);
bool (*wakeup)(struct hci_dev *hdev);
int (*set_quality_report)(struct hci_dev *hdev, bool enable);
+ void (*vendor_evt)(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb);
int (*get_data_path_id)(struct hci_dev *hdev, __u8 *data_path);
int (*get_codec_config_data)(struct hci_dev *hdev, __u8 type,
struct bt_codec *codec, __u8 *vnd_len,
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 6468ea0f71bd..e34dea0f0c2e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -4250,6 +4250,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, void *data,
* space to avoid collision.
*/
static unsigned char AOSP_BQR_PREFIX[] = { 0x58 };
+static unsigned char INTEL_PREFIX[] = { 0x87, 0x80 };
/* Some vendor prefixes are fixed values and lengths. */
#define FIXED_EVT_PREFIX(_prefix, _vendor_func) \
@@ -4273,6 +4274,16 @@ static unsigned char AOSP_BQR_PREFIX[] = { 0x58 };
.get_prefix_len = _prefix_len_func, \
}
+/* Every vendor that handles particular vendor events in its driver should
+ * 1. set up the vendor_evt callback in its driver and
+ * 2. add an entry in struct vendor_event_prefix.
+ */
+static void vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
+{
+ if (hdev->vendor_evt)
+ hdev->vendor_evt(hdev, data, skb);
+}
+
/* Every distinct vendor specification must have a well-defined vendor
* event prefix to determine if a vendor event meets the specification.
* If an event prefix is fixed, it should be delcared with FIXED_EVT_PREFIX.
@@ -4287,6 +4298,7 @@ struct vendor_event_prefix {
__u8 (*get_prefix_len)(struct hci_dev *hdev);
} evt_prefixes[] = {
FIXED_EVT_PREFIX(AOSP_BQR_PREFIX, aosp_quality_report_evt),
+ FIXED_EVT_PREFIX(INTEL_PREFIX, vendor_evt),
DYNAMIC_EVT_PREFIX(get_msft_evt_prefix, get_msft_evt_prefix_len,
msft_vendor_evt),
--
2.35.0.263.gb82422642f-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v3 3/3] Bluetooth: mgmt: add set_quality_report for MGMT_OP_SET_QUALITY_REPORT
2022-02-09 9:22 [PATCH v3 1/3] Bluetooth: aosp: surface AOSP quality report through mgmt Joseph Hwang
2022-02-09 9:22 ` [PATCH v3 2/3] Bluetooth: btintel: surface Intel telemetry events " Joseph Hwang
@ 2022-02-09 9:23 ` Joseph Hwang
2022-02-09 9:45 ` [v3,1/3] Bluetooth: aosp: surface AOSP quality report through mgmt bluez.test.bot
2 siblings, 0 replies; 4+ messages in thread
From: Joseph Hwang @ 2022-02-09 9:23 UTC (permalink / raw)
To: linux-bluetooth, marcel, luiz.dentz, pali
Cc: josephsih, chromeos-bluetooth-upstreaming, Joseph Hwang,
David S. Miller, Jakub Kicinski, Johan Hedberg, linux-kernel,
netdev
This patch adds a new set_quality_report mgmt handler to set
the quality report feature. The feature is removed from the
experimental features at the same time.
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
---
Changes in v3:
- This is a new patch to enable the quality report feature.
The reading and setting of the quality report feature are
removed from the experimental features.
include/net/bluetooth/mgmt.h | 7 ++
net/bluetooth/mgmt.c | 164 +++++++++++++++--------------------
2 files changed, 77 insertions(+), 94 deletions(-)
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 03204b4ba641..236e042fbc1c 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -109,6 +109,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_STATIC_ADDRESS 0x00008000
#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000
#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000
+#define MGMT_SETTING_QUALITY_REPORT 0x00040000
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
@@ -838,6 +839,12 @@ struct mgmt_cp_add_adv_patterns_monitor_rssi {
} __packed;
#define MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE 8
+#define MGMT_OP_SET_QUALITY_REPORT 0x0057
+struct mgmt_cp_set_quality_report {
+ __u8 action;
+} __packed;
+#define MGMT_SET_QUALITY_REPORT_SIZE 1
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index cab79f480a21..92de8e669897 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -857,6 +857,10 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_PHY_CONFIGURATION;
+ if (hdev && (aosp_has_quality_report(hdev) ||
+ hdev->set_quality_report))
+ settings |= MGMT_SETTING_QUALITY_REPORT;
+
return settings;
}
@@ -928,6 +932,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED))
settings |= MGMT_SETTING_WIDEBAND_SPEECH;
+ if (hci_dev_test_flag(hdev, HCI_QUALITY_REPORT))
+ settings |= MGMT_SETTING_QUALITY_REPORT;
+
return settings;
}
@@ -3871,12 +3878,6 @@ static const u8 debug_uuid[16] = {
};
#endif
-/* 330859bc-7506-492d-9370-9a6f0614037f */
-static const u8 quality_report_uuid[16] = {
- 0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93,
- 0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33,
-};
-
/* a6695ace-ee7f-4fb9-881a-5fac66c629af */
static const u8 offload_codecs_uuid[16] = {
0xaf, 0x29, 0xc6, 0x66, 0xac, 0x5f, 0x1a, 0x88,
@@ -3898,7 +3899,7 @@ static const u8 rpa_resolution_uuid[16] = {
static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
- char buf[102]; /* Enough space for 5 features: 2 + 20 * 5 */
+ char buf[82]; /* Enough space for 4 features: 2 + 20 * 4 */
struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
u16 idx = 0;
u32 flags;
@@ -3939,18 +3940,6 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
idx++;
}
- if (hdev && (aosp_has_quality_report(hdev) ||
- hdev->set_quality_report)) {
- if (hci_dev_test_flag(hdev, HCI_QUALITY_REPORT))
- flags = BIT(0);
- else
- flags = 0;
-
- memcpy(rp->features[idx].uuid, quality_report_uuid, 16);
- rp->features[idx].flags = cpu_to_le32(flags);
- idx++;
- }
-
if (hdev && hdev->get_data_path_id) {
if (hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED))
flags = BIT(0);
@@ -4163,80 +4152,6 @@ static int set_rpa_resolution_func(struct sock *sk, struct hci_dev *hdev,
return err;
}
-static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
- struct mgmt_cp_set_exp_feature *cp,
- u16 data_len)
-{
- struct mgmt_rp_set_exp_feature rp;
- bool val, changed;
- int err;
-
- /* Command requires to use a valid controller index */
- if (!hdev)
- return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
- MGMT_OP_SET_EXP_FEATURE,
- MGMT_STATUS_INVALID_INDEX);
-
- /* Parameters are limited to a single octet */
- if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
- return mgmt_cmd_status(sk, hdev->id,
- MGMT_OP_SET_EXP_FEATURE,
- MGMT_STATUS_INVALID_PARAMS);
-
- /* Only boolean on/off is supported */
- if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
- return mgmt_cmd_status(sk, hdev->id,
- MGMT_OP_SET_EXP_FEATURE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hci_req_sync_lock(hdev);
-
- val = !!cp->param[0];
- changed = (val != hci_dev_test_flag(hdev, HCI_QUALITY_REPORT));
-
- if (!aosp_has_quality_report(hdev) && !hdev->set_quality_report) {
- err = mgmt_cmd_status(sk, hdev->id,
- MGMT_OP_SET_EXP_FEATURE,
- MGMT_STATUS_NOT_SUPPORTED);
- goto unlock_quality_report;
- }
-
- if (changed) {
- if (hdev->set_quality_report)
- err = hdev->set_quality_report(hdev, val);
- else
- err = aosp_set_quality_report(hdev, val);
-
- if (err) {
- err = mgmt_cmd_status(sk, hdev->id,
- MGMT_OP_SET_EXP_FEATURE,
- MGMT_STATUS_FAILED);
- goto unlock_quality_report;
- }
-
- if (val)
- hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
- else
- hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
- }
-
- bt_dev_dbg(hdev, "quality report enable %d changed %d", val, changed);
-
- memcpy(rp.uuid, quality_report_uuid, 16);
- rp.flags = cpu_to_le32(val ? BIT(0) : 0);
- hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
-
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_EXP_FEATURE, 0,
- &rp, sizeof(rp));
-
- if (changed)
- exp_feature_changed(hdev, quality_report_uuid, val, sk);
-
-unlock_quality_report:
- hci_req_sync_unlock(hdev);
- return err;
-}
-
static int set_offload_codec_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp,
u16 data_len)
@@ -4363,7 +4278,6 @@ static const struct mgmt_exp_feature {
EXP_FEAT(debug_uuid, set_debug_func),
#endif
EXP_FEAT(rpa_resolution_uuid, set_rpa_resolution_func),
- EXP_FEAT(quality_report_uuid, set_quality_report_func),
EXP_FEAT(offload_codecs_uuid, set_offload_codec_func),
EXP_FEAT(le_simultaneous_roles_uuid, set_le_simultaneous_roles_func),
@@ -8656,6 +8570,67 @@ static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
return err;
}
+static int set_quality_report(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
+{
+ struct mgmt_cp_set_quality_report *cp = data;
+ bool enable, changed;
+ int err;
+
+ /* Command requires to use a valid controller index */
+ if (!hdev)
+ return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+ MGMT_OP_SET_QUALITY_REPORT,
+ MGMT_STATUS_INVALID_INDEX);
+
+ /* Only 0 (off) and 1 (on) is supported */
+ if (cp->action != 0x00 && cp->action != 0x01)
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_QUALITY_REPORT,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_req_sync_lock(hdev);
+
+ enable = !!cp->action;
+ changed = (enable != hci_dev_test_flag(hdev, HCI_QUALITY_REPORT));
+
+ if (!aosp_has_quality_report(hdev) && !hdev->set_quality_report) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_QUALITY_REPORT,
+ MGMT_STATUS_NOT_SUPPORTED);
+ goto unlock_quality_report;
+ }
+
+ if (changed) {
+ if (hdev->set_quality_report)
+ err = hdev->set_quality_report(hdev, enable);
+ else
+ err = aosp_set_quality_report(hdev, enable);
+
+ if (err) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_QUALITY_REPORT,
+ MGMT_STATUS_FAILED);
+ goto unlock_quality_report;
+ }
+
+ if (enable)
+ hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
+ else
+ hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
+ }
+
+ bt_dev_dbg(hdev, "quality report enable %d changed %d",
+ enable, changed);
+
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_QUALITY_REPORT, 0,
+ NULL, 0);
+
+unlock_quality_report:
+ hci_req_sync_unlock(hdev);
+ return err;
+}
+
static const struct hci_mgmt_handler mgmt_handlers[] = {
{ NULL }, /* 0x0000 (no command) */
{ read_version, MGMT_READ_VERSION_SIZE,
@@ -8782,6 +8757,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ add_adv_patterns_monitor_rssi,
MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE,
HCI_MGMT_VAR_LEN },
+ { set_quality_report, MGMT_SET_QUALITY_REPORT_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
--
2.35.0.263.gb82422642f-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread