2020-01-10 17:57:27 +00:00
/* packet-ftdi-mpsse.c
* Routines for FTDI Multi - Protocol Synchronous Serial Engine dissection
*
* Copyright 2020 Tomasz Mon
*
* Wireshark - Network traffic analyzer
* By Gerald Combs < gerald @ wireshark . org >
* Copyright 1998 Gerald Combs
*
* SPDX - License - Identifier : GPL - 2.0 - or - later
*/
# include "config.h"
# include <epan/packet.h>
# include <epan/expert.h>
2020-01-27 17:55:15 +00:00
# include <wsutil/str_util.h>
2020-02-22 10:18:03 +00:00
# include "packet-ftdi-ft.h"
2020-01-10 17:57:27 +00:00
static int proto_ftdi_mpsse = - 1 ;
2020-01-27 17:55:15 +00:00
static gint hf_mpsse_command = - 1 ;
/* Data Shifting commands bits (b0-b6 are relevant only if b7 is 0) */
static gint hf_mpsse_command_b0 = - 1 ;
static gint hf_mpsse_command_b1 = - 1 ;
static gint hf_mpsse_command_b2 = - 1 ;
static gint hf_mpsse_command_b3 = - 1 ;
static gint hf_mpsse_command_b4 = - 1 ;
static gint hf_mpsse_command_b5 = - 1 ;
static gint hf_mpsse_command_b6 = - 1 ;
static gint hf_mpsse_command_b7 = - 1 ;
2020-02-29 13:41:30 +00:00
static gint hf_mpsse_command_with_parameters = - 1 ;
2020-06-27 13:26:56 +00:00
static gint hf_mpsse_bad_command_error = - 1 ;
static gint hf_mpsse_bad_command_code = - 1 ;
2020-03-07 14:38:13 +00:00
static gint hf_mpsse_response = - 1 ;
static gint hf_mpsse_command_in = - 1 ;
static gint hf_mpsse_response_in = - 1 ;
2020-01-27 17:55:15 +00:00
static gint hf_mpsse_length_uint8 = - 1 ;
static gint hf_mpsse_length_uint16 = - 1 ;
static gint hf_mpsse_bytes_out = - 1 ;
2020-07-18 16:45:22 +00:00
static gint hf_mpsse_bytes_in = - 1 ;
2020-01-27 17:55:15 +00:00
static gint hf_mpsse_bits_out = - 1 ;
2020-07-18 16:45:22 +00:00
static gint hf_mpsse_bits_in = - 1 ;
2020-01-27 17:55:15 +00:00
static gint hf_mpsse_value = - 1 ;
static gint hf_mpsse_value_b0 = - 1 ;
static gint hf_mpsse_value_b1 = - 1 ;
static gint hf_mpsse_value_b2 = - 1 ;
static gint hf_mpsse_value_b3 = - 1 ;
static gint hf_mpsse_value_b4 = - 1 ;
static gint hf_mpsse_value_b5 = - 1 ;
static gint hf_mpsse_value_b6 = - 1 ;
static gint hf_mpsse_value_b7 = - 1 ;
static gint hf_mpsse_direction = - 1 ;
static gint hf_mpsse_direction_b0 = - 1 ;
static gint hf_mpsse_direction_b1 = - 1 ;
static gint hf_mpsse_direction_b2 = - 1 ;
static gint hf_mpsse_direction_b3 = - 1 ;
static gint hf_mpsse_direction_b4 = - 1 ;
static gint hf_mpsse_direction_b5 = - 1 ;
static gint hf_mpsse_direction_b6 = - 1 ;
static gint hf_mpsse_direction_b7 = - 1 ;
static gint hf_mpsse_cpumode_address_short = - 1 ;
static gint hf_mpsse_cpumode_address_extended = - 1 ;
static gint hf_mpsse_cpumode_data = - 1 ;
2020-02-22 20:17:40 +00:00
static gint hf_mpsse_clk_divisor = - 1 ;
2020-08-15 15:02:04 +00:00
static gint hf_mpsse_open_drain_enable_low = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b0 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b1 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b2 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b3 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b4 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b5 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b6 = - 1 ;
static gint hf_mpsse_open_drain_enable_low_b7 = - 1 ;
static gint hf_mpsse_open_drain_enable_high = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b0 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b1 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b2 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b3 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b4 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b5 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b6 = - 1 ;
static gint hf_mpsse_open_drain_enable_high_b7 = - 1 ;
2020-01-27 17:55:15 +00:00
2020-01-10 17:57:27 +00:00
static gint ett_ftdi_mpsse = - 1 ;
2020-01-27 17:55:15 +00:00
static gint ett_mpsse_command = - 1 ;
2020-02-29 13:41:30 +00:00
static gint ett_mpsse_command_with_parameters = - 1 ;
2020-03-07 14:38:13 +00:00
static gint ett_mpsse_response_data = - 1 ;
2020-01-27 17:55:15 +00:00
static gint ett_mpsse_value = - 1 ;
static gint ett_mpsse_direction = - 1 ;
2020-08-15 15:02:04 +00:00
static gint ett_mpsse_open_drain_enable = - 1 ;
2020-07-25 11:41:36 +00:00
static gint ett_mpsse_skipped_response_data = - 1 ;
2020-01-10 17:57:27 +00:00
static expert_field ei_undecoded = EI_INIT ;
2020-03-07 14:38:13 +00:00
static expert_field ei_response_without_command = EI_INIT ;
static expert_field ei_skipped_response_data = EI_INIT ;
2020-06-29 18:26:52 +00:00
static expert_field ei_reassembly_unavailable = EI_INIT ;
2020-01-10 17:57:27 +00:00
static dissector_handle_t ftdi_mpsse_handle ;
2020-07-26 11:39:34 +00:00
/* Commands expecting response add command_data_t entry to a list. The list is created when first command
* appears on MPSSE instance TX or when previously created list has matched responses to all entries .
* When a new list is created , head pointer is inserted into both tx_command_info and rx_command_info tree .
2020-07-12 13:53:22 +00:00
*
* When RX packet is dissected , it obtains the pointer to a list ( if there isn ' t any then the capture is
* incomplete / malformed and ei_response_without_command is presented to the user ) . The RX dissection code
* matches commands with responses and updates the response_in_packet and is_response_set flag . When next
* RX packet is being dissected , it skips all the command_data_t entries that have is_response_set flag set .
* To reduce the effective list length that needs to be traversed , a pointer to the first element that does
* not have is_response_set flag set , is added to rx_command_info with the current packet number in the key .
*
* After first pass , RX packets always obtain relevant command_data_t entry without traversing the list .
2020-07-26 11:39:34 +00:00
* If there wasn ' t a separate tree TX packets ( tx_command_info ) , TX packet dissection would have to to
* traverse the list from the pointer obtained from rx_command_info . In normal conditions the number of
* entries to skip in such case is low . However , when the capture file has either :
2020-07-12 13:53:22 +00:00
* * A lot of TX packets with commands expecting response but no RX packets , or
2020-07-26 11:39:34 +00:00
* * Bad Command in TX packet that does not have matching Bad Command response in RX data
* then the traversal time in TX packet dissection becomes significant . To bring performance to acceptable
2020-07-12 13:53:22 +00:00
* levels , tx_command_info tree is being used . It contains pointers to the same list as rx_command_info but
* allows TX dissection to obtain the relevant command_data_t entry without traversing the list .
*/
static wmem_tree_t * rx_command_info = NULL ;
static wmem_tree_t * tx_command_info = NULL ;
2020-03-07 14:38:13 +00:00
typedef struct _command_data command_data_t ;
struct _command_data {
ftdi_mpsse_info_t mpsse_info ;
2020-07-24 18:44:47 +00:00
/* TRUE if complete command parameters were not dissected yet */
gboolean preliminary ;
2020-03-07 14:38:13 +00:00
/* TRUE if response_in_packet has been set (response packet is known) */
gboolean is_response_set ;
guint8 cmd ;
gint32 response_length ;
guint32 command_in_packet ;
guint32 response_in_packet ;
command_data_t * next ;
} ;
2020-01-10 17:57:27 +00:00
void proto_register_ftdi_mpsse ( void ) ;
2020-07-20 17:16:58 +00:00
# define BAD_COMMAND_SYNC_CODE 0xFA
# define CMD_SET_DATA_BITS_LOW_BYTE 0x80
# define CMD_READ_DATA_BITS_LOW_BYTE 0x81
# define CMD_SET_DATA_BITS_HIGH_BYTE 0x82
# define CMD_READ_DATA_BITS_HIGH_BYTE 0x83
# define CMD_CLOCK_SET_DIVISOR 0x86
# define CMD_CLOCK_N_BITS 0x8E
# define CMD_CLOCK_N_TIMES_8_BITS 0x8F
# define CMD_CPUMODE_READ_SHORT_ADDR 0x90
# define CMD_CPUMODE_READ_EXT_ADDR 0x91
# define CMD_CPUMODE_WRITE_SHORT_ADDR 0x92
# define CMD_CPUMODE_WRITE_EXT_ADDR 0x93
# define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH 0x9C
# define CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW 0x9D
2020-08-15 15:02:04 +00:00
# define CMD_IO_OPEN_DRAIN_ENABLE 0x9E
2020-01-27 17:55:15 +00:00
static const value_string command_vals [ ] = {
{ 0x10 , " Clock Data Bytes Out on + ve clock edge MSB first(no read) [Use if CLK starts at '1'] " } ,
{ 0x11 , " Clock Data Bytes Out on -ve clock edge MSB first (no read) [Use if CLK starts at '0'] " } ,
{ 0x12 , " Clock Data Bits Out on +ve clock edge MSB first (no read) [Use if CLK starts at '1'] " } ,
{ 0x13 , " Clock Data Bits Out on -ve clock edge MSB first (no read) [Use if CLK starts at '0'] " } ,
{ 0x18 , " Clock Data Bytes Out on +ve clock edge LSB first (no read) [Use if CLK starts at '1'] " } ,
{ 0x19 , " Clock Data Bytes Out on -ve clock edge LSB first (no read) [Use if CLK starts at '0'] " } ,
{ 0x1A , " Clock Data Bits Out on +ve clock edge LSB first (no read) [Use if CLK starts at '1'] " } ,
{ 0x1B , " Clock Data Bits Out on -ve clock edge LSB first (no read) [Use if CLK starts at '0'] " } ,
{ 0x20 , " Clock Data Bytes In on +ve clock edge MSB first (no write) " } ,
{ 0x22 , " Clock Data Bits In on +ve clock edge MSB first (no write) [TDO/DI sampled just prior to rising edge] " } ,
{ 0x24 , " Clock Data Bytes In on -ve clock edge MSB first (no write) " } ,
{ 0x26 , " Clock Data Bits In on -ve clock edge MSB first (no write) [TDO/DI sampled just prior to falling edge] " } ,
{ 0x28 , " Clock Data Bytes In on +ve clock edge LSB first (no write) " } ,
{ 0x2A , " Clock Data Bits In on +ve clock edge LSB first (no write) [TDO/DI sampled just prior to rising edge] " } ,
{ 0x2C , " Clock Data Bytes In on -ve clock edge LSB first (no write) " } ,
{ 0x2E , " Clock Data Bits In on -ve clock edge LSB first (no write) [TDO/DI sampled just prior to falling edge] " } ,
{ 0x31 , " Clock Data Bytes In and Out MSB first [out on -ve edge, in on +ve edge] " } ,
{ 0x33 , " Clock Data Bits In and Out MSB first [out on -ve edge, in on +ve edge] " } ,
{ 0x34 , " Clock Data Bytes In and Out MSB first [out on +ve edge, in on -ve edge] " } ,
{ 0x36 , " Clock Data Bits In and Out MSB first [out on +ve edge, in on -ve edge] " } ,
{ 0x39 , " Clock Data Bytes In and Out LSB first [out on -ve edge, in on +ve edge] " } ,
{ 0x3B , " Clock Data Bits In and Out LSB first [out on -ve edge, in on +ve edge] " } ,
{ 0x3C , " Clock Data Bytes In and Out LSB first [out on +ve edge, in on -ve edge] " } ,
{ 0x3E , " Clock Data Bits In and Out LSB first [out on +ve edge, in on -ve edge] " } ,
{ 0x4A , " Clock Data to TMS pin (no read) [TMS with LSB first on +ve clk edge - use if clk is set to '1'] " } ,
{ 0x4B , " Clock Data to TMS pin (no read) [TMS with LSB first on -ve clk edge - use if clk is set to '0'] " } ,
{ 0x6A , " Clock Data to TMS pin with read [TMS with LSB first on +ve clk edge, read on +ve edge - use if clk is set to '1'] " } ,
{ 0x6B , " Clock Data to TMS pin with read [TMS with LSB first on -ve clk edge, read on +ve edge - use if clk is set to '0'] " } ,
{ 0x6E , " Clock Data to TMS pin with read [TMS with LSB first on +ve clk edge, read on -ve edge - use if clk is set to '1'] " } ,
{ 0x6F , " Clock Data to TMS pin with read [TMS with LSB first on -ve clk edge, read on -ve edge - use if clk is set to '0'] " } ,
{ CMD_SET_DATA_BITS_LOW_BYTE , " Set Data bits LowByte " } ,
2020-03-07 14:38:13 +00:00
{ CMD_READ_DATA_BITS_LOW_BYTE , " Read Data bits LowByte " } ,
2020-01-27 17:55:15 +00:00
{ CMD_SET_DATA_BITS_HIGH_BYTE , " Set Data bits HighByte " } ,
2020-03-07 14:38:13 +00:00
{ CMD_READ_DATA_BITS_HIGH_BYTE , " Read Data bits HighByte " } ,
2020-01-27 17:55:15 +00:00
{ 0x84 , " Connect TDI to TDO for Loopback " } ,
{ 0x85 , " Disconnect TDI to TDO for Loopback " } ,
{ 0x87 , " Send Immediate (flush buffer back to the PC) " } ,
{ 0x88 , " Wait On I/O High (wait until GPIOL1 (JTAG) or I/O1 (CPU) is high) " } ,
{ 0x89 , " Wait On I/O Low (wait until GPIOL1 (JTAG) or I/O1 (CPU) is low) " } ,
2020-06-27 13:26:56 +00:00
{ 0 , NULL }
} ;
static value_string_ext command_vals_ext = VALUE_STRING_EXT_INIT ( command_vals ) ;
static const value_string cpumode_command_vals [ ] = {
2020-01-27 17:55:15 +00:00
{ CMD_CPUMODE_READ_SHORT_ADDR , " CPUMode Read Short Address " } ,
{ CMD_CPUMODE_READ_EXT_ADDR , " CPUMode Read Extended Address " } ,
{ CMD_CPUMODE_WRITE_SHORT_ADDR , " CPUMode Write Short Address " } ,
{ CMD_CPUMODE_WRITE_EXT_ADDR , " CPUMode Write Extended Address " } ,
{ 0 , NULL }
} ;
2020-06-27 13:26:56 +00:00
static value_string_ext cpumode_command_vals_ext = VALUE_STRING_EXT_INIT ( cpumode_command_vals ) ;
2020-01-27 17:55:15 +00:00
2020-02-23 08:27:06 +00:00
static const value_string ft2232d_only_command_vals [ ] = {
2020-02-22 20:17:40 +00:00
{ CMD_CLOCK_SET_DIVISOR , " Set TCK/SK Divisor " } ,
2020-02-23 08:27:06 +00:00
{ 0 , NULL }
} ;
/* FT232H, FT2232H and FT4232H only commands */
static const value_string h_only_command_vals [ ] = {
2020-02-22 20:17:40 +00:00
{ CMD_CLOCK_SET_DIVISOR , " Set clk divisor " } ,
2020-02-23 08:27:06 +00:00
{ 0x8A , " Disable Clk Divide by 5 " } ,
{ 0x8B , " Enable Clk Divide by 5 " } ,
{ 0x8C , " Enable 3 Phase Data Clocking " } ,
{ 0x8D , " Disable 3 Phase Data Clocking " } ,
2020-07-20 17:16:58 +00:00
{ CMD_CLOCK_N_BITS , " Clock For n bits with no data transfer " } ,
{ CMD_CLOCK_N_TIMES_8_BITS , " Clock For n x 8 bits with no data transfer " } ,
2020-02-23 08:27:06 +00:00
{ 0x94 , " Clk continuously and Wait On I/O High " } ,
{ 0x95 , " Clk continuously and Wait On I/O Low " } ,
{ 0x96 , " Turn On Adaptive clocking " } ,
{ 0x97 , " Turn Off Adaptive clocking " } ,
2020-07-20 17:16:58 +00:00
{ CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH , " Clock For n x 8 bits with no data transfer or Until GPIOL1 is High " } ,
{ CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW , " Clock For n x 8 bits with no data transfer or Until GPIOL1 is Low " } ,
2020-02-23 08:27:06 +00:00
{ 0 , NULL }
} ;
static value_string_ext h_only_command_vals_ext = VALUE_STRING_EXT_INIT ( h_only_command_vals ) ;
static const value_string ft232h_only_command_vals [ ] = {
2020-08-15 15:02:04 +00:00
{ CMD_IO_OPEN_DRAIN_ENABLE , " Set I/O to only drive on a '0' and tristate on a '1' " } ,
2020-02-23 08:27:06 +00:00
{ 0 , NULL }
} ;
2020-01-27 17:55:15 +00:00
static const value_string data_shifting_command_b1_vals [ ] = {
{ 0 , " Byte mode " } ,
{ 1 , " Bit mode " } ,
{ 0 , NULL }
} ;
static const value_string data_shifting_command_b3_vals [ ] = {
{ 0 , " MSB first " } ,
{ 1 , " LSB first " } ,
{ 0 , NULL }
} ;
static const value_string command_b7_vals [ ] = {
{ 0 , " Data Shifting Command " } ,
{ 1 , " Other (Not Data Shifting) Command " } ,
{ 0 , NULL }
} ;
2020-02-23 08:27:06 +00:00
# define IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE(cmd) ((cmd & (1u << 7)) == 0)
# define IS_DATA_SHIFTING_BYTE_MODE(cmd) ((cmd & (1u << 1)) == 0)
2020-06-27 13:26:56 +00:00
# define IS_DATA_SHIFTING_MSB_FIRST(cmd) ((cmd & (1u << 3)) == 0)
2020-02-23 08:27:06 +00:00
# define IS_DATA_SHIFTING_WRITING_TDI(cmd) (cmd & (1u << 4))
2020-03-07 14:38:13 +00:00
# define IS_DATA_SHIFTING_READING_TDO(cmd) (cmd & (1u << 5))
2020-02-23 08:27:06 +00:00
# define IS_DATA_SHIFTING_WRITING_TMS(cmd) (cmd & (1u << 6))
static gboolean is_data_shifting_command ( guint8 cmd )
2020-01-27 17:55:15 +00:00
{
2020-02-23 08:27:06 +00:00
switch ( cmd )
{
/* Not all data shifting commands (with bit 7 clear) are explicitly listed in MPSSE documentation
* Some undocumented data shifting commands trigger BadCommmand response , but some seem to be handled by the device .
*
* Commands listed below ( with bit 7 clear ) trigger BadCommand response on FT2232L , FT232H and FT2232H
*/
case 0x00 : case 0x01 : case 0x02 : case 0x03 : case 0x04 : case 0x05 : case 0x06 : case 0x07 : case 0x08 : case 0x09 : case 0x0A : case 0x0B : case 0x0C : case 0x0D : case 0x0E : case 0x0F :
case 0x40 : case 0x41 : case 0x42 : case 0x43 : case 0x44 : case 0x45 : case 0x46 : case 0x47 : case 0x48 : case 0x49 :
case 0x4C : case 0x4D :
case 0x50 : case 0x51 : case 0x52 : case 0x53 : case 0x54 : case 0x55 : case 0x56 : case 0x57 : case 0x58 : case 0x59 :
case 0x5C : case 0x5D :
case 0x60 : case 0x61 :
case 0x64 : case 0x65 :
case 0x68 : case 0x69 :
case 0x6C : case 0x6D :
case 0x70 : case 0x71 :
case 0x74 : case 0x75 :
case 0x78 : case 0x79 :
case 0x7C : case 0x7D :
return FALSE ;
default :
return IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE ( cmd ) ;
}
}
2020-07-24 18:44:47 +00:00
static gboolean is_data_shifting_command_returning_response ( guint8 cmd , ftdi_mpsse_info_t * mpsse_info )
{
DISSECTOR_ASSERT ( is_data_shifting_command ( cmd ) ) ;
if ( mpsse_info - > mcu_mode )
{
/* MCU mode seems to consume data shifting payloads but do not actually return any response data */
return FALSE ;
}
return IS_DATA_SHIFTING_READING_TDO ( cmd ) ? TRUE : FALSE ;
}
2020-02-23 08:27:06 +00:00
/* Returns human-readable command description string or NULL on BadCommand */
static const char *
get_command_string ( guint8 cmd , ftdi_mpsse_info_t * mpsse_info )
{
const char * str ;
/* First, try commands that are common on all chips */
str = try_val_to_str_ext ( cmd , & command_vals_ext ) ;
if ( str )
{
return str ;
}
if ( is_data_shifting_command ( cmd ) )
{
return " Undocumented Data Shifting Command " ;
}
/* Check chip specific commands */
switch ( mpsse_info - > chip )
{
case FTDI_CHIP_FT2232D :
str = try_val_to_str ( cmd , ft2232d_only_command_vals ) ;
break ;
case FTDI_CHIP_FT232H :
str = try_val_to_str ( cmd , ft232h_only_command_vals ) ;
if ( str )
{
break ;
}
/* Fallthrough */
case FTDI_CHIP_FT2232H :
case FTDI_CHIP_FT4232H :
str = try_val_to_str_ext ( cmd , & h_only_command_vals_ext ) ;
break ;
default :
break ;
}
2020-06-27 13:26:56 +00:00
if ( ! str & & mpsse_info - > mcu_mode )
{
str = try_val_to_str_ext ( cmd , & cpumode_command_vals_ext ) ;
}
2020-02-23 08:27:06 +00:00
return str ;
2020-01-27 17:55:15 +00:00
}
2020-02-23 08:27:06 +00:00
static gboolean is_valid_command ( guint8 cmd , ftdi_mpsse_info_t * mpsse_info )
{
return get_command_string ( cmd , mpsse_info ) ! = NULL ;
}
2020-01-27 17:55:15 +00:00
2020-03-07 14:38:13 +00:00
static gboolean is_same_mpsse_instance ( ftdi_mpsse_info_t * info1 , ftdi_mpsse_info_t * info2 )
{
return ( info1 - > bus_id = = info2 - > bus_id ) & &
( info1 - > device_address = = info2 - > device_address ) & &
( info1 - > chip = = info2 - > chip ) & &
2020-06-27 13:26:56 +00:00
( info1 - > iface = = info2 - > iface ) & &
( info1 - > mcu_mode = = info2 - > mcu_mode ) ;
2020-03-07 14:38:13 +00:00
}
static command_data_t *
2020-07-12 13:53:22 +00:00
get_recorded_command_data ( wmem_tree_t * command_tree , packet_info * pinfo , ftdi_mpsse_info_t * mpsse_info )
2020-03-07 14:38:13 +00:00
{
guint32 k_bus_id = mpsse_info - > bus_id ;
guint32 k_device_address = mpsse_info - > device_address ;
guint32 k_chip = ( guint32 ) mpsse_info - > chip ;
guint32 k_interface = ( guint32 ) mpsse_info - > iface ;
2020-06-27 13:26:56 +00:00
guint32 k_mcu_mode = mpsse_info - > mcu_mode ;
2020-03-07 14:38:13 +00:00
wmem_tree_key_t key [ ] = {
{ 1 , & k_bus_id } ,
{ 1 , & k_device_address } ,
{ 1 , & k_chip } ,
{ 1 , & k_interface } ,
2020-06-27 13:26:56 +00:00
{ 1 , & k_mcu_mode } ,
2020-03-07 14:38:13 +00:00
{ 1 , & pinfo - > num } ,
{ 0 , NULL }
} ;
command_data_t * data = NULL ;
2020-07-12 13:53:22 +00:00
data = ( command_data_t * ) wmem_tree_lookup32_array_le ( command_tree , key ) ;
2020-03-07 14:38:13 +00:00
if ( data & & is_same_mpsse_instance ( mpsse_info , & data - > mpsse_info ) )
{
return data ;
}
return NULL ;
}
2020-07-12 13:53:22 +00:00
static void
insert_command_data_pointer ( wmem_tree_t * command_tree , packet_info * pinfo , ftdi_mpsse_info_t * mpsse_info , command_data_t * data )
2020-03-07 14:38:13 +00:00
{
guint32 k_bus_id = mpsse_info - > bus_id ;
guint32 k_device_address = mpsse_info - > device_address ;
guint32 k_chip = ( guint32 ) mpsse_info - > chip ;
guint32 k_interface = ( guint32 ) mpsse_info - > iface ;
2020-06-27 13:26:56 +00:00
guint32 k_mcu_mode = mpsse_info - > mcu_mode ;
2020-03-07 14:38:13 +00:00
wmem_tree_key_t key [ ] = {
{ 1 , & k_bus_id } ,
{ 1 , & k_device_address } ,
{ 1 , & k_chip } ,
{ 1 , & k_interface } ,
2020-06-27 13:26:56 +00:00
{ 1 , & k_mcu_mode } ,
2020-03-07 14:38:13 +00:00
{ 1 , & pinfo - > num } ,
{ 0 , NULL }
} ;
2020-07-12 13:53:22 +00:00
wmem_tree_insert32_array ( command_tree , key , data ) ;
2020-03-07 14:38:13 +00:00
}
2020-07-12 13:53:22 +00:00
static void
2020-07-24 18:44:47 +00:00
record_command_data ( command_data_t * * cmd_data , packet_info * pinfo , ftdi_mpsse_info_t * mpsse_info , guint8 cmd ,
gint32 response_length , gboolean preliminary )
2020-03-07 14:38:13 +00:00
{
2020-07-24 18:44:47 +00:00
command_data_t * data = * cmd_data ;
DISSECTOR_ASSERT ( response_length > 0 ) ;
if ( data & & data - > preliminary )
{
DISSECTOR_ASSERT ( data - > cmd = = cmd ) ;
DISSECTOR_ASSERT ( data - > response_length = = response_length ) ;
data - > command_in_packet = pinfo - > num ;
data - > preliminary = preliminary ;
return ;
}
data = wmem_new ( wmem_file_scope ( ) , command_data_t ) ;
2020-03-07 14:38:13 +00:00
memcpy ( & data - > mpsse_info , mpsse_info , sizeof ( ftdi_mpsse_info_t ) ) ;
2020-07-24 18:44:47 +00:00
data - > preliminary = preliminary ;
2020-03-07 14:38:13 +00:00
data - > is_response_set = FALSE ;
data - > cmd = cmd ;
data - > response_length = response_length ;
data - > command_in_packet = pinfo - > num ;
data - > response_in_packet = 0 ;
data - > next = NULL ;
2020-07-26 11:39:34 +00:00
if ( * cmd_data & & ( ! ( * cmd_data ) - > is_response_set ) )
2020-03-07 14:38:13 +00:00
{
DISSECTOR_ASSERT ( ( * cmd_data ) - > next = = NULL ) ;
( * cmd_data ) - > next = data ;
2020-07-12 13:53:22 +00:00
if ( ( * cmd_data ) - > command_in_packet ! = pinfo - > num )
{
insert_command_data_pointer ( tx_command_info , pinfo , mpsse_info , data ) ;
}
2020-03-07 14:38:13 +00:00
}
else
{
2020-07-12 13:53:22 +00:00
insert_command_data_pointer ( rx_command_info , pinfo , mpsse_info , data ) ;
insert_command_data_pointer ( tx_command_info , pinfo , mpsse_info , data ) ;
2020-03-07 14:38:13 +00:00
}
* cmd_data = data ;
}
2020-07-11 14:54:13 +00:00
static void expect_response ( command_data_t * * cmd_data , packet_info * pinfo , proto_tree * tree ,
ftdi_mpsse_info_t * mpsse_info , guint8 cmd , guint16 response_length )
{
if ( pinfo - > fd - > visited )
{
DISSECTOR_ASSERT ( * cmd_data ) ;
DISSECTOR_ASSERT ( ( * cmd_data ) - > cmd = = cmd ) ;
DISSECTOR_ASSERT ( ( * cmd_data ) - > response_length = = response_length ) ;
if ( ( * cmd_data ) - > is_response_set )
{
2020-07-12 13:53:22 +00:00
proto_tree * response_in = proto_tree_add_uint ( tree , hf_mpsse_response_in , NULL , 0 , 0 , ( * cmd_data ) - > response_in_packet ) ;
2020-07-11 14:54:13 +00:00
proto_item_set_generated ( response_in ) ;
2020-07-12 13:53:22 +00:00
DISSECTOR_ASSERT ( ( * cmd_data ) - > command_in_packet = = pinfo - > num ) ;
2020-07-11 14:54:13 +00:00
}
* cmd_data = ( * cmd_data ) - > next ;
}
else
{
2020-07-24 18:44:47 +00:00
record_command_data ( cmd_data , pinfo , mpsse_info , cmd , response_length , FALSE ) ;
2020-07-11 14:54:13 +00:00
}
}
2020-02-22 20:17:40 +00:00
static gchar * freq_to_str ( gfloat freq )
{
if ( freq < 1e3 )
{
return g_strdup_printf ( " %.12g Hz " , freq ) ;
}
else if ( freq < 1e6 )
{
2020-08-15 15:20:16 +00:00
return g_strdup_printf ( " %.12g kHz " , freq / 1e3 ) ;
2020-02-22 20:17:40 +00:00
}
else
{
return g_strdup_printf ( " %.12g MHz " , freq / 1e6 ) ;
}
}
2020-01-27 17:55:15 +00:00
static gint
2020-03-07 14:38:13 +00:00
dissect_data_shifting_command_parameters ( guint8 cmd , tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset ,
ftdi_mpsse_info_t * mpsse_info , command_data_t * * cmd_data )
2020-01-27 17:55:15 +00:00
{
gint offset_start = offset ;
2020-07-24 18:44:47 +00:00
gint32 length ;
2020-01-27 17:55:15 +00:00
2020-02-23 08:27:06 +00:00
DISSECTOR_ASSERT ( is_data_shifting_command ( cmd ) ) ;
2020-01-27 17:55:15 +00:00
if ( IS_DATA_SHIFTING_BYTE_MODE ( cmd ) )
{
length = tvb_get_guint16 ( tvb , offset , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_uint_format ( tree , hf_mpsse_length_uint16 , tvb , offset , 2 , length , " Length: %d byte%s " , length + 1 , plurality ( length + 1 , " " , " s " ) ) ;
offset + = 2 ;
if ( IS_DATA_SHIFTING_WRITING_TDI ( cmd ) )
{
proto_tree_add_item ( tree , hf_mpsse_bytes_out , tvb , offset , length + 1 , ENC_NA ) ;
offset + = length + 1 ;
}
}
else
{
length = tvb_get_guint8 ( tvb , offset ) ;
proto_tree_add_uint_format ( tree , hf_mpsse_length_uint8 , tvb , offset , 1 , length , " Length: %d bit%s " , length + 1 , plurality ( length + 1 , " " , " s " ) ) ;
offset + = 1 ;
2020-06-27 13:26:56 +00:00
if ( IS_DATA_SHIFTING_WRITING_TMS ( cmd ) & & IS_DATA_SHIFTING_READING_TDO ( cmd ) & & IS_DATA_SHIFTING_MSB_FIRST ( cmd ) )
{
/* These undocumented commands do not seem to consume the data byte, only the length */
}
else if ( IS_DATA_SHIFTING_WRITING_TDI ( cmd ) | | IS_DATA_SHIFTING_WRITING_TMS ( cmd ) )
2020-01-27 17:55:15 +00:00
{
proto_tree_add_item ( tree , hf_mpsse_bits_out , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
}
}
2020-07-24 18:44:47 +00:00
if ( is_data_shifting_command_returning_response ( cmd , mpsse_info ) )
2020-07-11 14:54:13 +00:00
{
expect_response ( cmd_data , pinfo , tree , mpsse_info , cmd , IS_DATA_SHIFTING_BYTE_MODE ( cmd ) ? length + 1 : 1 ) ;
2020-03-07 14:38:13 +00:00
}
2020-01-27 17:55:15 +00:00
return offset - offset_start ;
}
static gint
2020-02-22 10:18:03 +00:00
dissect_set_data_bits_parameters ( guint8 cmd _U_ , tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset ,
const char * signal_names [ 8 ] , const char * pin_prefix , guint num_pins )
2020-01-27 17:55:15 +00:00
{
static const gint * value_bits_hf [ ] = {
& hf_mpsse_value_b0 ,
& hf_mpsse_value_b1 ,
& hf_mpsse_value_b2 ,
& hf_mpsse_value_b3 ,
& hf_mpsse_value_b4 ,
& hf_mpsse_value_b5 ,
& hf_mpsse_value_b6 ,
& hf_mpsse_value_b7 ,
} ;
static const gint * direction_bits_hf [ ] = {
& hf_mpsse_direction_b0 ,
& hf_mpsse_direction_b1 ,
& hf_mpsse_direction_b2 ,
& hf_mpsse_direction_b3 ,
& hf_mpsse_direction_b4 ,
& hf_mpsse_direction_b5 ,
& hf_mpsse_direction_b6 ,
& hf_mpsse_direction_b7 ,
} ;
guint32 value , direction ;
2020-02-22 10:18:03 +00:00
proto_item * item ;
2020-01-27 17:55:15 +00:00
proto_item * value_item , * direction_item ;
proto_tree * value_tree , * direction_tree ;
guint bit ;
value_item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_value , tvb , offset , 1 , ENC_LITTLE_ENDIAN , & value ) ;
direction_item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_direction , tvb , offset + 1 , 1 , ENC_LITTLE_ENDIAN , & direction ) ;
value_tree = proto_item_add_subtree ( value_item , ett_mpsse_value ) ;
for ( bit = 0 ; bit < 8 ; bit + + )
{
const char * state ;
if ( ( 1 < < bit ) & direction )
{
state = ( ( 1 < < bit ) & value ) ? " Output High " : " Output Low " ;
}
else
{
state = " N/A (Input) " ;
}
2020-02-22 10:18:03 +00:00
item = proto_tree_add_uint_format_value ( value_tree , * value_bits_hf [ bit ] , tvb , offset , 1 , value , " %s " , signal_names [ bit ] ) ;
if ( pin_prefix & & ( bit < num_pins ) )
{
proto_item_append_text ( item , " [%s%d] " , pin_prefix , bit ) ;
}
proto_item_append_text ( item , " %s " , state ) ;
2020-01-27 17:55:15 +00:00
}
direction_tree = proto_item_add_subtree ( direction_item , ett_mpsse_direction ) ;
for ( bit = 0 ; bit < 8 ; bit + + )
{
const char * type = ( ( 1 < < bit ) & direction ) ? " Output " : " Input " ;
2020-02-22 10:18:03 +00:00
item = proto_tree_add_uint_format_value ( direction_tree , * direction_bits_hf [ bit ] , tvb , offset + 1 , 1 , direction , " %s " , signal_names [ bit ] ) ;
if ( pin_prefix & & ( bit < num_pins ) )
{
proto_item_append_text ( item , " [%s%d] " , pin_prefix , bit ) ;
}
proto_item_append_text ( item , " %s " , type ) ;
2020-01-27 17:55:15 +00:00
}
return 2 ;
}
static gint
2020-03-07 14:38:13 +00:00
dissect_cpumode_parameters ( guint8 cmd , tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset ,
ftdi_mpsse_info_t * mpsse_info , command_data_t * * cmd_data )
2020-01-27 17:55:15 +00:00
{
gint offset_start = offset ;
/* Address is either short (1 byte) or extended (2 bytes) */
if ( ( cmd = = CMD_CPUMODE_READ_SHORT_ADDR ) | | ( cmd = = CMD_CPUMODE_WRITE_SHORT_ADDR ) )
{
proto_tree_add_item ( tree , hf_mpsse_cpumode_address_short , tvb , offset , 1 , ENC_BIG_ENDIAN ) ;
offset + = 1 ;
}
else if ( ( cmd = = CMD_CPUMODE_READ_EXT_ADDR ) | | ( cmd = = CMD_CPUMODE_WRITE_EXT_ADDR ) )
{
proto_tree_add_item ( tree , hf_mpsse_cpumode_address_extended , tvb , offset , 2 , ENC_BIG_ENDIAN ) ;
offset + = 2 ;
}
/* Write commands have data parameter (1 byte) */
if ( ( cmd = = CMD_CPUMODE_WRITE_SHORT_ADDR ) | | ( cmd = = CMD_CPUMODE_WRITE_EXT_ADDR ) )
{
proto_tree_add_item ( tree , hf_mpsse_cpumode_data , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
}
2020-07-11 14:54:13 +00:00
if ( ( cmd = = CMD_CPUMODE_READ_SHORT_ADDR ) | | ( cmd = = CMD_CPUMODE_READ_EXT_ADDR ) )
2020-03-07 14:38:13 +00:00
{
2020-07-11 14:54:13 +00:00
expect_response ( cmd_data , pinfo , tree , mpsse_info , cmd , 1 ) ;
2020-03-07 14:38:13 +00:00
}
2020-01-27 17:55:15 +00:00
return offset - offset_start ;
}
2020-02-22 20:17:40 +00:00
static gint
dissect_clock_parameters ( guint8 cmd _U_ , tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset , ftdi_mpsse_info_t * mpsse_info )
{
gint offset_start = offset ;
guint32 value ;
proto_item * item ;
gchar * str_old , * str ;
item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_clk_divisor , tvb , offset , 2 , ENC_LITTLE_ENDIAN , & value ) ;
offset + = 2 ;
str_old = freq_to_str ( ( gfloat ) 12e6 / ( ( 1 + value ) * 2 ) ) ;
str = freq_to_str ( ( gfloat ) 60e6 / ( ( 1 + value ) * 2 ) ) ;
if ( mpsse_info - > chip = = FTDI_CHIP_FT2232D )
{
proto_item_append_text ( item , " , TCK/SK Max: %s " , str_old ) ;
}
else
{
proto_item_append_text ( item , " , TCK Max: %s (60 MHz master clock) or %s (12 MHz master clock) " , str , str_old ) ;
}
g_free ( str_old ) ;
g_free ( str ) ;
return offset - offset_start ;
}
2020-07-20 17:16:58 +00:00
static gint
dissect_clock_n_bits_parameters ( guint8 cmd _U_ , tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset , ftdi_mpsse_info_t * mpsse_info _U_ )
{
guint32 length = tvb_get_guint8 ( tvb , offset ) ;
proto_tree_add_uint_format ( tree , hf_mpsse_length_uint8 , tvb , offset , 1 , length , " Length: %d clock%s " , length + 1 , plurality ( length + 1 , " " , " s " ) ) ;
return 1 ;
}
static gint
dissect_clock_n_times_8_bits_parameters ( guint8 cmd _U_ , tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset , ftdi_mpsse_info_t * mpsse_info _U_ )
{
guint32 length = tvb_get_guint16 ( tvb , offset , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_uint_format ( tree , hf_mpsse_length_uint16 , tvb , offset , 2 , length , " Length: %d clocks " , ( length + 1 ) * 8 ) ;
return 2 ;
}
2020-07-19 14:10:19 +00:00
static const char *
get_data_bit_pin_prefix ( gboolean is_high_byte , ftdi_mpsse_info_t * mpsse_info , guint * out_num_pins , const char * ( * * out_names ) [ 8 ] )
2020-02-22 10:18:03 +00:00
{
2020-07-19 14:10:19 +00:00
static const char * low_byte_signal_names [ 8 ] = {
" TCK/SK " ,
" TDI/DO " ,
" TDO/DI " ,
" TMS/CS " ,
" GPIOL0 " ,
" GPIOL1 " ,
" GPIOL2 " ,
" GPIOL3 " ,
} ;
static const char * high_byte_signal_names [ 8 ] = {
" GPIOH0 " ,
" GPIOH1 " ,
" GPIOH2 " ,
" GPIOH3 " ,
" GPIOH4 " ,
" GPIOH5 " ,
" GPIOH6 " ,
" GPIOH7 " ,
} ;
* out_names = ( is_high_byte ) ? & high_byte_signal_names : & low_byte_signal_names ;
2020-02-22 10:18:03 +00:00
/* Based on table from FTDI AN_108 chapter 2.1 Data bit Definition */
switch ( mpsse_info - > chip )
{
case FTDI_CHIP_FT2232D :
if ( mpsse_info - > iface = = FTDI_INTERFACE_A )
{
* out_num_pins = ( is_high_byte ) ? 4 : 8 ;
return ( is_high_byte ) ? " ACBUS " : " ADBUS " ;
}
break ;
case FTDI_CHIP_FT232H :
* out_num_pins = 8 ;
return ( is_high_byte ) ? " ACBUS " : " ADBUS " ;
case FTDI_CHIP_FT2232H :
if ( mpsse_info - > iface = = FTDI_INTERFACE_A )
{
* out_num_pins = 8 ;
return ( is_high_byte ) ? " ACBUS " : " ADBUS " ;
}
else if ( mpsse_info - > iface = = FTDI_INTERFACE_B )
{
* out_num_pins = 8 ;
return ( is_high_byte ) ? " BCBUS " : " BDBUS " ;
}
break ;
case FTDI_CHIP_FT4232H :
if ( ! is_high_byte )
{
if ( mpsse_info - > iface = = FTDI_INTERFACE_A )
{
* out_num_pins = 8 ;
return " ADBUS " ;
}
else if ( mpsse_info - > iface = = FTDI_INTERFACE_B )
{
* out_num_pins = 8 ;
return " BDBUS " ;
}
}
break ;
default :
break ;
}
* out_num_pins = 0 ;
return NULL ;
}
2020-08-15 15:02:04 +00:00
static gint
dissect_io_open_drain_enable_parameters ( guint8 cmd _U_ , tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset , ftdi_mpsse_info_t * mpsse_info _U_ )
{
static const gint * low_byte_bits_hf [ ] = {
& hf_mpsse_open_drain_enable_low_b0 ,
& hf_mpsse_open_drain_enable_low_b1 ,
& hf_mpsse_open_drain_enable_low_b2 ,
& hf_mpsse_open_drain_enable_low_b3 ,
& hf_mpsse_open_drain_enable_low_b4 ,
& hf_mpsse_open_drain_enable_low_b5 ,
& hf_mpsse_open_drain_enable_low_b6 ,
& hf_mpsse_open_drain_enable_low_b7 ,
} ;
static const gint * high_byte_bits_hf [ ] = {
& hf_mpsse_open_drain_enable_high_b0 ,
& hf_mpsse_open_drain_enable_high_b1 ,
& hf_mpsse_open_drain_enable_high_b2 ,
& hf_mpsse_open_drain_enable_high_b3 ,
& hf_mpsse_open_drain_enable_high_b4 ,
& hf_mpsse_open_drain_enable_high_b5 ,
& hf_mpsse_open_drain_enable_high_b6 ,
& hf_mpsse_open_drain_enable_high_b7 ,
} ;
gint offset_start = offset ;
const char * pin_prefix = NULL ;
guint num_pins = 0 ;
const char * ( * signal_names ) [ 8 ] = NULL ;
guint32 value ;
proto_item * item ;
proto_item * byte_item ;
proto_tree * byte_tree ;
guint bit ;
pin_prefix = get_data_bit_pin_prefix ( FALSE , mpsse_info , & num_pins , & signal_names ) ;
byte_item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_open_drain_enable_low , tvb , offset , 1 , ENC_LITTLE_ENDIAN , & value ) ;
byte_tree = proto_item_add_subtree ( byte_item , ett_mpsse_open_drain_enable ) ;
for ( bit = 0 ; bit < 8 ; bit + + )
{
const char * output_type = ( ( 1 < < bit ) & value ) ? " Open-Drain " : " Push-Pull " ;
item = proto_tree_add_uint_format_value ( byte_tree , * low_byte_bits_hf [ bit ] , tvb , offset , 1 , value , " %s " , ( * signal_names ) [ bit ] ) ;
if ( pin_prefix & & ( bit < num_pins ) )
{
proto_item_append_text ( item , " [%s%d] " , pin_prefix , bit ) ;
}
proto_item_append_text ( item , " %s " , output_type ) ;
}
offset + + ;
pin_prefix = get_data_bit_pin_prefix ( TRUE , mpsse_info , & num_pins , & signal_names ) ;
byte_item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_open_drain_enable_high , tvb , offset , 1 , ENC_LITTLE_ENDIAN , & value ) ;
byte_tree = proto_item_add_subtree ( byte_item , ett_mpsse_open_drain_enable ) ;
for ( bit = 0 ; bit < 8 ; bit + + )
{
const char * output_type = ( ( 1 < < bit ) & value ) ? " Open-Drain " : " Push-Pull " ;
item = proto_tree_add_uint_format_value ( byte_tree , * high_byte_bits_hf [ bit ] , tvb , offset , 1 , value , " %s " , ( * signal_names ) [ bit ] ) ;
if ( pin_prefix & & ( bit < num_pins ) )
{
proto_item_append_text ( item , " [%s%d] " , pin_prefix , bit ) ;
}
proto_item_append_text ( item , " %s " , output_type ) ;
}
offset + + ;
return offset - offset_start ;
}
2020-01-27 17:55:15 +00:00
static gint
2020-03-07 14:38:13 +00:00
dissect_non_data_shifting_command_parameters ( guint8 cmd , tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset ,
ftdi_mpsse_info_t * mpsse_info , command_data_t * * cmd_data )
2020-01-27 17:55:15 +00:00
{
2020-07-19 14:10:19 +00:00
const char * pin_prefix = NULL ;
guint num_pins = 0 ;
const char * ( * signal_names ) [ 8 ] = NULL ;
2020-02-22 10:18:03 +00:00
2020-02-23 08:27:06 +00:00
DISSECTOR_ASSERT ( ! is_data_shifting_command ( cmd ) & & is_valid_command ( cmd , mpsse_info ) ) ;
2020-01-27 17:55:15 +00:00
switch ( cmd )
{
case CMD_SET_DATA_BITS_LOW_BYTE :
2020-07-19 14:10:19 +00:00
pin_prefix = get_data_bit_pin_prefix ( FALSE , mpsse_info , & num_pins , & signal_names ) ;
return dissect_set_data_bits_parameters ( cmd , tvb , pinfo , tree , offset , * signal_names , pin_prefix , num_pins ) ;
2020-01-27 17:55:15 +00:00
case CMD_SET_DATA_BITS_HIGH_BYTE :
2020-07-19 14:10:19 +00:00
pin_prefix = get_data_bit_pin_prefix ( TRUE , mpsse_info , & num_pins , & signal_names ) ;
return dissect_set_data_bits_parameters ( cmd , tvb , pinfo , tree , offset , * signal_names , pin_prefix , num_pins ) ;
2020-03-07 14:38:13 +00:00
case CMD_READ_DATA_BITS_LOW_BYTE :
case CMD_READ_DATA_BITS_HIGH_BYTE :
2020-07-11 14:54:13 +00:00
expect_response ( cmd_data , pinfo , tree , mpsse_info , cmd , 1 ) ;
2020-03-07 14:38:13 +00:00
return 0 ;
2020-01-27 17:55:15 +00:00
case CMD_CPUMODE_READ_SHORT_ADDR :
case CMD_CPUMODE_READ_EXT_ADDR :
case CMD_CPUMODE_WRITE_SHORT_ADDR :
case CMD_CPUMODE_WRITE_EXT_ADDR :
2020-03-07 14:38:13 +00:00
return dissect_cpumode_parameters ( cmd , tvb , pinfo , tree , offset , mpsse_info , cmd_data ) ;
2020-02-22 20:17:40 +00:00
case CMD_CLOCK_SET_DIVISOR :
return dissect_clock_parameters ( cmd , tvb , pinfo , tree , offset , mpsse_info ) ;
2020-07-20 17:16:58 +00:00
case CMD_CLOCK_N_BITS :
return dissect_clock_n_bits_parameters ( cmd , tvb , pinfo , tree , offset , mpsse_info ) ;
case CMD_CLOCK_N_TIMES_8_BITS :
case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_HIGH :
case CMD_CLOCK_N_TIMES_8_BITS_OR_UNTIL_L1_LOW :
return dissect_clock_n_times_8_bits_parameters ( cmd , tvb , pinfo , tree , offset , mpsse_info ) ;
2020-08-15 15:02:04 +00:00
case CMD_IO_OPEN_DRAIN_ENABLE :
return dissect_io_open_drain_enable_parameters ( cmd , tvb , pinfo , tree , offset , mpsse_info ) ;
2020-01-27 17:55:15 +00:00
default :
return 0 ;
}
}
2020-07-24 18:44:47 +00:00
static gint estimated_command_parameters_length ( guint8 cmd , tvbuff_t * tvb , packet_info * pinfo , gint offset ,
ftdi_mpsse_info_t * mpsse_info , command_data_t * * cmd_data )
2020-01-27 17:55:15 +00:00
{
gint parameters_length = 0 ;
2020-02-29 13:41:30 +00:00
if ( ! is_valid_command ( cmd , mpsse_info ) )
{
return 0 ;
}
2020-01-27 17:55:15 +00:00
2020-02-23 08:27:06 +00:00
if ( is_data_shifting_command ( cmd ) )
2020-01-27 17:55:15 +00:00
{
2020-07-24 18:44:47 +00:00
gint32 data_length = 0 ;
2020-01-27 17:55:15 +00:00
if ( IS_DATA_SHIFTING_BYTE_MODE ( cmd ) )
{
parameters_length = 2 ;
if ( IS_DATA_SHIFTING_WRITING_TDI ( cmd ) )
{
if ( tvb_reported_length_remaining ( tvb , offset ) > = 2 )
{
2020-07-24 18:44:47 +00:00
data_length = ( gint32 ) tvb_get_guint16 ( tvb , offset , ENC_LITTLE_ENDIAN ) + 1 ;
parameters_length + = data_length ;
2020-01-27 17:55:15 +00:00
}
/* else length is not available already so the caller will know that reassembly is needed */
}
}
else /* bit mode */
{
parameters_length = ( IS_DATA_SHIFTING_WRITING_TDI ( cmd ) | | IS_DATA_SHIFTING_WRITING_TMS ( cmd ) ) ? 2 : 1 ;
2020-07-24 18:44:47 +00:00
data_length = 1 ;
2020-06-27 13:26:56 +00:00
if ( IS_DATA_SHIFTING_WRITING_TMS ( cmd ) & & IS_DATA_SHIFTING_READING_TDO ( cmd ) & & IS_DATA_SHIFTING_MSB_FIRST ( cmd ) )
{
/* These undocumented commands do not seem to consume the data byte, only the length */
parameters_length = 1 ;
}
2020-01-27 17:55:15 +00:00
}
2020-07-24 18:44:47 +00:00
if ( ! pinfo - > fd - > visited )
{
if ( is_data_shifting_command_returning_response ( cmd , mpsse_info ) & & data_length )
{
/* Record preliminary command info so the response handler can find the matching command
* if host starts reading data before all output is sent . If this command requires reassembly
* the command_in_packet member will continue updating until the reassembly is complete .
* The preliminary flag will be reset when expect_response ( ) executes .
*/
record_command_data ( cmd_data , pinfo , mpsse_info , cmd , data_length , TRUE ) ;
}
}
2020-01-27 17:55:15 +00:00
}
else
{
switch ( cmd )
{
case CMD_CPUMODE_WRITE_EXT_ADDR :
parameters_length = 3 ;
break ;
case CMD_SET_DATA_BITS_LOW_BYTE :
case CMD_SET_DATA_BITS_HIGH_BYTE :
case CMD_CPUMODE_READ_EXT_ADDR :
case CMD_CPUMODE_WRITE_SHORT_ADDR :
2020-02-22 20:17:40 +00:00
case CMD_CLOCK_SET_DIVISOR :
case 0x8F : case 0x9C : case 0x9D : case 0x9E :
2020-01-27 17:55:15 +00:00
parameters_length = 2 ;
break ;
case CMD_CPUMODE_READ_SHORT_ADDR :
case 0x8E :
parameters_length = 1 ;
break ;
case 0x81 : case 0x83 : case 0x84 : case 0x85 : case 0x87 : case 0x88 : case 0x89 : case 0x8A : case 0x8B : case 0x8C : case 0x8D : case 0x94 : case 0x95 : case 0x96 : case 0x97 :
parameters_length = 0 ;
break ;
default :
DISSECTOR_ASSERT_NOT_REACHED ( ) ;
}
}
return parameters_length ;
}
2020-02-23 08:27:06 +00:00
static guint8
2020-02-29 13:41:30 +00:00
dissect_command_code ( guint8 cmd , const char * cmd_str , tvbuff_t * tvb , proto_tree * tree , gint offset , ftdi_mpsse_info_t * mpsse_info _U_ )
2020-01-27 17:55:15 +00:00
{
2020-02-23 08:27:06 +00:00
proto_item * cmd_item ;
proto_tree * cmd_tree ;
2020-06-19 01:14:46 +00:00
int * const * cmd_bits ;
static int * const data_shifting_cmd_bits [ ] = {
2020-01-27 17:55:15 +00:00
& hf_mpsse_command_b7 ,
& hf_mpsse_command_b6 ,
& hf_mpsse_command_b5 ,
& hf_mpsse_command_b4 ,
& hf_mpsse_command_b3 ,
& hf_mpsse_command_b2 ,
& hf_mpsse_command_b1 ,
& hf_mpsse_command_b0 ,
NULL
} ;
2020-06-19 01:14:46 +00:00
static int * const non_data_shifting_cmd_bits [ ] = {
2020-01-27 17:55:15 +00:00
& hf_mpsse_command_b7 ,
NULL
} ;
2020-02-29 13:41:30 +00:00
cmd_item = proto_tree_add_uint_format ( tree , hf_mpsse_command , tvb , offset , 1 , cmd , " Command: %s (0x%02x) " , cmd_str , cmd ) ;
2020-02-23 08:27:06 +00:00
cmd_tree = proto_item_add_subtree ( cmd_item , ett_mpsse_command ) ;
cmd_bits = IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE ( cmd ) ? data_shifting_cmd_bits : non_data_shifting_cmd_bits ;
proto_tree_add_bitmask_list_value ( cmd_tree , tvb , offset , 1 , cmd_bits , cmd ) ;
return cmd ;
}
static gint
2020-03-07 14:38:13 +00:00
dissect_command ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset , gboolean * need_reassembly ,
ftdi_mpsse_info_t * mpsse_info , command_data_t * * cmd_data )
2020-02-23 08:27:06 +00:00
{
guint8 cmd ;
2020-02-29 13:41:30 +00:00
const char * cmd_str ;
2020-02-23 08:27:06 +00:00
gint offset_start = offset ;
2020-02-29 13:41:30 +00:00
gint parameters_length ;
gint dissected ;
proto_item * cmd_with_parameters ;
proto_tree * cmd_tree ;
2020-02-23 08:27:06 +00:00
2020-02-29 13:41:30 +00:00
cmd = tvb_get_guint8 ( tvb , offset ) ;
cmd_str = get_command_string ( cmd , mpsse_info ) ;
2020-07-24 18:44:47 +00:00
parameters_length = estimated_command_parameters_length ( cmd , tvb , pinfo , offset + 1 , mpsse_info , cmd_data ) ;
2020-02-29 13:41:30 +00:00
if ( tvb_reported_length_remaining ( tvb , offset + 1 ) < parameters_length )
{
* need_reassembly = TRUE ;
return 0 ;
}
if ( ! cmd_str )
{
cmd_str = " Bad Command " ;
}
cmd_with_parameters = proto_tree_add_bytes_format ( tree , hf_mpsse_command_with_parameters , tvb , offset , 1 + parameters_length , NULL , " %s " , cmd_str ) ;
cmd_tree = proto_item_add_subtree ( cmd_with_parameters , ett_mpsse_command_with_parameters ) ;
cmd = dissect_command_code ( cmd , cmd_str , tvb , cmd_tree , offset , mpsse_info ) ;
2020-01-27 17:55:15 +00:00
offset + = 1 ;
2020-02-29 13:41:30 +00:00
* need_reassembly = FALSE ;
2020-03-07 14:38:13 +00:00
if ( is_valid_command ( cmd , mpsse_info ) )
2020-01-27 17:55:15 +00:00
{
2020-02-29 13:41:30 +00:00
if ( IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE ( cmd ) )
2020-01-27 17:55:15 +00:00
{
2020-03-07 14:38:13 +00:00
dissected = dissect_data_shifting_command_parameters ( cmd , tvb , pinfo , cmd_tree , offset , mpsse_info , cmd_data ) ;
2020-02-29 13:41:30 +00:00
DISSECTOR_ASSERT ( dissected = = parameters_length ) ;
offset + = dissected ;
2020-01-27 17:55:15 +00:00
}
else
{
2020-03-07 14:38:13 +00:00
dissected = dissect_non_data_shifting_command_parameters ( cmd , tvb , pinfo , cmd_tree , offset , mpsse_info , cmd_data ) ;
2020-02-29 13:41:30 +00:00
if ( parameters_length > dissected )
{
proto_tree_add_expert ( cmd_tree , pinfo , & ei_undecoded , tvb , offset + dissected , parameters_length - dissected ) ;
}
offset + = parameters_length ;
2020-01-27 17:55:15 +00:00
}
}
2020-07-11 14:54:13 +00:00
else
2020-03-07 14:38:13 +00:00
{
2020-07-11 14:54:13 +00:00
/* Expect Bad Command response */
expect_response ( cmd_data , pinfo , cmd_tree , mpsse_info , cmd , 2 ) ;
2020-03-07 14:38:13 +00:00
}
return offset - offset_start ;
}
2020-07-19 14:10:19 +00:00
static gint
dissect_read_data_bits_response ( tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset ,
const char * signal_names [ 8 ] , const char * pin_prefix , guint num_pins )
{
static const gint * value_bits_hf [ ] = {
& hf_mpsse_value_b0 ,
& hf_mpsse_value_b1 ,
& hf_mpsse_value_b2 ,
& hf_mpsse_value_b3 ,
& hf_mpsse_value_b4 ,
& hf_mpsse_value_b5 ,
& hf_mpsse_value_b6 ,
& hf_mpsse_value_b7 ,
} ;
guint32 value ;
proto_item * item ;
proto_item * value_item ;
proto_tree * value_tree ;
guint bit ;
value_item = proto_tree_add_item_ret_uint ( tree , hf_mpsse_value , tvb , offset , 1 , ENC_LITTLE_ENDIAN , & value ) ;
value_tree = proto_item_add_subtree ( value_item , ett_mpsse_value ) ;
for ( bit = 0 ; bit < 8 ; bit + + )
{
const char * state ;
state = ( ( 1 < < bit ) & value ) ? " High " : " Low " ;
item = proto_tree_add_uint_format_value ( value_tree , * value_bits_hf [ bit ] , tvb , offset , 1 , value , " %s " , signal_names [ bit ] ) ;
if ( pin_prefix & & ( bit < num_pins ) )
{
proto_item_append_text ( item , " [%s%d] " , pin_prefix , bit ) ;
}
proto_item_append_text ( item , " %s " , state ) ;
}
return 1 ;
}
2020-07-20 17:48:57 +00:00
static gint
dissect_cpumode_response ( tvbuff_t * tvb , packet_info * pinfo _U_ , proto_tree * tree , gint offset )
{
proto_tree_add_item ( tree , hf_mpsse_cpumode_data , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
return 1 ;
}
2020-07-19 14:10:19 +00:00
static gint
dissect_non_data_shifting_command_response ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset , command_data_t * cmd_data )
{
const char * pin_prefix = NULL ;
guint num_pins = 0 ;
const char * ( * signal_names ) [ 8 ] = NULL ;
DISSECTOR_ASSERT ( ! is_data_shifting_command ( cmd_data - > cmd ) & & is_valid_command ( cmd_data - > cmd , & cmd_data - > mpsse_info ) ) ;
switch ( cmd_data - > cmd )
{
case CMD_READ_DATA_BITS_LOW_BYTE :
pin_prefix = get_data_bit_pin_prefix ( FALSE , & cmd_data - > mpsse_info , & num_pins , & signal_names ) ;
return dissect_read_data_bits_response ( tvb , pinfo , tree , offset , * signal_names , pin_prefix , num_pins ) ;
case CMD_READ_DATA_BITS_HIGH_BYTE :
pin_prefix = get_data_bit_pin_prefix ( TRUE , & cmd_data - > mpsse_info , & num_pins , & signal_names ) ;
return dissect_read_data_bits_response ( tvb , pinfo , tree , offset , * signal_names , pin_prefix , num_pins ) ;
2020-07-20 17:48:57 +00:00
case CMD_CPUMODE_READ_SHORT_ADDR :
case CMD_CPUMODE_READ_EXT_ADDR :
return dissect_cpumode_response ( tvb , pinfo , tree , offset ) ;
2020-07-19 14:10:19 +00:00
default :
return 0 ;
}
}
2020-03-07 14:38:13 +00:00
static gint
dissect_response_data ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset , command_data_t * cmd_data )
{
2020-06-27 13:26:56 +00:00
gint offset_start = offset ;
2020-07-12 13:53:22 +00:00
if ( pinfo - > fd - > visited )
{
DISSECTOR_ASSERT ( cmd_data - > is_response_set & & cmd_data - > response_in_packet = = pinfo - > num ) ;
}
else
2020-03-07 14:38:13 +00:00
{
2020-07-12 13:53:22 +00:00
DISSECTOR_ASSERT ( ! cmd_data - > is_response_set ) ;
2020-03-07 14:38:13 +00:00
cmd_data - > response_in_packet = pinfo - > num ;
cmd_data - > is_response_set = TRUE ;
}
2020-06-27 13:26:56 +00:00
if ( is_valid_command ( cmd_data - > cmd , & cmd_data - > mpsse_info ) )
{
2020-07-18 16:45:22 +00:00
if ( IS_DATA_SHIFTING_COMMAND_BIT_ACTIVE ( cmd_data - > cmd ) )
{
if ( IS_DATA_SHIFTING_BYTE_MODE ( cmd_data - > cmd ) )
{
proto_tree_add_item ( tree , hf_mpsse_bytes_in , tvb , offset , cmd_data - > response_length , ENC_NA ) ;
}
else
{
proto_tree_add_item ( tree , hf_mpsse_bits_in , tvb , offset , cmd_data - > response_length , ENC_LITTLE_ENDIAN ) ;
}
offset + = cmd_data - > response_length ;
}
else
{
2020-07-19 14:10:19 +00:00
gint dissected ;
dissected = dissect_non_data_shifting_command_response ( tvb , pinfo , tree , offset , cmd_data ) ;
offset + = dissected ;
DISSECTOR_ASSERT ( dissected < = cmd_data - > response_length ) ;
if ( cmd_data - > response_length > dissected )
{
proto_tree_add_expert ( tree , pinfo , & ei_undecoded , tvb , offset , cmd_data - > response_length - dissected ) ;
offset + = ( cmd_data - > response_length - dissected ) ;
}
2020-07-18 16:45:22 +00:00
}
2020-06-27 13:26:56 +00:00
}
else
{
proto_tree_add_item ( tree , hf_mpsse_bad_command_error , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + + ;
2020-03-07 14:38:13 +00:00
2020-06-27 13:26:56 +00:00
proto_tree_add_item ( tree , hf_mpsse_bad_command_code , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + + ;
}
return offset - offset_start ;
2020-03-07 14:38:13 +00:00
}
static gint
dissect_response ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , gint offset , gboolean * need_reassembly ,
command_data_t * cmd_data )
{
const char * cmd_str ;
gint offset_start = offset ;
proto_item * rsp_data ;
proto_tree * rsp_tree ;
proto_item * command_in ;
cmd_str = get_command_string ( cmd_data - > cmd , & cmd_data - > mpsse_info ) ;
if ( ! cmd_str )
{
2020-07-12 18:38:26 +00:00
gboolean found = FALSE ;
gboolean request_reassembly = FALSE ;
2020-03-07 14:38:13 +00:00
DISSECTOR_ASSERT ( cmd_data - > response_length = = 2 ) ;
cmd_str = " Bad Command " ;
/* Look for Bad Command response in data */
while ( tvb_reported_length_remaining ( tvb , offset ) > = 2 )
{
if ( tvb_get_guint8 ( tvb , offset ) = = BAD_COMMAND_SYNC_CODE )
{
if ( tvb_get_guint8 ( tvb , offset + 1 ) = = cmd_data - > cmd )
{
2020-07-12 18:38:26 +00:00
found = TRUE ;
2020-03-07 14:38:13 +00:00
break ;
}
}
offset + + ;
}
2020-07-12 18:38:26 +00:00
if ( ! found )
{
if ( tvb_get_guint8 ( tvb , offset ) = = BAD_COMMAND_SYNC_CODE )
{
/* Request reassembly only if there is chance it will help */
request_reassembly = TRUE ;
}
else
{
offset + + ;
}
}
2020-03-07 14:38:13 +00:00
if ( offset ! = offset_start )
{
2020-07-25 11:41:36 +00:00
proto_item * item ;
proto_tree * expert_tree ;
item = proto_tree_add_expert ( tree , pinfo , & ei_skipped_response_data , tvb , offset_start , offset - offset_start ) ;
expert_tree = proto_item_add_subtree ( item , ett_mpsse_skipped_response_data ) ;
command_in = proto_tree_add_uint_format ( expert_tree , hf_mpsse_command_in , NULL , 0 , 0 , cmd_data - > command_in_packet ,
" Bad Command 0x%02x in: % " G_GUINT32_FORMAT , cmd_data - > cmd , cmd_data - > command_in_packet ) ;
proto_item_set_generated ( command_in ) ;
if ( cmd_data - > is_response_set )
{
proto_item * response_in ;
response_in = proto_tree_add_uint ( expert_tree , hf_mpsse_response_in , NULL , 0 , 0 , cmd_data - > response_in_packet ) ;
proto_item_set_generated ( response_in ) ;
}
2020-03-07 14:38:13 +00:00
}
2020-07-12 18:38:26 +00:00
if ( ! found )
{
* need_reassembly = request_reassembly ;
return offset - offset_start ;
}
2020-03-07 14:38:13 +00:00
}
if ( tvb_reported_length_remaining ( tvb , offset ) < cmd_data - > response_length )
{
* need_reassembly = TRUE ;
return 0 ;
}
rsp_data = proto_tree_add_bytes_format ( tree , hf_mpsse_response , tvb , offset , cmd_data - > response_length , NULL , " %s " , cmd_str ) ;
rsp_tree = proto_item_add_subtree ( rsp_data , ett_mpsse_response_data ) ;
2020-07-25 11:41:36 +00:00
command_in = proto_tree_add_uint_format ( rsp_tree , hf_mpsse_command_in , NULL , 0 , 0 , cmd_data - > command_in_packet ,
2020-07-11 14:54:13 +00:00
" Command 0x%02x in: % " G_GUINT32_FORMAT , cmd_data - > cmd , cmd_data - > command_in_packet ) ;
2020-03-07 14:38:13 +00:00
proto_item_set_generated ( command_in ) ;
offset + = dissect_response_data ( tvb , pinfo , rsp_tree , offset , cmd_data ) ;
2020-01-27 17:55:15 +00:00
return offset - offset_start ;
}
2020-01-10 17:57:27 +00:00
static gint
2020-02-22 10:18:03 +00:00
dissect_ftdi_mpsse ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , void * data )
2020-01-10 17:57:27 +00:00
{
2020-07-05 14:34:15 +00:00
gboolean need_reassembly = FALSE ;
2020-02-22 10:18:03 +00:00
ftdi_mpsse_info_t * mpsse_info = ( ftdi_mpsse_info_t * ) data ;
gint offset = 0 ;
proto_item * main_item ;
proto_tree * main_tree ;
if ( ! mpsse_info )
{
return offset ;
}
2020-01-10 17:57:27 +00:00
main_item = proto_tree_add_item ( tree , proto_ftdi_mpsse , tvb , offset , - 1 , ENC_NA ) ;
main_tree = proto_item_add_subtree ( main_item , ett_ftdi_mpsse ) ;
col_set_str ( pinfo - > cinfo , COL_PROTOCOL , " FTDI MPSSE " ) ;
2020-01-27 17:55:15 +00:00
if ( pinfo - > p2p_dir = = P2P_DIR_SENT )
{
2020-07-12 13:53:22 +00:00
command_data_t * iter = get_recorded_command_data ( tx_command_info , pinfo , mpsse_info ) ;
2020-03-07 14:38:13 +00:00
2020-07-12 13:53:22 +00:00
if ( ! pinfo - > fd - > visited )
2020-03-07 14:38:13 +00:00
{
/* Not visited yet - advance iterator to last element */
while ( iter & & iter - > next )
{
2020-07-24 18:44:47 +00:00
DISSECTOR_ASSERT ( ! iter - > preliminary ) ;
2020-03-07 14:38:13 +00:00
iter = iter - > next ;
}
}
while ( ( tvb_reported_length_remaining ( tvb , offset ) > 0 ) & & ( ! need_reassembly ) )
{
offset + = dissect_command ( tvb , pinfo , main_tree , offset , & need_reassembly , mpsse_info , & iter ) ;
}
}
else if ( pinfo - > p2p_dir = = P2P_DIR_RECV )
{
2020-07-12 13:53:22 +00:00
command_data_t * head = get_recorded_command_data ( rx_command_info , pinfo , mpsse_info ) ;
2020-03-07 14:38:13 +00:00
command_data_t * iter = head ;
if ( ! pinfo - > fd - > visited )
{
while ( iter & & iter - > is_response_set )
{
iter = iter - > next ;
}
if ( iter ! = head )
{
2020-07-12 13:53:22 +00:00
insert_command_data_pointer ( rx_command_info , pinfo , mpsse_info , iter ) ;
2020-03-07 14:38:13 +00:00
}
}
2020-01-27 17:55:15 +00:00
while ( ( tvb_reported_length_remaining ( tvb , offset ) > 0 ) & & ( ! need_reassembly ) )
{
2020-03-07 14:38:13 +00:00
if ( ! iter )
{
proto_tree_add_expert ( main_tree , pinfo , & ei_response_without_command , tvb , offset , - 1 ) ;
offset + = tvb_reported_length_remaining ( tvb , offset ) ;
}
else
{
offset + = dissect_response ( tvb , pinfo , main_tree , offset , & need_reassembly , iter ) ;
iter = iter - > next ;
}
2020-01-27 17:55:15 +00:00
}
2020-07-05 14:34:15 +00:00
}
2020-01-27 17:55:15 +00:00
2020-07-05 14:34:15 +00:00
if ( need_reassembly )
{
if ( pinfo - > can_desegment )
{
pinfo - > desegment_offset = offset ;
pinfo - > desegment_len = DESEGMENT_ONE_MORE_SEGMENT ;
}
else
2020-01-27 17:55:15 +00:00
{
2020-06-29 18:26:52 +00:00
proto_tree_add_expert ( main_tree , pinfo , & ei_reassembly_unavailable , tvb , offset , - 1 ) ;
2020-01-27 17:55:15 +00:00
}
2020-07-05 14:34:15 +00:00
offset + = tvb_reported_length_remaining ( tvb , offset ) ;
2020-01-27 17:55:15 +00:00
}
if ( tvb_reported_length_remaining ( tvb , offset ) > 0 )
{
proto_tree_add_expert ( main_tree , pinfo , & ei_undecoded , tvb , offset , - 1 ) ;
}
2020-01-10 17:57:27 +00:00
return tvb_reported_length ( tvb ) ;
}
void
proto_register_ftdi_mpsse ( void )
{
expert_module_t * expert_module ;
2020-01-27 17:55:15 +00:00
static hf_register_info hf [ ] = {
{ & hf_mpsse_command ,
{ " Command " , " ftdi-mpsse.command " ,
2020-02-23 08:27:06 +00:00
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
2020-01-27 17:55:15 +00:00
NULL , HFILL }
} ,
{ & hf_mpsse_command_b0 ,
{ " -ve CLK on write " , " ftdi-mpsse.command.b0 " ,
FT_BOOLEAN , 8 , NULL , ( 1 < < 0 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b1 ,
{ " Mode " , " ftdi-mpsse.command.b1 " ,
FT_UINT8 , BASE_DEC , VALS ( data_shifting_command_b1_vals ) , ( 1 < < 1 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b2 ,
{ " -ve CLK on read " , " ftdi-mpsse.command.b2 " ,
FT_BOOLEAN , 8 , NULL , ( 1 < < 2 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b3 ,
{ " Endianness " , " ftdi-mpsse.command.b3 " ,
FT_UINT8 , BASE_DEC , VALS ( data_shifting_command_b3_vals ) , ( 1 < < 3 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b4 ,
{ " Do write TDI " , " ftdi-mpsse.command.b4 " ,
FT_BOOLEAN , 8 , NULL , ( 1 < < 4 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b5 ,
{ " Do read TDO " , " ftdi-mpsse.command.b5 " ,
FT_BOOLEAN , 8 , NULL , ( 1 < < 5 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b6 ,
{ " Do write TMS " , " ftdi-mpsse.command.b6 " ,
FT_BOOLEAN , 8 , NULL , ( 1 < < 6 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_b7 ,
{ " Type " , " ftdi-mpsse.command.b7 " ,
FT_UINT8 , BASE_DEC , VALS ( command_b7_vals ) , ( 1 < < 7 ) ,
NULL , HFILL }
} ,
2020-03-07 14:38:13 +00:00
{ & hf_mpsse_command_with_parameters ,
{ " Command with parameters " , " ftdi-mpsse.command_with_parameters " ,
FT_BYTES , BASE_NONE , NULL , 0x0 ,
" Command including optional parameter bytes " , HFILL }
} ,
2020-06-27 13:26:56 +00:00
{ & hf_mpsse_bad_command_error ,
{ " Error code " , " ftdi-mpsse.bad_command.error " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
" Bad Command error code 0xFA " , HFILL }
} ,
{ & hf_mpsse_bad_command_code ,
{ " Received invalid command " , " ftdi-mpsse.bad_command.command " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
" Byte which caused the bad command " , HFILL }
} ,
2020-03-07 14:38:13 +00:00
{ & hf_mpsse_response ,
{ " Command response data " , " ftdi-mpsse.response " ,
FT_BYTES , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_command_in ,
{ " Command in " , " ftdi-mpsse.command.in " ,
FT_FRAMENUM , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_response_in ,
{ " Response in " , " ftdi-mpsse.response.in " ,
FT_FRAMENUM , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
2020-01-27 17:55:15 +00:00
{ & hf_mpsse_length_uint8 ,
{ " Length " , " ftdi-mpsse.length " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_length_uint16 ,
{ " Length " , " ftdi-mpsse.length " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_bytes_out ,
2020-07-18 16:45:22 +00:00
{ " Bytes out " , " ftdi-mpsse.bytes_out " ,
FT_BYTES , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_bytes_in ,
{ " Bytes in " , " ftdi-mpsse.bytes_in " ,
2020-01-27 17:55:15 +00:00
FT_BYTES , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_bits_out ,
2020-07-18 16:45:22 +00:00
{ " Bits out " , " ftdi-mpsse.bits_out " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_bits_in ,
{ " Bits in " , " ftdi-mpsse.bits_in " ,
2020-01-27 17:55:15 +00:00
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_value ,
{ " Value " , " ftdi-mpsse.value " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b0 ,
{ " Bit 0 " , " ftdi-mpsse.value.b0 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 0 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b1 ,
{ " Bit 1 " , " ftdi-mpsse.value.b1 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 1 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b2 ,
{ " Bit 2 " , " ftdi-mpsse.value.b2 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 2 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b3 ,
{ " Bit 3 " , " ftdi-mpsse.value.b3 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 3 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b4 ,
{ " Bit 4 " , " ftdi-mpsse.value.b4 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 4 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b5 ,
{ " Bit 5 " , " ftdi-mpsse.value.b5 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 5 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b6 ,
{ " Bit 6 " , " ftdi-mpsse.value.b6 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 6 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_value_b7 ,
{ " Bit 7 " , " ftdi-mpsse.value.b7 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 7 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction ,
{ " Direction " , " ftdi-mpsse.direction " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b0 ,
{ " Bit 0 " , " ftdi-mpsse.direction.b0 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 0 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b1 ,
{ " Bit 1 " , " ftdi-mpsse.direction.b1 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 1 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b2 ,
{ " Bit 2 " , " ftdi-mpsse.direction.b2 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 2 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b3 ,
{ " Bit 3 " , " ftdi-mpsse.direction.b3 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 3 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b4 ,
{ " Bit 4 " , " ftdi-mpsse.direction.b4 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 4 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b5 ,
{ " Bit 5 " , " ftdi-mpsse.direction.b5 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 5 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b6 ,
{ " Bit 6 " , " ftdi-mpsse.direction.b6 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 6 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_direction_b7 ,
{ " Bit 7 " , " ftdi-mpsse.direction.b7 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 7 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_cpumode_address_short ,
{ " Address " , " ftdi-mpsse.cpumode_address " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
" CPUMode Short Address " , HFILL }
} ,
{ & hf_mpsse_cpumode_address_extended ,
{ " Address " , " ftdi-mpsse.cpumode_address " ,
FT_UINT16 , BASE_HEX , NULL , 0x0 ,
" CPUMode Extended Address " , HFILL }
} ,
{ & hf_mpsse_cpumode_data ,
{ " Data " , " ftdi-mpsse.cpumode_data " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
2020-02-22 20:17:40 +00:00
{ & hf_mpsse_clk_divisor ,
{ " Divisor " , " ftdi-mpsse.clk_divisor " ,
FT_UINT16 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
2020-08-15 15:02:04 +00:00
{ & hf_mpsse_open_drain_enable_low ,
{ " Low Byte " , " ftdi-mpsse.open_drain_enable_low " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b0 ,
{ " Bit 0 " , " ftdi-mpsse.open_drain_enable_low.b0 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 0 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b1 ,
{ " Bit 1 " , " ftdi-mpsse.open_drain_enable_low.b1 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 1 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b2 ,
{ " Bit 2 " , " ftdi-mpsse.open_drain_enable_low.b2 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 2 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b3 ,
{ " Bit 3 " , " ftdi-mpsse.open_drain_enable_low.b3 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 3 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b4 ,
{ " Bit 4 " , " ftdi-mpsse.open_drain_enable_low.b4 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 4 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b5 ,
{ " Bit 5 " , " ftdi-mpsse.open_drain_enable_low.b5 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 5 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b6 ,
{ " Bit 6 " , " ftdi-mpsse.open_drain_enable_low.b6 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 6 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_low_b7 ,
{ " Bit 7 " , " ftdi-mpsse.open_drain_enable_low.b7 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 7 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high ,
{ " High Byte " , " ftdi-mpsse.open_drain_enable_high " ,
FT_UINT8 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b0 ,
{ " Bit 0 " , " ftdi-mpsse.open_drain_enable_high.b0 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 0 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b1 ,
{ " Bit 1 " , " ftdi-mpsse.open_drain_enable_high.b1 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 1 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b2 ,
{ " Bit 2 " , " ftdi-mpsse.open_drain_enable_high.b2 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 2 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b3 ,
{ " Bit 3 " , " ftdi-mpsse.open_drain_enable_high.b3 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 3 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b4 ,
{ " Bit 4 " , " ftdi-mpsse.open_drain_enable_high.b4 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 4 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b5 ,
{ " Bit 5 " , " ftdi-mpsse.open_drain_enable_high.b5 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 5 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b6 ,
{ " Bit 6 " , " ftdi-mpsse.open_drain_enable_high.b6 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 6 ) ,
NULL , HFILL }
} ,
{ & hf_mpsse_open_drain_enable_high_b7 ,
{ " Bit 7 " , " ftdi-mpsse.open_drain_enable_high.b7 " ,
FT_UINT8 , BASE_DEC , NULL , ( 1 < < 7 ) ,
NULL , HFILL }
} ,
2020-01-27 17:55:15 +00:00
} ;
2020-01-10 17:57:27 +00:00
static ei_register_info ei [ ] = {
{ & ei_undecoded , { " ftdi-mpsse.undecoded " , PI_UNDECODED , PI_WARN , " Not dissected yet (report to wireshark.org) " , EXPFILL } } ,
2020-03-07 14:38:13 +00:00
{ & ei_response_without_command , { " ftdi-mpsse.response_without_command " , PI_PROTOCOL , PI_ERROR , " Unable to associate response with command (response without command?) " , EXPFILL } } ,
{ & ei_skipped_response_data , { " ftdi-mpsse.skipped_response_data " , PI_PROTOCOL , PI_WARN , " Skipped response data while looking for Bad Command response " , EXPFILL } } ,
2020-06-29 18:26:52 +00:00
{ & ei_reassembly_unavailable , { " ftdi-mpsse.reassembly_unavailable " , PI_UNDECODED , PI_ERROR , " Data source dissector does not support reassembly. Dissection will get out of sync. " , EXPFILL } } ,
2020-01-10 17:57:27 +00:00
} ;
static gint * ett [ ] = {
& ett_ftdi_mpsse ,
2020-01-27 17:55:15 +00:00
& ett_mpsse_command ,
2020-02-29 13:41:30 +00:00
& ett_mpsse_command_with_parameters ,
2020-03-07 14:38:13 +00:00
& ett_mpsse_response_data ,
2020-01-27 17:55:15 +00:00
& ett_mpsse_value ,
& ett_mpsse_direction ,
2020-08-15 15:02:04 +00:00
& ett_mpsse_open_drain_enable ,
2020-07-25 11:41:36 +00:00
& ett_mpsse_skipped_response_data ,
2020-01-10 17:57:27 +00:00
} ;
2020-07-12 13:53:22 +00:00
rx_command_info = wmem_tree_new_autoreset ( wmem_epan_scope ( ) , wmem_file_scope ( ) ) ;
tx_command_info = wmem_tree_new_autoreset ( wmem_epan_scope ( ) , wmem_file_scope ( ) ) ;
2020-03-07 14:38:13 +00:00
2020-01-10 17:57:27 +00:00
proto_ftdi_mpsse = proto_register_protocol ( " FTDI Multi-Protocol Synchronous Serial Engine " , " FTDI MPSSE " , " ftdi-mpsse " ) ;
2020-01-27 17:55:15 +00:00
proto_register_field_array ( proto_ftdi_mpsse , hf , array_length ( hf ) ) ;
2020-01-10 17:57:27 +00:00
proto_register_subtree_array ( ett , array_length ( ett ) ) ;
ftdi_mpsse_handle = register_dissector ( " ftdi-mpsse " , dissect_ftdi_mpsse , proto_ftdi_mpsse ) ;
expert_module = expert_register_protocol ( proto_ftdi_mpsse ) ;
expert_register_field_array ( expert_module , ei , array_length ( ei ) ) ;
}
/*
* Editor modelines - https : //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 :
*/