From patchwork Fri Jan 21 11:24:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joseph Hwang X-Patchwork-Id: 12719625 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0615C433EF for ; Fri, 21 Jan 2022 11:24:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238106AbiAULYS (ORCPT ); Fri, 21 Jan 2022 06:24:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58642 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235434AbiAULYS (ORCPT ); Fri, 21 Jan 2022 06:24:18 -0500 Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07523C061574 for ; Fri, 21 Jan 2022 03:24:18 -0800 (PST) Received: by mail-pj1-x1036.google.com with SMTP id o64so9098637pjo.2 for ; Fri, 21 Jan 2022 03:24:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=6qhQ+XiUDFotyAbnnMpP6/VDi5tM667t82zQtkZl7MU=; b=CptpmIwjvJoo0hrTeckV0e31tqfPzzqL7i/gtumiW29zm62zT+oxjER6l3BePN2LYv QgMjpFojALMReqF+lmyEIpfs9jOX9HooxjXeplyZICxFASV7tzsQqs7ppJCnJ2+G9R23 5hsYnlc5VqBSlOemyYznHdsJdjSggbrzZH1Qs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=6qhQ+XiUDFotyAbnnMpP6/VDi5tM667t82zQtkZl7MU=; b=AS4oQz+PKa0BEFqUV+UdZBW2m1p3RwZPD3DeBo53DJMwa2zkTeU42aPeyM5DJp6Jcs kPB7GQTTg2zlgS1TIBnm3Xz6BnnT4mvKrCIk4SnmmY9y300BU8sc8flmKzYfMG3fHT+D PofoujCIFqC7aVjecReRA1UL4FgWZpuFyguPHXvFjO8CgCYDkB9PZvq9XytTcCsVMtXO MORmaTtzQDvH43K0C+D73qyhJhjy3oPmVuSA7+QkZK0BEsjrbqbxt2vg6EMYzqVrYxq8 PqRx7Nn2WyA3leGmMdSkupsuc8AJ+HtuSsnYit2POaPEFWMtromypVlggz/JlKCbZpkq uydw== X-Gm-Message-State: AOAM531HK8fvzgHhDoEArzT0cgCYSy+9RnY5ed4LwHn8axh/z6ge81tk eF67OW9nZWJHBVgDJPDaLWb/GzDJG3mdEQ== X-Google-Smtp-Source: ABdhPJxMb65pUG2A+cOAZN+wJGGozQZX4JmSWUcrAdUjbs64RKK2bHB2boSIqkfe4hAmsmb2tqfyJA== X-Received: by 2002:a17:902:c401:b0:14a:5f50:ece7 with SMTP id k1-20020a170902c40100b0014a5f50ece7mr3239662plk.29.1642764257223; Fri, 21 Jan 2022 03:24:17 -0800 (PST) Received: from localhost (174.71.80.34.bc.googleusercontent.com. [34.80.71.174]) by smtp.gmail.com with UTF8SMTPSA id c12sm6758055pfl.130.2022.01.21.03.24.15 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 21 Jan 2022 03:24:16 -0800 (PST) From: Joseph Hwang To: linux-bluetooth@vger.kernel.org, marcel@holtmann.org, luiz.dentz@gmail.com, pali@kernel.org Cc: chromeos-bluetooth-upstreaming@chromium.org, josephsih@google.com, Joseph Hwang , Archie Pusaka Subject: [BlueZ PATCH v1 1/2] adapter: support AOSP MGMT_EV_QUALITY_REPORT Date: Fri, 21 Jan 2022 19:24:11 +0800 Message-Id: <20220121192352.BlueZ.v1.1.Ie4d1be8ced51f0aa92ee6d8eacf9666a121a1292@changeid> X-Mailer: git-send-email 2.35.0.rc0.227.g00780c9af4-goog MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org 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 Signed-off-by: Joseph Hwang --- 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 +#include +#include +#include + +#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 + +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 */ From patchwork Fri Jan 21 11:24:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joseph Hwang X-Patchwork-Id: 12719626 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9A555C433EF for ; Fri, 21 Jan 2022 11:24:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349905AbiAULYW (ORCPT ); Fri, 21 Jan 2022 06:24:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235434AbiAULYV (ORCPT ); Fri, 21 Jan 2022 06:24:21 -0500 Received: from mail-pg1-x534.google.com (mail-pg1-x534.google.com [IPv6:2607:f8b0:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83E4FC061574 for ; Fri, 21 Jan 2022 03:24:21 -0800 (PST) Received: by mail-pg1-x534.google.com with SMTP id h23so7898725pgk.11 for ; Fri, 21 Jan 2022 03:24:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NYiyADlYTeMQufmxJhfnNW1qy/27PjrlW16z4gYZSdc=; b=jq6irRboSr7x/9JSefuQLxLBv6/rLrYDxRlNaYgxIdHjzveT13Qx6o9kPuCe80jvoP PsuLZo8SKQAfepoI+pu8S7FquLFLJmb6Il3DXtEztNkiQhQPAs9svZFnemnqgj2T0JcB eeNTlpAKZFlCvkfxv/v0fDYyxCVsquM7z4/6Q= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NYiyADlYTeMQufmxJhfnNW1qy/27PjrlW16z4gYZSdc=; b=1TrP5wCY8dpC9JysApP6vO+fPzWu13fLunNPltedHu2sGV+FKY4cgVUsOm7sTGHLyp bgUGE+1vaPQNovpn3bR1RSj6otL9DF1TX4x7pjRkBuiF7OhNmGvt55sYd2uQSEYUKIkJ d6FqAf5byug1K4+DlNgqlnJaU9H/+wVoIEt2nddUSq3yprwUykKxpA761fdhdwe6KwIN 8z8R90U0Xky1h0YpjecO1mhuGVM5AjkSoxoYH688c+vHwcN93stFrmqfTJgmrFFjZJbm WkZF0bVv0nyAfhm2Lsy3sXaLYMF/nycDvCB5HqOLrmXvcLHeo09MhivdWBoB9JWLSBm0 MfTQ== X-Gm-Message-State: AOAM530driEei/q0HlnfRpJL8g30DsHBMzzUgfQmutyZEPO2oIPnR9I6 feQ/CqfjTqByR6Jsb3LVUMySHqa+ujIXYw== X-Google-Smtp-Source: ABdhPJw3k3I6bfNi4n7lxg7zOm7XlTH/eAQujdjMfy6i3VYHD5+w8r3TwY3/6McJUGeVvL3jJRd5DQ== X-Received: by 2002:a63:4f23:: with SMTP id d35mr2585276pgb.166.1642764260460; Fri, 21 Jan 2022 03:24:20 -0800 (PST) Received: from localhost (174.71.80.34.bc.googleusercontent.com. [34.80.71.174]) by smtp.gmail.com with UTF8SMTPSA id d14sm7057317pfv.189.2022.01.21.03.24.18 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 21 Jan 2022 03:24:20 -0800 (PST) From: Joseph Hwang To: linux-bluetooth@vger.kernel.org, marcel@holtmann.org, luiz.dentz@gmail.com, pali@kernel.org Cc: chromeos-bluetooth-upstreaming@chromium.org, josephsih@google.com, Joseph Hwang , Archie Pusaka Subject: [BlueZ PATCH v1 2/2] adapter: support Intel MGMT_EV_QUALITY_REPORT Date: Fri, 21 Jan 2022 19:24:12 +0800 Message-Id: <20220121192352.BlueZ.v1.2.Ifdf5cf89a14b4f293d868910c6cb85e802f7eb9e@changeid> X-Mailer: git-send-email 2.35.0.rc0.227.g00780c9af4-goog In-Reply-To: <20220121192352.BlueZ.v1.1.Ie4d1be8ced51f0aa92ee6d8eacf9666a121a1292@changeid> References: <20220121192352.BlueZ.v1.1.Ie4d1be8ced51f0aa92ee6d8eacf9666a121a1292@changeid> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org 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 Signed-off-by: Joseph Hwang --- 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 +#include +#include +#include + +#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 + +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 */