@@ -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
@@ -1038,8 +1038,8 @@ struct mgmt_ev_adv_monitor_device_lost {
#define QUALITY_SPEC_AOSP_BQR 0x2
struct mgmt_ev_quality_report {
uint8_t quality_spec;
- uint32_t report_len;
- uint8_t report[];
+ uint32_t data_len;
+ uint8_t data[];
} __packed;
static const char *mgmt_op[] = {
@@ -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,28 @@ 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;
+
+ if (!ev)
+ return;
+
+ if (length < sizeof(*ev)) {
+ error("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 +9750,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 +9918,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 +10158,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 +10190,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,90 @@
+// 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;
+}
+
+bool process_aosp_quality_report(const struct mgmt_ev_quality_report *ev)
+{
+ const struct aosp_bqr *edata = (struct aosp_bqr *)ev->data;
+ struct aosp_bqr bqr;
+
+ if (edata->subevent_code != 0x58) {
+ util_debug(aosp_debug.callback, aosp_debug.data,
+ "error: %u not AOSP Bluetooth quality report subevent",
+ edata->subevent_code);
+ return false;
+ }
+
+ if (ev->data_len < sizeof(struct aosp_bqr)) {
+ util_debug(aosp_debug.callback, aosp_debug.data,
+ "error: AOSP report size %u too small (expect >= %lu).",
+ ev->data_len, sizeof(struct aosp_bqr));
+ return false;
+ }
+
+ /* Ignore the Vendor Specific Parameter (VSP) field for now
+ * due to the lack of standard way of reading it.
+ */
+ bqr.quality_report_id = edata->quality_report_id;
+ bqr.packet_type = edata->packet_type;
+ bqr.conn_handle = btohs(edata->conn_handle);
+ bqr.conn_role = edata->conn_role;
+ bqr.tx_power_level = edata->tx_power_level;
+ bqr.rssi = edata->rssi;
+ bqr.snr = edata->snr;
+ bqr.unused_afh_channel_count = edata->unused_afh_channel_count;
+ bqr.afh_select_unideal_channel_count =
+ edata->afh_select_unideal_channel_count;
+ bqr.lsto = btohs(edata->lsto);
+ bqr.conn_piconet_clock = btohl(edata->conn_piconet_clock);
+ bqr.retransmission_count = btohl(edata->retransmission_count);
+ bqr.no_rx_count = btohl(edata->no_rx_count);
+ bqr.nak_count = btohl(edata->nak_count);
+ bqr.last_tx_ack_timestamp = btohl(edata->last_tx_ack_timestamp);
+ bqr.flow_off_count = btohl(edata->flow_off_count);
+ bqr.last_flow_on_timestamp = btohl(edata->last_flow_on_timestamp);
+ bqr.buffer_overflow_bytes = btohl(edata->buffer_overflow_bytes);
+ bqr.buffer_underflow_bytes = btohl(edata->buffer_underflow_bytes);
+
+ util_debug(aosp_debug.callback, aosp_debug.data,
+ "AOSP report of connection hanle %u received", bqr.conn_handle);
+
+ return true;
+}
new file mode 100644
@@ -0,0 +1,58 @@
+/* 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 subevent_code;
+ 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 */