monitor/att: Add LTV deconding support for PAC/ASE
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 16 Jun 2022 22:59:12 +0000 (15:59 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 15 May 2023 09:25:54 +0000 (14:55 +0530)
This adds decoding support for PAC/ASE attributes:

> ACL Data RX: Handle 42 flags 0x02 dlen 31
      Channel: 65 len 27 sdu 25 [PSM 39 mode Enhanced Credit (0x81)] {chan 1}
      ATT: Read Response (0x0b) len 24
        Value: 010600000000100301ff0002020302030305041e00f00000
          Number of PAC(s): 1
          PAC #0:
            Codec: LC3 (0x06)
            Codec Specific Capabilities #0: len 0x03 type 0x01
              Sampling Frequencies: 0x00ff
                8 Khz (0x0001)
                11.25 Khz (0x0002)
                16 Khz (0x0004)
                22.05 Khz (0x0008)
                24 Khz (0x0010)
                32 Khz (0x0020)
                44.1 Khz (0x0040)
                48 Khz (0x0080)
            Codec Specific Capabilities #1: len 0x02 type 0x02
              Frame Duration: 0x0003
                7.5 ms (0x01)
                10 ms (0x02)
            Codec Specific Capabilities #2: len 0x02 type 0x03
              Audio Channel Count: 0x03
                1 channel (0x01)
                2 channels (0x02)
            Codec Specific Capabilities #3: len 0x05 type 0x04
              Frame Length: 30 (0x001e) - 240 (0x00f0)
> ACL Data RX: Handle 42 flags 0x02 dlen 30
      Channel: 64 len 26 sdu 24 [PSM 39 mode Enhanced Credit (0x81)] {chan 0}
      ATT: Write Command (0x52) len 23
        Handle: 0x0036 Type: ASE Control Point (0x2bc6)
          Data: 010101020206000000000a02010302020103042800
            Opcode: Codec Configuration (0x01)
            Number of ASE(s): 1
            ASE: #0
            ASE ID: 0x01
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: LC3 (0x06)
            Codec Specific Configuration #0: len 0x02 type 0x01
            Sampling Frequency: 16 Khz (0x03)
            Codec Specific Configuration #1: len 0x02 type 0x02
            Frame Duration: 10 ms (0x01)
            Codec Specific Configuration #2: len 0x03 type 0x04
            Frame Length: 40 (0x0028)

Signed-off-by: Manika Shrivastava <manika.sh@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
monitor/att.c
monitor/packet.c
monitor/packet.h

index 2fafea3..f49ef20 100644 (file)
@@ -278,7 +278,8 @@ static bool print_ase_codec(const struct l2cap_frame *frame)
        return true;
 }
 
-static bool print_ase_lv(const struct l2cap_frame *frame, const char *label)
+static bool print_ase_lv(const struct l2cap_frame *frame, const char *label,
+                       struct packet_ltv_decoder *decoder, size_t decoder_len)
 {
        struct bt_hci_lv_data *lv;
 
@@ -293,21 +294,299 @@ static bool print_ase_lv(const struct l2cap_frame *frame, const char *label)
                return false;
        }
 
-       packet_print_ltv(label, lv->data, lv->len);
+       packet_print_ltv(label, lv->data, lv->len, decoder, decoder_len);
 
        return true;
 }
 
-static bool print_ase_cc(const struct l2cap_frame *frame)
+static bool print_ase_cc(const struct l2cap_frame *frame, const char *label,
+                       struct packet_ltv_decoder *decoder, size_t decoder_len)
 {
-       return print_ase_lv(frame, "    Codec Specific Configuration");
+       return print_ase_lv(frame, label, decoder, decoder_len);
 }
 
+static const struct bitfield_data pac_context_table[] = {
+       {  0, "Unspecified (0x0001)"                    },
+       {  1, "Conversational (0x0002)"                 },
+       {  2, "Media (0x0004)"                          },
+       {  3, "Game (0x0008)"                           },
+       {  4, "Instructional (0x0010)"                  },
+       {  5, "Voice Assistants (0x0020)"               },
+       {  6, "Live (0x0040)"                           },
+       {  7, "Sound Effects (0x0080)"                  },
+       {  8, "Notifications (0x0100)"                  },
+       {  9, "Ringtone (0x0200)"                       },
+       {  10, "Alerts (0x0400)"                        },
+       {  11, "Emergency alarm (0x0800)"               },
+       {  12, "RFU (0x1000)"                           },
+       {  13, "RFU (0x2000)"                           },
+       {  14, "RFU (0x4000)"                           },
+       {  15, "RFU (0x8000)"                           },
+       { }
+};
+
+static void print_context(const struct l2cap_frame *frame, const char *label)
+{
+       uint16_t value;
+       uint16_t mask;
+
+       if (!l2cap_frame_get_le16((void *)frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("%s: 0x%4.4x", label, value);
+
+       mask = print_bitfield(8, value, pac_context_table);
+       if (mask)
+               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%4.4x)",
+                                                               mask);
+
+done:
+       if (frame->size)
+               print_hex_field("    Data", frame->data, frame->size);
+}
+
+static void ase_decode_preferred_context(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       print_context(&frame, "      Preferred Context");
+}
+
+static void ase_decode_context(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       print_context(&frame, "      Context");
+}
+
+static void ase_decode_program_info(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       const char *str;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       str = l2cap_frame_pull(&frame, &frame, len);
+       if (!str) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Program Info: %s", str);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static void ase_decode_language(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint32_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_le24(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Language: 0x%6.6x", value);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+struct packet_ltv_decoder ase_metadata_table[] = {
+       LTV_DEC(0x01, ase_decode_preferred_context),
+       LTV_DEC(0x02, ase_decode_context),
+       LTV_DEC(0x03, ase_decode_program_info),
+       LTV_DEC(0x04, ase_decode_language)
+};
+
 static bool print_ase_metadata(const struct l2cap_frame *frame)
 {
-       return print_ase_lv(frame, "    Metadata");
+       return print_ase_lv(frame, "    Metadata", NULL, 0);
+}
+
+static const struct bitfield_data pac_freq_table[] = {
+       {  0, "8 Khz (0x0001)"                          },
+       {  1, "11.25 Khz (0x0002)"                      },
+       {  2, "16 Khz (0x0004)"                         },
+       {  3, "22.05 Khz (0x0008)"                      },
+       {  4, "24 Khz (0x0010)"                         },
+       {  5, "32 Khz (0x0020)"                         },
+       {  6, "44.1 Khz (0x0040)"                       },
+       {  7, "48 Khz (0x0080)"                         },
+       {  8, "88.2 Khz (0x0100)"                       },
+       {  9, "96 Khz (0x0200)"                         },
+       {  10, "176.4 Khz (0x0400)"                     },
+       {  11, "192 Khz (0x0800)"                       },
+       {  12, "384 Khz (0x1000)"                       },
+       {  13, "RFU (0x2000)"                           },
+       {  14, "RFU (0x4000)"                           },
+       {  15, "RFU (0x8000)"                           },
+       { }
+};
+
+static void pac_decode_freq(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint16_t value;
+       uint16_t mask;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_le16(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Sampling Frequencies: 0x%4.4x", value);
+
+       mask = print_bitfield(8, value, pac_freq_table);
+       if (mask)
+               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%4.4x)",
+                                                               mask);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static const struct bitfield_data pac_duration_table[] = {
+       {  0, "7.5 ms (0x01)"                           },
+       {  1, "10 ms (0x02)"                            },
+       {  2, "RFU (0x04)"                              },
+       {  3, "RFU (0x08)"                              },
+       {  4, "7.5 ms preferred (0x10)"                 },
+       {  5, "10 ms preferred (0x20)"                  },
+       {  6, "RFU (0x40)"                              },
+       {  7, "RFU (0x80)"                              },
+       { }
+};
+
+static void pac_decode_duration(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+       uint8_t mask;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Frame Duration: 0x%4.4x", value);
+
+       mask = print_bitfield(8, value, pac_duration_table);
+       if (mask)
+               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%2.2x)",
+                                                               mask);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static const struct bitfield_data pac_channel_table[] = {
+       {  0, "1 channel (0x01)"                        },
+       {  1, "2 channels (0x02)"                       },
+       {  2, "3 channels (0x04)"                       },
+       {  3, "4 chanenls (0x08)"                       },
+       {  4, "5 channels (0x10)"                       },
+       {  5, "6 channels (0x20)"                       },
+       {  6, "7 channels (0x40)"                       },
+       {  7, "8 channels (0x80)"                       },
+       { }
+};
+
+static void pac_decode_channels(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+       uint8_t mask;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Audio Channel Count: 0x%2.2x", value);
+
+       mask = print_bitfield(8, value, pac_channel_table);
+       if (mask)
+               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%2.2x)",
+                                                               mask);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
 }
 
+static void pac_decode_frame_length(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint16_t min, max;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_le16(&frame, &min)) {
+               print_text(COLOR_ERROR, "    min: invalid size");
+               goto done;
+       }
+
+       if (!l2cap_frame_get_le16(&frame, &max)) {
+               print_text(COLOR_ERROR, "    min: invalid size");
+               goto done;
+       }
+
+       print_field("      Frame Length: %u (0x%4.4x) - %u (0x%4.4x)",
+                                                       min, min, max, max);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static void pac_decode_sdu(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Max SDU: %u (0x%2.2x)", value, value);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+struct packet_ltv_decoder pac_cap_table[] = {
+       LTV_DEC(0x01, pac_decode_freq),
+       LTV_DEC(0x02, pac_decode_duration),
+       LTV_DEC(0x03, pac_decode_channels),
+       LTV_DEC(0x04, pac_decode_frame_length),
+       LTV_DEC(0x05, pac_decode_sdu)
+};
+
 static void print_pac(const struct l2cap_frame *frame)
 {
        uint8_t num = 0, i;
@@ -325,7 +604,8 @@ static void print_pac(const struct l2cap_frame *frame)
                if (!print_ase_codec(frame))
                        goto done;
 
-               if (!print_ase_cc(frame))
+               if (!print_ase_cc(frame, "    Codec Specific Capabilities",
+                               pac_cap_table, ARRAY_SIZE(pac_cap_table)))
                        break;
 
                if (!print_ase_metadata(frame))
@@ -440,6 +720,210 @@ static bool print_ase_pd(const struct l2cap_frame *frame, const char *label)
        return true;
 }
 
+static void ase_decode_freq(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       switch (value) {
+       case 0x01:
+               print_field("      Sampling Frequency: 8 Khz (0x01)");
+               break;
+       case 0x02:
+               print_field("      Sampling Frequency: 11.25 Khz (0x02)");
+               break;
+       case 0x03:
+               print_field("      Sampling Frequency: 16 Khz (0x03)");
+               break;
+       case 0x04:
+               print_field("      Sampling Frequency: 22.05 Khz (0x04)");
+               break;
+       case 0x05:
+               print_field("      Sampling Frequency: 24 Khz (0x04)");
+               break;
+       case 0x06:
+               print_field("      Sampling Frequency: 32 Khz (0x04)");
+               break;
+       case 0x07:
+               print_field("      Sampling Frequency: 44.1 Khz (0x04)");
+               break;
+       case 0x08:
+               print_field("      Sampling Frequency: 48 Khz (0x04)");
+               break;
+       case 0x09:
+               print_field("      Sampling Frequency: 88.2 Khz (0x04)");
+               break;
+       case 0x0a:
+               print_field("      Sampling Frequency: 96 Khz (0x04)");
+               break;
+       case 0x0b:
+               print_field("      Sampling Frequency: 176.4 Khz (0x04)");
+               break;
+       case 0x0c:
+               print_field("      Sampling Frequency: 192 Khz (0x04)");
+               break;
+       case 0x0d:
+               print_field("      Sampling Frequency: 384 Khz (0x04)");
+               break;
+       default:
+               print_field("      Sampling Frequency: RFU (0x%2.2x)", value);
+               break;
+       }
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static void ase_decode_duration(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       switch (value) {
+       case 0x00:
+               print_field("      Frame Duration: 7.5 ms (0x00)");
+               break;
+       case 0x01:
+               print_field("      Frame Duration: 10 ms (0x01)");
+               break;
+       default:
+               print_field("      Frame Duration: RFU (0x%2.2x)", value);
+               break;
+       }
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static const struct bitfield_data channel_location_table[] = {
+       {  0, "Front Left (0x00000001)"                 },
+       {  1, "Front Right (0x00000002)"                },
+       {  2, "Front Center (0x00000004)"               },
+       {  3, "Low Frequency Effects 1 (0x00000008)"    },
+       {  4, "Back Left (0x00000010)"                  },
+       {  5, "Back Right (0x00000020)"                 },
+       {  6, "Front Left of Center (0x00000040)"       },
+       {  7, "Front Right of Center (0x00000080)"      },
+       {  8, "Back Center (0x00000100)"                },
+       {  9, "Low Frequency Effects 2 (0x00000200)"    },
+       {  10, "Side Left (0x00000400)"                 },
+       {  11, "Side Right (0x00000800)"                },
+       {  12, "Top Front Left (0x00001000)"            },
+       {  13, "Top Front Right (0x00002000)"           },
+       {  14, "Top Front Center (0x00004000)"          },
+       {  15, "Top Center (0x00008000)"                },
+       {  16, "Top Back Left (0x00010000)"             },
+       {  17, "Top Back Right (0x00020000)"            },
+       {  18, "Top Side Left (0x00040000)"             },
+       {  19, "Top Side Right (0x00080000)"            },
+       {  20, "Top Back Center (0x00100000)"           },
+       {  21, "Bottom Front Center (0x00200000)"       },
+       {  22, "Bottom Front Left (0x00400000)"         },
+       {  23, "Bottom Front Right (0x00800000)"        },
+       {  24, "Front Left Wide (0x01000000)"           },
+       {  25, "Front Right Wide (0x02000000)"          },
+       {  26, "Left Surround (0x04000000)"             },
+       {  27, "Right Surround (0x08000000)"            },
+       {  28, "RFU (0x10000000)"                       },
+       {  29, "RFU (0x20000000)"                       },
+       {  30, "RFU (0x40000000)"                       },
+       {  31, "RFU (0x80000000)"                       },
+       { }
+};
+
+static void print_location(const struct l2cap_frame *frame)
+{
+       uint32_t value;
+       uint32_t mask;
+
+       if (!l2cap_frame_get_le32((void *)frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("   Location: 0x%8.8x", value);
+
+       mask = print_bitfield(6, value, channel_location_table);
+       if (mask)
+               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%8.8x)",
+                                                               mask);
+
+done:
+       if (frame->size)
+               print_hex_field("  Data", frame->data, frame->size);
+}
+
+static void ase_decode_location(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       print_location(&frame);
+}
+
+static void ase_decode_frame_length(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint16_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_le16(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Frame Length: %u (0x%4.4x)", value, value);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+static void ase_decode_blocks(const uint8_t *data, uint8_t len)
+{
+       struct l2cap_frame frame;
+       uint8_t value;
+
+       l2cap_frame_init(&frame, 0, 0, 0, 0, 0, 0, data, len);
+
+       if (!l2cap_frame_get_u8(&frame, &value)) {
+               print_text(COLOR_ERROR, "    value: invalid size");
+               goto done;
+       }
+
+       print_field("      Frame Blocks per SDU: %u (0x%2.2x)", value, value);
+
+done:
+       if (frame.size)
+               print_hex_field("    Data", frame.data, frame.size);
+}
+
+struct packet_ltv_decoder ase_cc_table[] = {
+       LTV_DEC(0x01, ase_decode_freq),
+       LTV_DEC(0x02, ase_decode_duration),
+       LTV_DEC(0x03, ase_decode_location),
+       LTV_DEC(0x04, ase_decode_frame_length),
+       LTV_DEC(0x05, ase_decode_blocks)
+};
+
 static void print_ase_config(const struct l2cap_frame *frame)
 {
        if (!print_prefer_framing(frame))
@@ -469,7 +953,8 @@ static void print_ase_config(const struct l2cap_frame *frame)
        if (!print_ase_codec(frame))
                return;
 
-       print_ase_cc(frame);
+       print_ase_cc(frame, "    Codec Specific Configuration",
+                       ase_cc_table, ARRAY_SIZE(ase_cc_table));
 }
 
 static bool print_ase_framing(const struct l2cap_frame *frame,
@@ -703,7 +1188,8 @@ static bool ase_config_cmd(const struct l2cap_frame *frame)
        if (!print_ase_codec(frame))
                return false;
 
-       if (!print_ase_cc(frame))
+       if (!print_ase_cc(frame, "    Codec Specific Configuration",
+                               ase_cc_table, ARRAY_SIZE(ase_cc_table)))
                return false;
 
        return true;
@@ -1049,100 +1535,23 @@ static void ase_cp_notify(const struct l2cap_frame *frame)
        print_ase_cp_rsp(frame);
 }
 
-static const struct bitfield_data pac_loc_table[] = {
-       {  0, "Front Left (0x00000001)"                 },
-       {  1, "Front Right (0x00000002)"                },
-       {  2, "Front Center (0x00000004)"               },
-       {  3, "Low Frequency Effects 1 (0x00000008)"    },
-       {  4, "Back Left (0x00000010)"                  },
-       {  5, "Back Right (0x00000020)"                 },
-       {  6, "Front Left of Center (0x00000040)"       },
-       {  7, "Front Right of Center (0x00000080)"      },
-       {  8, "Back Center (0x00000100)"                },
-       {  9, "Low Frequency Effects 2 (0x00000200)"    },
-       {  10, "Side Left (0x00000400)"                 },
-       {  11, "Side Right (0x00000800)"                },
-       {  12, "Top Front Left (0x00001000)"            },
-       {  13, "Top Front Right (0x00002000)"           },
-       {  14, "Top Front Center (0x00004000)"          },
-       {  15, "Top Center (0x00008000)"                },
-       {  16, "Top Back Left (0x00010000)"             },
-       {  17, "Top Back Right (0x00020000)"            },
-       {  18, "Top Side Left (0x00040000)"             },
-       {  19, "Top Side Right (0x00080000)"            },
-       {  20, "Top Back Center (0x00100000)"           },
-       {  21, "Bottom Front Center (0x00200000)"       },
-       {  22, "Bottom Front Left (0x00400000)"         },
-       {  23, "Bottom Front Right (0x00800000)"        },
-       {  24, "Front Left Wide (0x01000000)"           },
-       {  25, "Front Right Wide (0x02000000)"          },
-       {  26, "Left Surround (0x04000000)"             },
-       {  27, "Right Surround (0x08000000)"            },
-       {  28, "RFU (0x10000000)"                       },
-       {  29, "RFU (0x20000000)"                       },
-       {  30, "RFU (0x40000000)"                       },
-       {  31, "RFU (0x80000000)"                       },
-       { }
-};
-
-static void print_loc_pac(const struct l2cap_frame *frame)
-{
-       uint32_t value;
-       uint8_t mask;
-
-       if (!l2cap_frame_get_le32((void *)frame, &value)) {
-               print_text(COLOR_ERROR, "    value: invalid size");
-               goto done;
-       }
-
-       print_field("  Location: 0x%8.8x", value);
-
-       mask = print_bitfield(4, value, pac_loc_table);
-       if (mask)
-               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%2.2x)",
-                                                               mask);
-
-done:
-       if (frame->size)
-               print_hex_field("  Data", frame->data, frame->size);
-}
-
 static void pac_loc_read(const struct l2cap_frame *frame)
 {
-       print_loc_pac(frame);
+       print_location(frame);
 }
 
 static void pac_loc_notify(const struct l2cap_frame *frame)
 {
-       print_loc_pac(frame);
+       print_location(frame);
 }
 
-static const struct bitfield_data pac_context_table[] = {
-       {  0, "Unspecified (0x0001)"                    },
-       {  1, "Conversational (0x0002)"                 },
-       {  2, "Media (0x0004)"                          },
-       {  3, "Game (0x0008)"                           },
-       {  4, "Instructional (0x0010)"                  },
-       {  5, "Voice Assistants (0x0020)"               },
-       {  6, "Live (0x0040)"                           },
-       {  7, "Sound Effects (0x0080)"                  },
-       {  8, "Notifications (0x0100)"                  },
-       {  9, "Ringtone (0x0200)"                       },
-       {  10, "Alerts (0x0400)"                        },
-       {  11, "Emergency alarm (0x0800)"               },
-       {  12, "RFU (0x1000)"                           },
-       {  13, "RFU (0x2000)"                           },
-       {  14, "RFU (0x4000)"                           },
-       {  15, "RFU (0x8000)"                           },
-};
-
 static void print_pac_context(const struct l2cap_frame *frame)
 {
        uint16_t snk, src;
        uint16_t mask;
 
        if (!l2cap_frame_get_le16((void *)frame, &snk)) {
-               print_text(COLOR_ERROR, "    value: invalid size");
+               print_text(COLOR_ERROR, "  sink: invalid size");
                goto done;
        }
 
@@ -1150,11 +1559,11 @@ static void print_pac_context(const struct l2cap_frame *frame)
 
        mask = print_bitfield(4, snk, pac_context_table);
        if (mask)
-               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%4.4x)",
+               print_text(COLOR_WHITE_BG, "  Unknown fields (0x%4.4x)",
                                                                mask);
 
        if (!l2cap_frame_get_le16((void *)frame, &src)) {
-               print_text(COLOR_ERROR, "    sink: invalid size");
+               print_text(COLOR_ERROR, "  source: invalid size");
                goto done;
        }
 
@@ -1162,7 +1571,7 @@ static void print_pac_context(const struct l2cap_frame *frame)
 
        mask = print_bitfield(4, src, pac_context_table);
        if (mask)
-               print_text(COLOR_WHITE_BG, "    Unknown fields (0x%4.4x)",
+               print_text(COLOR_WHITE_BG, "  Unknown fields (0x%4.4x)",
                                                                mask);
 
 done:
index 9a2611a..9b35706 100755 (executable)
@@ -3337,7 +3337,26 @@ static void *iov_pull(struct iovec *iov, size_t len)
        return data;
 }
 
-static void print_ltv(const char *label, const uint8_t *data, uint8_t len)
+static struct packet_ltv_decoder*
+get_ltv_decoder(struct packet_ltv_decoder *decoder, size_t num, uint8_t type)
+{
+       size_t i;
+
+       if (!decoder || !num)
+               return NULL;
+
+       for (i = 0; i < num; i++) {
+               struct packet_ltv_decoder *dec = &decoder[i];
+
+               if (dec->type == type)
+                       return dec;
+       }
+
+       return NULL;
+}
+
+static void print_ltv(const char *label, const uint8_t *data, uint8_t len,
+                       struct packet_ltv_decoder *decoder, size_t num)
 {
        struct iovec iov;
        int i;
@@ -3347,6 +3366,7 @@ static void print_ltv(const char *label, const uint8_t *data, uint8_t len)
 
        for (i = 0; iov.iov_len; i++) {
                uint8_t l, t, *v;
+               struct packet_ltv_decoder *dec;
 
                l = get_u8(iov_pull(&iov, sizeof(l)));
                if (!l) {
@@ -3368,16 +3388,21 @@ static void print_ltv(const char *label, const uint8_t *data, uint8_t len)
                if (!v)
                        break;
 
-               print_hex_field(label, v, l);
+               dec = get_ltv_decoder(decoder, num, t);
+               if (dec)
+                       dec->func(v, l);
+               else
+                       print_hex_field(label, v, l);
        }
 
        if (iov.iov_len)
                print_hex_field(label, iov.iov_base, iov.iov_len);
 }
 
-void packet_print_ltv(const char *label, const uint8_t *data, uint8_t len)
+void packet_print_ltv(const char *label, const uint8_t *data, uint8_t len,
+                       struct packet_ltv_decoder *decoder, size_t decoder_len)
 {
-       print_ltv(label, data, len);
+       print_ltv(label, data, len, decoder, decoder_len);
 }
 
 static void print_base_annoucement(const uint8_t *data, uint8_t data_len)
@@ -3431,7 +3456,8 @@ static void print_base_annoucement(const uint8_t *data, uint8_t data_len)
                        goto done;
 
                print_ltv("    Codec Specific Configuration",
-                                       codec_cfg->data, codec_cfg->len);
+                                       codec_cfg->data, codec_cfg->len,
+                                       NULL, 0);
 
                metadata = iov_pull(&iov, sizeof(*metadata));
                if (!metadata)
@@ -3440,7 +3466,8 @@ static void print_base_annoucement(const uint8_t *data, uint8_t data_len)
                if (!iov_pull(&iov, metadata->len))
                        goto done;
 
-               print_ltv("    Metadata", metadata->data, metadata->len);
+               print_ltv("    Metadata", metadata->data, metadata->len,
+                                       NULL, 0);
 
                /* Level 3 - BIS(s)*/
                for (j = 0; j < subgroup->num_bis; j++) {
index fbc878f..7557e47 100755 (executable)
@@ -63,7 +63,20 @@ void packet_print_channel_map_ll(const uint8_t *map);
 void packet_print_io_capability(uint8_t capability);
 void packet_print_io_authentication(uint8_t authentication);
 void packet_print_codec_id(const char *label, uint8_t codec);
-void packet_print_ltv(const char *label, const uint8_t *data, uint8_t len);
+
+#define LTV_DEC(_type, _func) \
+{ \
+       .type = _type, \
+       .func = _func, \
+}
+
+struct packet_ltv_decoder {
+       uint8_t  type;
+       void (*func)(const uint8_t *data, uint8_t len);
+};
+
+void packet_print_ltv(const char *label, const uint8_t *data, uint8_t len,
+                       struct packet_ltv_decoder *decoder, size_t num);
 
 void packet_control(struct timeval *tv, struct ucred *cred,
                                        uint16_t index, uint16_t opcode,