diff mbox series

[BlueZ,v5,1/3] monitor: add new Intel extended telemetry events

Message ID 20210629154652.BlueZ.v5.1.I832f2d744fe2cff0d9749e24c9ec27071fa0b4ed@changeid
State Superseded
Headers show
Series [BlueZ,v5,1/3] monitor: add new Intel extended telemetry events | expand

Commit Message

Joseph Hwang June 29, 2021, 7:47 a.m. UTC
This patch adds new Intel extended telemetry events for both ACL and
SCO/eSCO audio link quality reports.

For SCO/eSCO audio link quality report, it shows something like
> HCI Event: Vendor (0xff) plen 190  #120 [hci0] 2021-05-31 20:27:50.257
        Vendor Prefix (0x8780)
      Intel Extended Telemetry (0x87)
        Extended Telemetry (0x80): SubOpcode (0x03)
        Extended event type (0x01): Audio Link Quality Report Type(0x05)
        SCO/eSCO connection handle (0x6a): 0x0101
        Packets from host (0x6b): 399
        Tx packets (0x6c): 403
        Rx payload lost (0x6d): 3
        Tx payload lost (0x6e): 0
        Rx No SYNC errors (0x6f): 3 2 3 3 0
        Rx HEC errors (0x70): 0 0 0 0 0
        Rx CRC errors (0x71): 2 0 0 0 0
        Rx NAK errors (0x72): 6 0 0 0 0
        Failed Tx due to Wifi coex (0x73): 6 0 0 0 0
        Failed Rx due to Wifi coex (0x74): 0 0 0 0 0
        Late samples inserted based on CDC (0x75): 0
        Samples dropped (0x76): 0
        Mute samples sent at initial connection (0x77): 0
        PLC injection data (0x78): 0

For ACL audio link quality report, it shows something like
> HCI Event: Vendor (0xff) plen 142  #120 [hci0] 2021-05-31 20:27:50.261
        Vendor Prefix (0x8780)
      Intel Extended Telemetry (0x87)
        Extended Telemetry (0x80): SubOpcode (0x03)
        Extended event type (0x01): Audio Link Quality Report Type(0x05)
        ACL connection handle (0x4a): 0x0100
        Rx HEC errors (0x4b): 0
        Rx CRC errors (0x4c): 0
        Packets from host (0x4d): 100
        Tx packets (0x4e): 101
        Tx packets 0 retries (0x4f): 89
        Tx packets 1 retries (0x50): 11
        Tx packets 2 retries (0x51): 1
        Tx packets 3 retries (0x52): 0
        Tx packets 4 retries and more (0x53): 0
        Tx DH1 packets (0x54): 0
        Tx DH3 packets (0x55): 0
        Tx DH5 packets (0x56): 0
        Tx 2DH1 packets (0x57): 0
        Tx 2DH3 packets (0x58): 0
        Tx 2DH5 packets (0x59): 0
        Tx 3DH1 packets (0x5a): 6
        Tx 3DH3 packets (0x5b): 0
        Tx 3DH5 packets (0x5c): 94
        Rx packets (0x5d): 272
        ACL link throughput (KBps) (0x5e): 343815
        ACL max packet latency (ms) (0x5f): 20625
        ACL avg packet latency (ms) (0x60): 12

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

Changes in v5:
- Fix two Make errors.
- Please also review Series-changes 3.

Changes in v4:
- Fix a Make error.
- Please also review Series-changes 3.

Changes in v3:
- Define the packed struct intel_ext_evt for the extended telemetry
  event.
- Define the packed struct intel_tlv for the telemetry subevent.
- Define a new function intel_vendor_prefix_evt() to handle the new
  vendor event type with a vendor prefix
      0xff <length> <vendor_prefix> <subopcode> <data>
  while intel_vendor_evt() handles the original vendor event type
      0xff <length> <subopcode> <data>
- Add the vendor_prefix_evt_table table so that more subopcodes can be
  added for the events with a vendor prefix.
- Move the event data buffer check after processing the current tlv.
- Fix typos.

Changes in v2:
- Perform size checks for tlv subevents.
- Fix the Make errors about qualifiers.

 monitor/intel.c  | 492 ++++++++++++++++++++++++++++++++++++++++++++++-
 monitor/intel.h  |   2 +-
 monitor/packet.c |  18 +-
 3 files changed, 503 insertions(+), 9 deletions(-)

Comments

bluez.test.bot@gmail.com June 29, 2021, 8:40 a.m. UTC | #1
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=508413

---Test result---

Test Summary:
CheckPatch                    PASS      1.56 seconds
GitLint                       PASS      0.41 seconds
Prep - Setup ELL              PASS      48.73 seconds
Build - Prep                  PASS      0.10 seconds
Build - Configure             PASS      8.66 seconds
Build - Make                  PASS      209.56 seconds
Make Check                    PASS      8.98 seconds
Make Distcheck                PASS      245.71 seconds
Build w/ext ELL - Configure   PASS      8.66 seconds
Build w/ext ELL - Make        PASS      191.35 seconds

Details
##############################
Test: CheckPatch - PASS
Desc: Run checkpatch.pl script with rule in .checkpatch.conf

##############################
Test: GitLint - PASS
Desc: Run gitlint with rule in .gitlint

##############################
Test: Prep - Setup ELL - PASS
Desc: Clone, build, and install ELL

##############################
Test: Build - Prep - PASS
Desc: Prepare environment for build

##############################
Test: Build - Configure - PASS
Desc: Configure the BlueZ source tree

##############################
Test: Build - Make - PASS
Desc: Build the BlueZ source tree

##############################
Test: Make Check - PASS
Desc: Run 'make check'

##############################
Test: Make Distcheck - PASS
Desc: Run distcheck to check the distribution

##############################
Test: Build w/ext ELL - Configure - PASS
Desc: Configure BlueZ source with '--enable-external-ell' configuration

##############################
Test: Build w/ext ELL - Make - PASS
Desc: Build BlueZ source with '--enable-external-ell' configuration



---
Regards,
Linux Bluetooth
Luiz Augusto von Dentz June 29, 2021, 8:27 p.m. UTC | #2
Hi Joseph,

On Tue, Jun 29, 2021 at 12:47 AM Joseph Hwang <josephsih@chromium.org> wrote:
>
> This patch adds a new UUID for the quality report experimental
> feature. When reading the experimental features, it checks if
> the new feature is supported by the controller and stores the
> value in the quality_report_supported flag of the adapter.
>
> The quality_report_supported flag could be used by the bluetoothd
> to determine if the quality report feature can be enabled.
>
> Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
> ---
>
> (no changes since v1)
>
>  src/adapter.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
>
> diff --git a/src/adapter.c b/src/adapter.c
> index 98fc78f1e..e2873de46 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -284,6 +284,7 @@ struct btd_adapter {
>         bool is_default;                /* true if adapter is default one */
>
>         bool le_simult_roles_supported;
> +       bool quality_report_supported;
>  };
>
>  typedef enum {
> @@ -9234,6 +9235,12 @@ static const uint8_t le_simult_central_peripheral_uuid[16] = {
>         0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67,
>  };
>
> +/* 330859bc-7506-492d-9370-9a6f0614037f */
> +static const uint8_t quality_report_uuid[16] = {
> +       0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93,
> +       0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33,
> +};
> +

Please add support for decoding the new UUID on src/shared/util.c
first so the likes of btmon/bluetoothctl can properly print it.

>  /* 15c0a148-c273-11ea-b3de-0242ac130004 */
>  static const uint8_t rpa_resolution_uuid[16] = {
>         0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3,
> @@ -9276,6 +9283,14 @@ static void le_simult_central_peripheral_func(struct btd_adapter *adapter,
>         adapter->le_simult_roles_supported = flags & 0x01;
>  }
>
> +static void quality_report_func(struct btd_adapter *adapter, uint32_t flags)
> +{
> +       adapter->quality_report_supported = le32_to_cpu(flags) & 0x01;
> +
> +       btd_info(adapter->dev_id, "quality_report_supported %d",
> +                       adapter->quality_report_supported);
> +}
> +
>  static void set_rpa_resolution_complete(uint8_t status, uint16_t len,
>                                         const void *param, void *user_data)
>  {
> @@ -9313,6 +9328,7 @@ static const struct exp_feat {
>         EXP_FEAT(debug_uuid, exp_debug_func),
>         EXP_FEAT(le_simult_central_peripheral_uuid,
>                  le_simult_central_peripheral_func),
> +       EXP_FEAT(quality_report_uuid, quality_report_func),
>         EXP_FEAT(rpa_resolution_uuid, rpa_resolution_func),
>  };
>
> --
> 2.32.0.93.g670b81a890-goog
>
Joseph Hwang July 13, 2021, 2:08 a.m. UTC | #3
Hi Luiz:

  A gentle ping for a re-review on the new changes per your previous comments.

Thanks and best regards,
Joseph


On Tue, Jun 29, 2021 at 3:47 PM Joseph Hwang <josephsih@chromium.org> wrote:
>

> This patch adds new Intel extended telemetry events for both ACL and

> SCO/eSCO audio link quality reports.

>

> For SCO/eSCO audio link quality report, it shows something like

> > HCI Event: Vendor (0xff) plen 190  #120 [hci0] 2021-05-31 20:27:50.257

>         Vendor Prefix (0x8780)

>       Intel Extended Telemetry (0x87)

>         Extended Telemetry (0x80): SubOpcode (0x03)

>         Extended event type (0x01): Audio Link Quality Report Type(0x05)

>         SCO/eSCO connection handle (0x6a): 0x0101

>         Packets from host (0x6b): 399

>         Tx packets (0x6c): 403

>         Rx payload lost (0x6d): 3

>         Tx payload lost (0x6e): 0

>         Rx No SYNC errors (0x6f): 3 2 3 3 0

>         Rx HEC errors (0x70): 0 0 0 0 0

>         Rx CRC errors (0x71): 2 0 0 0 0

>         Rx NAK errors (0x72): 6 0 0 0 0

>         Failed Tx due to Wifi coex (0x73): 6 0 0 0 0

>         Failed Rx due to Wifi coex (0x74): 0 0 0 0 0

>         Late samples inserted based on CDC (0x75): 0

>         Samples dropped (0x76): 0

>         Mute samples sent at initial connection (0x77): 0

>         PLC injection data (0x78): 0

>

> For ACL audio link quality report, it shows something like

> > HCI Event: Vendor (0xff) plen 142  #120 [hci0] 2021-05-31 20:27:50.261

>         Vendor Prefix (0x8780)

>       Intel Extended Telemetry (0x87)

>         Extended Telemetry (0x80): SubOpcode (0x03)

>         Extended event type (0x01): Audio Link Quality Report Type(0x05)

>         ACL connection handle (0x4a): 0x0100

>         Rx HEC errors (0x4b): 0

>         Rx CRC errors (0x4c): 0

>         Packets from host (0x4d): 100

>         Tx packets (0x4e): 101

>         Tx packets 0 retries (0x4f): 89

>         Tx packets 1 retries (0x50): 11

>         Tx packets 2 retries (0x51): 1

>         Tx packets 3 retries (0x52): 0

>         Tx packets 4 retries and more (0x53): 0

>         Tx DH1 packets (0x54): 0

>         Tx DH3 packets (0x55): 0

>         Tx DH5 packets (0x56): 0

>         Tx 2DH1 packets (0x57): 0

>         Tx 2DH3 packets (0x58): 0

>         Tx 2DH5 packets (0x59): 0

>         Tx 3DH1 packets (0x5a): 6

>         Tx 3DH3 packets (0x5b): 0

>         Tx 3DH5 packets (0x5c): 94

>         Rx packets (0x5d): 272

>         ACL link throughput (KBps) (0x5e): 343815

>         ACL max packet latency (ms) (0x5f): 20625

>         ACL avg packet latency (ms) (0x60): 12

>

> Reviewed-by: Miao-chen Chou <mcchou@chromium.org>

> ---

>

> Changes in v5:

> - Fix two Make errors.

> - Please also review Series-changes 3.

>

> Changes in v4:

> - Fix a Make error.

> - Please also review Series-changes 3.

>

> Changes in v3:

> - Define the packed struct intel_ext_evt for the extended telemetry

>   event.

> - Define the packed struct intel_tlv for the telemetry subevent.

> - Define a new function intel_vendor_prefix_evt() to handle the new

>   vendor event type with a vendor prefix

>       0xff <length> <vendor_prefix> <subopcode> <data>

>   while intel_vendor_evt() handles the original vendor event type

>       0xff <length> <subopcode> <data>

> - Add the vendor_prefix_evt_table table so that more subopcodes can be

>   added for the events with a vendor prefix.

> - Move the event data buffer check after processing the current tlv.

> - Fix typos.

>

> Changes in v2:

> - Perform size checks for tlv subevents.

> - Fix the Make errors about qualifiers.

>

>  monitor/intel.c  | 492 ++++++++++++++++++++++++++++++++++++++++++++++-

>  monitor/intel.h  |   2 +-

>  monitor/packet.c |  18 +-

>  3 files changed, 503 insertions(+), 9 deletions(-)

>

> diff --git a/monitor/intel.c b/monitor/intel.c

> index d2aefa6a8..7591df4ee 100644

> --- a/monitor/intel.c

> +++ b/monitor/intel.c

> @@ -30,6 +30,7 @@

>

>  #define COLOR_UNKNOWN_EVENT_MASK       COLOR_WHITE_BG

>  #define COLOR_UNKNOWN_SCAN_STATUS      COLOR_WHITE_BG

> +#define COLOR_UNKNOWN_EXT_EVENT                COLOR_WHITE_BG

>

>  static void print_status(uint8_t status)

>  {

> @@ -992,14 +993,501 @@ static const struct vendor_evt vendor_evt_table[] = {

>         { }

>  };

>

> -const struct vendor_evt *intel_vendor_evt(uint8_t evt)

> +/*

> + * An Intel telemetry subevent is of the TLV format.

> + * - Type: takes 1 byte. This is the subevent_id.

> + * - Length: takes 1 byte.

> + * - Value: takes |Length| bytes.

> + */

> +struct intel_tlv {

> +       uint8_t subevent_id;

> +       uint8_t length;

> +       uint8_t value[];

> +};

> +

> +#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))

> +

> +static void ext_evt_type(const struct intel_tlv *tlv)

> +{

> +       uint8_t evt_type = get_u8(tlv->value);

> +       const char *str;

> +

> +       switch (evt_type) {

> +       case 0x00:

> +               str = "System Exception";

> +               break;

> +       case 0x01:

> +               str = "Fatal Exception";

> +               break;

> +       case 0x02:

> +               str = "Debug Exception";

> +               break;

> +       case 0x03:

> +               str = "Connection Event for BR/EDR Link Type";

> +               break;

> +       case 0x04:

> +               str = "Disconnection Event";

> +               break;

> +       case 0x05:

> +               str = "Audio Link Quality Report Type";

> +               break;

> +       case 0x06:

> +               str = "Stats for BR/EDR Link Type";

> +               break;

> +       default:

> +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> +                       "Unknown extended telemetry event type (0x%2.2x)",

> +                       evt_type);

> +               packet_hexdump((const void *) tlv,

> +                                       tlv->length + 2 * sizeof(uint8_t));

> +               return;

> +       }

> +

> +       print_field("Extended event type (0x%2.2x): %s (0x%2.2x)",

> +                       tlv->subevent_id, str, evt_type);

> +}

> +

> +static void ext_acl_evt_conn_handle(const struct intel_tlv *tlv)

> +{

> +       uint16_t conn_handle = get_le16(tlv->value);

> +

> +       print_field("ACL connection handle (0x%2.2x): 0x%4.4x",

> +                       tlv->subevent_id, conn_handle);

> +}

> +

> +static void ext_acl_evt_hec_errors(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Rx HEC errors (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_crc_errors(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Rx CRC errors (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_num_pkt_from_host(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Packets from host (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_num_tx_pkt_retry(const struct intel_tlv *tlv)

> +{

> +       char *subevent_str;

> +       uint32_t num = get_le32(tlv->value);

> +

> +       switch (tlv->subevent_id) {

> +       case 0x4f:

> +               subevent_str = "Tx packets 0 retries";

> +               break;

> +       case 0x50:

> +               subevent_str = "Tx packets 1 retries";

> +               break;

> +       case 0x51:

> +               subevent_str = "Tx packets 2 retries";

> +               break;

> +       case 0x52:

> +               subevent_str = "Tx packets 3 retries";

> +               break;

> +       case 0x53:

> +               subevent_str = "Tx packets 4 retries and more";

> +               break;

> +       default:

> +               subevent_str = "Unknown";

> +               break;

> +       }

> +

> +       print_field("%s (0x%2.2x): %d", subevent_str, tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_num_tx_pkt_type(const struct intel_tlv *tlv)

> +{

> +       char *packet_type_str;

> +       uint32_t num = get_le32(tlv->value);

> +

> +       switch (tlv->subevent_id) {

> +       case 0x54:

> +               packet_type_str = "DH1";

> +               break;

> +       case 0x55:

> +               packet_type_str = "DH3";

> +               break;

> +       case 0x56:

> +               packet_type_str = "DH5";

> +               break;

> +       case 0x57:

> +               packet_type_str = "2DH1";

> +               break;

> +       case 0x58:

> +               packet_type_str = "2DH3";

> +               break;

> +       case 0x59:

> +               packet_type_str = "2DH5";

> +               break;

> +       case 0x5a:

> +               packet_type_str = "3DH1";

> +               break;

> +       case 0x5b:

> +               packet_type_str = "3DH3";

> +               break;

> +       case 0x5c:

> +               packet_type_str = "3DH5";

> +               break;

> +       default:

> +               packet_type_str = "Unknown";

> +               break;

> +       }

> +

> +       print_field("Tx %s packets (0x%2.2x): %d",

> +                       packet_type_str, tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Rx packets (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_link_throughput(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("ACL link throughput (KBps) (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_max_packet_latency(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("ACL max packet latency (ms) (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_acl_evt_avg_packet_latency(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("ACL avg packet latency (ms) (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_conn_handle(const struct intel_tlv *tlv)

> +{

> +       uint16_t conn_handle = get_le16(tlv->value);

> +

> +       print_field("SCO/eSCO connection handle (0x%2.2x): 0x%4.4x",

> +                       tlv->subevent_id, conn_handle);

> +}

> +

> +static void ext_sco_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

>  {

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_num_rx_payloads_lost(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Rx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_num_tx_payloads_lost(const struct intel_tlv *tlv)

> +{

> +

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Tx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void slots_errors(const struct intel_tlv *tlv, const char *type_str)

> +{

> +       /* The subevent has 5 slots where each slot is of the uint32_t type. */

> +       uint32_t num[5];

> +       const uint8_t *data = tlv->value;

> +       int i;

> +

> +       if (tlv->length != 5 * sizeof(uint32_t)) {

> +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> +                               "  Invalid subevent length (%d)", tlv->length);

> +               return;

> +       }

> +

> +       for (i = 0; i < 5; i++) {

> +               num[i] = get_le32(data);

> +               data += sizeof(uint32_t);

> +       }

> +

> +       print_field("%s (0x%2.2x): %d %d %d %d %d", type_str, tlv->subevent_id,

> +                       num[0], num[1], num[2], num[3], num[4]);

> +}

> +

> +static void ext_sco_evt_num_no_sync_errors(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Rx No SYNC errors");

> +}

> +

> +static void ext_sco_evt_num_hec_errors(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Rx HEC errors");

> +}

> +

> +static void ext_sco_evt_num_crc_errors(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Rx CRC errors");

> +}

> +

> +static void ext_sco_evt_num_naks(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Rx NAK errors");

> +}

> +

> +static void ext_sco_evt_num_failed_tx_by_wifi(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Failed Tx due to Wifi coex");

> +}

> +

> +static void ext_sco_evt_num_failed_rx_by_wifi(const struct intel_tlv *tlv)

> +{

> +       slots_errors(tlv, "Failed Rx due to Wifi coex");

> +}

> +

> +static void ext_sco_evt_samples_inserted(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Late samples inserted based on CDC (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_samples_dropped(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Samples dropped (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_mute_samples(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("Mute samples sent at initial connection (0x%2.2x): %d",

> +                       tlv->subevent_id, num);

> +}

> +

> +static void ext_sco_evt_plc_injection_data(const struct intel_tlv *tlv)

> +{

> +       uint32_t num = get_le32(tlv->value);

> +

> +       print_field("PLC injection data (0x%2.2x): %d", tlv->subevent_id, num);

> +}

> +

> +static const struct intel_ext_subevent {

> +       uint8_t subevent_id;

> +       uint8_t length;

> +       void (*func)(const struct intel_tlv *tlv);

> +} intel_ext_subevent_table[] = {

> +       { 0x01, 1, ext_evt_type },

> +

> +       /* ACL audio link quality subevents */

> +       { 0x4a, 2, ext_acl_evt_conn_handle },

> +       { 0x4b, 4, ext_acl_evt_hec_errors },

> +       { 0x4c, 4, ext_acl_evt_crc_errors },

> +       { 0x4d, 4, ext_acl_evt_num_pkt_from_host },

> +       { 0x4e, 4, ext_acl_evt_num_tx_pkt_to_air },

> +       { 0x4f, 4, ext_acl_evt_num_tx_pkt_retry },

> +       { 0x50, 4, ext_acl_evt_num_tx_pkt_retry },

> +       { 0x51, 4, ext_acl_evt_num_tx_pkt_retry },

> +       { 0x52, 4, ext_acl_evt_num_tx_pkt_retry },

> +       { 0x53, 4, ext_acl_evt_num_tx_pkt_retry },

> +       { 0x54, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x55, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x56, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x57, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x58, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x59, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x5a, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x5b, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x5c, 4, ext_acl_evt_num_tx_pkt_type },

> +       { 0x5d, 4, ext_acl_evt_num_rx_pkt_from_air },

> +       { 0x5e, 4, ext_acl_evt_link_throughput },

> +       { 0x5f, 4, ext_acl_evt_max_packet_latency },

> +       { 0x60, 4, ext_acl_evt_avg_packet_latency },

> +

> +       /* SCO/eSCO audio link quality subevents */

> +       { 0x6a, 2, ext_sco_evt_conn_handle },

> +       { 0x6b, 4, ext_sco_evt_num_rx_pkt_from_air },

> +       { 0x6c, 4, ext_sco_evt_num_tx_pkt_to_air },

> +       { 0x6d, 4, ext_sco_evt_num_rx_payloads_lost },

> +       { 0x6e, 4, ext_sco_evt_num_tx_payloads_lost },

> +       { 0x6f, 20, ext_sco_evt_num_no_sync_errors },

> +       { 0x70, 20, ext_sco_evt_num_hec_errors },

> +       { 0x71, 20, ext_sco_evt_num_crc_errors },

> +       { 0x72, 20, ext_sco_evt_num_naks },

> +       { 0x73, 20, ext_sco_evt_num_failed_tx_by_wifi },

> +       { 0x74, 20, ext_sco_evt_num_failed_rx_by_wifi },

> +       { 0x75, 4, ext_sco_evt_samples_inserted },

> +       { 0x76, 4, ext_sco_evt_samples_dropped },

> +       { 0x77, 4, ext_sco_evt_mute_samples },

> +       { 0x78, 4, ext_sco_evt_plc_injection_data },

> +

> +       /* end */

> +       { 0x0, 0}

> +};

> +

> +static const struct intel_tlv *process_ext_subevent(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].length > 0; i++) {

> +               if (intel_ext_subevent_table[i].subevent_id ==

> +                                                       tlv->subevent_id) {

> +                       subevent = &intel_ext_subevent_table[i];

> +                       break;

> +               }

> +       }

> +

> +       if (!subevent) {

> +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> +                               "Unknown extended subevent 0x%2.2x",

> +                               tlv->subevent_id);

> +               return NULL;

> +       }

> +

> +       if (tlv->length != subevent->length) {

> +               print_text(COLOR_ERROR, "Invalid length %d of subevent 0x%2.2x",

> +                               tlv->length, tlv->subevent_id);

> +               return NULL;

> +       }

> +

> +       if (next_tlv > last_tlv) {

> +               print_text(COLOR_ERROR, "Subevent exceeds the buffer size.");

> +               return NULL;

> +       }

> +

> +       subevent->func(tlv);

> +

> +       return next_tlv;

> +}

> +

> +static void intel_vendor_ext_evt(const void *data, uint8_t size)

> +{

> +       /* The data pointer points to a number of tlv.*/

> +       const struct intel_tlv *tlv = data;

> +       const struct intel_tlv *last_tlv = data + size;

> +

> +       /* Process every tlv subevent until reaching last_tlv.

> +        * The decoding process terminates normally when tlv == last_tlv.

> +        */

> +       while (tlv && tlv < last_tlv)

> +               tlv = process_ext_subevent(tlv, last_tlv);

> +

> +       /* If an error occurs in decoding the subevents, hexdump the packet. */

> +       if (!tlv)

> +               packet_hexdump(data, size);

> +}

> +

> +/* Vendor extended events with a vendor prefix. */

> +static const struct vendor_evt vendor_prefix_evt_table[] = {

> +       { 0x03, "Extended Telemetry", intel_vendor_ext_evt },

> +       { }

> +};

> +

> +const uint8_t intel_vendor_prefix[] = {0x87, 0x80};

> +#define INTEL_VENDOR_PREFIX_SIZE sizeof(intel_vendor_prefix)

> +

> +/*

> + * The vendor event with Intel vendor prefix.

> + * Its format looks like

> + *   0xff <length> <vendor_prefix> <subopcode> <data>

> + *   where Intel's <vendor_prefix> is 0x8780.

> + *

> + *   When <subopcode> == 0x03, it is a telemetry event; and

> + *   <data> is a number of tlv data.

> + */

> +struct vendor_prefix_evt {

> +       uint8_t prefix_data[INTEL_VENDOR_PREFIX_SIZE];

> +       uint8_t subopcode;

> +};

> +

> +static const struct vendor_evt *intel_vendor_prefix_evt(const void *data,

> +                                                       int *consumed_size)

> +{

> +       unsigned int i;

> +       const struct vendor_prefix_evt *vnd = data;

> +       char prefix_string[INTEL_VENDOR_PREFIX_SIZE * 2 + 1] = { 0 };

> +

> +       /* Check if the vendor prefix matches. */

> +       for (i = 0; i < INTEL_VENDOR_PREFIX_SIZE; i++) {

> +               if (vnd->prefix_data[i] != intel_vendor_prefix[i])

> +                       return NULL;

> +               sprintf(prefix_string + i * 2, "%02x", vnd->prefix_data[i]);

> +       }

> +       print_field("Vendor Prefix (0x%s)", prefix_string);

> +

> +       /*

> +        * Handle the vendor event with a vendor prefix.

> +        *   0xff <length> <vendor_prefix> <subopcode> <data>

> +        * This loop checks whether the <subopcode> exists in the

> +        * vendor_prefix_evt_table.

> +        */

> +       for (i = 0; vendor_prefix_evt_table[i].str; i++) {

> +               if (vendor_prefix_evt_table[i].evt == vnd->subopcode) {

> +                       *consumed_size = sizeof(struct vendor_prefix_evt);

> +                       return &vendor_prefix_evt_table[i];

> +               }

> +       }

> +

> +       return NULL;

> +}

> +

> +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size)

> +{

> +       uint8_t evt = *((const uint8_t *) data);

> +       int i;

> +

> +       /*

> +        * Handle the vendor event without a vendor prefix.

> +        *   0xff <length> <evt> <data>

> +        * This loop checks whether the <evt> exists in the vendor_evt_table.

> +        */

>         for (i = 0; vendor_evt_table[i].str; i++) {

>                 if (vendor_evt_table[i].evt == evt)

>                         return &vendor_evt_table[i];

>         }

>

> -       return NULL;

> +       /*

> +        * It is not a regular event. Check whether it is a vendor extended

> +        * event that comes with a vendor prefix followed by a subopcode.

> +        */

> +       return intel_vendor_prefix_evt(data, consumed_size);

>  }

> diff --git a/monitor/intel.h b/monitor/intel.h

> index bf00ad491..bfb04540c 100644

> --- a/monitor/intel.h

> +++ b/monitor/intel.h

> @@ -15,4 +15,4 @@ struct vendor_ocf;

>  struct vendor_evt;

>

>  const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf);

> -const struct vendor_evt *intel_vendor_evt(uint8_t evt);

> +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size);

> diff --git a/monitor/packet.c b/monitor/packet.c

> index 82513a63c..4a371f508 100644

> --- a/monitor/packet.c

> +++ b/monitor/packet.c

> @@ -9371,9 +9371,14 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)

>         return NULL;

>  }

>

> -static const struct vendor_evt *current_vendor_evt(uint8_t evt)

> +static const struct vendor_evt *current_vendor_evt(const void *data,

> +                                                       int *consumed_size)

>  {

>         uint16_t manufacturer, msft_opcode;

> +       uint8_t evt = *((const uint8_t *) data);

> +

> +       /* A regular vendor event consumes 1 byte. */

> +       *consumed_size = 1;

>

>         if (index_current < MAX_INDEX) {

>                 manufacturer = index_list[index_current].manufacturer;

> @@ -9388,7 +9393,7 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)

>

>         switch (manufacturer) {

>         case 2:

> -               return intel_vendor_evt(evt);

> +               return intel_vendor_evt(data, consumed_size);

>         case 15:

>                 return broadcom_vendor_evt(evt);

>         }

> @@ -11007,10 +11012,10 @@ static void le_meta_event_evt(const void *data, uint8_t size)

>

>  static void vendor_evt(const void *data, uint8_t size)

>  {

> -       uint8_t subevent = *((const uint8_t *) data);

>         struct subevent_data vendor_data;

>         char vendor_str[150];

> -       const struct vendor_evt *vnd = current_vendor_evt(subevent);

> +       int consumed_size;

> +       const struct vendor_evt *vnd = current_vendor_evt(data, &consumed_size);

>

>         if (vnd) {

>                 const char *str = current_vendor_str();

> @@ -11021,12 +11026,13 @@ static void vendor_evt(const void *data, uint8_t size)

>                         vendor_data.str = vendor_str;

>                 } else

>                         vendor_data.str = vnd->str;

> -               vendor_data.subevent = subevent;

> +               vendor_data.subevent = vnd->evt;

>                 vendor_data.func = vnd->evt_func;

>                 vendor_data.size = vnd->evt_size;

>                 vendor_data.fixed = vnd->evt_fixed;

>

> -               print_subevent(&vendor_data, data + 1, size - 1);

> +               print_subevent(&vendor_data, data + consumed_size,

> +                                                       size - consumed_size);

>         } else {

>                 uint16_t manufacturer;

>

> --

> 2.32.0.93.g670b81a890-goog

>



-- 

Joseph Shyh-In Hwang
Email: josephsih@google.com
Luiz Augusto von Dentz July 14, 2021, 10:21 p.m. UTC | #4
Hi Joseph,

On Mon, Jul 12, 2021 at 7:08 PM Joseph Hwang <josephsih@google.com> wrote:
>

> Hi Luiz:

>

>   A gentle ping for a re-review on the new changes per your previous comments.


Ive left comments to patch 2/3 and 3/3 that still haven't been addressed:

2/3: We need to new UUID to be added to util.c like the rest of
experimental UUIDs:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/shared/util.c#n1023

3/3: Instead of having a function that doesn't seem to be used
anywhere it would probably be easier to enable whenever
btd_opts.experimental is enable e.g.:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/adapter.c#n9293

> Thanks and best regards,

> Joseph

>

>

> On Tue, Jun 29, 2021 at 3:47 PM Joseph Hwang <josephsih@chromium.org> wrote:

> >

> > This patch adds new Intel extended telemetry events for both ACL and

> > SCO/eSCO audio link quality reports.

> >

> > For SCO/eSCO audio link quality report, it shows something like

> > > HCI Event: Vendor (0xff) plen 190  #120 [hci0] 2021-05-31 20:27:50.257

> >         Vendor Prefix (0x8780)

> >       Intel Extended Telemetry (0x87)

> >         Extended Telemetry (0x80): SubOpcode (0x03)

> >         Extended event type (0x01): Audio Link Quality Report Type(0x05)

> >         SCO/eSCO connection handle (0x6a): 0x0101

> >         Packets from host (0x6b): 399

> >         Tx packets (0x6c): 403

> >         Rx payload lost (0x6d): 3

> >         Tx payload lost (0x6e): 0

> >         Rx No SYNC errors (0x6f): 3 2 3 3 0

> >         Rx HEC errors (0x70): 0 0 0 0 0

> >         Rx CRC errors (0x71): 2 0 0 0 0

> >         Rx NAK errors (0x72): 6 0 0 0 0

> >         Failed Tx due to Wifi coex (0x73): 6 0 0 0 0

> >         Failed Rx due to Wifi coex (0x74): 0 0 0 0 0

> >         Late samples inserted based on CDC (0x75): 0

> >         Samples dropped (0x76): 0

> >         Mute samples sent at initial connection (0x77): 0

> >         PLC injection data (0x78): 0

> >

> > For ACL audio link quality report, it shows something like

> > > HCI Event: Vendor (0xff) plen 142  #120 [hci0] 2021-05-31 20:27:50.261

> >         Vendor Prefix (0x8780)

> >       Intel Extended Telemetry (0x87)

> >         Extended Telemetry (0x80): SubOpcode (0x03)

> >         Extended event type (0x01): Audio Link Quality Report Type(0x05)

> >         ACL connection handle (0x4a): 0x0100

> >         Rx HEC errors (0x4b): 0

> >         Rx CRC errors (0x4c): 0

> >         Packets from host (0x4d): 100

> >         Tx packets (0x4e): 101

> >         Tx packets 0 retries (0x4f): 89

> >         Tx packets 1 retries (0x50): 11

> >         Tx packets 2 retries (0x51): 1

> >         Tx packets 3 retries (0x52): 0

> >         Tx packets 4 retries and more (0x53): 0

> >         Tx DH1 packets (0x54): 0

> >         Tx DH3 packets (0x55): 0

> >         Tx DH5 packets (0x56): 0

> >         Tx 2DH1 packets (0x57): 0

> >         Tx 2DH3 packets (0x58): 0

> >         Tx 2DH5 packets (0x59): 0

> >         Tx 3DH1 packets (0x5a): 6

> >         Tx 3DH3 packets (0x5b): 0

> >         Tx 3DH5 packets (0x5c): 94

> >         Rx packets (0x5d): 272

> >         ACL link throughput (KBps) (0x5e): 343815

> >         ACL max packet latency (ms) (0x5f): 20625

> >         ACL avg packet latency (ms) (0x60): 12

> >

> > Reviewed-by: Miao-chen Chou <mcchou@chromium.org>

> > ---

> >

> > Changes in v5:

> > - Fix two Make errors.

> > - Please also review Series-changes 3.

> >

> > Changes in v4:

> > - Fix a Make error.

> > - Please also review Series-changes 3.

> >

> > Changes in v3:

> > - Define the packed struct intel_ext_evt for the extended telemetry

> >   event.

> > - Define the packed struct intel_tlv for the telemetry subevent.

> > - Define a new function intel_vendor_prefix_evt() to handle the new

> >   vendor event type with a vendor prefix

> >       0xff <length> <vendor_prefix> <subopcode> <data>

> >   while intel_vendor_evt() handles the original vendor event type

> >       0xff <length> <subopcode> <data>

> > - Add the vendor_prefix_evt_table table so that more subopcodes can be

> >   added for the events with a vendor prefix.

> > - Move the event data buffer check after processing the current tlv.

> > - Fix typos.

> >

> > Changes in v2:

> > - Perform size checks for tlv subevents.

> > - Fix the Make errors about qualifiers.

> >

> >  monitor/intel.c  | 492 ++++++++++++++++++++++++++++++++++++++++++++++-

> >  monitor/intel.h  |   2 +-

> >  monitor/packet.c |  18 +-

> >  3 files changed, 503 insertions(+), 9 deletions(-)

> >

> > diff --git a/monitor/intel.c b/monitor/intel.c

> > index d2aefa6a8..7591df4ee 100644

> > --- a/monitor/intel.c

> > +++ b/monitor/intel.c

> > @@ -30,6 +30,7 @@

> >

> >  #define COLOR_UNKNOWN_EVENT_MASK       COLOR_WHITE_BG

> >  #define COLOR_UNKNOWN_SCAN_STATUS      COLOR_WHITE_BG

> > +#define COLOR_UNKNOWN_EXT_EVENT                COLOR_WHITE_BG

> >

> >  static void print_status(uint8_t status)

> >  {

> > @@ -992,14 +993,501 @@ static const struct vendor_evt vendor_evt_table[] = {

> >         { }

> >  };

> >

> > -const struct vendor_evt *intel_vendor_evt(uint8_t evt)

> > +/*

> > + * An Intel telemetry subevent is of the TLV format.

> > + * - Type: takes 1 byte. This is the subevent_id.

> > + * - Length: takes 1 byte.

> > + * - Value: takes |Length| bytes.

> > + */

> > +struct intel_tlv {

> > +       uint8_t subevent_id;

> > +       uint8_t length;

> > +       uint8_t value[];

> > +};

> > +

> > +#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))

> > +

> > +static void ext_evt_type(const struct intel_tlv *tlv)

> > +{

> > +       uint8_t evt_type = get_u8(tlv->value);

> > +       const char *str;

> > +

> > +       switch (evt_type) {

> > +       case 0x00:

> > +               str = "System Exception";

> > +               break;

> > +       case 0x01:

> > +               str = "Fatal Exception";

> > +               break;

> > +       case 0x02:

> > +               str = "Debug Exception";

> > +               break;

> > +       case 0x03:

> > +               str = "Connection Event for BR/EDR Link Type";

> > +               break;

> > +       case 0x04:

> > +               str = "Disconnection Event";

> > +               break;

> > +       case 0x05:

> > +               str = "Audio Link Quality Report Type";

> > +               break;

> > +       case 0x06:

> > +               str = "Stats for BR/EDR Link Type";

> > +               break;

> > +       default:

> > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > +                       "Unknown extended telemetry event type (0x%2.2x)",

> > +                       evt_type);

> > +               packet_hexdump((const void *) tlv,

> > +                                       tlv->length + 2 * sizeof(uint8_t));

> > +               return;

> > +       }

> > +

> > +       print_field("Extended event type (0x%2.2x): %s (0x%2.2x)",

> > +                       tlv->subevent_id, str, evt_type);

> > +}

> > +

> > +static void ext_acl_evt_conn_handle(const struct intel_tlv *tlv)

> > +{

> > +       uint16_t conn_handle = get_le16(tlv->value);

> > +

> > +       print_field("ACL connection handle (0x%2.2x): 0x%4.4x",

> > +                       tlv->subevent_id, conn_handle);

> > +}

> > +

> > +static void ext_acl_evt_hec_errors(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Rx HEC errors (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_crc_errors(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Rx CRC errors (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_num_pkt_from_host(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Packets from host (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_num_tx_pkt_retry(const struct intel_tlv *tlv)

> > +{

> > +       char *subevent_str;

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       switch (tlv->subevent_id) {

> > +       case 0x4f:

> > +               subevent_str = "Tx packets 0 retries";

> > +               break;

> > +       case 0x50:

> > +               subevent_str = "Tx packets 1 retries";

> > +               break;

> > +       case 0x51:

> > +               subevent_str = "Tx packets 2 retries";

> > +               break;

> > +       case 0x52:

> > +               subevent_str = "Tx packets 3 retries";

> > +               break;

> > +       case 0x53:

> > +               subevent_str = "Tx packets 4 retries and more";

> > +               break;

> > +       default:

> > +               subevent_str = "Unknown";

> > +               break;

> > +       }

> > +

> > +       print_field("%s (0x%2.2x): %d", subevent_str, tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_num_tx_pkt_type(const struct intel_tlv *tlv)

> > +{

> > +       char *packet_type_str;

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       switch (tlv->subevent_id) {

> > +       case 0x54:

> > +               packet_type_str = "DH1";

> > +               break;

> > +       case 0x55:

> > +               packet_type_str = "DH3";

> > +               break;

> > +       case 0x56:

> > +               packet_type_str = "DH5";

> > +               break;

> > +       case 0x57:

> > +               packet_type_str = "2DH1";

> > +               break;

> > +       case 0x58:

> > +               packet_type_str = "2DH3";

> > +               break;

> > +       case 0x59:

> > +               packet_type_str = "2DH5";

> > +               break;

> > +       case 0x5a:

> > +               packet_type_str = "3DH1";

> > +               break;

> > +       case 0x5b:

> > +               packet_type_str = "3DH3";

> > +               break;

> > +       case 0x5c:

> > +               packet_type_str = "3DH5";

> > +               break;

> > +       default:

> > +               packet_type_str = "Unknown";

> > +               break;

> > +       }

> > +

> > +       print_field("Tx %s packets (0x%2.2x): %d",

> > +                       packet_type_str, tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Rx packets (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_link_throughput(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("ACL link throughput (KBps) (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_max_packet_latency(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("ACL max packet latency (ms) (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_acl_evt_avg_packet_latency(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("ACL avg packet latency (ms) (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_conn_handle(const struct intel_tlv *tlv)

> > +{

> > +       uint16_t conn_handle = get_le16(tlv->value);

> > +

> > +       print_field("SCO/eSCO connection handle (0x%2.2x): 0x%4.4x",

> > +                       tlv->subevent_id, conn_handle);

> > +}

> > +

> > +static void ext_sco_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

> >  {

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_num_rx_payloads_lost(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Rx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_num_tx_payloads_lost(const struct intel_tlv *tlv)

> > +{

> > +

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Tx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void slots_errors(const struct intel_tlv *tlv, const char *type_str)

> > +{

> > +       /* The subevent has 5 slots where each slot is of the uint32_t type. */

> > +       uint32_t num[5];

> > +       const uint8_t *data = tlv->value;

> > +       int i;

> > +

> > +       if (tlv->length != 5 * sizeof(uint32_t)) {

> > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > +                               "  Invalid subevent length (%d)", tlv->length);

> > +               return;

> > +       }

> > +

> > +       for (i = 0; i < 5; i++) {

> > +               num[i] = get_le32(data);

> > +               data += sizeof(uint32_t);

> > +       }

> > +

> > +       print_field("%s (0x%2.2x): %d %d %d %d %d", type_str, tlv->subevent_id,

> > +                       num[0], num[1], num[2], num[3], num[4]);

> > +}

> > +

> > +static void ext_sco_evt_num_no_sync_errors(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Rx No SYNC errors");

> > +}

> > +

> > +static void ext_sco_evt_num_hec_errors(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Rx HEC errors");

> > +}

> > +

> > +static void ext_sco_evt_num_crc_errors(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Rx CRC errors");

> > +}

> > +

> > +static void ext_sco_evt_num_naks(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Rx NAK errors");

> > +}

> > +

> > +static void ext_sco_evt_num_failed_tx_by_wifi(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Failed Tx due to Wifi coex");

> > +}

> > +

> > +static void ext_sco_evt_num_failed_rx_by_wifi(const struct intel_tlv *tlv)

> > +{

> > +       slots_errors(tlv, "Failed Rx due to Wifi coex");

> > +}

> > +

> > +static void ext_sco_evt_samples_inserted(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Late samples inserted based on CDC (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_samples_dropped(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Samples dropped (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_mute_samples(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("Mute samples sent at initial connection (0x%2.2x): %d",

> > +                       tlv->subevent_id, num);

> > +}

> > +

> > +static void ext_sco_evt_plc_injection_data(const struct intel_tlv *tlv)

> > +{

> > +       uint32_t num = get_le32(tlv->value);

> > +

> > +       print_field("PLC injection data (0x%2.2x): %d", tlv->subevent_id, num);

> > +}

> > +

> > +static const struct intel_ext_subevent {

> > +       uint8_t subevent_id;

> > +       uint8_t length;

> > +       void (*func)(const struct intel_tlv *tlv);

> > +} intel_ext_subevent_table[] = {

> > +       { 0x01, 1, ext_evt_type },

> > +

> > +       /* ACL audio link quality subevents */

> > +       { 0x4a, 2, ext_acl_evt_conn_handle },

> > +       { 0x4b, 4, ext_acl_evt_hec_errors },

> > +       { 0x4c, 4, ext_acl_evt_crc_errors },

> > +       { 0x4d, 4, ext_acl_evt_num_pkt_from_host },

> > +       { 0x4e, 4, ext_acl_evt_num_tx_pkt_to_air },

> > +       { 0x4f, 4, ext_acl_evt_num_tx_pkt_retry },

> > +       { 0x50, 4, ext_acl_evt_num_tx_pkt_retry },

> > +       { 0x51, 4, ext_acl_evt_num_tx_pkt_retry },

> > +       { 0x52, 4, ext_acl_evt_num_tx_pkt_retry },

> > +       { 0x53, 4, ext_acl_evt_num_tx_pkt_retry },

> > +       { 0x54, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x55, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x56, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x57, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x58, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x59, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x5a, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x5b, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x5c, 4, ext_acl_evt_num_tx_pkt_type },

> > +       { 0x5d, 4, ext_acl_evt_num_rx_pkt_from_air },

> > +       { 0x5e, 4, ext_acl_evt_link_throughput },

> > +       { 0x5f, 4, ext_acl_evt_max_packet_latency },

> > +       { 0x60, 4, ext_acl_evt_avg_packet_latency },

> > +

> > +       /* SCO/eSCO audio link quality subevents */

> > +       { 0x6a, 2, ext_sco_evt_conn_handle },

> > +       { 0x6b, 4, ext_sco_evt_num_rx_pkt_from_air },

> > +       { 0x6c, 4, ext_sco_evt_num_tx_pkt_to_air },

> > +       { 0x6d, 4, ext_sco_evt_num_rx_payloads_lost },

> > +       { 0x6e, 4, ext_sco_evt_num_tx_payloads_lost },

> > +       { 0x6f, 20, ext_sco_evt_num_no_sync_errors },

> > +       { 0x70, 20, ext_sco_evt_num_hec_errors },

> > +       { 0x71, 20, ext_sco_evt_num_crc_errors },

> > +       { 0x72, 20, ext_sco_evt_num_naks },

> > +       { 0x73, 20, ext_sco_evt_num_failed_tx_by_wifi },

> > +       { 0x74, 20, ext_sco_evt_num_failed_rx_by_wifi },

> > +       { 0x75, 4, ext_sco_evt_samples_inserted },

> > +       { 0x76, 4, ext_sco_evt_samples_dropped },

> > +       { 0x77, 4, ext_sco_evt_mute_samples },

> > +       { 0x78, 4, ext_sco_evt_plc_injection_data },

> > +

> > +       /* end */

> > +       { 0x0, 0}

> > +};

> > +

> > +static const struct intel_tlv *process_ext_subevent(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].length > 0; i++) {

> > +               if (intel_ext_subevent_table[i].subevent_id ==

> > +                                                       tlv->subevent_id) {

> > +                       subevent = &intel_ext_subevent_table[i];

> > +                       break;

> > +               }

> > +       }

> > +

> > +       if (!subevent) {

> > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > +                               "Unknown extended subevent 0x%2.2x",

> > +                               tlv->subevent_id);

> > +               return NULL;

> > +       }

> > +

> > +       if (tlv->length != subevent->length) {

> > +               print_text(COLOR_ERROR, "Invalid length %d of subevent 0x%2.2x",

> > +                               tlv->length, tlv->subevent_id);

> > +               return NULL;

> > +       }

> > +

> > +       if (next_tlv > last_tlv) {

> > +               print_text(COLOR_ERROR, "Subevent exceeds the buffer size.");

> > +               return NULL;

> > +       }

> > +

> > +       subevent->func(tlv);

> > +

> > +       return next_tlv;

> > +}

> > +

> > +static void intel_vendor_ext_evt(const void *data, uint8_t size)

> > +{

> > +       /* The data pointer points to a number of tlv.*/

> > +       const struct intel_tlv *tlv = data;

> > +       const struct intel_tlv *last_tlv = data + size;

> > +

> > +       /* Process every tlv subevent until reaching last_tlv.

> > +        * The decoding process terminates normally when tlv == last_tlv.

> > +        */

> > +       while (tlv && tlv < last_tlv)

> > +               tlv = process_ext_subevent(tlv, last_tlv);

> > +

> > +       /* If an error occurs in decoding the subevents, hexdump the packet. */

> > +       if (!tlv)

> > +               packet_hexdump(data, size);

> > +}

> > +

> > +/* Vendor extended events with a vendor prefix. */

> > +static const struct vendor_evt vendor_prefix_evt_table[] = {

> > +       { 0x03, "Extended Telemetry", intel_vendor_ext_evt },

> > +       { }

> > +};

> > +

> > +const uint8_t intel_vendor_prefix[] = {0x87, 0x80};

> > +#define INTEL_VENDOR_PREFIX_SIZE sizeof(intel_vendor_prefix)

> > +

> > +/*

> > + * The vendor event with Intel vendor prefix.

> > + * Its format looks like

> > + *   0xff <length> <vendor_prefix> <subopcode> <data>

> > + *   where Intel's <vendor_prefix> is 0x8780.

> > + *

> > + *   When <subopcode> == 0x03, it is a telemetry event; and

> > + *   <data> is a number of tlv data.

> > + */

> > +struct vendor_prefix_evt {

> > +       uint8_t prefix_data[INTEL_VENDOR_PREFIX_SIZE];

> > +       uint8_t subopcode;

> > +};

> > +

> > +static const struct vendor_evt *intel_vendor_prefix_evt(const void *data,

> > +                                                       int *consumed_size)

> > +{

> > +       unsigned int i;

> > +       const struct vendor_prefix_evt *vnd = data;

> > +       char prefix_string[INTEL_VENDOR_PREFIX_SIZE * 2 + 1] = { 0 };

> > +

> > +       /* Check if the vendor prefix matches. */

> > +       for (i = 0; i < INTEL_VENDOR_PREFIX_SIZE; i++) {

> > +               if (vnd->prefix_data[i] != intel_vendor_prefix[i])

> > +                       return NULL;

> > +               sprintf(prefix_string + i * 2, "%02x", vnd->prefix_data[i]);

> > +       }

> > +       print_field("Vendor Prefix (0x%s)", prefix_string);

> > +

> > +       /*

> > +        * Handle the vendor event with a vendor prefix.

> > +        *   0xff <length> <vendor_prefix> <subopcode> <data>

> > +        * This loop checks whether the <subopcode> exists in the

> > +        * vendor_prefix_evt_table.

> > +        */

> > +       for (i = 0; vendor_prefix_evt_table[i].str; i++) {

> > +               if (vendor_prefix_evt_table[i].evt == vnd->subopcode) {

> > +                       *consumed_size = sizeof(struct vendor_prefix_evt);

> > +                       return &vendor_prefix_evt_table[i];

> > +               }

> > +       }

> > +

> > +       return NULL;

> > +}

> > +

> > +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size)

> > +{

> > +       uint8_t evt = *((const uint8_t *) data);

> > +       int i;

> > +

> > +       /*

> > +        * Handle the vendor event without a vendor prefix.

> > +        *   0xff <length> <evt> <data>

> > +        * This loop checks whether the <evt> exists in the vendor_evt_table.

> > +        */

> >         for (i = 0; vendor_evt_table[i].str; i++) {

> >                 if (vendor_evt_table[i].evt == evt)

> >                         return &vendor_evt_table[i];

> >         }

> >

> > -       return NULL;

> > +       /*

> > +        * It is not a regular event. Check whether it is a vendor extended

> > +        * event that comes with a vendor prefix followed by a subopcode.

> > +        */

> > +       return intel_vendor_prefix_evt(data, consumed_size);

> >  }

> > diff --git a/monitor/intel.h b/monitor/intel.h

> > index bf00ad491..bfb04540c 100644

> > --- a/monitor/intel.h

> > +++ b/monitor/intel.h

> > @@ -15,4 +15,4 @@ struct vendor_ocf;

> >  struct vendor_evt;

> >

> >  const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf);

> > -const struct vendor_evt *intel_vendor_evt(uint8_t evt);

> > +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size);

> > diff --git a/monitor/packet.c b/monitor/packet.c

> > index 82513a63c..4a371f508 100644

> > --- a/monitor/packet.c

> > +++ b/monitor/packet.c

> > @@ -9371,9 +9371,14 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)

> >         return NULL;

> >  }

> >

> > -static const struct vendor_evt *current_vendor_evt(uint8_t evt)

> > +static const struct vendor_evt *current_vendor_evt(const void *data,

> > +                                                       int *consumed_size)

> >  {

> >         uint16_t manufacturer, msft_opcode;

> > +       uint8_t evt = *((const uint8_t *) data);

> > +

> > +       /* A regular vendor event consumes 1 byte. */

> > +       *consumed_size = 1;

> >

> >         if (index_current < MAX_INDEX) {

> >                 manufacturer = index_list[index_current].manufacturer;

> > @@ -9388,7 +9393,7 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)

> >

> >         switch (manufacturer) {

> >         case 2:

> > -               return intel_vendor_evt(evt);

> > +               return intel_vendor_evt(data, consumed_size);

> >         case 15:

> >                 return broadcom_vendor_evt(evt);

> >         }

> > @@ -11007,10 +11012,10 @@ static void le_meta_event_evt(const void *data, uint8_t size)

> >

> >  static void vendor_evt(const void *data, uint8_t size)

> >  {

> > -       uint8_t subevent = *((const uint8_t *) data);

> >         struct subevent_data vendor_data;

> >         char vendor_str[150];

> > -       const struct vendor_evt *vnd = current_vendor_evt(subevent);

> > +       int consumed_size;

> > +       const struct vendor_evt *vnd = current_vendor_evt(data, &consumed_size);

> >

> >         if (vnd) {

> >                 const char *str = current_vendor_str();

> > @@ -11021,12 +11026,13 @@ static void vendor_evt(const void *data, uint8_t size)

> >                         vendor_data.str = vendor_str;

> >                 } else

> >                         vendor_data.str = vnd->str;

> > -               vendor_data.subevent = subevent;

> > +               vendor_data.subevent = vnd->evt;

> >                 vendor_data.func = vnd->evt_func;

> >                 vendor_data.size = vnd->evt_size;

> >                 vendor_data.fixed = vnd->evt_fixed;

> >

> > -               print_subevent(&vendor_data, data + 1, size - 1);

> > +               print_subevent(&vendor_data, data + consumed_size,

> > +                                                       size - consumed_size);

> >         } else {

> >                 uint16_t manufacturer;

> >

> > --

> > 2.32.0.93.g670b81a890-goog

> >

>

>

> --

>

> Joseph Shyh-In Hwang

> Email: josephsih@google.com




-- 
Luiz Augusto von Dentz
Joseph Hwang July 15, 2021, 10 a.m. UTC | #5
Hi Luiz:

  I am sorry that I did not notice your previous replies and review
comments for patches 2/3 and 3/3. I have fixed them and sent v6. PTAL.

Thank you very much!
Joseph

On Thu, Jul 15, 2021 at 6:22 AM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>

> Hi Joseph,

>

> On Mon, Jul 12, 2021 at 7:08 PM Joseph Hwang <josephsih@google.com> wrote:

> >

> > Hi Luiz:

> >

> >   A gentle ping for a re-review on the new changes per your previous comments.

>

> Ive left comments to patch 2/3 and 3/3 that still haven't been addressed:

>

> 2/3: We need to new UUID to be added to util.c like the rest of

> experimental UUIDs:

> https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/shared/util.c#n1023

>

> 3/3: Instead of having a function that doesn't seem to be used

> anywhere it would probably be easier to enable whenever

> btd_opts.experimental is enable e.g.:

> https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/adapter.c#n9293

>

> > Thanks and best regards,

> > Joseph

> >

> >

> > On Tue, Jun 29, 2021 at 3:47 PM Joseph Hwang <josephsih@chromium.org> wrote:

> > >

> > > This patch adds new Intel extended telemetry events for both ACL and

> > > SCO/eSCO audio link quality reports.

> > >

> > > For SCO/eSCO audio link quality report, it shows something like

> > > > HCI Event: Vendor (0xff) plen 190  #120 [hci0] 2021-05-31 20:27:50.257

> > >         Vendor Prefix (0x8780)

> > >       Intel Extended Telemetry (0x87)

> > >         Extended Telemetry (0x80): SubOpcode (0x03)

> > >         Extended event type (0x01): Audio Link Quality Report Type(0x05)

> > >         SCO/eSCO connection handle (0x6a): 0x0101

> > >         Packets from host (0x6b): 399

> > >         Tx packets (0x6c): 403

> > >         Rx payload lost (0x6d): 3

> > >         Tx payload lost (0x6e): 0

> > >         Rx No SYNC errors (0x6f): 3 2 3 3 0

> > >         Rx HEC errors (0x70): 0 0 0 0 0

> > >         Rx CRC errors (0x71): 2 0 0 0 0

> > >         Rx NAK errors (0x72): 6 0 0 0 0

> > >         Failed Tx due to Wifi coex (0x73): 6 0 0 0 0

> > >         Failed Rx due to Wifi coex (0x74): 0 0 0 0 0

> > >         Late samples inserted based on CDC (0x75): 0

> > >         Samples dropped (0x76): 0

> > >         Mute samples sent at initial connection (0x77): 0

> > >         PLC injection data (0x78): 0

> > >

> > > For ACL audio link quality report, it shows something like

> > > > HCI Event: Vendor (0xff) plen 142  #120 [hci0] 2021-05-31 20:27:50.261

> > >         Vendor Prefix (0x8780)

> > >       Intel Extended Telemetry (0x87)

> > >         Extended Telemetry (0x80): SubOpcode (0x03)

> > >         Extended event type (0x01): Audio Link Quality Report Type(0x05)

> > >         ACL connection handle (0x4a): 0x0100

> > >         Rx HEC errors (0x4b): 0

> > >         Rx CRC errors (0x4c): 0

> > >         Packets from host (0x4d): 100

> > >         Tx packets (0x4e): 101

> > >         Tx packets 0 retries (0x4f): 89

> > >         Tx packets 1 retries (0x50): 11

> > >         Tx packets 2 retries (0x51): 1

> > >         Tx packets 3 retries (0x52): 0

> > >         Tx packets 4 retries and more (0x53): 0

> > >         Tx DH1 packets (0x54): 0

> > >         Tx DH3 packets (0x55): 0

> > >         Tx DH5 packets (0x56): 0

> > >         Tx 2DH1 packets (0x57): 0

> > >         Tx 2DH3 packets (0x58): 0

> > >         Tx 2DH5 packets (0x59): 0

> > >         Tx 3DH1 packets (0x5a): 6

> > >         Tx 3DH3 packets (0x5b): 0

> > >         Tx 3DH5 packets (0x5c): 94

> > >         Rx packets (0x5d): 272

> > >         ACL link throughput (KBps) (0x5e): 343815

> > >         ACL max packet latency (ms) (0x5f): 20625

> > >         ACL avg packet latency (ms) (0x60): 12

> > >

> > > Reviewed-by: Miao-chen Chou <mcchou@chromium.org>

> > > ---

> > >

> > > Changes in v5:

> > > - Fix two Make errors.

> > > - Please also review Series-changes 3.

> > >

> > > Changes in v4:

> > > - Fix a Make error.

> > > - Please also review Series-changes 3.

> > >

> > > Changes in v3:

> > > - Define the packed struct intel_ext_evt for the extended telemetry

> > >   event.

> > > - Define the packed struct intel_tlv for the telemetry subevent.

> > > - Define a new function intel_vendor_prefix_evt() to handle the new

> > >   vendor event type with a vendor prefix

> > >       0xff <length> <vendor_prefix> <subopcode> <data>

> > >   while intel_vendor_evt() handles the original vendor event type

> > >       0xff <length> <subopcode> <data>

> > > - Add the vendor_prefix_evt_table table so that more subopcodes can be

> > >   added for the events with a vendor prefix.

> > > - Move the event data buffer check after processing the current tlv.

> > > - Fix typos.

> > >

> > > Changes in v2:

> > > - Perform size checks for tlv subevents.

> > > - Fix the Make errors about qualifiers.

> > >

> > >  monitor/intel.c  | 492 ++++++++++++++++++++++++++++++++++++++++++++++-

> > >  monitor/intel.h  |   2 +-

> > >  monitor/packet.c |  18 +-

> > >  3 files changed, 503 insertions(+), 9 deletions(-)

> > >

> > > diff --git a/monitor/intel.c b/monitor/intel.c

> > > index d2aefa6a8..7591df4ee 100644

> > > --- a/monitor/intel.c

> > > +++ b/monitor/intel.c

> > > @@ -30,6 +30,7 @@

> > >

> > >  #define COLOR_UNKNOWN_EVENT_MASK       COLOR_WHITE_BG

> > >  #define COLOR_UNKNOWN_SCAN_STATUS      COLOR_WHITE_BG

> > > +#define COLOR_UNKNOWN_EXT_EVENT                COLOR_WHITE_BG

> > >

> > >  static void print_status(uint8_t status)

> > >  {

> > > @@ -992,14 +993,501 @@ static const struct vendor_evt vendor_evt_table[] = {

> > >         { }

> > >  };

> > >

> > > -const struct vendor_evt *intel_vendor_evt(uint8_t evt)

> > > +/*

> > > + * An Intel telemetry subevent is of the TLV format.

> > > + * - Type: takes 1 byte. This is the subevent_id.

> > > + * - Length: takes 1 byte.

> > > + * - Value: takes |Length| bytes.

> > > + */

> > > +struct intel_tlv {

> > > +       uint8_t subevent_id;

> > > +       uint8_t length;

> > > +       uint8_t value[];

> > > +};

> > > +

> > > +#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))

> > > +

> > > +static void ext_evt_type(const struct intel_tlv *tlv)

> > > +{

> > > +       uint8_t evt_type = get_u8(tlv->value);

> > > +       const char *str;

> > > +

> > > +       switch (evt_type) {

> > > +       case 0x00:

> > > +               str = "System Exception";

> > > +               break;

> > > +       case 0x01:

> > > +               str = "Fatal Exception";

> > > +               break;

> > > +       case 0x02:

> > > +               str = "Debug Exception";

> > > +               break;

> > > +       case 0x03:

> > > +               str = "Connection Event for BR/EDR Link Type";

> > > +               break;

> > > +       case 0x04:

> > > +               str = "Disconnection Event";

> > > +               break;

> > > +       case 0x05:

> > > +               str = "Audio Link Quality Report Type";

> > > +               break;

> > > +       case 0x06:

> > > +               str = "Stats for BR/EDR Link Type";

> > > +               break;

> > > +       default:

> > > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > > +                       "Unknown extended telemetry event type (0x%2.2x)",

> > > +                       evt_type);

> > > +               packet_hexdump((const void *) tlv,

> > > +                                       tlv->length + 2 * sizeof(uint8_t));

> > > +               return;

> > > +       }

> > > +

> > > +       print_field("Extended event type (0x%2.2x): %s (0x%2.2x)",

> > > +                       tlv->subevent_id, str, evt_type);

> > > +}

> > > +

> > > +static void ext_acl_evt_conn_handle(const struct intel_tlv *tlv)

> > > +{

> > > +       uint16_t conn_handle = get_le16(tlv->value);

> > > +

> > > +       print_field("ACL connection handle (0x%2.2x): 0x%4.4x",

> > > +                       tlv->subevent_id, conn_handle);

> > > +}

> > > +

> > > +static void ext_acl_evt_hec_errors(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Rx HEC errors (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_crc_errors(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Rx CRC errors (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_num_pkt_from_host(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Packets from host (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_num_tx_pkt_retry(const struct intel_tlv *tlv)

> > > +{

> > > +       char *subevent_str;

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       switch (tlv->subevent_id) {

> > > +       case 0x4f:

> > > +               subevent_str = "Tx packets 0 retries";

> > > +               break;

> > > +       case 0x50:

> > > +               subevent_str = "Tx packets 1 retries";

> > > +               break;

> > > +       case 0x51:

> > > +               subevent_str = "Tx packets 2 retries";

> > > +               break;

> > > +       case 0x52:

> > > +               subevent_str = "Tx packets 3 retries";

> > > +               break;

> > > +       case 0x53:

> > > +               subevent_str = "Tx packets 4 retries and more";

> > > +               break;

> > > +       default:

> > > +               subevent_str = "Unknown";

> > > +               break;

> > > +       }

> > > +

> > > +       print_field("%s (0x%2.2x): %d", subevent_str, tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_num_tx_pkt_type(const struct intel_tlv *tlv)

> > > +{

> > > +       char *packet_type_str;

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       switch (tlv->subevent_id) {

> > > +       case 0x54:

> > > +               packet_type_str = "DH1";

> > > +               break;

> > > +       case 0x55:

> > > +               packet_type_str = "DH3";

> > > +               break;

> > > +       case 0x56:

> > > +               packet_type_str = "DH5";

> > > +               break;

> > > +       case 0x57:

> > > +               packet_type_str = "2DH1";

> > > +               break;

> > > +       case 0x58:

> > > +               packet_type_str = "2DH3";

> > > +               break;

> > > +       case 0x59:

> > > +               packet_type_str = "2DH5";

> > > +               break;

> > > +       case 0x5a:

> > > +               packet_type_str = "3DH1";

> > > +               break;

> > > +       case 0x5b:

> > > +               packet_type_str = "3DH3";

> > > +               break;

> > > +       case 0x5c:

> > > +               packet_type_str = "3DH5";

> > > +               break;

> > > +       default:

> > > +               packet_type_str = "Unknown";

> > > +               break;

> > > +       }

> > > +

> > > +       print_field("Tx %s packets (0x%2.2x): %d",

> > > +                       packet_type_str, tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Rx packets (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_link_throughput(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("ACL link throughput (KBps) (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_max_packet_latency(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("ACL max packet latency (ms) (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_acl_evt_avg_packet_latency(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("ACL avg packet latency (ms) (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_conn_handle(const struct intel_tlv *tlv)

> > > +{

> > > +       uint16_t conn_handle = get_le16(tlv->value);

> > > +

> > > +       print_field("SCO/eSCO connection handle (0x%2.2x): 0x%4.4x",

> > > +                       tlv->subevent_id, conn_handle);

> > > +}

> > > +

> > > +static void ext_sco_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)

> > >  {

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_num_rx_payloads_lost(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Rx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_num_tx_payloads_lost(const struct intel_tlv *tlv)

> > > +{

> > > +

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Tx payload lost (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void slots_errors(const struct intel_tlv *tlv, const char *type_str)

> > > +{

> > > +       /* The subevent has 5 slots where each slot is of the uint32_t type. */

> > > +       uint32_t num[5];

> > > +       const uint8_t *data = tlv->value;

> > > +       int i;

> > > +

> > > +       if (tlv->length != 5 * sizeof(uint32_t)) {

> > > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > > +                               "  Invalid subevent length (%d)", tlv->length);

> > > +               return;

> > > +       }

> > > +

> > > +       for (i = 0; i < 5; i++) {

> > > +               num[i] = get_le32(data);

> > > +               data += sizeof(uint32_t);

> > > +       }

> > > +

> > > +       print_field("%s (0x%2.2x): %d %d %d %d %d", type_str, tlv->subevent_id,

> > > +                       num[0], num[1], num[2], num[3], num[4]);

> > > +}

> > > +

> > > +static void ext_sco_evt_num_no_sync_errors(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Rx No SYNC errors");

> > > +}

> > > +

> > > +static void ext_sco_evt_num_hec_errors(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Rx HEC errors");

> > > +}

> > > +

> > > +static void ext_sco_evt_num_crc_errors(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Rx CRC errors");

> > > +}

> > > +

> > > +static void ext_sco_evt_num_naks(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Rx NAK errors");

> > > +}

> > > +

> > > +static void ext_sco_evt_num_failed_tx_by_wifi(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Failed Tx due to Wifi coex");

> > > +}

> > > +

> > > +static void ext_sco_evt_num_failed_rx_by_wifi(const struct intel_tlv *tlv)

> > > +{

> > > +       slots_errors(tlv, "Failed Rx due to Wifi coex");

> > > +}

> > > +

> > > +static void ext_sco_evt_samples_inserted(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Late samples inserted based on CDC (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_samples_dropped(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Samples dropped (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_mute_samples(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("Mute samples sent at initial connection (0x%2.2x): %d",

> > > +                       tlv->subevent_id, num);

> > > +}

> > > +

> > > +static void ext_sco_evt_plc_injection_data(const struct intel_tlv *tlv)

> > > +{

> > > +       uint32_t num = get_le32(tlv->value);

> > > +

> > > +       print_field("PLC injection data (0x%2.2x): %d", tlv->subevent_id, num);

> > > +}

> > > +

> > > +static const struct intel_ext_subevent {

> > > +       uint8_t subevent_id;

> > > +       uint8_t length;

> > > +       void (*func)(const struct intel_tlv *tlv);

> > > +} intel_ext_subevent_table[] = {

> > > +       { 0x01, 1, ext_evt_type },

> > > +

> > > +       /* ACL audio link quality subevents */

> > > +       { 0x4a, 2, ext_acl_evt_conn_handle },

> > > +       { 0x4b, 4, ext_acl_evt_hec_errors },

> > > +       { 0x4c, 4, ext_acl_evt_crc_errors },

> > > +       { 0x4d, 4, ext_acl_evt_num_pkt_from_host },

> > > +       { 0x4e, 4, ext_acl_evt_num_tx_pkt_to_air },

> > > +       { 0x4f, 4, ext_acl_evt_num_tx_pkt_retry },

> > > +       { 0x50, 4, ext_acl_evt_num_tx_pkt_retry },

> > > +       { 0x51, 4, ext_acl_evt_num_tx_pkt_retry },

> > > +       { 0x52, 4, ext_acl_evt_num_tx_pkt_retry },

> > > +       { 0x53, 4, ext_acl_evt_num_tx_pkt_retry },

> > > +       { 0x54, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x55, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x56, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x57, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x58, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x59, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x5a, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x5b, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x5c, 4, ext_acl_evt_num_tx_pkt_type },

> > > +       { 0x5d, 4, ext_acl_evt_num_rx_pkt_from_air },

> > > +       { 0x5e, 4, ext_acl_evt_link_throughput },

> > > +       { 0x5f, 4, ext_acl_evt_max_packet_latency },

> > > +       { 0x60, 4, ext_acl_evt_avg_packet_latency },

> > > +

> > > +       /* SCO/eSCO audio link quality subevents */

> > > +       { 0x6a, 2, ext_sco_evt_conn_handle },

> > > +       { 0x6b, 4, ext_sco_evt_num_rx_pkt_from_air },

> > > +       { 0x6c, 4, ext_sco_evt_num_tx_pkt_to_air },

> > > +       { 0x6d, 4, ext_sco_evt_num_rx_payloads_lost },

> > > +       { 0x6e, 4, ext_sco_evt_num_tx_payloads_lost },

> > > +       { 0x6f, 20, ext_sco_evt_num_no_sync_errors },

> > > +       { 0x70, 20, ext_sco_evt_num_hec_errors },

> > > +       { 0x71, 20, ext_sco_evt_num_crc_errors },

> > > +       { 0x72, 20, ext_sco_evt_num_naks },

> > > +       { 0x73, 20, ext_sco_evt_num_failed_tx_by_wifi },

> > > +       { 0x74, 20, ext_sco_evt_num_failed_rx_by_wifi },

> > > +       { 0x75, 4, ext_sco_evt_samples_inserted },

> > > +       { 0x76, 4, ext_sco_evt_samples_dropped },

> > > +       { 0x77, 4, ext_sco_evt_mute_samples },

> > > +       { 0x78, 4, ext_sco_evt_plc_injection_data },

> > > +

> > > +       /* end */

> > > +       { 0x0, 0}

> > > +};

> > > +

> > > +static const struct intel_tlv *process_ext_subevent(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].length > 0; i++) {

> > > +               if (intel_ext_subevent_table[i].subevent_id ==

> > > +                                                       tlv->subevent_id) {

> > > +                       subevent = &intel_ext_subevent_table[i];

> > > +                       break;

> > > +               }

> > > +       }

> > > +

> > > +       if (!subevent) {

> > > +               print_text(COLOR_UNKNOWN_EXT_EVENT,

> > > +                               "Unknown extended subevent 0x%2.2x",

> > > +                               tlv->subevent_id);

> > > +               return NULL;

> > > +       }

> > > +

> > > +       if (tlv->length != subevent->length) {

> > > +               print_text(COLOR_ERROR, "Invalid length %d of subevent 0x%2.2x",

> > > +                               tlv->length, tlv->subevent_id);

> > > +               return NULL;

> > > +       }

> > > +

> > > +       if (next_tlv > last_tlv) {

> > > +               print_text(COLOR_ERROR, "Subevent exceeds the buffer size.");

> > > +               return NULL;

> > > +       }

> > > +

> > > +       subevent->func(tlv);

> > > +

> > > +       return next_tlv;

> > > +}

> > > +

> > > +static void intel_vendor_ext_evt(const void *data, uint8_t size)

> > > +{

> > > +       /* The data pointer points to a number of tlv.*/

> > > +       const struct intel_tlv *tlv = data;

> > > +       const struct intel_tlv *last_tlv = data + size;

> > > +

> > > +       /* Process every tlv subevent until reaching last_tlv.

> > > +        * The decoding process terminates normally when tlv == last_tlv.

> > > +        */

> > > +       while (tlv && tlv < last_tlv)

> > > +               tlv = process_ext_subevent(tlv, last_tlv);

> > > +

> > > +       /* If an error occurs in decoding the subevents, hexdump the packet. */

> > > +       if (!tlv)

> > > +               packet_hexdump(data, size);

> > > +}

> > > +

> > > +/* Vendor extended events with a vendor prefix. */

> > > +static const struct vendor_evt vendor_prefix_evt_table[] = {

> > > +       { 0x03, "Extended Telemetry", intel_vendor_ext_evt },

> > > +       { }

> > > +};

> > > +

> > > +const uint8_t intel_vendor_prefix[] = {0x87, 0x80};

> > > +#define INTEL_VENDOR_PREFIX_SIZE sizeof(intel_vendor_prefix)

> > > +

> > > +/*

> > > + * The vendor event with Intel vendor prefix.

> > > + * Its format looks like

> > > + *   0xff <length> <vendor_prefix> <subopcode> <data>

> > > + *   where Intel's <vendor_prefix> is 0x8780.

> > > + *

> > > + *   When <subopcode> == 0x03, it is a telemetry event; and

> > > + *   <data> is a number of tlv data.

> > > + */

> > > +struct vendor_prefix_evt {

> > > +       uint8_t prefix_data[INTEL_VENDOR_PREFIX_SIZE];

> > > +       uint8_t subopcode;

> > > +};

> > > +

> > > +static const struct vendor_evt *intel_vendor_prefix_evt(const void *data,

> > > +                                                       int *consumed_size)

> > > +{

> > > +       unsigned int i;

> > > +       const struct vendor_prefix_evt *vnd = data;

> > > +       char prefix_string[INTEL_VENDOR_PREFIX_SIZE * 2 + 1] = { 0 };

> > > +

> > > +       /* Check if the vendor prefix matches. */

> > > +       for (i = 0; i < INTEL_VENDOR_PREFIX_SIZE; i++) {

> > > +               if (vnd->prefix_data[i] != intel_vendor_prefix[i])

> > > +                       return NULL;

> > > +               sprintf(prefix_string + i * 2, "%02x", vnd->prefix_data[i]);

> > > +       }

> > > +       print_field("Vendor Prefix (0x%s)", prefix_string);

> > > +

> > > +       /*

> > > +        * Handle the vendor event with a vendor prefix.

> > > +        *   0xff <length> <vendor_prefix> <subopcode> <data>

> > > +        * This loop checks whether the <subopcode> exists in the

> > > +        * vendor_prefix_evt_table.

> > > +        */

> > > +       for (i = 0; vendor_prefix_evt_table[i].str; i++) {

> > > +               if (vendor_prefix_evt_table[i].evt == vnd->subopcode) {

> > > +                       *consumed_size = sizeof(struct vendor_prefix_evt);

> > > +                       return &vendor_prefix_evt_table[i];

> > > +               }

> > > +       }

> > > +

> > > +       return NULL;

> > > +}

> > > +

> > > +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size)

> > > +{

> > > +       uint8_t evt = *((const uint8_t *) data);

> > > +       int i;

> > > +

> > > +       /*

> > > +        * Handle the vendor event without a vendor prefix.

> > > +        *   0xff <length> <evt> <data>

> > > +        * This loop checks whether the <evt> exists in the vendor_evt_table.

> > > +        */

> > >         for (i = 0; vendor_evt_table[i].str; i++) {

> > >                 if (vendor_evt_table[i].evt == evt)

> > >                         return &vendor_evt_table[i];

> > >         }

> > >

> > > -       return NULL;

> > > +       /*

> > > +        * It is not a regular event. Check whether it is a vendor extended

> > > +        * event that comes with a vendor prefix followed by a subopcode.

> > > +        */

> > > +       return intel_vendor_prefix_evt(data, consumed_size);

> > >  }

> > > diff --git a/monitor/intel.h b/monitor/intel.h

> > > index bf00ad491..bfb04540c 100644

> > > --- a/monitor/intel.h

> > > +++ b/monitor/intel.h

> > > @@ -15,4 +15,4 @@ struct vendor_ocf;

> > >  struct vendor_evt;

> > >

> > >  const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf);

> > > -const struct vendor_evt *intel_vendor_evt(uint8_t evt);

> > > +const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size);

> > > diff --git a/monitor/packet.c b/monitor/packet.c

> > > index 82513a63c..4a371f508 100644

> > > --- a/monitor/packet.c

> > > +++ b/monitor/packet.c

> > > @@ -9371,9 +9371,14 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)

> > >         return NULL;

> > >  }

> > >

> > > -static const struct vendor_evt *current_vendor_evt(uint8_t evt)

> > > +static const struct vendor_evt *current_vendor_evt(const void *data,

> > > +                                                       int *consumed_size)

> > >  {

> > >         uint16_t manufacturer, msft_opcode;

> > > +       uint8_t evt = *((const uint8_t *) data);

> > > +

> > > +       /* A regular vendor event consumes 1 byte. */

> > > +       *consumed_size = 1;

> > >

> > >         if (index_current < MAX_INDEX) {

> > >                 manufacturer = index_list[index_current].manufacturer;

> > > @@ -9388,7 +9393,7 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)

> > >

> > >         switch (manufacturer) {

> > >         case 2:

> > > -               return intel_vendor_evt(evt);

> > > +               return intel_vendor_evt(data, consumed_size);

> > >         case 15:

> > >                 return broadcom_vendor_evt(evt);

> > >         }

> > > @@ -11007,10 +11012,10 @@ static void le_meta_event_evt(const void *data, uint8_t size)

> > >

> > >  static void vendor_evt(const void *data, uint8_t size)

> > >  {

> > > -       uint8_t subevent = *((const uint8_t *) data);

> > >         struct subevent_data vendor_data;

> > >         char vendor_str[150];

> > > -       const struct vendor_evt *vnd = current_vendor_evt(subevent);

> > > +       int consumed_size;

> > > +       const struct vendor_evt *vnd = current_vendor_evt(data, &consumed_size);

> > >

> > >         if (vnd) {

> > >                 const char *str = current_vendor_str();

> > > @@ -11021,12 +11026,13 @@ static void vendor_evt(const void *data, uint8_t size)

> > >                         vendor_data.str = vendor_str;

> > >                 } else

> > >                         vendor_data.str = vnd->str;

> > > -               vendor_data.subevent = subevent;

> > > +               vendor_data.subevent = vnd->evt;

> > >                 vendor_data.func = vnd->evt_func;

> > >                 vendor_data.size = vnd->evt_size;

> > >                 vendor_data.fixed = vnd->evt_fixed;

> > >

> > > -               print_subevent(&vendor_data, data + 1, size - 1);

> > > +               print_subevent(&vendor_data, data + consumed_size,

> > > +                                                       size - consumed_size);

> > >         } else {

> > >                 uint16_t manufacturer;

> > >

> > > --

> > > 2.32.0.93.g670b81a890-goog

> > >

> >

> >

> > --

> >

> > Joseph Shyh-In Hwang

> > Email: josephsih@google.com

>

>

>

> --

> Luiz Augusto von Dentz




-- 

Joseph Shyh-In Hwang
Email: josephsih@google.com
diff mbox series

Patch

diff --git a/monitor/intel.c b/monitor/intel.c
index d2aefa6a8..7591df4ee 100644
--- a/monitor/intel.c
+++ b/monitor/intel.c
@@ -30,6 +30,7 @@ 
 
 #define COLOR_UNKNOWN_EVENT_MASK	COLOR_WHITE_BG
 #define COLOR_UNKNOWN_SCAN_STATUS	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_EXT_EVENT		COLOR_WHITE_BG
 
 static void print_status(uint8_t status)
 {
@@ -992,14 +993,501 @@  static const struct vendor_evt vendor_evt_table[] = {
 	{ }
 };
 
-const struct vendor_evt *intel_vendor_evt(uint8_t evt)
+/*
+ * An Intel telemetry subevent is of the TLV format.
+ * - Type: takes 1 byte. This is the subevent_id.
+ * - Length: takes 1 byte.
+ * - Value: takes |Length| bytes.
+ */
+struct intel_tlv {
+	uint8_t subevent_id;
+	uint8_t length;
+	uint8_t value[];
+};
+
+#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))
+
+static void ext_evt_type(const struct intel_tlv *tlv)
+{
+	uint8_t evt_type = get_u8(tlv->value);
+	const char *str;
+
+	switch (evt_type) {
+	case 0x00:
+		str = "System Exception";
+		break;
+	case 0x01:
+		str = "Fatal Exception";
+		break;
+	case 0x02:
+		str = "Debug Exception";
+		break;
+	case 0x03:
+		str = "Connection Event for BR/EDR Link Type";
+		break;
+	case 0x04:
+		str = "Disconnection Event";
+		break;
+	case 0x05:
+		str = "Audio Link Quality Report Type";
+		break;
+	case 0x06:
+		str = "Stats for BR/EDR Link Type";
+		break;
+	default:
+		print_text(COLOR_UNKNOWN_EXT_EVENT,
+			"Unknown extended telemetry event type (0x%2.2x)",
+			evt_type);
+		packet_hexdump((const void *) tlv,
+					tlv->length + 2 * sizeof(uint8_t));
+		return;
+	}
+
+	print_field("Extended event type (0x%2.2x): %s (0x%2.2x)",
+			tlv->subevent_id, str, evt_type);
+}
+
+static void ext_acl_evt_conn_handle(const struct intel_tlv *tlv)
+{
+	uint16_t conn_handle = get_le16(tlv->value);
+
+	print_field("ACL connection handle (0x%2.2x): 0x%4.4x",
+			tlv->subevent_id, conn_handle);
+}
+
+static void ext_acl_evt_hec_errors(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Rx HEC errors (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_crc_errors(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Rx CRC errors (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_num_pkt_from_host(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Packets from host (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_num_tx_pkt_retry(const struct intel_tlv *tlv)
+{
+	char *subevent_str;
+	uint32_t num = get_le32(tlv->value);
+
+	switch (tlv->subevent_id) {
+	case 0x4f:
+		subevent_str = "Tx packets 0 retries";
+		break;
+	case 0x50:
+		subevent_str = "Tx packets 1 retries";
+		break;
+	case 0x51:
+		subevent_str = "Tx packets 2 retries";
+		break;
+	case 0x52:
+		subevent_str = "Tx packets 3 retries";
+		break;
+	case 0x53:
+		subevent_str = "Tx packets 4 retries and more";
+		break;
+	default:
+		subevent_str = "Unknown";
+		break;
+	}
+
+	print_field("%s (0x%2.2x): %d", subevent_str, tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_num_tx_pkt_type(const struct intel_tlv *tlv)
+{
+	char *packet_type_str;
+	uint32_t num = get_le32(tlv->value);
+
+	switch (tlv->subevent_id) {
+	case 0x54:
+		packet_type_str = "DH1";
+		break;
+	case 0x55:
+		packet_type_str = "DH3";
+		break;
+	case 0x56:
+		packet_type_str = "DH5";
+		break;
+	case 0x57:
+		packet_type_str = "2DH1";
+		break;
+	case 0x58:
+		packet_type_str = "2DH3";
+		break;
+	case 0x59:
+		packet_type_str = "2DH5";
+		break;
+	case 0x5a:
+		packet_type_str = "3DH1";
+		break;
+	case 0x5b:
+		packet_type_str = "3DH3";
+		break;
+	case 0x5c:
+		packet_type_str = "3DH5";
+		break;
+	default:
+		packet_type_str = "Unknown";
+		break;
+	}
+
+	print_field("Tx %s packets (0x%2.2x): %d",
+			packet_type_str, tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Rx packets (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_link_throughput(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("ACL link throughput (KBps) (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_max_packet_latency(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("ACL max packet latency (ms) (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_acl_evt_avg_packet_latency(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("ACL avg packet latency (ms) (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_conn_handle(const struct intel_tlv *tlv)
+{
+	uint16_t conn_handle = get_le16(tlv->value);
+
+	print_field("SCO/eSCO connection handle (0x%2.2x): 0x%4.4x",
+			tlv->subevent_id, conn_handle);
+}
+
+static void ext_sco_evt_num_rx_pkt_from_air(const struct intel_tlv *tlv)
 {
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Packets from host (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_num_tx_pkt_to_air(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Tx packets (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_num_rx_payloads_lost(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Rx payload lost (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_num_tx_payloads_lost(const struct intel_tlv *tlv)
+{
+
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Tx payload lost (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void slots_errors(const struct intel_tlv *tlv, const char *type_str)
+{
+	/* The subevent has 5 slots where each slot is of the uint32_t type. */
+	uint32_t num[5];
+	const uint8_t *data = tlv->value;
+	int i;
+
+	if (tlv->length != 5 * sizeof(uint32_t)) {
+		print_text(COLOR_UNKNOWN_EXT_EVENT,
+				"  Invalid subevent length (%d)", tlv->length);
+		return;
+	}
+
+	for (i = 0; i < 5; i++) {
+		num[i] = get_le32(data);
+		data += sizeof(uint32_t);
+	}
+
+	print_field("%s (0x%2.2x): %d %d %d %d %d", type_str, tlv->subevent_id,
+			num[0], num[1], num[2], num[3], num[4]);
+}
+
+static void ext_sco_evt_num_no_sync_errors(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Rx No SYNC errors");
+}
+
+static void ext_sco_evt_num_hec_errors(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Rx HEC errors");
+}
+
+static void ext_sco_evt_num_crc_errors(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Rx CRC errors");
+}
+
+static void ext_sco_evt_num_naks(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Rx NAK errors");
+}
+
+static void ext_sco_evt_num_failed_tx_by_wifi(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Failed Tx due to Wifi coex");
+}
+
+static void ext_sco_evt_num_failed_rx_by_wifi(const struct intel_tlv *tlv)
+{
+	slots_errors(tlv, "Failed Rx due to Wifi coex");
+}
+
+static void ext_sco_evt_samples_inserted(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Late samples inserted based on CDC (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_samples_dropped(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Samples dropped (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_mute_samples(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("Mute samples sent at initial connection (0x%2.2x): %d",
+			tlv->subevent_id, num);
+}
+
+static void ext_sco_evt_plc_injection_data(const struct intel_tlv *tlv)
+{
+	uint32_t num = get_le32(tlv->value);
+
+	print_field("PLC injection data (0x%2.2x): %d", tlv->subevent_id, num);
+}
+
+static const struct intel_ext_subevent {
+	uint8_t subevent_id;
+	uint8_t length;
+	void (*func)(const struct intel_tlv *tlv);
+} intel_ext_subevent_table[] = {
+	{ 0x01, 1, ext_evt_type },
+
+	/* ACL audio link quality subevents */
+	{ 0x4a, 2, ext_acl_evt_conn_handle },
+	{ 0x4b, 4, ext_acl_evt_hec_errors },
+	{ 0x4c, 4, ext_acl_evt_crc_errors },
+	{ 0x4d, 4, ext_acl_evt_num_pkt_from_host },
+	{ 0x4e, 4, ext_acl_evt_num_tx_pkt_to_air },
+	{ 0x4f, 4, ext_acl_evt_num_tx_pkt_retry },
+	{ 0x50, 4, ext_acl_evt_num_tx_pkt_retry },
+	{ 0x51, 4, ext_acl_evt_num_tx_pkt_retry },
+	{ 0x52, 4, ext_acl_evt_num_tx_pkt_retry },
+	{ 0x53, 4, ext_acl_evt_num_tx_pkt_retry },
+	{ 0x54, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x55, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x56, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x57, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x58, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x59, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x5a, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x5b, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x5c, 4, ext_acl_evt_num_tx_pkt_type },
+	{ 0x5d, 4, ext_acl_evt_num_rx_pkt_from_air },
+	{ 0x5e, 4, ext_acl_evt_link_throughput },
+	{ 0x5f, 4, ext_acl_evt_max_packet_latency },
+	{ 0x60, 4, ext_acl_evt_avg_packet_latency },
+
+	/* SCO/eSCO audio link quality subevents */
+	{ 0x6a, 2, ext_sco_evt_conn_handle },
+	{ 0x6b, 4, ext_sco_evt_num_rx_pkt_from_air },
+	{ 0x6c, 4, ext_sco_evt_num_tx_pkt_to_air },
+	{ 0x6d, 4, ext_sco_evt_num_rx_payloads_lost },
+	{ 0x6e, 4, ext_sco_evt_num_tx_payloads_lost },
+	{ 0x6f, 20, ext_sco_evt_num_no_sync_errors },
+	{ 0x70, 20, ext_sco_evt_num_hec_errors },
+	{ 0x71, 20, ext_sco_evt_num_crc_errors },
+	{ 0x72, 20, ext_sco_evt_num_naks },
+	{ 0x73, 20, ext_sco_evt_num_failed_tx_by_wifi },
+	{ 0x74, 20, ext_sco_evt_num_failed_rx_by_wifi },
+	{ 0x75, 4, ext_sco_evt_samples_inserted },
+	{ 0x76, 4, ext_sco_evt_samples_dropped },
+	{ 0x77, 4, ext_sco_evt_mute_samples },
+	{ 0x78, 4, ext_sco_evt_plc_injection_data },
+
+	/* end */
+	{ 0x0, 0}
+};
+
+static const struct intel_tlv *process_ext_subevent(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].length > 0; i++) {
+		if (intel_ext_subevent_table[i].subevent_id ==
+							tlv->subevent_id) {
+			subevent = &intel_ext_subevent_table[i];
+			break;
+		}
+	}
+
+	if (!subevent) {
+		print_text(COLOR_UNKNOWN_EXT_EVENT,
+				"Unknown extended subevent 0x%2.2x",
+				tlv->subevent_id);
+		return NULL;
+	}
+
+	if (tlv->length != subevent->length) {
+		print_text(COLOR_ERROR, "Invalid length %d of subevent 0x%2.2x",
+				tlv->length, tlv->subevent_id);
+		return NULL;
+	}
+
+	if (next_tlv > last_tlv) {
+		print_text(COLOR_ERROR, "Subevent exceeds the buffer size.");
+		return NULL;
+	}
+
+	subevent->func(tlv);
+
+	return next_tlv;
+}
+
+static void intel_vendor_ext_evt(const void *data, uint8_t size)
+{
+	/* The data pointer points to a number of tlv.*/
+	const struct intel_tlv *tlv = data;
+	const struct intel_tlv *last_tlv = data + size;
+
+	/* Process every tlv subevent until reaching last_tlv.
+	 * The decoding process terminates normally when tlv == last_tlv.
+	 */
+	while (tlv && tlv < last_tlv)
+		tlv = process_ext_subevent(tlv, last_tlv);
+
+	/* If an error occurs in decoding the subevents, hexdump the packet. */
+	if (!tlv)
+		packet_hexdump(data, size);
+}
+
+/* Vendor extended events with a vendor prefix. */
+static const struct vendor_evt vendor_prefix_evt_table[] = {
+	{ 0x03, "Extended Telemetry", intel_vendor_ext_evt },
+	{ }
+};
+
+const uint8_t intel_vendor_prefix[] = {0x87, 0x80};
+#define INTEL_VENDOR_PREFIX_SIZE sizeof(intel_vendor_prefix)
+
+/*
+ * The vendor event with Intel vendor prefix.
+ * Its format looks like
+ *   0xff <length> <vendor_prefix> <subopcode> <data>
+ *   where Intel's <vendor_prefix> is 0x8780.
+ *
+ *   When <subopcode> == 0x03, it is a telemetry event; and
+ *   <data> is a number of tlv data.
+ */
+struct vendor_prefix_evt {
+	uint8_t prefix_data[INTEL_VENDOR_PREFIX_SIZE];
+	uint8_t subopcode;
+};
+
+static const struct vendor_evt *intel_vendor_prefix_evt(const void *data,
+							int *consumed_size)
+{
+	unsigned int i;
+	const struct vendor_prefix_evt *vnd = data;
+	char prefix_string[INTEL_VENDOR_PREFIX_SIZE * 2 + 1] = { 0 };
+
+	/* Check if the vendor prefix matches. */
+	for (i = 0; i < INTEL_VENDOR_PREFIX_SIZE; i++) {
+		if (vnd->prefix_data[i] != intel_vendor_prefix[i])
+			return NULL;
+		sprintf(prefix_string + i * 2, "%02x", vnd->prefix_data[i]);
+	}
+	print_field("Vendor Prefix (0x%s)", prefix_string);
+
+	/*
+	 * Handle the vendor event with a vendor prefix.
+	 *   0xff <length> <vendor_prefix> <subopcode> <data>
+	 * This loop checks whether the <subopcode> exists in the
+	 * vendor_prefix_evt_table.
+	 */
+	for (i = 0; vendor_prefix_evt_table[i].str; i++) {
+		if (vendor_prefix_evt_table[i].evt == vnd->subopcode) {
+			*consumed_size = sizeof(struct vendor_prefix_evt);
+			return &vendor_prefix_evt_table[i];
+		}
+	}
+
+	return NULL;
+}
+
+const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size)
+{
+	uint8_t evt = *((const uint8_t *) data);
+	int i;
+
+	/*
+	 * Handle the vendor event without a vendor prefix.
+	 *   0xff <length> <evt> <data>
+	 * This loop checks whether the <evt> exists in the vendor_evt_table.
+	 */
 	for (i = 0; vendor_evt_table[i].str; i++) {
 		if (vendor_evt_table[i].evt == evt)
 			return &vendor_evt_table[i];
 	}
 
-	return NULL;
+	/*
+	 * It is not a regular event. Check whether it is a vendor extended
+	 * event that comes with a vendor prefix followed by a subopcode.
+	 */
+	return intel_vendor_prefix_evt(data, consumed_size);
 }
diff --git a/monitor/intel.h b/monitor/intel.h
index bf00ad491..bfb04540c 100644
--- a/monitor/intel.h
+++ b/monitor/intel.h
@@ -15,4 +15,4 @@  struct vendor_ocf;
 struct vendor_evt;
 
 const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf);
-const struct vendor_evt *intel_vendor_evt(uint8_t evt);
+const struct vendor_evt *intel_vendor_evt(const void *data, int *consumed_size);
diff --git a/monitor/packet.c b/monitor/packet.c
index 82513a63c..4a371f508 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -9371,9 +9371,14 @@  static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
 	return NULL;
 }
 
-static const struct vendor_evt *current_vendor_evt(uint8_t evt)
+static const struct vendor_evt *current_vendor_evt(const void *data,
+							int *consumed_size)
 {
 	uint16_t manufacturer, msft_opcode;
+	uint8_t evt = *((const uint8_t *) data);
+
+	/* A regular vendor event consumes 1 byte. */
+	*consumed_size = 1;
 
 	if (index_current < MAX_INDEX) {
 		manufacturer = index_list[index_current].manufacturer;
@@ -9388,7 +9393,7 @@  static const struct vendor_evt *current_vendor_evt(uint8_t evt)
 
 	switch (manufacturer) {
 	case 2:
-		return intel_vendor_evt(evt);
+		return intel_vendor_evt(data, consumed_size);
 	case 15:
 		return broadcom_vendor_evt(evt);
 	}
@@ -11007,10 +11012,10 @@  static void le_meta_event_evt(const void *data, uint8_t size)
 
 static void vendor_evt(const void *data, uint8_t size)
 {
-	uint8_t subevent = *((const uint8_t *) data);
 	struct subevent_data vendor_data;
 	char vendor_str[150];
-	const struct vendor_evt *vnd = current_vendor_evt(subevent);
+	int consumed_size;
+	const struct vendor_evt *vnd = current_vendor_evt(data, &consumed_size);
 
 	if (vnd) {
 		const char *str = current_vendor_str();
@@ -11021,12 +11026,13 @@  static void vendor_evt(const void *data, uint8_t size)
 			vendor_data.str = vendor_str;
 		} else
 			vendor_data.str = vnd->str;
-		vendor_data.subevent = subevent;
+		vendor_data.subevent = vnd->evt;
 		vendor_data.func = vnd->evt_func;
 		vendor_data.size = vnd->evt_size;
 		vendor_data.fixed = vnd->evt_fixed;
 
-		print_subevent(&vendor_data, data + 1, size - 1);
+		print_subevent(&vendor_data, data + consumed_size,
+							size - consumed_size);
 	} else {
 		uint16_t manufacturer;