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@xxxxxxxxxxxx> Signed-off-by: Joseph Hwang <josephsih@xxxxxxxxxxxx> --- 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