@@ -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
@@ -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",
@@ -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:
@@ -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,
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 */