linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT
@ 2022-01-21 11:24 Joseph Hwang
  2022-01-21 11:24 ` [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Joseph Hwang
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Joseph Hwang @ 2022-01-21 11:24 UTC (permalink / raw)
  To: linux-bluetooth, marcel, luiz.dentz, pali
  Cc: chromeos-bluetooth-upstreaming, josephsih, Joseph Hwang, Archie Pusaka

This patch supports a new MGMT event of AOSP bluetooth quality report.

An AOSP quality report looks like:

AOSP Quality Report
  quality_report_id 1
  packet_type 23
  conn_handle 2
  conn_role 1
  tx_power_level 5
  rssi -36
  snr 0
  unused_afh_channel_count 0
  afh_select_unideal_channel_count 0
  lsto 20000.00
  conn_piconet_clock 63486046.56
  retransmission_count 1638
  no_rx_count 1638
  nak_count 0
  last_tx_ack_timestamp 115636.25
  flow_off_count 0
  last_flow_on_timestamp 63368203.12
  buffer_overflow_bytes 0
  buffer_underflow_bytes 0

Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
---

 Makefile.am       |   3 +-
 lib/mgmt.h        |  10 ++++
 src/adapter.c     |  58 ++++++++++++++++++++++
 src/adapter.h     |   2 +
 src/shared/aosp.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/aosp.h |  57 +++++++++++++++++++++
 6 files changed, 253 insertions(+), 1 deletion(-)
 create mode 100644 src/shared/aosp.c
 create mode 100644 src/shared/aosp.h

diff --git a/Makefile.am b/Makefile.am
index e391d7ae8..baab40369 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -230,7 +230,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
 			src/shared/gatt-db.h src/shared/gatt-db.c \
 			src/shared/gap.h src/shared/gap.c \
 			src/shared/log.h src/shared/log.c \
-			src/shared/tty.h
+			src/shared/tty.h \
+			src/shared/aosp.h src/shared/aosp.c
 
 if READLINE
 shared_sources += src/shared/shell.c src/shared/shell.h
diff --git a/lib/mgmt.h b/lib/mgmt.h
index 922a24367..1caecc43e 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -1032,6 +1032,16 @@ struct mgmt_ev_adv_monitor_device_lost {
 	struct mgmt_addr_info addr;
 } __packed;
 
+#define MGMT_EV_QUALITY_REPORT			0x0031
+#define QUALITY_SPEC_NA				0x0
+#define QUALITY_SPEC_INTEL_TELEMETRY		0x1
+#define QUALITY_SPEC_AOSP_BQR			0x2
+struct mgmt_ev_quality_report {
+	uint8_t quality_spec;
+	uint8_t report_len;
+	uint8_t report[0];
+} __packed;
+
 static const char *mgmt_op[] = {
 	"<0x0000>",
 	"Read Version",
diff --git a/src/adapter.c b/src/adapter.c
index 9772e843a..03f0e1ca6 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -47,6 +47,7 @@
 #include "src/shared/att.h"
 #include "src/shared/gatt-db.h"
 #include "src/shared/timeout.h"
+#include "src/shared/aosp.h"
 
 #include "btio/btio.h"
 #include "btd.h"
@@ -9312,6 +9313,30 @@ static void controller_resume_callback(uint16_t index, uint16_t length,
 	controller_resume_notify(adapter);
 }
 
+static void quality_report_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_quality_report *ev = param;
+	struct btd_adapter *adapter = user_data;
+
+	if (!ev)
+		return;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id,
+				"MGMT_EV_QUALITY_REPORT event too small");
+		return;
+	}
+
+	if (ev->quality_spec == QUALITY_SPEC_AOSP_BQR) {
+		if (!process_aosp_quality_report(ev))
+			error("processing aosp quality report");
+	} else {
+		error("quality report spec %u not supported.",
+			ev->quality_spec);
+	}
+}
+
 static void device_blocked_callback(uint16_t index, uint16_t length,
 					const void *param, void *user_data)
 {
@@ -9727,6 +9752,19 @@ static void le_simult_central_peripheral_func(struct btd_adapter *adapter,
 				(void *)le_simult_central_peripheral_uuid.val);
 }
 
+static bool is_exp_feature_uuid_the_same(const void *data,
+							const void *match_data)
+{
+	return memcmp(data, match_data,
+			sizeof(((struct mgmt_exp_uuid *)NULL)->val)) == 0;
+}
+
+bool is_quality_report_supported(struct btd_adapter *adapter)
+{
+	return queue_find(adapter->exps, is_exp_feature_uuid_the_same,
+				(void *)quality_report_uuid.val) != NULL;
+}
+
 static void quality_report_func(struct btd_adapter *adapter, uint8_t action)
 {
 	if (action)
@@ -9882,6 +9920,18 @@ static void read_exp_features(struct btd_adapter *adapter)
 	btd_error(adapter->dev_id, "Failed to read exp features info");
 }
 
+static void quality_report_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	info("%s%s", prefix, str);
+}
+
+static void quality_set_debug(struct btd_adapter *adapter)
+{
+	aosp_set_debug(quality_report_debug, "quality: ");
+}
+
 static void read_info_complete(uint8_t status, uint16_t length,
 					const void *param, void *user_data)
 {
@@ -10110,6 +10160,11 @@ static void read_info_complete(uint8_t status, uint16_t length,
 						controller_resume_callback,
 						adapter, NULL);
 
+	mgmt_register(adapter->mgmt, MGMT_EV_QUALITY_REPORT,
+						adapter->dev_id,
+						quality_report_callback,
+						adapter, NULL);
+
 	set_dev_class(adapter);
 
 	set_name(adapter, btd_adapter_get_name(adapter));
@@ -10137,6 +10192,9 @@ static void read_info_complete(uint8_t status, uint16_t length,
 	if (btd_adapter_get_powered(adapter))
 		adapter_start(adapter);
 
+	if (is_quality_report_supported(adapter) && getenv("QUALITY_DEBUG"))
+		quality_set_debug(adapter);
+
 	return;
 
 failed:
diff --git a/src/adapter.h b/src/adapter.h
index 35deb1d11..c199e358a 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -266,6 +266,8 @@ enum kernel_features {
 
 bool btd_has_kernel_features(uint32_t feature);
 
+bool is_quality_report_supported(struct btd_adapter *adapter);
+
 bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter,
 							struct queue *uuids);
 bool btd_adapter_is_uuid_allowed(struct btd_adapter *adapter,
diff --git a/src/shared/aosp.c b/src/shared/aosp.c
new file mode 100644
index 000000000..132389600
--- /dev/null
+++ b/src/shared/aosp.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021 Google LLC
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/aosp.h"
+#include "src/shared/util.h"
+
+static struct {
+	aosp_debug_func_t callback;
+	void *data;
+} aosp_debug;
+
+void aosp_set_debug(aosp_debug_func_t callback, void *user_data)
+{
+	aosp_debug.callback = callback;
+	aosp_debug.data = user_data;
+}
+
+static void debug(const char *format, ...)
+{
+	va_list ap;
+	char str[256];
+
+	if (!aosp_debug.callback || !aosp_debug.data)
+		return;
+
+	va_start(ap, format);
+	vsnprintf(str, sizeof(str), format, ap);
+	aosp_debug.callback(str, aosp_debug.data);
+	va_end(ap);
+}
+
+static void print_quality_report_evt(const struct aosp_bqr *bqr)
+{
+	debug("AOSP Quality Report");
+	debug("  quality_report_id %u", bqr->quality_report_id);
+	debug("  packet_type %u", bqr->packet_type);
+	debug("  conn_handle %u", bqr->conn_handle);
+	debug("  conn_role %u", bqr->conn_role);
+	debug("  tx_power_level %d", bqr->tx_power_level);
+	debug("  rssi %d", bqr->rssi);
+	debug("  snr %u", bqr->snr);
+	debug("  unused_afh_channel_count %u", bqr->unused_afh_channel_count);
+	debug("  afh_select_unideal_channel_count %u",
+					bqr->afh_select_unideal_channel_count);
+	debug("  lsto %.2f", bqr->lsto * 0.625);
+	debug("  conn_piconet_clock %.2f", bqr->conn_piconet_clock * 0.3125);
+	debug("  retransmission_count %u", bqr->retransmission_count);
+	debug("  no_rx_count %u", bqr->no_rx_count);
+	debug("  nak_count %u", bqr->nak_count);
+	debug("  last_tx_ack_timestamp %.2f", bqr->last_tx_ack_timestamp *
+					0.3125);
+	debug("  flow_off_count %u", bqr->flow_off_count);
+	debug("  last_flow_on_timestamp %.2f", bqr->last_flow_on_timestamp *
+					0.3125);
+	debug("  buffer_overflow_bytes %u", bqr->buffer_overflow_bytes);
+	debug("  buffer_underflow_bytes %u", bqr->buffer_underflow_bytes);
+}
+
+bool process_aosp_quality_report(const struct mgmt_ev_quality_report *ev)
+{
+	const struct aosp_bqr *ev_report;
+	struct aosp_bqr bqr;
+
+	if (ev->report_len < sizeof(struct aosp_bqr)) {
+		debug("error: AOSP report size %u too small (expect >= %u).",
+				ev->report_len, sizeof(struct aosp_bqr));
+		return false;
+	}
+
+	ev_report = (struct aosp_bqr *)ev->report;
+
+	/* Ignore the Vendor Specific Parameter (VSP) field for now
+	 * due to the lack of standard way of reading it.
+	 */
+	bqr.quality_report_id = ev_report->quality_report_id;
+	bqr.packet_type = ev_report->packet_type;
+	bqr.conn_handle = btohs(ev_report->conn_handle);
+	bqr.conn_role = ev_report->conn_role;
+	bqr.tx_power_level = ev_report->tx_power_level;
+	bqr.rssi = ev_report->rssi;
+	bqr.snr = ev_report->snr;
+	bqr.unused_afh_channel_count = ev_report->unused_afh_channel_count;
+	bqr.afh_select_unideal_channel_count =
+				ev_report->afh_select_unideal_channel_count;
+	bqr.lsto = btohs(ev_report->lsto);
+	bqr.conn_piconet_clock = btohl(ev_report->conn_piconet_clock);
+	bqr.retransmission_count = btohl(ev_report->retransmission_count);
+	bqr.no_rx_count = btohl(ev_report->no_rx_count);
+	bqr.nak_count = btohl(ev_report->nak_count);
+	bqr.last_tx_ack_timestamp = btohl(ev_report->last_tx_ack_timestamp);
+	bqr.flow_off_count = btohl(ev_report->flow_off_count);
+	bqr.last_flow_on_timestamp = btohl(ev_report->last_flow_on_timestamp);
+	bqr.buffer_overflow_bytes = btohl(ev_report->buffer_overflow_bytes);
+	bqr.buffer_underflow_bytes = btohl(ev_report->buffer_underflow_bytes);
+
+	print_quality_report_evt(&bqr);
+
+	return true;
+}
diff --git a/src/shared/aosp.h b/src/shared/aosp.h
new file mode 100644
index 000000000..e7b13f41f
--- /dev/null
+++ b/src/shared/aosp.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021 Google LLC
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifndef __AOSP_H
+#define __AOSP_H
+
+#include <stdbool.h>
+
+struct mgmt_ev_quality_report;
+
+struct aosp_bqr {
+	uint8_t quality_report_id;
+	uint8_t packet_type;
+	uint16_t conn_handle;
+	uint8_t conn_role;
+	int8_t tx_power_level;			/* -30  to 20 dbm */
+	int8_t rssi;				/* -127 to 20 dbm */
+	uint8_t snr;				/* db */
+	uint8_t unused_afh_channel_count;
+	uint8_t afh_select_unideal_channel_count;
+	uint16_t lsto;
+	uint32_t conn_piconet_clock;
+	uint32_t retransmission_count;
+	uint32_t no_rx_count;
+	uint32_t nak_count;
+	uint32_t last_tx_ack_timestamp;
+	uint32_t flow_off_count;
+	uint32_t last_flow_on_timestamp;
+	uint32_t buffer_overflow_bytes;
+	uint32_t buffer_underflow_bytes;
+
+	uint8_t vsp[0];			/* Vendor Specific Parameter */
+} __packed;
+
+typedef void (*aosp_debug_func_t)(const char *str, void *user_data);
+void aosp_set_debug(aosp_debug_func_t callback, void *user_data);
+
+bool process_aosp_quality_report(const struct mgmt_ev_quality_report *ev);
+
+#endif /* __AOSP_H */
-- 
2.35.0.rc0.227.g00780c9af4-goog


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT
  2022-01-21 11:24 [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT Joseph Hwang
@ 2022-01-21 11:24 ` Joseph Hwang
  2022-01-21 21:11   ` Luiz Augusto von Dentz
  2022-01-21 13:30 ` [BlueZ,v1,1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT bluez.test.bot
  2022-01-21 21:06 ` [BlueZ PATCH v1 1/2] " Luiz Augusto von Dentz
  2 siblings, 1 reply; 5+ messages in thread
From: Joseph Hwang @ 2022-01-21 11:24 UTC (permalink / raw)
  To: linux-bluetooth, marcel, luiz.dentz, pali
  Cc: chromeos-bluetooth-upstreaming, josephsih, Joseph Hwang, Archie Pusaka

This patch supports a new MGMT event of Intel telemetry report.

An ACL telemetry report looks like:

quality: Intel Extended Telemetry Event
quality:   ACL connection handle: 0x0100
quality:   Rx HEC errors: 0
quality:   Rx CRC errors: 0
quality:   Packets from host: 424
quality:   Tx packets: 425
quality:   Tx packets 0 retries: 391
quality:   Tx packets 1 retries: 33
quality:   Tx packets 2 retries: 1
quality:   Tx packets 3 retries: 0
quality:   Tx packets 4 retries: 0
quality:   Tx DH1 packets: 0
quality:   Tx DH3 packets: 0
quality:   Tx DH5 packets: 0
quality:   Tx 2DH1 packets: 0
quality:   Tx 2DH3 packets: 0
quality:   Tx 2DH5 packets: 0
quality:   Tx 3DH1 packets: 9
quality:   Tx 3DH3 packets: 0
quality:   Tx 3DH5 packets: 415
quality:   Rx packets: 1568
quality:   ACL link throughput: 34071
quality:   ACL max packet latency: 44375
quality:   ACL avg packet latency: 13

A SCO telemetry report looks like:

quality: Intel Extended Telemetry Event
quality:   SCO connection handle: 0x010a
quality:   Packets from host: 667
quality:   Tx packets: 667
quality:   Rx payload lost: 5
quality:   Tx payload lost: 36
quality:   Rx No SYNC errors (slot 0): 11
quality:   Rx No SYNC errors (slot 1): 32
quality:   Rx No SYNC errors (slot 2): 33
quality:   Rx No SYNC errors (slot 3): 13
quality:   Rx No SYNC errors (slot 4): 0
quality:   Rx HEC errors (slot 0): 9
quality:   Rx HEC errors (slot 1): 4
quality:   Rx HEC errors (slot 2): 0
quality:   Rx HEC errors (slot 3): 0
quality:   Rx HEC errors (slot 4): 0
quality:   Rx CRC errors (slot 0): 9
quality:   Rx CRC errors (slot 1): 2
quality:   Rx CRC errors (slot 2): 0
quality:   Rx CRC errors (slot 3): 0
quality:   Rx CRC errors (slot 4): 0
quality:   Rx NAK errors (slot 0): 647
quality:   Rx NAK errors (slot 1): 14
quality:   Rx NAK errors (slot 2): 2
quality:   Rx NAK errors (slot 3): 0
quality:   Rx NAK errors (slot 4): 0
quality:   Failed Tx due to Wifi coex (slot 0): 0
quality:   Failed Tx due to Wifi coex (slot 1): 0
quality:   Failed Tx due to Wifi coex (slot 2): 0
quality:   Failed Tx due to Wifi coex (slot 3): 0
quality:   Failed Tx due to Wifi coex (slot 4): 0
quality:   Failed Rx due to Wifi coex (slot 0): 0
quality:   Failed Rx due to Wifi coex (slot 1): 0
quality:   Failed Rx due to Wifi coex (slot 2): 0
quality:   Failed Rx due to Wifi coex (slot 3): 0
quality:   Failed Rx due to Wifi coex (slot 4): 0
quality:   Late samples inserted based on CDC: 0
quality:   Samples dropped: 0
quality:   Mute samples sent at initial connection: 0
quality:   PLC injection data: 0

Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
---

 Makefile.am        |   3 +-
 src/adapter.c      |   9 +-
 src/shared/intel.c | 327 +++++++++++++++++++++++++++++++++++++++++++++
 src/shared/intel.h | 155 +++++++++++++++++++++
 4 files changed, 492 insertions(+), 2 deletions(-)
 create mode 100644 src/shared/intel.c
 create mode 100644 src/shared/intel.h

diff --git a/Makefile.am b/Makefile.am
index baab40369..abbe3897b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -231,7 +231,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
 			src/shared/gap.h src/shared/gap.c \
 			src/shared/log.h src/shared/log.c \
 			src/shared/tty.h \
-			src/shared/aosp.h src/shared/aosp.c
+			src/shared/aosp.h src/shared/aosp.c \
+			src/shared/intel.h src/shared/intel.c
 
 if READLINE
 shared_sources += src/shared/shell.c src/shared/shell.h
diff --git a/src/adapter.c b/src/adapter.c
index 03f0e1ca6..6f3bf495d 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -48,6 +48,7 @@
 #include "src/shared/gatt-db.h"
 #include "src/shared/timeout.h"
 #include "src/shared/aosp.h"
+#include "src/shared/intel.h"
 
 #include "btio/btio.h"
 #include "btd.h"
@@ -9331,6 +9332,9 @@ static void quality_report_callback(uint16_t index, uint16_t length,
 	if (ev->quality_spec == QUALITY_SPEC_AOSP_BQR) {
 		if (!process_aosp_quality_report(ev))
 			error("processing aosp quality report");
+	} else if (ev->quality_spec == QUALITY_SPEC_INTEL_TELEMETRY) {
+		if (!process_intel_telemetry_report(ev))
+			error("processing intel telemetry report");
 	} else {
 		error("quality report spec %u not supported.",
 			ev->quality_spec);
@@ -9929,7 +9933,10 @@ static void quality_report_debug(const char *str, void *user_data)
 
 static void quality_set_debug(struct btd_adapter *adapter)
 {
-	aosp_set_debug(quality_report_debug, "quality: ");
+	if (is_manufacturer_intel(adapter->manufacturer))
+		intel_set_debug(quality_report_debug, "quality: ");
+	else
+		aosp_set_debug(quality_report_debug, "quality: ");
 }
 
 static void read_info_complete(uint8_t status, uint16_t length,
diff --git a/src/shared/intel.c b/src/shared/intel.c
new file mode 100644
index 000000000..87cc300dc
--- /dev/null
+++ b/src/shared/intel.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021 Google LLC
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/intel.h"
+#include "src/shared/util.h"
+
+#define COMPANY_ID_INTEL	0x0002
+
+struct intel_ext_telemetry_event tev;
+
+static struct {
+	intel_debug_func_t callback;
+	void *data;
+} intel_debug;
+
+static char *bredr_packet_type[INTEL_NUM_PACKET_TYPES] = {
+	"DH1",
+	"DH3",
+	"DH5",
+	"2DH1",
+	"2DH3",
+	"2DH5",
+	"3DH1",
+	"3DH3",
+	"3DH5",
+};
+
+/* Use offsetof to access the attributes of structures. This makes
+ * simple traversing and assigning values to the attributes.
+ */
+#define TELEM_OFFSET(a)		offsetof(struct intel_ext_telemetry_event, a)
+#define TELEM_ATTR(a)		(((uint8_t *)&tev) + TELEM_OFFSET(a))
+
+#define ACL_OFFSET(a)		offsetof(struct intel_acl_event, a)
+#define ACL_ATTR(a)		(((uint8_t *)&tev.conn.acl) + ACL_OFFSET(a))
+#define ACL_ATTR_ARRAY(a, i)	(ACL_ATTR(a) + i * sizeof(tev.conn.acl.a[0]))
+
+#define SCO_OFFSET(a)		offsetof(struct intel_sco_event, a)
+#define SCO_ATTR(a)		(((uint8_t *)&tev.conn.sco) + SCO_OFFSET(a))
+
+static const struct intel_ext_subevent {
+	uint8_t id;
+	uint8_t size;
+	uint8_t elements;
+	uint8_t *attr;  /* address of the attribute in tev */
+} intel_ext_subevent_table[] = {
+	{ 0x01, 1, 1, TELEM_ATTR(telemetry_ev_type) },
+
+	/* ACL audio link quality subevents */
+	{ 0x4a, 2, 1, ACL_ATTR(conn_handle) },
+	{ 0x4b, 4, 1, ACL_ATTR(rx_hec_error) },
+	{ 0x4c, 4, 1, ACL_ATTR(rx_crc_error) },
+	{ 0x4d, 4, 1, ACL_ATTR(packets_from_host) },
+	{ 0x4e, 4, 1, ACL_ATTR(tx_packets) },
+	{ 0x4f, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 0) },
+	{ 0x50, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 1) },
+	{ 0x51, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 2) },
+	{ 0x52, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 3) },
+	{ 0x53, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 4) },
+	{ 0x54, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 0) },
+	{ 0x55, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 1) },
+	{ 0x56, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 2) },
+	{ 0x57, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 3) },
+	{ 0x58, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 4) },
+	{ 0x59, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 5) },
+	{ 0x5a, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 6) },
+	{ 0x5b, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 7) },
+	{ 0x5c, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 8) },
+	{ 0x5d, 4, 1, ACL_ATTR(rx_packets) },
+	{ 0x5e, 4, 1, ACL_ATTR(link_throughput) },
+	{ 0x5f, 4, 1, ACL_ATTR(max_packet_letency) },
+	{ 0x60, 4, 1, ACL_ATTR(avg_packet_letency) },
+
+	/* SCO/eSCO audio link quality subevents */
+	{ 0x6a, 2, 1, SCO_ATTR(conn_handle) },
+	{ 0x6b, 4, 1, SCO_ATTR(packets_from_host) },
+	{ 0x6c, 4, 1, SCO_ATTR(tx_packets) },
+	{ 0x6d, 4, 1, SCO_ATTR(rx_payload_lost) },
+	{ 0x6e, 4, 1, SCO_ATTR(tx_payload_lost) },
+	{ 0x6f, 4, 5, SCO_ATTR(rx_no_sync_error) },
+	{ 0x70, 4, 5, SCO_ATTR(rx_hec_error) },
+	{ 0x71, 4, 5, SCO_ATTR(rx_crc_error) },
+	{ 0x72, 4, 5, SCO_ATTR(rx_nak_error) },
+	{ 0x73, 4, 5, SCO_ATTR(tx_failed_wifi_coex) },
+	{ 0x74, 4, 5, SCO_ATTR(rx_failed_wifi_coex) },
+	{ 0x75, 4, 1, SCO_ATTR(samples_inserted_by_CDC) },
+	{ 0x76, 4, 1, SCO_ATTR(samples_dropped) },
+	{ 0x77, 4, 1, SCO_ATTR(mute_samples) },
+	{ 0x78, 4, 1, SCO_ATTR(plc_injection) },
+
+	/* end */
+	{ 0x0, 0, 0 }
+};
+
+bool is_manufacturer_intel(uint16_t manufacturer)
+{
+	return manufacturer == COMPANY_ID_INTEL;
+}
+
+void intel_set_debug(intel_debug_func_t callback, void *user_data)
+{
+	intel_debug.callback = callback;
+	intel_debug.data = user_data;
+}
+
+static void debug(const char *format, ...)
+{
+	va_list ap;
+	char str[256];
+
+	if (!intel_debug.callback || !intel_debug.data)
+		return;
+
+	va_start(ap, format);
+	vsnprintf(str, sizeof(str), format, ap);
+	intel_debug.callback(str, intel_debug.data);
+	va_end(ap);
+}
+
+static void print_intel_telemetry_evt(struct intel_ext_telemetry_event *tev)
+{
+	if (!tev)
+		return;
+
+	if (tev->link_type == TELEMETRY_ACL_LINK) {
+		struct intel_acl_event *acl = &tev->conn.acl;
+		int i;
+
+		debug("Intel Extended Telemetry Event");
+		debug("  ACL connection handle: 0x%4.4x", acl->conn_handle);
+		debug("  Rx HEC errors: %u", acl->rx_hec_error);
+		debug("  Rx CRC errors: %u", acl->rx_crc_error);
+		debug("  Packets from host: %u", acl->packets_from_host);
+		debug("  Tx packets: %u", acl->tx_packets);
+
+		for (i = 0; i < INTEL_NUM_RETRIES; i++)
+			debug("  Tx packets %u retries: %u",
+						i, acl->tx_packets_retry[i]);
+
+		for (i = 0; i < INTEL_NUM_PACKET_TYPES; i++)
+			debug("  Tx %s packets: %u", bredr_packet_type[i],
+						acl->tx_packets_by_type[i]);
+
+		debug("  Rx packets: %u", acl->rx_packets);
+		debug("  ACL link throughput: %u", acl->link_throughput);
+		debug("  ACL max packet latency: %u", acl->max_packet_letency);
+		debug("  ACL avg packet latency: %u", acl->avg_packet_letency);
+
+	} else if (tev->link_type == TELEMETRY_SCO_LINK) {
+		struct intel_sco_event *sco = &tev->conn.sco;
+		int i;
+
+		debug("Intel Extended Telemetry Event");
+		debug("  SCO connection handle: 0x%4.4x", sco->conn_handle);
+		debug("  Packets from host: %u", sco->packets_from_host);
+		debug("  Tx packets: %u", sco->tx_packets);
+		debug("  Rx payload lost: %u", sco->rx_payload_lost);
+		debug("  Tx payload lost: %u", sco->tx_payload_lost);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Rx No SYNC errors (slot %u): %u",
+						i, sco->rx_no_sync_error[i]);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Rx HEC errors (slot %u): %u",
+						i, sco->rx_hec_error[i]);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Rx CRC errors (slot %u): %u",
+						i, sco->rx_crc_error[i]);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Rx NAK errors (slot %u): %u",
+						i, sco->rx_nak_error[i]);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Failed Tx due to Wifi coex (slot %u): %u",
+						i, sco->tx_failed_wifi_coex[i]);
+
+		for (i = 0; i < INTEL_NUM_SLOTS; i++)
+			debug("  Failed Rx due to Wifi coex (slot %u): %u",
+						i, sco->rx_failed_wifi_coex[i]);
+
+		debug("  Late samples inserted based on CDC: %u",
+						sco->samples_inserted_by_CDC);
+		debug("  Samples dropped: %u", sco->samples_dropped);
+		debug("  Mute samples sent at initial connection: %u",
+						sco->mute_samples);
+		debug("  PLC injection data: %u", sco->plc_injection);
+	}
+}
+
+static const struct intel_tlv *process_ext_subevent(
+					struct intel_ext_telemetry_event *tev,
+					const struct intel_tlv *tlv,
+					const struct intel_tlv *last_tlv)
+{
+	const struct intel_tlv *next_tlv = NEXT_TLV(tlv);
+	const struct intel_ext_subevent *subevent = NULL;
+	int i;
+
+	for (i = 0; intel_ext_subevent_table[i].size > 0; i++) {
+		if (intel_ext_subevent_table[i].id == tlv->id) {
+			subevent = &intel_ext_subevent_table[i];
+			break;
+		}
+	}
+
+	if (!subevent) {
+		debug("error: unknown Intel telemetry subevent 0x%2.2x",
+			tlv->id);
+		return NULL;
+	}
+
+	if (tlv->length != subevent->size * subevent->elements) {
+		debug("error: invalid length %d of subevent 0x%2.2x",
+			tlv->length, tlv->id);
+		return NULL;
+	}
+
+	if (next_tlv > last_tlv) {
+		debug("error: subevent 0x%2.2x exceeds the buffer size.",
+			tlv->id);
+		return NULL;
+	}
+
+	/* Assign tlv value to the corresponding attribute of acl/sco struct. */
+	switch (subevent->size) {
+	case 1:
+		*subevent->attr = get_u8(tlv->value);
+		break;
+
+	case 2:
+		*((uint16_t *)subevent->attr) = get_le16(tlv->value);
+		break;
+
+	case 4:
+		if (subevent->elements == 1) {
+			*((uint32_t *)subevent->attr) = get_le32(tlv->value);
+			break;
+		}
+
+		for (i = 0; i < subevent->elements; i++) {
+			/* Both acl and sco structs are __packed such that
+			 * the addresses of array elements can be calculated.
+			 */
+			*((uint32_t *)(subevent->attr + i * subevent->size)) =
+					get_le32((uint32_t *)tlv->value + i);
+		}
+		break;
+
+	default:
+		debug("error: subevent id %u: size %u not supported",
+			subevent->id, subevent->size);
+		break;
+
+	}
+
+	switch (subevent->id) {
+	case EXT_EVT_TYPE:
+		/* Only interested in the LINK_QUALITY_REPORT type for now. */
+		if (*subevent->attr != LINK_QUALITY_REPORT)
+			return NULL;
+		break;
+
+	case ACL_CONNECTION_HANDLE:
+		tev->link_type = TELEMETRY_ACL_LINK;
+		break;
+
+	case SCO_CONNECTION_HANDLE:
+		tev->link_type = TELEMETRY_SCO_LINK;
+		break;
+
+	default:
+		break;
+	}
+
+	return next_tlv;
+}
+
+bool process_intel_telemetry_report(const struct mgmt_ev_quality_report *ev)
+{
+	/* The ev->report points to a number of consecutive tlv.*/
+	const struct intel_tlv *tlv = (const struct intel_tlv *)ev->report;
+	const struct intel_tlv *last_tlv =
+			(const struct intel_tlv *)(ev->report + ev->report_len);
+
+	/* Read every tlv subevent into tev.
+	 * The decoding process terminates normally when tlv == last_tlv.
+	 */
+	memset(&tev, 0, sizeof(tev));
+	while (tlv && tlv < last_tlv)
+		tlv = process_ext_subevent(&tev, tlv, last_tlv);
+
+	/* If the decoding completes successfully, tlv would be non-NULL */
+	if (tlv) {
+		print_intel_telemetry_evt(&tev);
+		return true;
+	}
+
+	return false;
+}
diff --git a/src/shared/intel.h b/src/shared/intel.h
new file mode 100644
index 000000000..2b20f803e
--- /dev/null
+++ b/src/shared/intel.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021 Google LLC
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifndef __INTEL_H
+#define __INTEL_H
+
+#include <stdbool.h>
+
+struct mgmt_ev_quality_report;
+
+enum intel_telemetry_event_type {
+	SYSTEM_EXCEPTION,
+	FATAL_EXCEPTION,
+	DEBUG_EXCEPTION,
+	CONNECTION_EVENT,
+	DISCONNECTION_EVENT,
+	LINK_QUALITY_REPORT,
+};
+
+enum intel_telemetry_link_type {
+	TELEMETRY_UNKNOWN_LINK,
+	TELEMETRY_ACL_LINK,
+	TELEMETRY_SCO_LINK,
+};
+
+/* The subevent indices of the complete list of Intel telemetry subevents. */
+enum intel_subevt_list {
+	EXT_EVT_TYPE = 0x01,
+
+	ACL_CONNECTION_HANDLE = 0x4a,
+	ACL_HEC_ERRORS,
+	ACL_CRC_ERRORS,
+	ACL_PACKETS_FROM_HOST,
+	ACL_TX_PACKETS_TO_AIR,
+	ACL_TX_PACKETS_0_RETRY,
+	ACL_TX_PACKETS_1_RETRY,
+	ACL_TX_PACKETS_2_RETRY,
+	ACL_TX_PACKETS_3_RETRY,
+	ACL_TX_PACKETS_MORE_RETRY,
+	ACL_TX_PACKETS_DH1,
+	ACL_TX_PACKETS_DH3,
+	ACL_TX_PACKETS_DH5,
+	ACL_TX_PACKETS_2DH1,
+	ACL_TX_PACKETS_2DH3,
+	ACL_TX_PACKETS_2DH5,
+	ACL_TX_PACKETS_3DH1,
+	ACL_TX_PACKETS_3DH3,
+	ACL_TX_PACKETS_3DH5,
+	ACL_RX_PACKETS,
+	ACL_LINK_THROUGHPUT,
+	ACL_MAX_PACKET_LATENCY,
+	ACL_AVG_PACKET_LATENCY,
+
+	SCO_CONNECTION_HANDLE = 0x6a,
+	SCO_RX_PACKETS,
+	SCO_TX_PACKETS,
+	SCO_RX_PACKETS_LOST,
+	SCO_TX_PACKETS_LOST,
+	SCO_RX_NO_SYNC_ERROR,
+	SCO_RX_HEC_ERROR,
+	SCO_RX_CRC_ERROR,
+	SCO_RX_NAK_ERROR,
+	SCO_TX_FAILED_BY_WIFI,
+	SCO_RX_FAILED_BY_WIFI,
+	SCO_SAMPLES_INSERTED,
+	SCO_SAMPLES_DROPPED,
+	SCO_MUTE_SAMPLES,
+	SCO_PLC_INJECTION_DATA,
+};
+
+#define INTEL_NUM_SLOTS		5
+#define INTEL_NUM_RETRIES	5
+#define INTEL_NUM_PACKET_TYPES	9
+
+/* An Intel telemetry subevent is of the TLV format.
+ * - id: takes 1 byte. This is the subevent id.
+ * - length: takes 1 byte.
+ * - value: takes |length| bytes.
+ */
+struct intel_tlv {
+	uint8_t id;
+	uint8_t length;
+	uint8_t value[0];
+};
+
+#define TLV_SIZE(tlv) (*((const uint8_t *) tlv + 1) + 2 * sizeof(uint8_t))
+#define NEXT_TLV(tlv) ((const struct intel_tlv *)\
+					((const uint8_t *)tlv + TLV_SIZE(tlv)))
+
+struct intel_acl_event {
+	uint16_t conn_handle;
+	uint32_t rx_hec_error;
+	uint32_t rx_crc_error;
+	uint32_t packets_from_host;
+	uint32_t tx_packets;
+	uint32_t tx_packets_retry[INTEL_NUM_RETRIES];
+	uint32_t tx_packets_by_type[INTEL_NUM_PACKET_TYPES];
+	uint32_t rx_packets;
+	uint32_t link_throughput;
+	uint32_t max_packet_letency;
+	uint32_t avg_packet_letency;
+} __packed;
+
+struct intel_sco_event {
+	uint16_t conn_handle;
+	uint32_t packets_from_host;
+	uint32_t tx_packets;
+	uint32_t rx_payload_lost;
+	uint32_t tx_payload_lost;
+	uint32_t rx_no_sync_error[INTEL_NUM_SLOTS];
+	uint32_t rx_hec_error[INTEL_NUM_SLOTS];
+	uint32_t rx_crc_error[INTEL_NUM_SLOTS];
+	uint32_t rx_nak_error[INTEL_NUM_SLOTS];
+	uint32_t tx_failed_wifi_coex[INTEL_NUM_SLOTS];
+	uint32_t rx_failed_wifi_coex[INTEL_NUM_SLOTS];
+	uint32_t samples_inserted_by_CDC;
+	uint32_t samples_dropped;
+	uint32_t mute_samples;
+	uint32_t plc_injection;
+} __packed;
+
+struct intel_ext_telemetry_event {
+	uint8_t telemetry_ev_type; /* one in enum intel_telemetry_event_type */
+	uint8_t link_type;
+	union {
+		struct intel_sco_event sco;
+		struct intel_acl_event acl;
+	} conn;
+} __packed;
+
+typedef void (*intel_debug_func_t)(const char *str, void *user_data);
+
+bool is_manufacturer_intel(uint16_t manufacturer);
+void intel_set_debug(intel_debug_func_t callback, void *user_data);
+
+bool process_intel_telemetry_report(const struct mgmt_ev_quality_report *ev);
+
+#endif /* __INTEL_H */
-- 
2.35.0.rc0.227.g00780c9af4-goog


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* RE: [BlueZ,v1,1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT
  2022-01-21 11:24 [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT Joseph Hwang
  2022-01-21 11:24 ` [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Joseph Hwang
@ 2022-01-21 13:30 ` bluez.test.bot
  2022-01-21 21:06 ` [BlueZ PATCH v1 1/2] " Luiz Augusto von Dentz
  2 siblings, 0 replies; 5+ messages in thread
From: bluez.test.bot @ 2022-01-21 13:30 UTC (permalink / raw)
  To: linux-bluetooth, josephsih

[-- Attachment #1: Type: text/plain, Size: 998 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=607234

---Test result---

Test Summary:
CheckPatch                    PASS      3.93 seconds
GitLint                       PASS      2.13 seconds
Prep - Setup ELL              PASS      47.63 seconds
Build - Prep                  PASS      0.70 seconds
Build - Configure             PASS      9.44 seconds
Build - Make                  PASS      1425.43 seconds
Make Check                    PASS      12.99 seconds
Make Check w/Valgrind         PASS      513.54 seconds
Make Distcheck                PASS      239.71 seconds
Build w/ext ELL - Configure   PASS      8.79 seconds
Build w/ext ELL - Make        PASS      1279.33 seconds
Incremental Build with patchesPASS      2864.90 seconds



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT
  2022-01-21 11:24 [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT Joseph Hwang
  2022-01-21 11:24 ` [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Joseph Hwang
  2022-01-21 13:30 ` [BlueZ,v1,1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT bluez.test.bot
@ 2022-01-21 21:06 ` Luiz Augusto von Dentz
  2 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2022-01-21 21:06 UTC (permalink / raw)
  To: Joseph Hwang
  Cc: linux-bluetooth, Marcel Holtmann, Pali Rohár,
	ChromeOS Bluetooth Upstreaming, Joseph Hwang, Archie Pusaka

Hi Joseph,

On Fri, Jan 21, 2022 at 3:24 AM Joseph Hwang <josephsih@chromium.org> wrote:
>
> This patch supports a new MGMT event of AOSP bluetooth quality report.
>
> An AOSP quality report looks like:
>
> AOSP Quality Report
>   quality_report_id 1
>   packet_type 23
>   conn_handle 2
>   conn_role 1
>   tx_power_level 5
>   rssi -36
>   snr 0
>   unused_afh_channel_count 0
>   afh_select_unideal_channel_count 0
>   lsto 20000.00
>   conn_piconet_clock 63486046.56
>   retransmission_count 1638
>   no_rx_count 1638
>   nak_count 0
>   last_tx_ack_timestamp 115636.25
>   flow_off_count 0
>   last_flow_on_timestamp 63368203.12
>   buffer_overflow_bytes 0
>   buffer_underflow_bytes 0
>
> Reviewed-by: Archie Pusaka <apusaka@chromium.org>
> Signed-off-by: Joseph Hwang <josephsih@chromium.org>
> ---
>
>  Makefile.am       |   3 +-
>  lib/mgmt.h        |  10 ++++
>  src/adapter.c     |  58 ++++++++++++++++++++++
>  src/adapter.h     |   2 +
>  src/shared/aosp.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++
>  src/shared/aosp.h |  57 +++++++++++++++++++++
>  6 files changed, 253 insertions(+), 1 deletion(-)
>  create mode 100644 src/shared/aosp.c
>  create mode 100644 src/shared/aosp.h
>
> diff --git a/Makefile.am b/Makefile.am
> index e391d7ae8..baab40369 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -230,7 +230,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
>                         src/shared/gatt-db.h src/shared/gatt-db.c \
>                         src/shared/gap.h src/shared/gap.c \
>                         src/shared/log.h src/shared/log.c \
> -                       src/shared/tty.h
> +                       src/shared/tty.h \
> +                       src/shared/aosp.h src/shared/aosp.c
>
>  if READLINE
>  shared_sources += src/shared/shell.c src/shared/shell.h
> diff --git a/lib/mgmt.h b/lib/mgmt.h
> index 922a24367..1caecc43e 100644
> --- a/lib/mgmt.h
> +++ b/lib/mgmt.h
> @@ -1032,6 +1032,16 @@ struct mgmt_ev_adv_monitor_device_lost {
>         struct mgmt_addr_info addr;
>  } __packed;
>
> +#define MGMT_EV_QUALITY_REPORT                 0x0031
> +#define QUALITY_SPEC_NA                                0x0
> +#define QUALITY_SPEC_INTEL_TELEMETRY           0x1
> +#define QUALITY_SPEC_AOSP_BQR                  0x2
> +struct mgmt_ev_quality_report {
> +       uint8_t quality_spec;
> +       uint8_t report_len;
> +       uint8_t report[0];
> +} __packed;
> +

This should probably be introduced separately, and I do expect it to
be properly decoded by the likes of btmon.

>  static const char *mgmt_op[] = {
>         "<0x0000>",
>         "Read Version",
> diff --git a/src/adapter.c b/src/adapter.c
> index 9772e843a..03f0e1ca6 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -47,6 +47,7 @@
>  #include "src/shared/att.h"
>  #include "src/shared/gatt-db.h"
>  #include "src/shared/timeout.h"
> +#include "src/shared/aosp.h"
>
>  #include "btio/btio.h"
>  #include "btd.h"
> @@ -9312,6 +9313,30 @@ static void controller_resume_callback(uint16_t index, uint16_t length,
>         controller_resume_notify(adapter);
>  }
>
> +static void quality_report_callback(uint16_t index, uint16_t length,
> +                                       const void *param, void *user_data)
> +{
> +       const struct mgmt_ev_quality_report *ev = param;
> +       struct btd_adapter *adapter = user_data;
> +
> +       if (!ev)
> +               return;
> +
> +       if (length < sizeof(*ev)) {
> +               btd_error(adapter->dev_id,
> +                               "MGMT_EV_QUALITY_REPORT event too small");
> +               return;
> +       }
> +
> +       if (ev->quality_spec == QUALITY_SPEC_AOSP_BQR) {
> +               if (!process_aosp_quality_report(ev))
> +                       error("processing aosp quality report");
> +       } else {
> +               error("quality report spec %u not supported.",
> +                       ev->quality_spec);
> +       }
> +}
> +
>  static void device_blocked_callback(uint16_t index, uint16_t length,
>                                         const void *param, void *user_data)
>  {
> @@ -9727,6 +9752,19 @@ static void le_simult_central_peripheral_func(struct btd_adapter *adapter,
>                                 (void *)le_simult_central_peripheral_uuid.val);
>  }
>
> +static bool is_exp_feature_uuid_the_same(const void *data,
> +                                                       const void *match_data)
> +{
> +       return memcmp(data, match_data,
> +                       sizeof(((struct mgmt_exp_uuid *)NULL)->val)) == 0;
> +}
> +
> +bool is_quality_report_supported(struct btd_adapter *adapter)
> +{
> +       return queue_find(adapter->exps, is_exp_feature_uuid_the_same,
> +                               (void *)quality_report_uuid.val) != NULL;
> +}
> +
>  static void quality_report_func(struct btd_adapter *adapter, uint8_t action)
>  {
>         if (action)
> @@ -9882,6 +9920,18 @@ static void read_exp_features(struct btd_adapter *adapter)
>         btd_error(adapter->dev_id, "Failed to read exp features info");
>  }
>
> +static void quality_report_debug(const char *str, void *user_data)
> +{
> +       const char *prefix = user_data;
> +
> +       info("%s%s", prefix, str);
> +}
> +
> +static void quality_set_debug(struct btd_adapter *adapter)
> +{
> +       aosp_set_debug(quality_report_debug, "quality: ");
> +}
> +
>  static void read_info_complete(uint8_t status, uint16_t length,
>                                         const void *param, void *user_data)
>  {
> @@ -10110,6 +10160,11 @@ static void read_info_complete(uint8_t status, uint16_t length,
>                                                 controller_resume_callback,
>                                                 adapter, NULL);
>
> +       mgmt_register(adapter->mgmt, MGMT_EV_QUALITY_REPORT,
> +                                               adapter->dev_id,
> +                                               quality_report_callback,
> +                                               adapter, NULL);
> +
>         set_dev_class(adapter);
>
>         set_name(adapter, btd_adapter_get_name(adapter));
> @@ -10137,6 +10192,9 @@ static void read_info_complete(uint8_t status, uint16_t length,
>         if (btd_adapter_get_powered(adapter))
>                 adapter_start(adapter);
>
> +       if (is_quality_report_supported(adapter) && getenv("QUALITY_DEBUG"))
> +               quality_set_debug(adapter);
> +
>         return;
>
>  failed:
> diff --git a/src/adapter.h b/src/adapter.h
> index 35deb1d11..c199e358a 100644
> --- a/src/adapter.h
> +++ b/src/adapter.h
> @@ -266,6 +266,8 @@ enum kernel_features {
>
>  bool btd_has_kernel_features(uint32_t feature);
>
> +bool is_quality_report_supported(struct btd_adapter *adapter);
> +
>  bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter,
>                                                         struct queue *uuids);
>  bool btd_adapter_is_uuid_allowed(struct btd_adapter *adapter,
> diff --git a/src/shared/aosp.c b/src/shared/aosp.c
> new file mode 100644
> index 000000000..132389600
> --- /dev/null
> +++ b/src/shared/aosp.c
> @@ -0,0 +1,124 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2021 Google LLC
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + */
> +
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include "lib/bluetooth.h"
> +#include "lib/mgmt.h"
> +
> +#include "src/shared/aosp.h"
> +#include "src/shared/util.h"
> +
> +static struct {
> +       aosp_debug_func_t callback;
> +       void *data;
> +} aosp_debug;
> +
> +void aosp_set_debug(aosp_debug_func_t callback, void *user_data)
> +{
> +       aosp_debug.callback = callback;
> +       aosp_debug.data = user_data;
> +}
> +
> +static void debug(const char *format, ...)
> +{
> +       va_list ap;
> +       char str[256];
> +
> +       if (!aosp_debug.callback || !aosp_debug.data)
> +               return;
> +
> +       va_start(ap, format);
> +       vsnprintf(str, sizeof(str), format, ap);
> +       aosp_debug.callback(str, aosp_debug.data);
> +       va_end(ap);
> +}
> +
> +static void print_quality_report_evt(const struct aosp_bqr *bqr)
> +{
> +       debug("AOSP Quality Report");
> +       debug("  quality_report_id %u", bqr->quality_report_id);
> +       debug("  packet_type %u", bqr->packet_type);
> +       debug("  conn_handle %u", bqr->conn_handle);
> +       debug("  conn_role %u", bqr->conn_role);
> +       debug("  tx_power_level %d", bqr->tx_power_level);
> +       debug("  rssi %d", bqr->rssi);
> +       debug("  snr %u", bqr->snr);
> +       debug("  unused_afh_channel_count %u", bqr->unused_afh_channel_count);
> +       debug("  afh_select_unideal_channel_count %u",
> +                                       bqr->afh_select_unideal_channel_count);
> +       debug("  lsto %.2f", bqr->lsto * 0.625);
> +       debug("  conn_piconet_clock %.2f", bqr->conn_piconet_clock * 0.3125);
> +       debug("  retransmission_count %u", bqr->retransmission_count);
> +       debug("  no_rx_count %u", bqr->no_rx_count);
> +       debug("  nak_count %u", bqr->nak_count);
> +       debug("  last_tx_ack_timestamp %.2f", bqr->last_tx_ack_timestamp *
> +                                       0.3125);
> +       debug("  flow_off_count %u", bqr->flow_off_count);
> +       debug("  last_flow_on_timestamp %.2f", bqr->last_flow_on_timestamp *
> +                                       0.3125);
> +       debug("  buffer_overflow_bytes %u", bqr->buffer_overflow_bytes);
> +       debug("  buffer_underflow_bytes %u", bqr->buffer_underflow_bytes);
> +}

There is util_debug for this sort of debugging, please use that
instead of reinventing it.

> +bool process_aosp_quality_report(const struct mgmt_ev_quality_report *ev)
> +{
> +       const struct aosp_bqr *ev_report;
> +       struct aosp_bqr bqr;
> +
> +       if (ev->report_len < sizeof(struct aosp_bqr)) {
> +               debug("error: AOSP report size %u too small (expect >= %u).",
> +                               ev->report_len, sizeof(struct aosp_bqr));
> +               return false;
> +       }
> +
> +       ev_report = (struct aosp_bqr *)ev->report;
> +
> +       /* Ignore the Vendor Specific Parameter (VSP) field for now
> +        * due to the lack of standard way of reading it.
> +        */
> +       bqr.quality_report_id = ev_report->quality_report_id;
> +       bqr.packet_type = ev_report->packet_type;
> +       bqr.conn_handle = btohs(ev_report->conn_handle);
> +       bqr.conn_role = ev_report->conn_role;
> +       bqr.tx_power_level = ev_report->tx_power_level;
> +       bqr.rssi = ev_report->rssi;
> +       bqr.snr = ev_report->snr;
> +       bqr.unused_afh_channel_count = ev_report->unused_afh_channel_count;
> +       bqr.afh_select_unideal_channel_count =
> +                               ev_report->afh_select_unideal_channel_count;
> +       bqr.lsto = btohs(ev_report->lsto);
> +       bqr.conn_piconet_clock = btohl(ev_report->conn_piconet_clock);
> +       bqr.retransmission_count = btohl(ev_report->retransmission_count);
> +       bqr.no_rx_count = btohl(ev_report->no_rx_count);
> +       bqr.nak_count = btohl(ev_report->nak_count);
> +       bqr.last_tx_ack_timestamp = btohl(ev_report->last_tx_ack_timestamp);
> +       bqr.flow_off_count = btohl(ev_report->flow_off_count);
> +       bqr.last_flow_on_timestamp = btohl(ev_report->last_flow_on_timestamp);
> +       bqr.buffer_overflow_bytes = btohl(ev_report->buffer_overflow_bytes);
> +       bqr.buffer_underflow_bytes = btohl(ev_report->buffer_underflow_bytes);
> +
> +       print_quality_report_evt(&bqr);
> +
> +       return true;
> +}

If this is just logging the event we are much better off supporting it
directly inside the likes of btmon, or you do plan to make some use of
it other than just print out the fields?

> diff --git a/src/shared/aosp.h b/src/shared/aosp.h
> new file mode 100644
> index 000000000..e7b13f41f
> --- /dev/null
> +++ b/src/shared/aosp.h
> @@ -0,0 +1,57 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2021 Google LLC
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + */
> +
> +#ifndef __AOSP_H
> +#define __AOSP_H
> +
> +#include <stdbool.h>
> +
> +struct mgmt_ev_quality_report;
> +
> +struct aosp_bqr {
> +       uint8_t quality_report_id;
> +       uint8_t packet_type;
> +       uint16_t conn_handle;
> +       uint8_t conn_role;
> +       int8_t tx_power_level;                  /* -30  to 20 dbm */
> +       int8_t rssi;                            /* -127 to 20 dbm */
> +       uint8_t snr;                            /* db */
> +       uint8_t unused_afh_channel_count;
> +       uint8_t afh_select_unideal_channel_count;
> +       uint16_t lsto;
> +       uint32_t conn_piconet_clock;
> +       uint32_t retransmission_count;
> +       uint32_t no_rx_count;
> +       uint32_t nak_count;
> +       uint32_t last_tx_ack_timestamp;
> +       uint32_t flow_off_count;
> +       uint32_t last_flow_on_timestamp;
> +       uint32_t buffer_overflow_bytes;
> +       uint32_t buffer_underflow_bytes;
> +
> +       uint8_t vsp[0];                 /* Vendor Specific Parameter */
> +} __packed;
> +
> +typedef void (*aosp_debug_func_t)(const char *str, void *user_data);
> +void aosp_set_debug(aosp_debug_func_t callback, void *user_data);
> +
> +bool process_aosp_quality_report(const struct mgmt_ev_quality_report *ev);
> +
> +#endif /* __AOSP_H */
> --
> 2.35.0.rc0.227.g00780c9af4-goog
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT
  2022-01-21 11:24 ` [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Joseph Hwang
@ 2022-01-21 21:11   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2022-01-21 21:11 UTC (permalink / raw)
  To: Joseph Hwang
  Cc: linux-bluetooth, Marcel Holtmann, Pali Rohár,
	ChromeOS Bluetooth Upstreaming, Joseph Hwang, Archie Pusaka

Hi Joseph,

On Fri, Jan 21, 2022 at 3:24 AM Joseph Hwang <josephsih@chromium.org> wrote:
>
> This patch supports a new MGMT event of Intel telemetry report.
>
> An ACL telemetry report looks like:
>
> quality: Intel Extended Telemetry Event
> quality:   ACL connection handle: 0x0100
> quality:   Rx HEC errors: 0
> quality:   Rx CRC errors: 0
> quality:   Packets from host: 424
> quality:   Tx packets: 425
> quality:   Tx packets 0 retries: 391
> quality:   Tx packets 1 retries: 33
> quality:   Tx packets 2 retries: 1
> quality:   Tx packets 3 retries: 0
> quality:   Tx packets 4 retries: 0
> quality:   Tx DH1 packets: 0
> quality:   Tx DH3 packets: 0
> quality:   Tx DH5 packets: 0
> quality:   Tx 2DH1 packets: 0
> quality:   Tx 2DH3 packets: 0
> quality:   Tx 2DH5 packets: 0
> quality:   Tx 3DH1 packets: 9
> quality:   Tx 3DH3 packets: 0
> quality:   Tx 3DH5 packets: 415
> quality:   Rx packets: 1568
> quality:   ACL link throughput: 34071
> quality:   ACL max packet latency: 44375
> quality:   ACL avg packet latency: 13
>
> A SCO telemetry report looks like:
>
> quality: Intel Extended Telemetry Event
> quality:   SCO connection handle: 0x010a
> quality:   Packets from host: 667
> quality:   Tx packets: 667
> quality:   Rx payload lost: 5
> quality:   Tx payload lost: 36
> quality:   Rx No SYNC errors (slot 0): 11
> quality:   Rx No SYNC errors (slot 1): 32
> quality:   Rx No SYNC errors (slot 2): 33
> quality:   Rx No SYNC errors (slot 3): 13
> quality:   Rx No SYNC errors (slot 4): 0
> quality:   Rx HEC errors (slot 0): 9
> quality:   Rx HEC errors (slot 1): 4
> quality:   Rx HEC errors (slot 2): 0
> quality:   Rx HEC errors (slot 3): 0
> quality:   Rx HEC errors (slot 4): 0
> quality:   Rx CRC errors (slot 0): 9
> quality:   Rx CRC errors (slot 1): 2
> quality:   Rx CRC errors (slot 2): 0
> quality:   Rx CRC errors (slot 3): 0
> quality:   Rx CRC errors (slot 4): 0
> quality:   Rx NAK errors (slot 0): 647
> quality:   Rx NAK errors (slot 1): 14
> quality:   Rx NAK errors (slot 2): 2
> quality:   Rx NAK errors (slot 3): 0
> quality:   Rx NAK errors (slot 4): 0
> quality:   Failed Tx due to Wifi coex (slot 0): 0
> quality:   Failed Tx due to Wifi coex (slot 1): 0
> quality:   Failed Tx due to Wifi coex (slot 2): 0
> quality:   Failed Tx due to Wifi coex (slot 3): 0
> quality:   Failed Tx due to Wifi coex (slot 4): 0
> quality:   Failed Rx due to Wifi coex (slot 0): 0
> quality:   Failed Rx due to Wifi coex (slot 1): 0
> quality:   Failed Rx due to Wifi coex (slot 2): 0
> quality:   Failed Rx due to Wifi coex (slot 3): 0
> quality:   Failed Rx due to Wifi coex (slot 4): 0
> quality:   Late samples inserted based on CDC: 0
> quality:   Samples dropped: 0
> quality:   Mute samples sent at initial connection: 0
> quality:   PLC injection data: 0
>
> Reviewed-by: Archie Pusaka <apusaka@chromium.org>
> Signed-off-by: Joseph Hwang <josephsih@chromium.org>
> ---
>
>  Makefile.am        |   3 +-
>  src/adapter.c      |   9 +-
>  src/shared/intel.c | 327 +++++++++++++++++++++++++++++++++++++++++++++
>  src/shared/intel.h | 155 +++++++++++++++++++++
>  4 files changed, 492 insertions(+), 2 deletions(-)
>  create mode 100644 src/shared/intel.c
>  create mode 100644 src/shared/intel.h
>
> diff --git a/Makefile.am b/Makefile.am
> index baab40369..abbe3897b 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -231,7 +231,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
>                         src/shared/gap.h src/shared/gap.c \
>                         src/shared/log.h src/shared/log.c \
>                         src/shared/tty.h \
> -                       src/shared/aosp.h src/shared/aosp.c
> +                       src/shared/aosp.h src/shared/aosp.c \
> +                       src/shared/intel.h src/shared/intel.c
>
>  if READLINE
>  shared_sources += src/shared/shell.c src/shared/shell.h
> diff --git a/src/adapter.c b/src/adapter.c
> index 03f0e1ca6..6f3bf495d 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -48,6 +48,7 @@
>  #include "src/shared/gatt-db.h"
>  #include "src/shared/timeout.h"
>  #include "src/shared/aosp.h"
> +#include "src/shared/intel.h"
>
>  #include "btio/btio.h"
>  #include "btd.h"
> @@ -9331,6 +9332,9 @@ static void quality_report_callback(uint16_t index, uint16_t length,
>         if (ev->quality_spec == QUALITY_SPEC_AOSP_BQR) {
>                 if (!process_aosp_quality_report(ev))
>                         error("processing aosp quality report");
> +       } else if (ev->quality_spec == QUALITY_SPEC_INTEL_TELEMETRY) {
> +               if (!process_intel_telemetry_report(ev))
> +                       error("processing intel telemetry report");
>         } else {
>                 error("quality report spec %u not supported.",
>                         ev->quality_spec);
> @@ -9929,7 +9933,10 @@ static void quality_report_debug(const char *str, void *user_data)
>
>  static void quality_set_debug(struct btd_adapter *adapter)
>  {
> -       aosp_set_debug(quality_report_debug, "quality: ");
> +       if (is_manufacturer_intel(adapter->manufacturer))
> +               intel_set_debug(quality_report_debug, "quality: ");
> +       else
> +               aosp_set_debug(quality_report_debug, "quality: ");
>  }
>
>  static void read_info_complete(uint8_t status, uint16_t length,
> diff --git a/src/shared/intel.c b/src/shared/intel.c
> new file mode 100644
> index 000000000..87cc300dc
> --- /dev/null
> +++ b/src/shared/intel.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2021 Google LLC
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + */
> +
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include "lib/bluetooth.h"
> +#include "lib/mgmt.h"
> +
> +#include "src/shared/intel.h"
> +#include "src/shared/util.h"
> +
> +#define COMPANY_ID_INTEL       0x0002
> +
> +struct intel_ext_telemetry_event tev;
> +
> +static struct {
> +       intel_debug_func_t callback;
> +       void *data;
> +} intel_debug;
> +
> +static char *bredr_packet_type[INTEL_NUM_PACKET_TYPES] = {
> +       "DH1",
> +       "DH3",
> +       "DH5",
> +       "2DH1",
> +       "2DH3",
> +       "2DH5",
> +       "3DH1",
> +       "3DH3",
> +       "3DH5",
> +};
> +
> +/* Use offsetof to access the attributes of structures. This makes
> + * simple traversing and assigning values to the attributes.
> + */
> +#define TELEM_OFFSET(a)                offsetof(struct intel_ext_telemetry_event, a)
> +#define TELEM_ATTR(a)          (((uint8_t *)&tev) + TELEM_OFFSET(a))
> +
> +#define ACL_OFFSET(a)          offsetof(struct intel_acl_event, a)
> +#define ACL_ATTR(a)            (((uint8_t *)&tev.conn.acl) + ACL_OFFSET(a))
> +#define ACL_ATTR_ARRAY(a, i)   (ACL_ATTR(a) + i * sizeof(tev.conn.acl.a[0]))
> +
> +#define SCO_OFFSET(a)          offsetof(struct intel_sco_event, a)
> +#define SCO_ATTR(a)            (((uint8_t *)&tev.conn.sco) + SCO_OFFSET(a))
> +
> +static const struct intel_ext_subevent {
> +       uint8_t id;
> +       uint8_t size;
> +       uint8_t elements;
> +       uint8_t *attr;  /* address of the attribute in tev */
> +} intel_ext_subevent_table[] = {
> +       { 0x01, 1, 1, TELEM_ATTR(telemetry_ev_type) },
> +
> +       /* ACL audio link quality subevents */
> +       { 0x4a, 2, 1, ACL_ATTR(conn_handle) },
> +       { 0x4b, 4, 1, ACL_ATTR(rx_hec_error) },
> +       { 0x4c, 4, 1, ACL_ATTR(rx_crc_error) },
> +       { 0x4d, 4, 1, ACL_ATTR(packets_from_host) },
> +       { 0x4e, 4, 1, ACL_ATTR(tx_packets) },
> +       { 0x4f, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 0) },
> +       { 0x50, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 1) },
> +       { 0x51, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 2) },
> +       { 0x52, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 3) },
> +       { 0x53, 4, 1, ACL_ATTR_ARRAY(tx_packets_retry, 4) },
> +       { 0x54, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 0) },
> +       { 0x55, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 1) },
> +       { 0x56, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 2) },
> +       { 0x57, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 3) },
> +       { 0x58, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 4) },
> +       { 0x59, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 5) },
> +       { 0x5a, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 6) },
> +       { 0x5b, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 7) },
> +       { 0x5c, 4, 1, ACL_ATTR_ARRAY(tx_packets_by_type, 8) },
> +       { 0x5d, 4, 1, ACL_ATTR(rx_packets) },
> +       { 0x5e, 4, 1, ACL_ATTR(link_throughput) },
> +       { 0x5f, 4, 1, ACL_ATTR(max_packet_letency) },
> +       { 0x60, 4, 1, ACL_ATTR(avg_packet_letency) },
> +
> +       /* SCO/eSCO audio link quality subevents */
> +       { 0x6a, 2, 1, SCO_ATTR(conn_handle) },
> +       { 0x6b, 4, 1, SCO_ATTR(packets_from_host) },
> +       { 0x6c, 4, 1, SCO_ATTR(tx_packets) },
> +       { 0x6d, 4, 1, SCO_ATTR(rx_payload_lost) },
> +       { 0x6e, 4, 1, SCO_ATTR(tx_payload_lost) },
> +       { 0x6f, 4, 5, SCO_ATTR(rx_no_sync_error) },
> +       { 0x70, 4, 5, SCO_ATTR(rx_hec_error) },
> +       { 0x71, 4, 5, SCO_ATTR(rx_crc_error) },
> +       { 0x72, 4, 5, SCO_ATTR(rx_nak_error) },
> +       { 0x73, 4, 5, SCO_ATTR(tx_failed_wifi_coex) },
> +       { 0x74, 4, 5, SCO_ATTR(rx_failed_wifi_coex) },
> +       { 0x75, 4, 1, SCO_ATTR(samples_inserted_by_CDC) },
> +       { 0x76, 4, 1, SCO_ATTR(samples_dropped) },
> +       { 0x77, 4, 1, SCO_ATTR(mute_samples) },
> +       { 0x78, 4, 1, SCO_ATTR(plc_injection) },
> +
> +       /* end */
> +       { 0x0, 0, 0 }
> +};
> +
> +bool is_manufacturer_intel(uint16_t manufacturer)
> +{
> +       return manufacturer == COMPANY_ID_INTEL;
> +}
> +
> +void intel_set_debug(intel_debug_func_t callback, void *user_data)
> +{
> +       intel_debug.callback = callback;
> +       intel_debug.data = user_data;
> +}
> +
> +static void debug(const char *format, ...)
> +{
> +       va_list ap;
> +       char str[256];
> +
> +       if (!intel_debug.callback || !intel_debug.data)
> +               return;
> +
> +       va_start(ap, format);
> +       vsnprintf(str, sizeof(str), format, ap);
> +       intel_debug.callback(str, intel_debug.data);
> +       va_end(ap);
> +}
> +
> +static void print_intel_telemetry_evt(struct intel_ext_telemetry_event *tev)
> +{
> +       if (!tev)
> +               return;
> +
> +       if (tev->link_type == TELEMETRY_ACL_LINK) {
> +               struct intel_acl_event *acl = &tev->conn.acl;
> +               int i;
> +
> +               debug("Intel Extended Telemetry Event");
> +               debug("  ACL connection handle: 0x%4.4x", acl->conn_handle);
> +               debug("  Rx HEC errors: %u", acl->rx_hec_error);
> +               debug("  Rx CRC errors: %u", acl->rx_crc_error);
> +               debug("  Packets from host: %u", acl->packets_from_host);
> +               debug("  Tx packets: %u", acl->tx_packets);
> +
> +               for (i = 0; i < INTEL_NUM_RETRIES; i++)
> +                       debug("  Tx packets %u retries: %u",
> +                                               i, acl->tx_packets_retry[i]);
> +
> +               for (i = 0; i < INTEL_NUM_PACKET_TYPES; i++)
> +                       debug("  Tx %s packets: %u", bredr_packet_type[i],
> +                                               acl->tx_packets_by_type[i]);
> +
> +               debug("  Rx packets: %u", acl->rx_packets);
> +               debug("  ACL link throughput: %u", acl->link_throughput);
> +               debug("  ACL max packet latency: %u", acl->max_packet_letency);
> +               debug("  ACL avg packet latency: %u", acl->avg_packet_letency);
> +
> +       } else if (tev->link_type == TELEMETRY_SCO_LINK) {
> +               struct intel_sco_event *sco = &tev->conn.sco;
> +               int i;
> +
> +               debug("Intel Extended Telemetry Event");
> +               debug("  SCO connection handle: 0x%4.4x", sco->conn_handle);
> +               debug("  Packets from host: %u", sco->packets_from_host);
> +               debug("  Tx packets: %u", sco->tx_packets);
> +               debug("  Rx payload lost: %u", sco->rx_payload_lost);
> +               debug("  Tx payload lost: %u", sco->tx_payload_lost);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Rx No SYNC errors (slot %u): %u",
> +                                               i, sco->rx_no_sync_error[i]);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Rx HEC errors (slot %u): %u",
> +                                               i, sco->rx_hec_error[i]);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Rx CRC errors (slot %u): %u",
> +                                               i, sco->rx_crc_error[i]);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Rx NAK errors (slot %u): %u",
> +                                               i, sco->rx_nak_error[i]);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Failed Tx due to Wifi coex (slot %u): %u",
> +                                               i, sco->tx_failed_wifi_coex[i]);
> +
> +               for (i = 0; i < INTEL_NUM_SLOTS; i++)
> +                       debug("  Failed Rx due to Wifi coex (slot %u): %u",
> +                                               i, sco->rx_failed_wifi_coex[i]);
> +
> +               debug("  Late samples inserted based on CDC: %u",
> +                                               sco->samples_inserted_by_CDC);
> +               debug("  Samples dropped: %u", sco->samples_dropped);
> +               debug("  Mute samples sent at initial connection: %u",
> +                                               sco->mute_samples);
> +               debug("  PLC injection data: %u", sco->plc_injection);
> +       }
> +}
> +
> +static const struct intel_tlv *process_ext_subevent(
> +                                       struct intel_ext_telemetry_event *tev,
> +                                       const struct intel_tlv *tlv,
> +                                       const struct intel_tlv *last_tlv)
> +{
> +       const struct intel_tlv *next_tlv = NEXT_TLV(tlv);
> +       const struct intel_ext_subevent *subevent = NULL;
> +       int i;
> +
> +       for (i = 0; intel_ext_subevent_table[i].size > 0; i++) {
> +               if (intel_ext_subevent_table[i].id == tlv->id) {
> +                       subevent = &intel_ext_subevent_table[i];
> +                       break;
> +               }
> +       }
> +
> +       if (!subevent) {
> +               debug("error: unknown Intel telemetry subevent 0x%2.2x",
> +                       tlv->id);
> +               return NULL;
> +       }
> +
> +       if (tlv->length != subevent->size * subevent->elements) {
> +               debug("error: invalid length %d of subevent 0x%2.2x",
> +                       tlv->length, tlv->id);
> +               return NULL;
> +       }
> +
> +       if (next_tlv > last_tlv) {
> +               debug("error: subevent 0x%2.2x exceeds the buffer size.",
> +                       tlv->id);
> +               return NULL;
> +       }
> +
> +       /* Assign tlv value to the corresponding attribute of acl/sco struct. */
> +       switch (subevent->size) {
> +       case 1:
> +               *subevent->attr = get_u8(tlv->value);
> +               break;
> +
> +       case 2:
> +               *((uint16_t *)subevent->attr) = get_le16(tlv->value);
> +               break;
> +
> +       case 4:
> +               if (subevent->elements == 1) {
> +                       *((uint32_t *)subevent->attr) = get_le32(tlv->value);
> +                       break;
> +               }
> +
> +               for (i = 0; i < subevent->elements; i++) {
> +                       /* Both acl and sco structs are __packed such that
> +                        * the addresses of array elements can be calculated.
> +                        */
> +                       *((uint32_t *)(subevent->attr + i * subevent->size)) =
> +                                       get_le32((uint32_t *)tlv->value + i);
> +               }
> +               break;
> +
> +       default:
> +               debug("error: subevent id %u: size %u not supported",
> +                       subevent->id, subevent->size);
> +               break;
> +
> +       }
> +
> +       switch (subevent->id) {
> +       case EXT_EVT_TYPE:
> +               /* Only interested in the LINK_QUALITY_REPORT type for now. */
> +               if (*subevent->attr != LINK_QUALITY_REPORT)
> +                       return NULL;
> +               break;
> +
> +       case ACL_CONNECTION_HANDLE:
> +               tev->link_type = TELEMETRY_ACL_LINK;
> +               break;
> +
> +       case SCO_CONNECTION_HANDLE:
> +               tev->link_type = TELEMETRY_SCO_LINK;
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       return next_tlv;
> +}
> +
> +bool process_intel_telemetry_report(const struct mgmt_ev_quality_report *ev)
> +{
> +       /* The ev->report points to a number of consecutive tlv.*/
> +       const struct intel_tlv *tlv = (const struct intel_tlv *)ev->report;
> +       const struct intel_tlv *last_tlv =
> +                       (const struct intel_tlv *)(ev->report + ev->report_len);
> +
> +       /* Read every tlv subevent into tev.
> +        * The decoding process terminates normally when tlv == last_tlv.
> +        */
> +       memset(&tev, 0, sizeof(tev));
> +       while (tlv && tlv < last_tlv)
> +               tlv = process_ext_subevent(&tev, tlv, last_tlv);
> +
> +       /* If the decoding completes successfully, tlv would be non-NULL */
> +       if (tlv) {
> +               print_intel_telemetry_evt(&tev);
> +               return true;
> +       }
> +
> +       return false;
> +}
> diff --git a/src/shared/intel.h b/src/shared/intel.h
> new file mode 100644
> index 000000000..2b20f803e
> --- /dev/null
> +++ b/src/shared/intel.h
> @@ -0,0 +1,155 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2021 Google LLC
> + *
> + *
> + *  This program is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + */
> +
> +#ifndef __INTEL_H
> +#define __INTEL_H
> +
> +#include <stdbool.h>
> +
> +struct mgmt_ev_quality_report;
> +
> +enum intel_telemetry_event_type {
> +       SYSTEM_EXCEPTION,
> +       FATAL_EXCEPTION,
> +       DEBUG_EXCEPTION,
> +       CONNECTION_EVENT,
> +       DISCONNECTION_EVENT,
> +       LINK_QUALITY_REPORT,
> +};
> +
> +enum intel_telemetry_link_type {
> +       TELEMETRY_UNKNOWN_LINK,
> +       TELEMETRY_ACL_LINK,
> +       TELEMETRY_SCO_LINK,
> +};
> +
> +/* The subevent indices of the complete list of Intel telemetry subevents. */
> +enum intel_subevt_list {
> +       EXT_EVT_TYPE = 0x01,
> +
> +       ACL_CONNECTION_HANDLE = 0x4a,
> +       ACL_HEC_ERRORS,
> +       ACL_CRC_ERRORS,
> +       ACL_PACKETS_FROM_HOST,
> +       ACL_TX_PACKETS_TO_AIR,
> +       ACL_TX_PACKETS_0_RETRY,
> +       ACL_TX_PACKETS_1_RETRY,
> +       ACL_TX_PACKETS_2_RETRY,
> +       ACL_TX_PACKETS_3_RETRY,
> +       ACL_TX_PACKETS_MORE_RETRY,
> +       ACL_TX_PACKETS_DH1,
> +       ACL_TX_PACKETS_DH3,
> +       ACL_TX_PACKETS_DH5,
> +       ACL_TX_PACKETS_2DH1,
> +       ACL_TX_PACKETS_2DH3,
> +       ACL_TX_PACKETS_2DH5,
> +       ACL_TX_PACKETS_3DH1,
> +       ACL_TX_PACKETS_3DH3,
> +       ACL_TX_PACKETS_3DH5,
> +       ACL_RX_PACKETS,
> +       ACL_LINK_THROUGHPUT,
> +       ACL_MAX_PACKET_LATENCY,
> +       ACL_AVG_PACKET_LATENCY,
> +
> +       SCO_CONNECTION_HANDLE = 0x6a,
> +       SCO_RX_PACKETS,
> +       SCO_TX_PACKETS,
> +       SCO_RX_PACKETS_LOST,
> +       SCO_TX_PACKETS_LOST,
> +       SCO_RX_NO_SYNC_ERROR,
> +       SCO_RX_HEC_ERROR,
> +       SCO_RX_CRC_ERROR,
> +       SCO_RX_NAK_ERROR,
> +       SCO_TX_FAILED_BY_WIFI,
> +       SCO_RX_FAILED_BY_WIFI,
> +       SCO_SAMPLES_INSERTED,
> +       SCO_SAMPLES_DROPPED,
> +       SCO_MUTE_SAMPLES,
> +       SCO_PLC_INJECTION_DATA,
> +};
> +
> +#define INTEL_NUM_SLOTS                5
> +#define INTEL_NUM_RETRIES      5
> +#define INTEL_NUM_PACKET_TYPES 9
> +
> +/* An Intel telemetry subevent is of the TLV format.
> + * - id: takes 1 byte. This is the subevent id.
> + * - length: takes 1 byte.
> + * - value: takes |length| bytes.
> + */
> +struct intel_tlv {
> +       uint8_t id;
> +       uint8_t length;
> +       uint8_t value[0];
> +};
> +
> +#define TLV_SIZE(tlv) (*((const uint8_t *) tlv + 1) + 2 * sizeof(uint8_t))
> +#define NEXT_TLV(tlv) ((const struct intel_tlv *)\
> +                                       ((const uint8_t *)tlv + TLV_SIZE(tlv)))
> +
> +struct intel_acl_event {
> +       uint16_t conn_handle;
> +       uint32_t rx_hec_error;
> +       uint32_t rx_crc_error;
> +       uint32_t packets_from_host;
> +       uint32_t tx_packets;
> +       uint32_t tx_packets_retry[INTEL_NUM_RETRIES];
> +       uint32_t tx_packets_by_type[INTEL_NUM_PACKET_TYPES];
> +       uint32_t rx_packets;
> +       uint32_t link_throughput;
> +       uint32_t max_packet_letency;
> +       uint32_t avg_packet_letency;
> +} __packed;
> +
> +struct intel_sco_event {
> +       uint16_t conn_handle;
> +       uint32_t packets_from_host;
> +       uint32_t tx_packets;
> +       uint32_t rx_payload_lost;
> +       uint32_t tx_payload_lost;
> +       uint32_t rx_no_sync_error[INTEL_NUM_SLOTS];
> +       uint32_t rx_hec_error[INTEL_NUM_SLOTS];
> +       uint32_t rx_crc_error[INTEL_NUM_SLOTS];
> +       uint32_t rx_nak_error[INTEL_NUM_SLOTS];
> +       uint32_t tx_failed_wifi_coex[INTEL_NUM_SLOTS];
> +       uint32_t rx_failed_wifi_coex[INTEL_NUM_SLOTS];
> +       uint32_t samples_inserted_by_CDC;
> +       uint32_t samples_dropped;
> +       uint32_t mute_samples;
> +       uint32_t plc_injection;
> +} __packed;
> +
> +struct intel_ext_telemetry_event {
> +       uint8_t telemetry_ev_type; /* one in enum intel_telemetry_event_type */
> +       uint8_t link_type;
> +       union {
> +               struct intel_sco_event sco;
> +               struct intel_acl_event acl;
> +       } conn;
> +} __packed;
> +
> +typedef void (*intel_debug_func_t)(const char *str, void *user_data);
> +
> +bool is_manufacturer_intel(uint16_t manufacturer);
> +void intel_set_debug(intel_debug_func_t callback, void *user_data);
> +
> +bool process_intel_telemetry_report(const struct mgmt_ev_quality_report *ev);
>

Same thing here, not sure where you got this idea that it would be
nice to print the telemetry as bluetoothd output rather than on btmon
like everything else.

> +#endif /* __INTEL_H */
> --
> 2.35.0.rc0.227.g00780c9af4-goog
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-01-21 21:11 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-21 11:24 [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT Joseph Hwang
2022-01-21 11:24 ` [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Joseph Hwang
2022-01-21 21:11   ` Luiz Augusto von Dentz
2022-01-21 13:30 ` [BlueZ,v1,1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT bluez.test.bot
2022-01-21 21:06 ` [BlueZ PATCH v1 1/2] " Luiz Augusto von Dentz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).