1570 lines
50 KiB
C
1570 lines
50 KiB
C
/* packet-h4bcm.c
|
|
* Routines for Bluetooth H4 Broadcom vendor specific additions
|
|
* Copyright 2019, Jiska Classen / Secure Mobile Networking Lab
|
|
*
|
|
* $Id$
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <epan/packet.h>
|
|
#include <epan/prefs.h>
|
|
#include <epan/dissectors/packet-bthci_acl.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
/* type definitions for Broadcom diagnostics */
|
|
#define DIA_LM_SENT 0x00
|
|
#define DIA_LM_RECV 0x01
|
|
#define DIA_MEM_PEEK_RESP 0x03
|
|
#define DIA_MEM_DUMP_RESP 0x04
|
|
#define DIA_TEST_COMPL 0x0a
|
|
#define DIA_MEM_POKE_RESP 0x11
|
|
#define DIA_CPU_LOAD_RESP 0x15
|
|
#define DIA_ACL_BR_RESP 0x16
|
|
#define DIA_ACL_EDR_RESP 0x17
|
|
#define DIA_AUX_RESP 0x18
|
|
#define DIA_ACL_UNKN1_RESP 0x1a
|
|
#define DIA_ACL_UNKN2_RESP 0x1b
|
|
#define DIA_CON_RESP 0x1f
|
|
#define DIA_LE_SENT 0x80
|
|
#define DIA_LE_RECV 0x81
|
|
#define DIA_ACL_BR_RESET 0xb9
|
|
#define DIA_ACL_BR_GET 0xc1
|
|
#define DIA_ACL_EDR_GET 0xc2
|
|
#define DIA_AUX_GET 0xc3
|
|
#define DIA_ACL_UNKN1_GET 0xc5
|
|
#define DIA_ACL_UNKN2_GET 0xc6
|
|
#define DIA_CON_GET 0xcf
|
|
#define DIA_LM_ENABLE 0xf0
|
|
#define DIA_MEM_PEEK_GET 0xf1
|
|
#define DIA_MEM_POKE_GET 0xf2
|
|
#define DIA_MEM_DUMP_GET 0xf3
|
|
#define DIA_PKT_TEST 0xf6
|
|
|
|
/* function prototypes */
|
|
void proto_reg_handoff_h4bcm(void);
|
|
|
|
/* initialize the protocol and registered fields */
|
|
static int proto_h4bcm = -1;
|
|
static int hf_h4bcm_type = -1;
|
|
static int hf_h4bcm_clock = -1;
|
|
static int hf_h4bcm_maclow = -1;
|
|
static int hf_h4bcm_pldhdr = -1;
|
|
static int hf_h4bcm_aclhdr = -1;
|
|
static int hf_h4bcm_llid = -1;
|
|
static int hf_h4bcm_llid_acl = -1;
|
|
static int hf_h4bcm_acl_fragment = -1;
|
|
static int hf_h4bcm_pldflow = -1;
|
|
static int hf_h4bcm_pldflow_acl = -1;
|
|
static int hf_h4bcm_length = -1;
|
|
static int hf_h4bcm_length_acl = -1;
|
|
static int hf_h4bcm_rfu = -1;
|
|
static int hf_h4bcm_payload = -1;
|
|
static int hf_h4bcm_lm_toggle = -1;
|
|
static int hf_h4bcm_stats_null_rcvd = -1;
|
|
static int hf_h4bcm_stats_poll_rcvd = -1;
|
|
static int hf_h4bcm_stats_dm1_rcvd = -1;
|
|
static int hf_h4bcm_stats_dh1_rcvd = -1;
|
|
static int hf_h4bcm_stats_dv_rcvd = -1;
|
|
static int hf_h4bcm_stats_aux1_rcvd = -1;
|
|
static int hf_h4bcm_stats_dm3_rcvd = -1;
|
|
static int hf_h4bcm_stats_dh3_rcvd = -1;
|
|
static int hf_h4bcm_stats_dm5_rcvd = -1;
|
|
static int hf_h4bcm_stats_dh5_rcvd = -1;
|
|
static int hf_h4bcm_stats_null_tx = -1;
|
|
static int hf_h4bcm_stats_poll_tx = -1;
|
|
static int hf_h4bcm_stats_dm1_tx = -1;
|
|
static int hf_h4bcm_stats_dh1_tx = -1;
|
|
static int hf_h4bcm_stats_dv_tx = -1;
|
|
static int hf_h4bcm_stats_aux1_tx = -1;
|
|
static int hf_h4bcm_stats_dm3_tx = -1;
|
|
static int hf_h4bcm_stats_dh3_tx = -1;
|
|
static int hf_h4bcm_stats_dm5_tx = -1;
|
|
static int hf_h4bcm_stats_dh5_tx = -1;
|
|
static int hf_h4bcm_stats_acl_rx = -1;
|
|
static int hf_h4bcm_stats_acl_tx = -1;
|
|
static int hf_h4bcm_stats_hec_err = -1;
|
|
static int hf_h4bcm_stats_crc_err = -1;
|
|
static int hf_h4bcm_stats_seqn_rep = -1;
|
|
static int hf_h4bcm_stats_soft_rst = -1;
|
|
static int hf_h4bcm_stats_test_tx = -1;
|
|
|
|
static int hf_h4bcm_stats_test_rx = -1;
|
|
static int hf_h4bcm_stats_test_err = -1;
|
|
static int hf_h4bcm_stats_2dh1_rcvd = -1;
|
|
static int hf_h4bcm_stats_3dh1_rcvd = -1;
|
|
static int hf_h4bcm_stats_2dh3_rcvd = -1;
|
|
static int hf_h4bcm_stats_3dh3_rcvd = -1;
|
|
static int hf_h4bcm_stats_2dh5_rcvd = -1;
|
|
static int hf_h4bcm_stats_3dh5_rcvd = -1;
|
|
static int hf_h4bcm_stats_2dh1_tx = -1;
|
|
static int hf_h4bcm_stats_3dh1_tx = -1;
|
|
static int hf_h4bcm_stats_2dh3_tx = -1;
|
|
static int hf_h4bcm_stats_3dh3_tx = -1;
|
|
static int hf_h4bcm_stats_2dh5_tx = -1;
|
|
static int hf_h4bcm_stats_3dh5_tx = -1;
|
|
static int hf_h4bcm_le_ether = -1;
|
|
static int hf_h4bcm_le_handle = -1;
|
|
static int hf_h4bcm_le_opcode = -1;
|
|
static int hf_h4bcm_le_opcode_ext = -1;
|
|
static int hf_h4bcm_ll_version_ind_versnr = -1;
|
|
static int hf_h4bcm_ll_version_ind_compid = -1;
|
|
static int hf_h4bcm_ll_version_ind_subversnr = -1;
|
|
// Baseband
|
|
static int hf_btbbd = -1;
|
|
// Baseband Metadata
|
|
static int hf_btbbd_meta = -1;
|
|
static int hf_btbbd_clk = -1;
|
|
static int hf_btbbd_channel = -1;
|
|
static int hf_btbbd_ptt = -1;
|
|
static int hf_btbbd_role = -1;
|
|
static int hf_btbbd_tx_encrypted = -1;
|
|
static int hf_btbbd_rx_encrypted = -1;
|
|
static int hf_btbbd_is_eir = -1;
|
|
// Baseband Packet Header
|
|
static int hf_btbbd_pkthdr = -1;
|
|
static int hf_btbbd_ltaddr = -1;
|
|
static int hf_btbbd_type = -1;
|
|
static int hf_btbbd_type_br = -1;
|
|
static int hf_btbbd_type_edr = -1;
|
|
static int hf_btbbd_flags = -1;
|
|
static int hf_btbbd_flow = -1;
|
|
static int hf_btbbd_arqn = -1;
|
|
static int hf_btbbd_seqn = -1;
|
|
static int hf_btbbd_hec = -1;
|
|
// FHS
|
|
static int hf_btbbd_fhs = -1;
|
|
static int hf_btbbd_fhs_parity = -1;
|
|
static int hf_btbbd_fhs_lap = -1;
|
|
static int hf_btbbd_fhs_eir = -1;
|
|
static int hf_btbbd_fhs_sr = -1;
|
|
static int hf_btbbd_fhs_uap = -1;
|
|
static int hf_btbbd_fhs_nap = -1;
|
|
static int hf_btbbd_fhs_class = -1;
|
|
static int hf_btbbd_fhs_ltaddr = -1;
|
|
static int hf_btbbd_fhs_clk = -1;
|
|
static int hf_btbbd_fhs_psmode = -1;
|
|
|
|
/* initialize the subtree pointers */
|
|
static gint ett_h4bcm = -1;
|
|
static gint ett_h4bcm_pldhdr = -1;
|
|
static gint ett_h4bcm_aclhdr = -1;
|
|
static gint ett_h4bcm_acl_br_stats = -1;
|
|
static gint ett_h4bcm_acl_edr_stats = -1;
|
|
static gint ett_btbbd = -1;
|
|
static gint ett_btbbd_meta = -1;
|
|
static gint ett_btbbd_pkthdr = -1;
|
|
static gint ett_btbbd_fhs = -1;
|
|
|
|
/* subdissectors */
|
|
static dissector_handle_t btlmp_handle = NULL;
|
|
static dissector_handle_t btl2cap_handle = NULL;
|
|
static dissector_handle_t packetlogger_handle = NULL;
|
|
|
|
/* reversed Broadcom diagnostic types */
|
|
static const value_string h4bcm_types[] = {
|
|
{DIA_LM_SENT, "LM Sent"},
|
|
{DIA_LM_RECV, "LM Received"},
|
|
{DIA_MEM_PEEK_RESP, "Memory Access Response to Peek"},
|
|
{DIA_MEM_DUMP_RESP, "Memory Hex Dump Response"},
|
|
{DIA_TEST_COMPL, "Reported Completed Test"},
|
|
{DIA_MEM_POKE_RESP, "Memory Access Response to Poke"},
|
|
{DIA_CPU_LOAD_RESP, "CPU Load"},
|
|
{DIA_ACL_BR_RESP, "Basic Rate ACL Stats Data"},
|
|
{DIA_ACL_EDR_RESP, "EDR ACL Stats Data"},
|
|
{DIA_AUX_RESP, "Received Aux Response"},
|
|
{DIA_ACL_UNKN1_RESP, "ACL Stats Data (Type 0x1A)"},
|
|
{DIA_ACL_UNKN2_RESP, "ACL Stats Data (Type 0x1B)"},
|
|
{DIA_CON_RESP, "Get Connection Response"},
|
|
{DIA_LE_SENT, "LE LM Sent"}, // Low Energy LL Control PDU LMP Message
|
|
{DIA_LE_RECV, "LE LM Received"},
|
|
{DIA_ACL_BR_RESET, "Reset Basic Rate ACL Stats"}, // memclr(DHM_ACLPktStats)
|
|
{DIA_ACL_BR_GET, "Get Basic Rate ACL Stats"},
|
|
{DIA_ACL_EDR_GET, "Get EDR ACL Stats"},
|
|
{DIA_ACL_UNKN1_GET, "Get ACL Stats (Type 0x1A)"}, // BTMUtil_Send_2045_ACL_Stats(0x1a, cmd)
|
|
{DIA_ACL_UNKN2_GET, "Get ACL Stats (Type 0x1B)"},
|
|
{DIA_AUX_GET, "Get Aux Stats"}, // BTMUtil_SendAuxStats
|
|
{DIA_CON_GET, "Get Connection Stats"}, // ulp_send_connection_stats(0x1F)
|
|
{DIA_LM_ENABLE, "Toggle LMP Logging"},
|
|
{DIA_MEM_PEEK_GET, "Memory Peek"},
|
|
{DIA_MEM_POKE_GET, "Memory Poke"},
|
|
{DIA_MEM_DUMP_GET, "Memory Hex Dump"},
|
|
{DIA_PKT_TEST, "BTMMstr_BBPktTest"},
|
|
};
|
|
|
|
static const value_string llid_codes[] = {
|
|
{0x0, "undefined"},
|
|
{0x1, "Continuation fragment of an L2CAP message (ACL-U)"},
|
|
{0x2, "Start of an L2CAP message or no fragmentation (ACL-U)"},
|
|
{0x3, "LMP message (ACL-C)"},
|
|
{0, NULL}};
|
|
|
|
/* This table is needed due to Brodcoms wrong LMP length passing... */
|
|
static const int lmp_lengths[] = {
|
|
0, /* LMP_VSC */
|
|
2, /* LMP_NAME_REQ */
|
|
17, /* LMP_NAME_RES */
|
|
2, /* LMP_ACCEPTED */
|
|
3, /* LMP_NOT_ACCEPTED */
|
|
1, /* LMP_CLKOFFSET_REQ */
|
|
3, /* LMP_CLKOFFSET_RES */
|
|
2, /* LMP_DETACH */
|
|
17, /* LMP_IN_RAND */
|
|
17, /* LMP_COMB_KEY */
|
|
17, /* LMP_UNIT_KEY */
|
|
17, /* LMP_AU_RAND */
|
|
5, /* LMP_SRES */
|
|
17, /* LMP_TEMP_RAND */
|
|
17, /* LMP_TEMP_KEY */
|
|
2, /* LMP_ENCRYPTION_MODE_REQ */
|
|
2, /* LMP_ENCRYPTION_KEY_SIZE_REQ */
|
|
17, /* LMP_START_ENCRYPTION_REQ */
|
|
1, /* LMP_STOP_ENCRYPTION_REQ */
|
|
5, /* LMP_SWITCH_REQ */
|
|
7, /* LMP_HOLD */
|
|
7, /* LMP_HOLD_REQ */
|
|
10, /* LMP_SNIFF_REQ */
|
|
0,
|
|
1, /* LMP_UNSNIFF_REQ */
|
|
17, /* LMP_PARK_REQ */
|
|
0,
|
|
4, /* LMP_SET_BROADCAST_SCAN_WINDOW */
|
|
11, /* LMP_MODIFY_BEACON */
|
|
15, /* LMP_UNPARK_BD_ADDR_REQ */
|
|
13, /* LMP_UNPARK_PM_ADDR_REQ */
|
|
2, /* LMP_INCR_POWER_REQ */
|
|
2, /* LMP_DECR_POWER_REQ */
|
|
1, /* LMP_MAX_POWER */
|
|
1, /* LMP_MIN_POWER */
|
|
1, /* LMP_AUTO_RATE */
|
|
2, /* LMP_PREFERRED_RATE */
|
|
6, /* LMP_VERSION_REQ */
|
|
6, /* LMP_VERSION_RES */
|
|
9, /* LMP_FEATURES_REQ */
|
|
9, /* LMP_FEATURES_RES */
|
|
4, /* LMP_QUALITY_OF_SERVICE */
|
|
4, /* LMP_QUALITY_OF_SERVICE_REQ */
|
|
7, /* LMP_SCO_LINK_REQ */
|
|
3, /* LMP_REMOVE_SCO_LINK_REQ */
|
|
2, /* LMP_MAX_SLOT */
|
|
2, /* LMP_MAX_SLOT_REQ */
|
|
1, /* LMP_TIMING_ACCURACY_REQ */
|
|
3, /* LMP_TIMING_ACCURACY_RES */
|
|
1, /* LMP_SETUP_COMPLETE */
|
|
1, /* LMP_USE_SEMI_PERMANENT_KEY */
|
|
1, /* LMP_HOST_CONNECTION_REQ */
|
|
9, /* LMP_SLOT_OFFSET */
|
|
3, /* LMP_PAGE_MODE_REQ */
|
|
3, /* LMP_PAGE_SCAN_MODE_REQ */
|
|
3, /* LMP_SUPERVISION_TIMEOUT */
|
|
1, /* LMP_TEST_ACTIVATE */
|
|
10, /* LMP_TEST_CONTROL */
|
|
1, /* LMP_ENCRYPTION_KEY_SIZE_MASK_REQ */
|
|
3, /* LMP_ENCRYPTION_KEY_SIZE_MASK_RES */
|
|
16, /* LMP_SET_AFH */
|
|
4, /* LMP_ENCAPSULATED_HEADER */
|
|
17, /* LMP_ENCAPSULATED_PAYLOAD */
|
|
17, /* LMP_SIMPLE_PAIRING_CONFIRM */
|
|
17, /* LMP_SIMPLE_PAIRING_NUMBER */
|
|
17, /* LMP_DHKEY_CHECK */
|
|
};
|
|
|
|
static const int lmp_lengths_ext[] = {
|
|
0,
|
|
4, /* LMP_ACCEPTED_EXT */
|
|
5, /* LMP_NOT_ACCEPTED_EXT */
|
|
12, /* LMP_FEATURES_REQ_EXT */
|
|
12, /* LMP_FEATURES_RES_EXT */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
3, /* LMP_PACKET_TYPE_TABLE_REQ */
|
|
16, /* LMP_ESCO_LINK_REQ */
|
|
4, /* LMP_REMOVE_ESCO_LINK_REQ */
|
|
0,
|
|
0,
|
|
7, /* LMP_CHANNEL_CLASSIFICATION_REQ */
|
|
12, /* LMP_CHANNEL_CLASSIFICATION */
|
|
0,
|
|
0,
|
|
0,
|
|
9, /* LMP_SNIFF_SUBRATING_REQ */
|
|
9, /* LMP_SNIFF_SUBRATING_RES */
|
|
2, /* LMP_PAUSE_ENCRYPTION_REQ */
|
|
2, /* LMP_RESUME_ENCRYPTION_REQ */
|
|
5, /* LMP_IO_CAPABILITY_REQ */
|
|
5, /* LMP_IO_CAPABILITY_RES */
|
|
2, /* LMP_NUMERIC_COMPARISON_FAILED */
|
|
2, /* LMP_PASSKEY_FAILED */
|
|
2, /* LMP_OOB_FAILED */
|
|
3, /* LMP_KEYPRESS_NOTIFICATION */
|
|
3, /* LMP_POWER_CONTROL_REQ */
|
|
3, /* LMP_POWER_CONTROL_RES */
|
|
2, /* LMP_PING_REQ */
|
|
2, /* LMP_PING_RES */
|
|
};
|
|
|
|
/* Bluetooth 5.0 specification p. 2589 */
|
|
static const value_string lm_le_opcodes[] = {
|
|
{0x0, "LE LL Connection Update Request"},
|
|
{0x1, "LE LL Channel Map Request"},
|
|
{0x2, "LE LL Terminate Indication"},
|
|
{0x3, "LE LL Encryption Request"},
|
|
{0x4, "LE LL Encryption Response"},
|
|
{0x5, "LE LL Start Encryption Request"},
|
|
{0x6, "LE LL Start Encryption Response"},
|
|
{0x7, "LE LL Unknown Response"},
|
|
{0x8, "LE LL Feature Request"},
|
|
{0x9, "LE LL Feature Response"},
|
|
{0xa, "LE LL Pause Encryption Request"},
|
|
{0xb, "LE LL Pause Encryption Response"},
|
|
{0xc, "LE LL Version Indication"},
|
|
{0xd, "LE LL Reject Indication"},
|
|
{0xe, "LE LL Slave Feture Request"},
|
|
{0xf, "LE LL Connection Parameter Request"},
|
|
{0x10, "LE LL Connection Parameter Response"},
|
|
{0x11, "LE LL Extended Reject Indication"},
|
|
{0x12, "LE LL Ping Request"},
|
|
{0x13, "LE LL Ping Response"},
|
|
{0x14, "LE LL Length Request"},
|
|
{0x15, "LE LL Length Response"},
|
|
{0x16, "LE LL Update Indication"},
|
|
{0x17, "LE LL Physical Layers Request"},
|
|
{0x18, "LE LL Physical Layers Response"},
|
|
{0x19, "LE LL Minimum Number of Used Channels Indication"},
|
|
{0xff, "LE LL Broadcom Vendor Specific"},
|
|
{0, NULL}};
|
|
|
|
static const value_string lm_le_opcodes_ext[] = {
|
|
{0x1, "LE LL Vendor Specific Feature Request"},
|
|
{0x2, "LE LL Vendor Specific Feature Response"},
|
|
{0x3, "LE LL Vendor Specific Enable Bcs Timeline"},
|
|
{0x4, "LE LL Random Address Change"},
|
|
{0, NULL}};
|
|
|
|
static const true_false_string lm_toggle = {
|
|
"true",
|
|
"false"};
|
|
|
|
static const true_false_string packet_table_type_bits = {
|
|
"EDR", // Enhanced Data Rate
|
|
"BR", // Basic Rate
|
|
};
|
|
|
|
static const true_false_string packet_role_bits = {
|
|
"Slave",
|
|
"Master",
|
|
};
|
|
|
|
static const value_string packet_types[] = {
|
|
/* generic names for unknown logical transport */
|
|
{0x0, "NULL"},
|
|
{0x1, "POLL"},
|
|
{0x2, "FHS"},
|
|
{0x3, "DM1"},
|
|
{0x4, "DH1/2-DH1"},
|
|
{0x5, "HV1"},
|
|
{0x6, "HV2/2-EV3"},
|
|
{0x7, "HV3/EV3/3-EV3"},
|
|
{0x8, "DV/3-DH1"},
|
|
{0x9, "AUX1"},
|
|
{0xa, "DM3/2-DH3"},
|
|
{0xb, "DH3/3-DH3"},
|
|
{0xc, "EV4/2-EV5"},
|
|
{0xd, "EV5/3-EV5"},
|
|
{0xe, "DM5/2-DH5"},
|
|
{0xf, "DH5/3-DH5"},
|
|
{0, NULL}};
|
|
|
|
static const value_string packet_types_br[] = {
|
|
/* generic names for unknown logical transport */
|
|
{0x0, "NULL"},
|
|
{0x1, "POLL"},
|
|
{0x2, "FHS"},
|
|
{0x3, "DM1"},
|
|
{0x4, "DH1"},
|
|
{0x9, "AUX1"},
|
|
{0xa, "DM3"},
|
|
{0xb, "DH3"},
|
|
{0xe, "DM5"},
|
|
{0xf, "DH5"},
|
|
{0, NULL}};
|
|
|
|
static const value_string packet_types_edr[] = {
|
|
/* generic names for unknown logical transport */
|
|
{0x0, "NULL"},
|
|
{0x1, "POLL"},
|
|
{0x2, "FHS"},
|
|
{0x3, "DM1"},
|
|
{0x4, "2-DH1"},
|
|
{0x8, "3-DH1"},
|
|
{0x9, "AUX1"},
|
|
{0xa, "2-DH3"},
|
|
{0xb, "3-DH3"},
|
|
{0xe, "2-DH5"},
|
|
{0xf, "3-DH5"},
|
|
{0, NULL}};
|
|
|
|
static const value_string sr_modes[] = {
|
|
{0x0, "R0"},
|
|
{0x1, "R1"},
|
|
{0x2, "R2"},
|
|
{0x3, "Reserved"},
|
|
{0, NULL}};
|
|
|
|
static const range_string ps_modes[] = {
|
|
{0x0, 0x0, "Mandatory scan mode"},
|
|
{0x1, 0x7, "Reserved"},
|
|
{0, 0, NULL}};
|
|
|
|
/* one byte payload header */
|
|
static int dissect_payload_header1(proto_tree *tree, tvbuff_t *tvb, int offset)
|
|
{
|
|
proto_item *hdr_item;
|
|
proto_tree *hdr_tree;
|
|
|
|
hdr_item = proto_tree_add_item(tree, hf_h4bcm_pldhdr, tvb, offset, 1, ENC_NA);
|
|
hdr_tree = proto_item_add_subtree(hdr_item, ett_h4bcm_pldhdr);
|
|
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_llid, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_pldflow, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_length, tvb, offset, 1, ENC_NA);
|
|
|
|
/* payload length */
|
|
return tvb_get_guint8(tvb, offset) >> 3;
|
|
}
|
|
|
|
/* two byte payload header */
|
|
static int dissect_payload_header_acl_edr(proto_tree *tree, tvbuff_t *tvb, int offset)
|
|
{
|
|
proto_item *hdr_item;
|
|
proto_tree *hdr_tree;
|
|
|
|
hdr_item = proto_tree_add_item(tree, hf_h4bcm_aclhdr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
hdr_tree = proto_item_add_subtree(hdr_item, ett_h4bcm_aclhdr);
|
|
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_llid_acl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_pldflow_acl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_length_acl, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(hdr_tree, hf_h4bcm_rfu, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
/* payload length */
|
|
return (tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN) >> 3) & 0x3FF;
|
|
}
|
|
|
|
/* Dissect common LM and LE LM header */
|
|
static void dissect_lm_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int is_sent)
|
|
{
|
|
guint32 mac;
|
|
gchar *mac_string = (gchar *)g_malloc(12);
|
|
|
|
/* LMP and LCP are only transmitted within full diagnostic reports */
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
/* clock of the BT master */
|
|
proto_tree_add_item(tree, hf_h4bcm_clock, tvb, offset, 4, ENC_BIG_ENDIAN);
|
|
offset += 4;
|
|
|
|
/* decode and display MAC address in src/dst fields */
|
|
mac = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
|
|
g_snprintf(mac_string, 12,
|
|
"%02x:%02x:%02x:%02x",
|
|
(mac & 0xff000000) >> 24,
|
|
(mac & 0x00ff0000) >> 16,
|
|
(mac & 0x0000ff00) >> 8,
|
|
(mac & 0x000000ff));
|
|
|
|
proto_tree_add_item(tree, hf_h4bcm_maclow, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
|
|
if (is_sent == 1)
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "controller");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, mac_string);
|
|
}
|
|
else
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, mac_string);
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "controller");
|
|
}
|
|
}
|
|
|
|
/* Pass LMP handling to existing dissector if available */
|
|
static void dissect_lmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
int len;
|
|
int opcode;
|
|
int dm1_hdr;
|
|
tvbuff_t *pld_tvb;
|
|
|
|
/* LMP is only transmitted within full diagnostic reports */
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
/* DM1 header is common in both directions */
|
|
dm1_hdr = tvb_get_guint8(tvb, offset);
|
|
len = dissect_payload_header1(tree, tvb, offset);
|
|
|
|
/* Longest LMP packet is 17 bytes */
|
|
DISSECTOR_ASSERT(len <= 17);
|
|
|
|
offset += 1;
|
|
|
|
/* In receive direction, diagnostic LMP always sends a packet length 17,
|
|
* which makes failed assertions inside the LMP decoder...
|
|
* The fixed length corresponds to a DM1 header of 0x8f in flow direction
|
|
* receive, so we can check this directly instead of maybe re-checking
|
|
* valid length 17 in sent direction.
|
|
* This fix is really ugly, but it makes the LMP decoders assertions pass.
|
|
*/
|
|
if (dm1_hdr == 0x8f)
|
|
{
|
|
/* Get normal / extended opcode length. Will be 0 if undefined. */
|
|
len = 0;
|
|
opcode = tvb_get_guint8(tvb, offset) >> 1;
|
|
if (opcode <= 65)
|
|
{
|
|
len = lmp_lengths[opcode];
|
|
}
|
|
else if (opcode == 127)
|
|
{
|
|
opcode = tvb_get_guint8(tvb, offset + 1);
|
|
if (opcode <= 32)
|
|
{
|
|
len = lmp_lengths_ext[opcode];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check that we have a LMP dissector or else just display raw payload */
|
|
if (btlmp_handle && len != 0)
|
|
{
|
|
pld_tvb = tvb_new_subset_length_caplen(tvb, offset, len, len);
|
|
call_dissector(btlmp_handle, pld_tvb, pinfo, tree);
|
|
}
|
|
else
|
|
{
|
|
/* Maximum (constant) LMP length is 17 */
|
|
proto_tree_add_item(tree, hf_h4bcm_payload, tvb, offset, 17, ENC_LITTLE_ENDIAN);
|
|
}
|
|
}
|
|
|
|
/* Pass ACL handling to existing dissector if available */
|
|
static int dissect_acl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
{
|
|
tvbuff_t *pld_tvb;
|
|
int len;
|
|
int opcode;
|
|
int offset = 0;
|
|
int llid;
|
|
|
|
// struct timespec start_time;
|
|
// struct timespec end_time;
|
|
// clock_gettime(CLOCK_MONOTONIC, &start_time);
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ACL");
|
|
|
|
len = dissect_payload_header_acl_edr(tree, tvb, offset);
|
|
llid = tvb_get_guint8(tvb, offset) & 0x03;
|
|
|
|
offset += 2;
|
|
|
|
if (len)
|
|
{
|
|
pld_tvb = tvb_new_subset_length(tvb, offset, len);
|
|
switch (llid)
|
|
{
|
|
case 1:
|
|
proto_tree_add_item(tree, hf_h4bcm_acl_fragment, tvb, offset, len, ENC_LITTLE_ENDIAN);
|
|
switch (pinfo->p2p_dir)
|
|
{
|
|
case P2P_DIR_SENT:
|
|
col_set_str(pinfo->cinfo, COL_INFO, "TX --> Fragment");
|
|
break;
|
|
case P2P_DIR_RECV:
|
|
col_set_str(pinfo->cinfo, COL_INFO, "RX <-- Fragment");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
call_dissector(btl2cap_handle, pld_tvb, pinfo, tree);
|
|
break;
|
|
|
|
case 3:
|
|
call_dissector(btlmp_handle, pld_tvb, pinfo, tree);
|
|
break;
|
|
|
|
default:
|
|
proto_tree_add_item(tree, hf_h4bcm_payload, tvb, offset, len, ENC_LITTLE_ENDIAN);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
proto_tree_add_item(tree, hf_h4bcm_payload, tvb, offset, len, ENC_LITTLE_ENDIAN);
|
|
}
|
|
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
static void dissect_fhs(proto_tree *tree, tvbuff_t *tvb, int offset)
|
|
{
|
|
proto_item *fhs_item, *psmode_item;
|
|
proto_tree *fhs_tree;
|
|
const gchar *description;
|
|
guint8 psmode;
|
|
|
|
fhs_item = proto_tree_add_item(tree, hf_btbbd_fhs, tvb, offset, -1, ENC_NA);
|
|
fhs_tree = proto_item_add_subtree(fhs_item, ett_btbbd_fhs);
|
|
|
|
/* Use proto_tree_add_bits_item() to get around 32bit limit on bitmasks */
|
|
proto_tree_add_bits_item(fhs_tree, hf_btbbd_fhs_parity, tvb, offset * 8, 34, ENC_LITTLE_ENDIAN);
|
|
/* proto_tree_add_item(fhs_tree, hf_btbb_fhs_parity, tvb, offset, 5, ENC_LITTLE_ENDIAN); */
|
|
offset += 4;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_lap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 3;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_eir, tvb, offset, 1, ENC_NA);
|
|
/* skipping 1 undefined bit */
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_sr, tvb, offset, 1, ENC_NA);
|
|
/* skipping 2 reserved bits */
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_uap, tvb, offset, 1, ENC_NA);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_nap, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_class, tvb, offset, 3, ENC_LITTLE_ENDIAN);
|
|
offset += 3;
|
|
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_ltaddr, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item(fhs_tree, hf_btbbd_fhs_clk, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 3;
|
|
|
|
psmode = tvb_get_guint8(tvb, offset);
|
|
description = try_rval_to_str(psmode, ps_modes);
|
|
psmode_item = proto_tree_add_item(fhs_tree, hf_btbbd_fhs_psmode, tvb, offset, 1, ENC_NA);
|
|
if (description)
|
|
proto_item_append_text(psmode_item, " (%s)", description);
|
|
offset += 1;
|
|
}
|
|
|
|
static int dissect_bt_baseband(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
{
|
|
tvbuff_t *pld_tvb;
|
|
int offset = 0;
|
|
int bb_type;
|
|
int ptt;
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Baseband");
|
|
|
|
// // Baseband protocol
|
|
proto_item *btbb_item = proto_tree_add_item(tree, hf_btbbd, tvb, offset, 11, ENC_LITTLE_ENDIAN);
|
|
proto_tree *btbb_tree = proto_item_add_subtree(btbb_item, ett_btbbd);
|
|
// // Baseband Metadata
|
|
proto_item *btbb_meta_item = proto_tree_add_item(btbb_tree, hf_btbbd_meta, tvb, offset, 5, ENC_LITTLE_ENDIAN);
|
|
proto_tree *btbb_meta_tree = proto_item_add_subtree(btbb_meta_item, ett_btbbd_meta);
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_clk, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 4;
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_channel, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
offset += 1;
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_ptt, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_role, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_tx_encrypted, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_rx_encrypted, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
proto_tree_add_item(btbb_meta_tree, hf_btbbd_is_eir, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
// ptt = tvb_get_guint8(tvb, offset) & 0b1;
|
|
offset += 1;
|
|
// Baseband Packet Header
|
|
proto_item *btbb_pkthdr_item = proto_tree_add_item(btbb_tree, hf_btbbd_pkthdr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
proto_tree *btbb_pkthdr_tree = proto_item_add_subtree(btbb_pkthdr_item, ett_btbbd_pkthdr);
|
|
proto_tree_add_item(btbb_pkthdr_tree, hf_btbbd_ltaddr, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item_ret_uint(btbb_pkthdr_tree, hf_btbbd_type, tvb, offset, 1, ENC_NA, &bb_type);
|
|
// if (!ptt)
|
|
// proto_tree_add_item_ret_uint(btbb_pkthdr_tree, hf_btbbd_type_br, tvb, offset, 1, ENC_NA, &bb_type);
|
|
// else
|
|
// proto_tree_add_item_ret_uint(btbb_pkthdr_tree, hf_btbbd_type_edr, tvb, offset, 1, ENC_NA, &bb_type);
|
|
proto_tree_add_item(btbb_pkthdr_tree, hf_btbbd_flow, tvb, offset, 1, ENC_NA);
|
|
offset += 1;
|
|
proto_tree_add_item(btbb_pkthdr_tree, hf_btbbd_arqn, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item(btbb_pkthdr_tree, hf_btbbd_seqn, tvb, offset, 1, ENC_NA);
|
|
proto_tree_add_item(btbb_pkthdr_tree, hf_btbbd_hec, tvb, offset, 1, ENC_NA);
|
|
offset += 1;
|
|
|
|
switch (pinfo->p2p_dir)
|
|
{
|
|
case P2P_DIR_SENT:
|
|
col_set_str(pinfo->cinfo, COL_INFO, "TX --> ");
|
|
break;
|
|
case P2P_DIR_RECV:
|
|
col_set_str(pinfo->cinfo, COL_INFO, "RX <-- ");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* payload */
|
|
switch (bb_type)
|
|
{
|
|
case 0x0: /* NULL */
|
|
col_append_str(pinfo->cinfo, COL_INFO, "NULL");
|
|
break;
|
|
case 0x1: /* POLL */
|
|
col_append_str(pinfo->cinfo, COL_INFO, "POLL");
|
|
break;
|
|
case 0x2: /* FHS */
|
|
col_append_str(pinfo->cinfo, COL_INFO, "FHS");
|
|
dissect_fhs(tree, tvb, offset);
|
|
break;
|
|
case 0x3: /* DM1 */
|
|
case 0x4: /* DH1/2-DH1 */
|
|
case 0x5: /* HV1 */
|
|
case 0x6: /* HV2/2-EV3 */
|
|
case 0x7: /* HV3/EV3/3-EV3 */
|
|
case 0x8: /* DV/3-DH1 */
|
|
case 0x9: /* AUX1 */
|
|
case 0xa: /* DM3/2-DH3 */
|
|
case 0xb: /* DH3/3-DH3 */
|
|
case 0xc: /* EV4/2-EV5 */
|
|
case 0xd: /* EV5/3-EV5 */
|
|
case 0xe: /* DM5/2-DH5 */
|
|
case 0xf: /* DH5/3-DH5 */
|
|
pld_tvb = tvb_new_subset_remaining(tvb, offset);
|
|
dissect_acl(pld_tvb, pinfo, tree, &ptt);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
/* TODO placeholder for responses we don't know yet */
|
|
static void dissect_unkn_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
proto_tree_add_item(tree, hf_h4bcm_type, tvb, offset - 1, 1, ENC_NA);
|
|
|
|
/* Sent from chip to host */
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "controller");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "host");
|
|
}
|
|
|
|
/* TODO placeholder for commands with arguments that are none or unknown */
|
|
static void dissect_unkn_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) >= 1);
|
|
|
|
proto_tree_add_item(tree, hf_h4bcm_type, tvb, offset - 1, 1, ENC_NA);
|
|
|
|
/* Sent from host to chip */
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "host");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "controller");
|
|
}
|
|
|
|
/* ACL BR stats */
|
|
static void dissect_acl_br_stats(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
|
|
{
|
|
proto_item *stats_item;
|
|
proto_tree *stats_tree;
|
|
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
/* Sent from chip to host */
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "controller");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "host");
|
|
|
|
/* Display previous item as tree header */
|
|
stats_item = proto_tree_add_item(tree, hf_h4bcm_type, tvb, offset - 1, 1, ENC_NA);
|
|
|
|
/* Make stats subtree */
|
|
stats_tree = proto_item_add_subtree(stats_item, ett_h4bcm_acl_br_stats);
|
|
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_null_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_poll_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dv_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_aux1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm3_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh3_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm5_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh5_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_null_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_poll_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dv_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_aux1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm3_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh3_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm5_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dh5_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
/* FIXME within the next 16 bytes, some are 4 bytes long ... not 100% sure which */
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_acl_rx, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 4;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_acl_tx, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 4;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_hec_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_crc_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_seqn_rep, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_soft_rst, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_rx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
}
|
|
|
|
/* ACL EDR stats */
|
|
static void dissect_acl_edr_stats(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
proto_item *stats_item;
|
|
proto_tree *stats_tree;
|
|
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
/* Sent from chip to host */
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "controller");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "host");
|
|
|
|
/* Display previous item as tree header */
|
|
stats_item = proto_tree_add_item(tree, hf_h4bcm_type, tvb, offset - 1, 1, ENC_NA);
|
|
|
|
/* Make stats subtree */
|
|
stats_tree = proto_item_add_subtree(stats_item, ett_h4bcm_acl_edr_stats);
|
|
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_null_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_poll_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh1_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh3_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh3_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh5_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh5_rcvd, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
offset += 2; /* with this offset, null packets match */
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_null_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_poll_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_dm1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh1_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh3_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh3_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_2dh5_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_3dh5_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
offset += 2; /* with this offset, acl bytes are correct */
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_acl_rx, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 4;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_acl_tx, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
|
offset += 4;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_hec_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_crc_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_seqn_rep, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_soft_rst, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_tx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_rx, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
proto_tree_add_item(stats_tree, hf_h4bcm_stats_test_err, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
}
|
|
|
|
/* LL_VERSION_IND p. 2594
|
|
*/
|
|
static void dissect_ll_version_ind(proto_tree *tree, tvbuff_t *tvb, int offset)
|
|
{
|
|
proto_tree_add_item(tree, hf_h4bcm_ll_version_ind_versnr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
offset += 1;
|
|
|
|
proto_tree_add_item(tree, hf_h4bcm_ll_version_ind_compid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(tree, hf_h4bcm_ll_version_ind_subversnr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
}
|
|
|
|
/* LM LE
|
|
* Most of this is already implemented in "btle" in Wireshark...
|
|
* But somewhat different format :( So we do it here.
|
|
*/
|
|
static void dissect_lm_le(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int is_sent)
|
|
{
|
|
int opcode;
|
|
int opcode_ext;
|
|
guint64 mac;
|
|
gchar *mac_string = (gchar *)g_malloc(18);
|
|
|
|
/* LMP and LCP are only transmitted within full diagnostic reports */
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) == 63);
|
|
|
|
/* clock of the BT master */
|
|
proto_tree_add_item(tree, hf_h4bcm_clock, tvb, offset, 4, ENC_BIG_ENDIAN);
|
|
offset += 4;
|
|
|
|
/* standard MAC address this time */
|
|
mac = tvb_get_guint64(tvb, offset - 2, ENC_BIG_ENDIAN);
|
|
g_snprintf(mac_string, 18,
|
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
|
(unsigned int)((mac & 0xff0000000000) >> 40),
|
|
(unsigned int)((mac & 0x00ff00000000) >> 32),
|
|
(unsigned int)((mac & 0x0000ff000000) >> 24),
|
|
(unsigned int)((mac & 0x000000ff0000) >> 16),
|
|
(unsigned int)((mac & 0x00000000ff00) >> 8),
|
|
(unsigned int)((mac & 0x0000000000ff)));
|
|
proto_tree_add_item(tree, hf_h4bcm_le_ether, tvb, offset, 6, ENC_LITTLE_ENDIAN);
|
|
offset += 6;
|
|
|
|
if (is_sent == 1)
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "controller");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, mac_string);
|
|
}
|
|
else
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, mac_string);
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "controller");
|
|
}
|
|
|
|
/* Handle (only 1 byte, even though it can be 2 bytes long?!) */
|
|
proto_tree_add_item(tree, hf_h4bcm_le_handle, tvb, offset, 1, ENC_NA);
|
|
offset += 1;
|
|
|
|
opcode = tvb_get_guint8(tvb, offset);
|
|
proto_tree_add_item(tree, hf_h4bcm_le_opcode, tvb, offset, 1, ENC_NA);
|
|
col_add_str(pinfo->cinfo, COL_INFO, val_to_str(opcode, lm_le_opcodes, "LE LL Unknown Opcode (%d)"));
|
|
offset += 1;
|
|
|
|
switch (opcode)
|
|
{
|
|
case 0x0c:
|
|
dissect_ll_version_ind(tree, tvb, offset);
|
|
break;
|
|
/* Broadcom vendor specific stuff... */
|
|
case 0xff:
|
|
opcode_ext = tvb_get_guint8(tvb, offset);
|
|
proto_tree_add_item(tree, hf_h4bcm_le_opcode_ext, tvb, offset, 1, ENC_NA);
|
|
col_add_str(pinfo->cinfo, COL_INFO, val_to_str(opcode_ext, lm_le_opcodes_ext, "LE LL Unknown VSC Opcode (%d)"));
|
|
offset += 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Show if LM + LM LE logging was enabled or disabled */
|
|
static void dissect_lm_toggle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
|
|
{
|
|
DISSECTOR_ASSERT(tvb_reported_length(tvb) >= 1);
|
|
|
|
/* Sent from host UART to chip */
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "host");
|
|
col_set_str(pinfo->cinfo, COL_RES_DL_DST, "controller");
|
|
|
|
/* OFF and ON */
|
|
proto_tree_add_item(tree, hf_h4bcm_lm_toggle, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset += 1;
|
|
}
|
|
|
|
/* dissect a packet */
|
|
static int
|
|
dissect_h4bcm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
{
|
|
proto_item *h4bcm_item;
|
|
proto_tree *h4bcm_tree;
|
|
int offset;
|
|
int h4bcm_type;
|
|
|
|
/* sanity check: length */
|
|
if (tvb_reported_length(tvb) < 1)
|
|
/* bad length: look for a different dissector */
|
|
return 0;
|
|
|
|
/* fprintf(stderr, "total len %d\n", tvb_reported_length(tvb)); */
|
|
|
|
/* make entries in protocol column and info column on summary display */
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "HCI H4 Broadcom");
|
|
|
|
/* create display subtree for the protocol */
|
|
offset = 0;
|
|
h4bcm_item = proto_tree_add_item(tree, proto_h4bcm, tvb, offset, -1, ENC_NA);
|
|
h4bcm_tree = proto_item_add_subtree(h4bcm_item, ett_h4bcm);
|
|
|
|
h4bcm_type = tvb_get_guint8(tvb, offset);
|
|
col_add_str(pinfo->cinfo, COL_INFO, val_to_str(h4bcm_type, h4bcm_types, "Unknown Type (%d)"));
|
|
offset += 1;
|
|
|
|
switch (h4bcm_type)
|
|
{
|
|
case DIA_LM_SENT:
|
|
dissect_lm_header(tvb, pinfo, h4bcm_tree, offset, 1);
|
|
offset += 8;
|
|
dissect_lmp(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_LM_RECV:
|
|
dissect_lm_header(tvb, pinfo, h4bcm_tree, offset, 0);
|
|
offset += 11;
|
|
dissect_lmp(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_ACL_BR_RESP:
|
|
dissect_acl_br_stats(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_ACL_EDR_RESP:
|
|
dissect_acl_edr_stats(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_LE_SENT:
|
|
dissect_lm_le(tvb, pinfo, h4bcm_tree, offset, 1);
|
|
break;
|
|
case DIA_LE_RECV:
|
|
dissect_lm_le(tvb, pinfo, h4bcm_tree, offset, 0);
|
|
break;
|
|
case DIA_LM_ENABLE:
|
|
dissect_lm_toggle(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_MEM_PEEK_RESP:
|
|
case DIA_MEM_DUMP_RESP:
|
|
case DIA_TEST_COMPL:
|
|
case DIA_MEM_POKE_RESP:
|
|
case DIA_CPU_LOAD_RESP:
|
|
case DIA_AUX_RESP:
|
|
case DIA_ACL_UNKN1_RESP:
|
|
case DIA_ACL_UNKN2_RESP:
|
|
dissect_unkn_resp(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
case DIA_ACL_BR_RESET:
|
|
case DIA_ACL_BR_GET:
|
|
case DIA_ACL_EDR_GET:
|
|
case DIA_AUX_GET:
|
|
case DIA_ACL_UNKN1_GET:
|
|
case DIA_ACL_UNKN2_GET:
|
|
case DIA_CON_GET:
|
|
case DIA_MEM_PEEK_GET:
|
|
case DIA_MEM_POKE_GET:
|
|
case DIA_MEM_DUMP_GET:
|
|
case DIA_PKT_TEST:
|
|
dissect_unkn_get(tvb, pinfo, h4bcm_tree, offset);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Return the amount of data this dissector was able to dissect */
|
|
return tvb_reported_length(tvb);
|
|
}
|
|
|
|
/* register the protocol with Wireshark */
|
|
void proto_register_h4bcm(void)
|
|
{
|
|
/* list of fields */
|
|
static hf_register_info hf[] = {
|
|
{&hf_h4bcm_type,
|
|
{"Type", "h4bcm.type",
|
|
FT_UINT8, BASE_HEX, VALS(h4bcm_types), 0x0,
|
|
"Diagnostic Information Type", HFILL}},
|
|
{&hf_h4bcm_clock,
|
|
{"Clock", "h4bcm.clock",
|
|
FT_UINT32, BASE_HEX, NULL, 0x0,
|
|
"Bluetooth Master Clock", HFILL}},
|
|
{&hf_h4bcm_maclow,
|
|
{"Remote MAC Address", "h4bcm.maclow",
|
|
FT_BYTES, SEP_COLON, NULL, 0x0,
|
|
"Lower MAC address part, sufficient for l2ping ff:ff:maclow", HFILL}},
|
|
{&hf_h4bcm_pldhdr,
|
|
{"Payload Header", "h4bcm.pldhdr",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_aclhdr,
|
|
{"ACL Header", "h4bcm.aclhdr",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_llid,
|
|
{"LLID", "h4bcm.llid",
|
|
FT_UINT8, BASE_HEX, VALS(llid_codes), 0x03,
|
|
"Logical Link ID", HFILL}},
|
|
{&hf_h4bcm_llid_acl,
|
|
{"LLID", "h4bcm.llid_acl",
|
|
FT_UINT16, BASE_HEX, VALS(llid_codes), 0x03,
|
|
"Logical Link ID", HFILL}},
|
|
{&hf_h4bcm_pldflow_acl,
|
|
{"Flow", "h4bcm.flow_acl",
|
|
FT_BOOLEAN, 16, NULL, 0x04,
|
|
"Payload Flow indication", HFILL}},
|
|
{&hf_h4bcm_rfu,
|
|
{"RFU", "h4bcm.rfu_acl",
|
|
FT_UINT16, BASE_HEX, 0, 0xE000,
|
|
"RFU", HFILL}},
|
|
{&hf_h4bcm_pldflow,
|
|
{"Flow", "h4bcm.flow",
|
|
FT_BOOLEAN, 8, NULL, 0x04,
|
|
"Payload Flow indication", HFILL}},
|
|
{&hf_h4bcm_length,
|
|
{"Length", "h4bcm.length",
|
|
FT_UINT8, BASE_DEC, NULL, 0xf8,
|
|
"Payload Length", HFILL}},
|
|
{&hf_h4bcm_length_acl,
|
|
{"Length", "h4bcm.length_acl",
|
|
FT_UINT16, BASE_DEC, NULL, 0x1ff8,
|
|
"Payload Length", HFILL}},
|
|
{&hf_h4bcm_payload,
|
|
{"Payload", "h4bcm.payload",
|
|
FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_acl_fragment,
|
|
{"Payload", "h4bcm.acl_fragment",
|
|
FT_BYTES, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_lm_toggle,
|
|
{"LM and LM LE Logging", "h4bcm.logging",
|
|
FT_BOOLEAN, 8, TFS(&lm_toggle), 0x01,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_null_rcvd,
|
|
{"Null Packets Received", "h4bcm.stats.null_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_poll_rcvd,
|
|
{"Poll Packets Received", "h4bcm.stats.poll_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm1_rcvd,
|
|
{"DM1 Packets Received", "h4bcm.stats.dm1_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh1_rcvd,
|
|
{"DH1 Packets Received", "h4bcm.stats.dh1_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dv_rcvd,
|
|
{"DV Packets Received", "h4bcm.stats.dv_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_aux1_rcvd,
|
|
{"AUX1 Packets Received", "h4bcm.stats.aux1_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm3_rcvd,
|
|
{"DM3 Packets Received", "h4bcm.stats.dm3_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh3_rcvd,
|
|
{"DH3 Packets Received", "h4bcm.stats.dh3_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm5_rcvd,
|
|
{"DM5 Packets Received", "h4bcm.stats.dm5_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh5_rcvd,
|
|
{"DH5 Packets Received", "h4bcm.stats.dh5_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_null_tx,
|
|
{"Null Packets Transmitted", "h4bcm.stats.null_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_poll_tx,
|
|
{"Poll Packets Transmitted", "h4bcm.stats.poll_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm1_tx,
|
|
{"DM1 Packets Transmitted", "h4bcm.stats.dm1_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh1_tx,
|
|
{"DH1 Packets Transmitted", "h4bcm.stats.dh1_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dv_tx,
|
|
{"DV Packets Transmitted", "h4bcm.stats.dv_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_aux1_tx,
|
|
{"AUX1 Packets Transmitted", "h4bcm.stats.aux1_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm3_tx,
|
|
{"DM3 Packets Transmitted", "h4bcm.stats.dm3_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh3_tx,
|
|
{"DH3 Packets Transmitted", "h4bcm.stats.dh3_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dm5_tx,
|
|
{"DM5 Packets Transmitted", "h4bcm.stats.dm5_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_dh5_tx,
|
|
{"DH5 Packets Transmitted", "h4bcm.stats.dh5_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_acl_rx,
|
|
{"Total Received ACL Bytes", "h4bcm.stats.acl_rx",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_acl_tx,
|
|
{"Total Transmitted ACL Bytes", "h4bcm.stats.acl_tx",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_hec_err,
|
|
{"HEC Errors", "h4bcm.stats.hec_err",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_crc_err,
|
|
{"CRC Errors", "h4bcm.stats.crc_err",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_seqn_rep,
|
|
{"Seqn Repeat", "h4bcm.stats.seqn_rep",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_soft_rst,
|
|
{"Soft Reset", "h4bcm.stats.soft_rst",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_test_tx,
|
|
{"TestMode Transmitted Packets", "h4bcm.stats.test_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_test_rx,
|
|
{"TestMode Received Packets", "h4bcm.stats.test_rx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_test_err,
|
|
{"TestMode Packet Errors", "h4bcm.stats.test_err",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh1_rcvd,
|
|
{"2DH1 Packets Received", "h4bcm.stats.2dh1_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh1_rcvd,
|
|
{"3DH1 Packets Received", "h4bcm.stats.3dh1_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh3_rcvd,
|
|
{"2DH3 Packets Received", "h4bcm.stats.2dh3_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh3_rcvd,
|
|
{"3DH3 Packets Received", "h4bcm.stats.3dh3_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh5_rcvd,
|
|
{"2DH5 Packets Received", "h4bcm.stats.2dh5_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh5_rcvd,
|
|
{"3DH5 Packets Received", "h4bcm.stats.3dh5_rcvd",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh1_tx,
|
|
{"2DH1 Packets Transmitted", "h4bcm.stats.2dh1_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh1_tx,
|
|
{"3DH1 Packets Transmitted", "h4bcm.stats.3dh1_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh3_tx,
|
|
{"2DH3 Packets Transmitted", "h4bcm.stats.2dh3_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh3_tx,
|
|
{"3DH3 Packets Transmitted", "h4bcm.stats.3dh3_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_2dh5_tx,
|
|
{"2DH5 Packets Transmitted", "h4bcm.stats.2dh5_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_stats_3dh5_tx,
|
|
{"3DH5 Packets Transmitted", "h4bcm.stats.3dh5_tx",
|
|
FT_UINT16, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_le_ether,
|
|
{"Remote MAC Address", "h4bcm.le.address",
|
|
FT_BYTES, SEP_COLON, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_le_handle,
|
|
{"Handle", "h4bcm.le.handle",
|
|
FT_UINT8, BASE_HEX, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_le_opcode,
|
|
{"Opcode", "h4bcm.le.opcode",
|
|
FT_UINT8, BASE_HEX, VALS(lm_le_opcodes), 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_le_opcode_ext,
|
|
{"Broadcom Specific Opcode", "h4bcm.le.opcodeext",
|
|
FT_UINT8, BASE_HEX, VALS(lm_le_opcodes_ext), 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_h4bcm_ll_version_ind_versnr,
|
|
{"VersNr", "h4bcm.le.versnr",
|
|
FT_UINT8, BASE_HEX, NULL, 0x0,
|
|
"Version", HFILL}},
|
|
{&hf_h4bcm_ll_version_ind_subversnr,
|
|
{"SubVersNr", "h4bcm.le.subversnr",
|
|
FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"Subversion", HFILL}},
|
|
{&hf_h4bcm_ll_version_ind_compid,
|
|
{"CompID", "h4bcm.le.compid",
|
|
FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"Company", HFILL}},
|
|
{&hf_btbbd,
|
|
{"Baseband", "btbbd",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_meta,
|
|
{"Meta Data", "btbbd.meta",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_clk,
|
|
{"CLK", "btbbd.clk",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_channel,
|
|
{"Channel", "btbbd.ch",
|
|
FT_UINT32, BASE_DEC, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_ptt,
|
|
{"Table Type", "btbbd.ptt",
|
|
FT_BOOLEAN, 8, TFS(&packet_table_type_bits), 0b00000001,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_role,
|
|
{"Role", "btbbd.role",
|
|
FT_BOOLEAN, 8, TFS(&packet_role_bits), 0b00000010,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_tx_encrypted,
|
|
{"TX Enc.", "btbbd.txenc",
|
|
FT_BOOLEAN, 8, TFS(&lm_toggle), 0b00100000,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_rx_encrypted,
|
|
{"RX Enc.", "btbbd.rxenc",
|
|
FT_BOOLEAN, 8, TFS(&lm_toggle), 0b01000000,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_is_eir,
|
|
{"Is EIR", "btbbd.iseir",
|
|
FT_BOOLEAN, 8, TFS(&lm_toggle), 0b10000000,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_pkthdr,
|
|
{"Packet Header", "btbbd.pkthdr",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_ltaddr,
|
|
{"LT_ADDR", "btbbd.lt_addr",
|
|
FT_UINT16, BASE_HEX, NULL, 0x07,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_type,
|
|
{"Type", "btbbd.type",
|
|
FT_UINT16, BASE_HEX, VALS(packet_types), 0x78,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_type_br,
|
|
{"Type", "btbbd.type_br",
|
|
FT_UINT16, BASE_HEX, VALS(packet_types_br), 0x78,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_type_edr,
|
|
{"Type", "btbbd.type_edr",
|
|
FT_UINT16, BASE_HEX, VALS(packet_types_edr), 0x78,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_flow,
|
|
{"FLOW", "btbbd.flow",
|
|
FT_BOOLEAN, 16, NULL, 0x80,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_arqn,
|
|
{"ARQN", "btbbd.arqn",
|
|
FT_BOOLEAN, 8, NULL, 0x1,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_seqn,
|
|
{"SEQN", "btbbd.seqn",
|
|
FT_BOOLEAN, 8, NULL, 0x2,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_hec,
|
|
{"HEC", "btbbd.hec",
|
|
FT_UINT8, BASE_HEX, NULL, 0xFC,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_fhs,
|
|
{"FHS", "fhs",
|
|
FT_NONE, BASE_NONE, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_fhs_parity,
|
|
{"Parity", "fhs.parity",
|
|
/* FIXME this doesn't work because bitmasks can only be 32 bits */
|
|
FT_UINT64, BASE_HEX, NULL, /*0x00000003ffffffffULL,*/ 0x0,
|
|
"LAP parity", HFILL}},
|
|
{&hf_btbbd_fhs_lap,
|
|
{"LAP", "fhs.lap",
|
|
FT_UINT24, BASE_HEX, NULL, 0x03fffffc,
|
|
"Lower Address Part", HFILL}},
|
|
{&hf_btbbd_fhs_eir,
|
|
{"EIR", "fhs.eir",
|
|
FT_BOOLEAN, 8, NULL, 0x04,
|
|
"Extended Inquiry Response packet may follow", HFILL}},
|
|
{&hf_btbbd_fhs_sr,
|
|
{"SR", "fhs.sr",
|
|
FT_UINT8, BASE_HEX, VALS(sr_modes), 0x30,
|
|
"Scan Repetition", HFILL}},
|
|
{&hf_btbbd_fhs_uap,
|
|
{"UAP", "fhs.uap",
|
|
FT_UINT8, BASE_HEX, NULL, 0x0,
|
|
"Upper Address Part", HFILL}},
|
|
{&hf_btbbd_fhs_nap,
|
|
{"NAP", "fhs.nap",
|
|
FT_UINT16, BASE_HEX, NULL, 0x0,
|
|
"Non-Significant Address Part", HFILL}},
|
|
{&hf_btbbd_fhs_class, /* TODO: More options */
|
|
{"Class of Device", "fhs.class",
|
|
FT_UINT24, BASE_HEX, NULL, 0x0,
|
|
NULL, HFILL}},
|
|
{&hf_btbbd_fhs_ltaddr,
|
|
{"LT_ADDR", "fhs.lt_addr",
|
|
FT_UINT8, BASE_HEX, NULL, 0x07,
|
|
"Logical Transport Address", HFILL}},
|
|
{&hf_btbbd_fhs_clk,
|
|
{"CLK", "fhs.clk",
|
|
FT_UINT32, BASE_HEX, NULL, 0x1ffffff8,
|
|
"Clock bits 2 through 27", HFILL}},
|
|
{&hf_btbbd_fhs_psmode,
|
|
{"Page Scan Mode", "fhs.psmode",
|
|
FT_UINT8, BASE_HEX, NULL, 0xe0,
|
|
NULL, HFILL}},
|
|
};
|
|
|
|
/* protocol subtree arrays */
|
|
static gint *ett[] = {
|
|
&ett_h4bcm,
|
|
&ett_h4bcm_pldhdr,
|
|
&ett_h4bcm_aclhdr,
|
|
&ett_h4bcm_acl_br_stats,
|
|
&ett_h4bcm_acl_edr_stats,
|
|
&ett_btbbd,
|
|
&ett_btbbd_meta,
|
|
&ett_btbbd_pkthdr,
|
|
&ett_btbbd_fhs,
|
|
};
|
|
|
|
/* register the protocol name and description */
|
|
proto_h4bcm = proto_register_protocol(
|
|
"Bluetooth H4 Serial Broadcom Vendor Specific", /* full name */
|
|
"H4 Broadcom", /* short name */
|
|
"h4bcm" /* abbreviation (e.g. for filters) */
|
|
);
|
|
|
|
register_dissector("btacl", dissect_acl, proto_h4bcm);
|
|
|
|
/* register the header fields and subtrees used */
|
|
proto_register_field_array(proto_h4bcm, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
}
|
|
|
|
void proto_reg_handoff_h4bcm(void)
|
|
{
|
|
dissector_handle_t h4bcm_handle;
|
|
|
|
// Original InternalBlue Broadcom dissector
|
|
h4bcm_handle = create_dissector_handle(dissect_h4bcm, proto_h4bcm);
|
|
dissector_add_uint("hci_h4.type", 0x07, h4bcm_handle);
|
|
|
|
// Bluetooth ACL Header (Payload Header)
|
|
h4bcm_handle = create_dissector_handle(dissect_acl, proto_h4bcm);
|
|
dissector_add_uint("hci_h4.type", 0x08, h4bcm_handle);
|
|
|
|
// Bluetooth Baseband (Packet Header)
|
|
h4bcm_handle = create_dissector_handle(dissect_bt_baseband, proto_h4bcm);
|
|
dissector_add_uint("hci_h4.type", 0x09, h4bcm_handle);
|
|
|
|
// PacketLogger
|
|
packetlogger_handle = find_dissector("packetlogger");
|
|
dissector_add_uint("hci_h4.type", 0x0A, packetlogger_handle);
|
|
|
|
/* LMP dissector from https://github.com/greatscottgadgets/libbtbb */
|
|
btlmp_handle = find_dissector("btlmp");
|
|
btl2cap_handle = find_dissector("btl2cap");
|
|
|
|
// dissector_add_uint("wtap_encap", WTAP_ENCAP_ESPRESSIF_BT, h4bcm_handle);
|
|
}
|
|
|
|
/*
|
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 4
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
*/
|