/* packet-ecmp.c
 *
 * Copyright 2014, James Lynch <lynch007@gmail.com>, Control Techniques
 * Copyright 2015, Luke Orehawa <lukeorehawa@gmail.com>, Control Techniques
 *
 * Revisions:
 * - James Lynch 2014-07-22
 *   - Initial plugin development
 * - Luke Orehawa 2015-11-26
 *	 - Removed commands not yet in released specification
 *	 - All commands implemented are as V0.26 of ECMP Specification
 *	 - Modifications of code to meet Wireshark coding style and current APIs
 *
 * 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>
#include <epan/tfs.h>
#include <epan/unit_strings.h>
#include "packet-mbtcp.h"

#define ECMP_TCP_PORT   6160

void proto_reg_handoff_ecmp(void);
void proto_register_ecmp (void);

static dissector_handle_t ecmp_tcp_handle, ecmp_udp_handle;

/* Wireshark ID of the ECMP protocol */
static int proto_ecmp;

/* Used to set Modbus protocol data */
static int proto_modbus;

/* These are the handles of our subdissectors */
static dissector_handle_t modbus_handle;

/*smallest size of a packet, number of bytes*/
static const int ecmp_min_packet_size  = 6;

/* ECMP request codes  */
#define ECMP_COMMAND_IDENTIFY		0x00
#define ECMP_COMMAND_INFO		0x01
#define ECMP_COMMAND_INTERROGATE	0x02
#define ECMP_COMMAND_READ		0x10
#define ECMP_COMMAND_READWITHTYPE	0x11
#define ECMP_COMMAND_WRITE		0x12
#define ECMP_COMMAND_OBJECTINFO		0x13
#define ECMP_COMMAND_GETNEXTOBJECTS	0x14
#define ECMP_COMMAND_FILEOPEN		0x20
#define ECMP_COMMAND_FILEREAD		0x21
#define ECMP_COMMAND_FILEWRITE		0x22
#define ECMP_COMMAND_FILECLOSE		0x23
#define ECMP_COMMAND_FILEINFO		0x24
#define ECMP_COMMAND_FILEDELETE		0x25
#define ECMP_COMMAND_FILESTATE		0x26
#define ECMP_COMMAND_FILEPOS		0x27
#define ECMP_COMMAND_FILELIST		0x28
#define ECMP_COMMAND_FILEEXISTS		0x2a
#define ECMP_COMMAND_CYCLICLINK		0x31
#define ECMP_COMMAND_PROGRAMCONTROL	0x60
#define ECMP_COMMAND_PROGRAMSTATUS	0x61
#define ECMP_COMMAND_CYCLICFRAME	0x70
#define ECMP_COMMAND_TUNNELFRAME	0x73
#define ECMP_COMMAND_MODBUSPDU		0x74

/* cyclic display formats */
static const uint8_t cyclic_display_byte_format;
static const uint8_t cyclic_display_word_format = 1;
static const uint8_t cyclic_display_long_format = 2;

/* Addressing scheme Structure */
static const value_string address_scheme [] = {
	{ 0, "No Route" },
	{ 1, "Intercept" },
	{ 2, "Default Route" },
	{ 3, "Diagnostics" },
	{ 4, "Named" },
	{ 0, NULL }
	/*other commands to be added */
};

/* Address Structure */
static const value_string diagnostic [] = {
	{ 0, "Status" },
	{ 1, "Alarm" },
	{ 2, "Network" },
	{ 3, "Application" },
	{ 0, NULL }
	/*other commands to be added*/
};

/* Command Structure*/
static const value_string command_vals [] = {
	{ 0x00, "Identify"},
	{ 0x01, "Info"},
	{ 0x02, "Interrogate"},
	{ 0x10, "Read"},
	{ 0x11, "ReadWithType"},
	{ 0x12, "Write"},
	{ 0x13, "ObjectInfo"},
	{ 0x14, "GetNextObjects"},
	{ 0x20, "FileOpen"},
	{ 0x21, "FileRead"},
	{ 0x22, "FileWrite"},
	{ 0x23, "FileClose"},
	{ 0x24, "FileInfo"},
	{ 0x25, "FileDelete"},
	{ 0x26, "FileState"},
	{ 0x27, "FilePos"},
	{ 0x28, "FileList"},
	{ 0x2A, "FileExists"},
	{ 0x31, "CyclicSetup"},
	{ 0x60, "ProgramControl"},
	{ 0x61, "ProgramStatus"},
	{ 0x70, "CyclicFrame"},
	{ 0x73, "TunnelFrame"},
	{ 0x74, "ModbusPDU"},
	{ 0, NULL }
	/*other commands to be added*/
};

/* Option Code structure*/
static const value_string option_code [] = {
	{ 0, "End of Options"},
	{ 1, "Dummy" },
	{ 2, "Process At" },
	{ 3, "Route to Custom Target"},
	{ 0, NULL }
	/* other - "Unknown" */
};

/* Attribute type Structure */
static const value_string attribute [] = {
	{ 0, "Manufacturer Name" },
	{ 1, "Product Family" },
	{ 2, "Product Model" },
	{ 3, "Serial Number" },
	{ 4, "Order Number" },
	{ 5, "Date Code" },
	{ 6, "Device Name" },
	{ 7, "Version Summary" },
	{ 8, "Colour Codes" },
	{ 0, NULL }
	/* other - Unknown*/
};

/* Status type Structure */
static const value_string status [] = {
	{ 0, "OK (no errors detected in request)" },
	{ 1, "OK, chunks follow" },
	{ 2, "Processing Request" },
	{ -1, "Error - Slave not ready" },
	{ -2, "Error - Request Too Long" },
	{ -3, "Error - Chunking Error" },
	{ 0, NULL }
	/* other - Unknown*/
};

/* Category (device) structure*/
static const value_string category [] = {
	{ 0, "Drive" },
	{ 1, "Option Module" },
	{ 0, NULL }
};

/* Cyclic data alignment */
static const value_string cyclic_align [] = {
	{ 0, "8bit" },
	{ 1, "8bit" },   /* TODO: is this correct? */
	{ 2, "16bit" },
	{ 4, "32bit" },
	{ 8, "64bit" },
	{ 0, NULL }
};

/* Cyclic data scheme */
static const value_string cyclic_scheme [] = {
	{ 0, "Standard" },
	{ 1, "Synchronised" },
	{ 0, NULL }
};

/* Parameter addressing scheme */
static const value_string parameter_address_scheme [] = {
	{ 0, "Standard" },
	{ 1, "Slot Specific" },
	{ 3, "Variable" },
	{ 0, NULL }
};

#if 0
static const value_string route_address_scheme [] = {
	{ 1, "Intercept" },
	{ 2, "DefaultRoute" },
	{ 0, NULL }
};
#endif

/* Parameter access status */
static const value_string parameter_access_status [] = {
	{ 0, "OK" },
	{ 1, "OK - Converted"},
	{ 2, "OK - Clamped"},
	{ -1, "ERROR - Address Type"},
	{ -2, "ERROR - Timeout"},
	{ -3, "ERROR - Access Denied"},
	{ -4, "ERROR - Does not exist"},
	{ -5, "ERROR - Data Type"},
	{ -6, "ERROR - Failed Read"},
	{ -7, "ERROR - Failed Write"},
	{ -8, "ERROR - Not Readable"},
	{ -9, "ERROR - Not Writeable"},
	{ -10, "ERROR - Over Range"},
	{ -11, "ERROR - Request Invalid"},
	{ -12, "ERROR - Response Too Big"},
	{ -13, "ERROR - Decimal Place"},
	{ 0, NULL}
};

/* Parameter data types */
static const value_string parameter_data_types [] = {
	{ 0, "Boolean"},
	{ 1, "INT8"},
	{ 2, "UINT8"},
	{ 3, "INT16"},
	{ 4, "UINT16"},
	{ 5, "INT32"},
	{ 6, "UINT32"},
	{ 7, "INT64"},
	{ 8, "UINT64"},
	{ 9, "INT128"},
	{ 10, "UINT128"},
	{ 20, "SINGLE"},
	{ 21, "DOUBLE"},
	{ 30, "String ID"},
	{ 31, "String"},
	{ 0, NULL}
};

/* Info types */
static const value_string info_type [] = {
	{ 0, "No Information"},
	{ 1, "Lowest Numbered Parameter in Menu"},
	{ 2, "Highest Numbered Parameter in Menu"},
	{ 3, "Parameter Format"},
	{ 4, "Minimum Value allowed for Parameter"},
	{ 5, "Maximum Value allowed for Parameter"},
	{ 6, "Object Unit Information"},
	{ 7, "Data Type of Parameter"},
	{ 0, NULL }
};

/* Display formats */
static const value_string display_format [] = {
	{ 0, "Standard format"},
	{ 1, "Date format (xx,yy,zz)"},
	{ 2, "Time with seconds format (xx.yy.zz)"},
	{ 3, "Character format"},
	{ 4, "Binary format"},
	{ 5, "IP address format (www.xxx.yyy.zzz)"},
	{ 6, "MAC address format (AA:BB:CC:DD:EE:FF)"},
	{ 7, "Version number (ww.xx.yy.zz)"},
	{ 8, "Slot menu parameter format (x,yy,zzz)"},
	{ 0, NULL}
};

/* Format units */
static const value_string format_units [] = {
	{ 0, "No units"},
	{ 1, "Custom units"},
	{ 2, "Millimetres (mm)"},
	{ 3, "Metres (m)"},
	{ 4, "User units (UU)"},
	{ 5, "Revolutions (revs)"},
	{ 6, "Degrees (')"},
/*	{ 7, ""}, */
	{ 8, "General position unit"},
	{ 9, "Millimetres per second (mm/s)"},
	{ 10, "User units per millisecond (UU/ms)"},
	{ 11, "Revolutions per minute (Rpm)"},
	{ 12, "Hertz (Hz)"},
	{ 13, "Kilohertz (kHz)"},
	{ 14, "Megahertz (MHz)"},
	{ 15, "General speed unit (Hz, rpm, mm/s)"},
	{ 16, "Closed loop speed unit (rpm, mm/s)"},
	{ 17, "Seconds per one thousand millimetres per seconds (s/m/s)"},
	{ 18, "User units per millimetre per second (UU/mm/s)"},
	{ 19, "Seconds per one thousand revolution per minute (s/1000rpm)"},
	{ 20, "Seconds per one hundred hertz (s/100Hz)"},
	{ 21, "General acceleration unit"},
	{ 22, "Closed loop acceleration unit"},
	{ 23, "Seconds squared per one thousand millimetres per second (s^2/1000ms/s)"},
	{ 24, "Seconds squared per user units per millisecond (s^2/UU/ms"},
	{ 25, "Seconds squared per one thousand revolutions per minute (s^2/1000rpm)"},
	{ 26, "Seconds squared per one hundred hertz (s^2/100Hz)"},
	{ 27, "General jerk unit"},
	{ 28, "Closed loop jerk unit"},
	{ 29, "Messages per second (Msg/s)"},
	{ 30, "Hours (Hours)"},
	{ 31, "Minutes (Mins)"},
	{ 32, "Seconds (s)"},
	{ 33, "Milliseconds (ms)"},
	{ 34, "Microseconds (us)"},
	{ 35, "Nanoseconds (ns)"},
	{ 36, "Volts (V)"},
	{ 37, "Amperes (A)"},
	{ 38, "Ohms (Ohms)"},
	{ 39, "Millihenrys (mH)"},
	{ 40, "Kilowatts (kW)"},
	{ 41, "Kilo-Volt-Amps-Reactive (kVAr)"},
	{ 42, "Megawatt hours (MWh)"},
	{ 43, "Kilowatt hours (kWh)"},
	{ 44, "Degrees Celsius ('C)"},
	{ 45, "Reciprocal of degrees Celsius (/'C)"},
	{ 46, "Kilogram-metres squared (kgm^2)"},
	{ 47, "Newton metres (Nm)"},
	{ 48, "Newton metres per ampere (Nm/A)"},
	{ 49, "open-circuit volts per 1000rpm (V/1000rpm)"},
	{ 50, "Bits (Bits)"},
	{ 51, "Bytes (Bytes)"},
	{ 52, "Kilobytes (kB)"},
	{ 53, "Megabytes (MB)"},
	{ 54, "Bits per second (Bit/s)"},
	{ 55, "Baud (Baud)"},
	{ 56, "Kilobaud (kBaud)"},
	{ 57, "Megabaud (MBaud)"},
	{ 58, "Poles (Poles)"},
	{ 59, "Percent (%)"},
	{ 60, "Volts per millisecond (V/ms)"},
	{ 0, NULL}
};

/* File status */
static const value_string file_status [] = {
	{ 0, "Processing"},
	{ 1, "OK"},
	{ 2, "OK - More Data"},
	{ 3, "OK - EOF"},
	{ -1, "ERROR - File Handle"},
	{ -2, "ERROR - Blocked"},
	{ -3, "ERROR - Blocking Mode"},
	{ -4, "ERROR - Not in Progress"},
	{ -5, "ERROR - Not Found"},
	{ -6, "ERROR - Read Only"},
	{ -7, "ERROR - Write Only"},
	{ -8, "ERROR - Not Created"},
	{ -9, "ERROR - No Data"},
	{ -10, "ERROR - Wrong Mode"},
	{ -11, "ERROR - Too Big"},
	{ -12, "ERROR - Protected"},
	{ -13, "ERROR - CRC"},
	{ -14, "ERROR - Length"},
	{ -15, "ERROR - Too Many Open"},
	{ -16, "ERROR - Invalid File"},
	{ -17, "ERROR - Invalid Request"},
	{ -18, "ERROR - No Append"},
	{ -19, "ERROR - Invalid State"},
	{ -20, "ERROR - Incompatible"},
	{ -21, "ERROR - Uninitialized"},
	{ 0, NULL}
};

/* File status mode */
static const value_string file_status_mode [] = {
	{ 0, "Information"},
	{ 1, "Read"},
	{ 2, "Create"},
	{ 3, "Append"},
	{ 4, "New Directory"},
	{ 0, NULL}
};

/* File attributes */
static const value_string file_attributes [] = {
	{ 0, "File Length"},
	{ 1, "File Integrity"},
	{ 2, "Calculate CRC32"},
	{ 3, "File Attributes"},
	{ 4, "Creation Date and Time"},
	{ 5, "Modification Date and Time"},
	{ 0, NULL}
};

/* File reference position */
static const value_string file_ref_point [] = {
	{ 0, "SoF - Start of file"},
	{ 1, "EoF - End of file"},
	{ 2, "Current - Use current file pointer"},
	{ 0, NULL}
};

static const value_string cyclic_setup_mode [] = {
	{ 0, "Create"},
	{ 1, "Edit"},
	{ 2, "Finalise"},
	{ 3, "Delete"},
	{ 4, "Exist"},
	{ 5, "List"},
	{ 6, "Info"},
	{ 10, "Set"},
	{ 11, "Get"},
	{ 12, "Get mappings"},
	{ 0, NULL}
};

static const value_string cyclic_attributes [] = {
	{ 0, "State"},
	{ 1, "Rx/Tx"},
	{ 2, "Synchronised"},
	{ 3, "MEC Offset"},
	{ 4, "Sample Period"},
	{ 5, "MEC Delay"},
	{ 6, "Data Change"},
	{ 7, "Rx Timeout Handler"},
	{ 8, "Rx Data Late Handler"},
	{ 9, "Transport Address"},
	{ 10, "Max Mappings"},
	{ 11, "Number Of Mappings"},
	{ 12, "Mapping Item"},
	{ 13, "Saveable"},
	{ 128, "Max RX Links"},
	{ 129, "Max TX Links"},
	{ 130, "Max Mappings Per Link"},
	{ 131, "Max Sync RX Links"},
	{ 132, "Max Sync TX Links"},
	{ 133, "Max Mappings Per Sync Link"},
	{ 134, "'Process At' Queue Depth"},
	{ 135, "MEC Period"},
	{ 0, NULL}
};

static const value_string cyclic_setup_link_dir [] = {
	{ 0, "Rx"},
	{ 1, "Tx"},
	{ 0, NULL}
};

static const value_string cyclic_setup_link_exists [] = {
	{ 0, "Does not exist"},
	{ 1, "Exists"},
	{ 0, NULL}
};

static const value_string additional_scheme_vals [] = {
	{ 0, "None"},
	{ 1, "Generic"},
	{ 0, NULL}
};

/* Program Control - command codes  */
static const value_string command_code_list [] = {
	{ 0, "Stop"},
	{ 1, "Start"},
	{ 2, "Reset"},
	{ 0, NULL }
	/*other commands to be added*/
};

/* Program Control - sub command codes  */
static const value_string sub_command_code_list [] = {
	{ 0, "Default"},
	{ 1, "User1"},
	{ 2, "User2"},
	{ 0, NULL }
	/*other sub commands to be added*/
};

/* Program Control - status codes  */
static const value_string status_list [] = {
	{ 0,   "OK"},
	{ -1,  "Error"},
	{ 0,   NULL }
	/*other status to be added*/
};

	/* Program Status - running state codes  */
static const value_string running_state_list [] = {
	{ 0,   "Stopped"},
	{ 1,   "Running"},
	{ 2,   "Exception"},
	{ 3,   "None (no program found in device)"},
	{ 0,   NULL }
	/*other status to be added*/
};

	/* Interrogate - command support states  */
static const value_string Interrogate_support_state [] = {
	{ 0,  "Not Supported"},
	{ 1,  "Supported"},
	{ 0,   NULL }
	/*other status to be added*/
};

	/* Interrogate - command / option states  */
static const value_string Interrogate_command_option_state [] = {
	{ 0,  "Command"},
	{ 1,  "Option"},
	{ 0,   NULL }
	/*other status to be added*/
};

static const value_string item_type_vals[] = {
	{ 0,  "File"},
	{ 1,  "Directory"},
	{ 0,   NULL }
};


/* The following hf_* variables are used to hold the Wireshark IDs of
* our header fields; they are filled out when we call
* proto_register_field_array() in proto_register_ecmp()
*/
static int hf_ecmp_command;
static int hf_ecmp_destination_address;
static int hf_ecmp_source_address;
static int hf_ecmp_diagnostic;
static int hf_ecmp_type_rr;
static int hf_ecmp_chunking;
static int hf_ecmp_max_response_size;
static int hf_ecmp_category;
static int hf_ecmp_option;
static int hf_ecmp_attribute;
static int hf_ecmp_no_of_attributes;
static int hf_ecmp_chunk_id;
static int hf_ecmp_transaction_id;
static int hf_ecmp_status;
static int hf_ecmp_drive_type;
static int hf_ecmp_drive_derivative;
static int hf_ecmp_drive_factory_fit_category_id;
static int hf_ecmp_category_id;
static int hf_ecmp_attribute_string;
static int hf_ecmp_file_name;
static int hf_ecmp_info_command;
static int hf_ecmp_directory;
static int hf_ecmp_names_scheme;
static int hf_ecmp_variable_name;
static int hf_ecmp_unit_id_string;
static int hf_ecmp_ecmp_string;
static int hf_ecmp_process_time;
static int hf_ecmp_cyclic_frame_time;
static int hf_ecmp_grandmaster;
static int hf_ecmp_data;
static int hf_ecmp_response_data;

static int hf_ecmp_cyclic_link_num;
static int hf_ecmp_cyclic_align;
static int hf_ecmp_cyclic_scheme;
static int hf_ecmp_cyclic_link_number_display;

/* Cyclic setup */
static int hf_ecmp_cyclic_setup_mode;
static int hf_ecmp_cyclic_setup_linkno;
static int hf_ecmp_cyclic_setup_dir;
static int hf_ecmp_cyclic_setup_attrib_count;
static int hf_ecmp_cyclic_setup_rsp_status;
static int hf_ecmp_cyclic_setup_rsp_err_idx;
static int hf_ecmp_cyclic_setup_attrib;
static int hf_ecmp_cyclic_setup_link_exists;
static int hf_ecmp_cyclic_link_req_resp;

/*for info command */
static int hf_ecmp_buffer_size;
static int hf_ecmp_max_response;
static int hf_ecmp_max_handle;
static int hf_ecmp_info_address;

/*for parameter access commands*/
static int hf_ecmp_parameter_address;
static int hf_ecmp_number_of_parameter_definitions;
static int hf_ecmp_number_of_parameter_responses;
static int hf_ecmp_parameter_status;
static int hf_ecmp_data_type;
static int hf_ecmp_info_type;

/* for file access commands */
static int hf_ecmp_file_status;
static int hf_ecmp_file_handle;
static int hf_ecmp_file_attributes;
static int hf_ecmp_file_ref_point;


/* for tunnel frame command */
#define TUNNEL_START_FLAG		0x01
#define TUNNEL_END_FLAG			0x02
#define TUNNEL_CHECK_OUTPUT_FLAG	0x04

static int hf_ecmp_tunnel_control;
static int hf_ecmp_tunnel_start_flag;
static int hf_ecmp_tunnel_end_flag;
static int hf_ecmp_tunnel_check_output_flag;
static int hf_ecmp_tunnel_size;

/* Generated from convert_proto_tree_add_text.pl */
static int hf_ecmp_physical_address;
static int hf_ecmp_logical_address;
static int hf_ecmp_primary_colour;
static int hf_ecmp_secondary_colour;
static int hf_ecmp_number_of_subsequent_object_requests;
static int hf_ecmp_number_of_decimal_places;
static int hf_ecmp_no_information_available;
static int hf_ecmp_param_format_bit_default_unipolar;
static int hf_ecmp_param_format_write_allowed;
static int hf_ecmp_param_format_read_not_allowed;
static int hf_ecmp_param_format_protected_from_destinations;
static int hf_ecmp_param_format_parameter_not_visible;
static int hf_ecmp_param_format_not_clonable;
static int hf_ecmp_param_format_voltage_or_current_rating_dependent;
static int hf_ecmp_param_format_parameter_has_no_default;
static int hf_ecmp_param_format_number_of_decimal_places;
static int hf_ecmp_param_format_variable_maximum_and_minimum;
static int hf_ecmp_param_format_string_parameter;
static int hf_ecmp_param_format_destination_set_up_parameter;
static int hf_ecmp_param_format_filtered_when_displayed;
static int hf_ecmp_param_format_pseudo_read_only;
static int hf_ecmp_param_format_display_format;
static int hf_ecmp_param_format_floating_point_value;
static int hf_ecmp_param_format_units;
static int hf_ecmp_string_id;
static int hf_ecmp_address_scheme_menu;
static int hf_ecmp_address_scheme_parameter;
static int hf_ecmp_address_scheme_slot;
static int hf_ecmp_address_scheme_null_byte_size;
static int hf_ecmp_display_unit_id;
static int hf_ecmp_data_boolean;
static int hf_ecmp_data_int8;
static int hf_ecmp_data_uint8;
static int hf_ecmp_data_int16;
static int hf_ecmp_data_uint16;
static int hf_ecmp_data_int32;
static int hf_ecmp_data_uint32;
static int hf_ecmp_data_int64;
static int hf_ecmp_data_uint64;
static int hf_ecmp_data_float;
static int hf_ecmp_data_double;
static int hf_ecmp_access_mode;
static int hf_ecmp_open_in_non_blocking_mode;
static int hf_ecmp_open_file_relative_to_specified_directory_handle;
static int hf_ecmp_file_access_mode;
static int hf_ecmp_additional_scheme;
static int hf_ecmp_scheme_data_length;
static int hf_ecmp_number_of_requested_bytes;
static int hf_ecmp_number_of_bytes_transferred;
static int hf_ecmp_crc;
static int hf_ecmp_ref_offset;
static int hf_ecmp_number_of_files_to_list;
static int hf_ecmp_file_hash;
static int hf_ecmp_item_type;
static int hf_ecmp_file_integrity;
static int hf_ecmp_display_attr_read_only;
static int hf_ecmp_display_attr_hidden;
static int hf_ecmp_display_attr_system;
static int hf_ecmp_display_attr_volume_label;
static int hf_ecmp_display_attr_subdirectory;
static int hf_ecmp_display_attr_archive;
static int hf_ecmp_display_creation;
static int hf_ecmp_display_modification;
static int hf_ecmp_interrogate_item_type;
static int hf_ecmp_interrogate_count;
static int hf_ecmp_modbus_pdu_size;
/* static int hf_ecmp_destination_scheme; */
static int hf_ecmp_program_control_target;
static int hf_ecmp_program_control_command;
static int hf_ecmp_program_control_sub_command;
static int hf_ecmp_program_control_status;
static int hf_ecmp_program_status_target;
static int hf_ecmp_program_status_status;
static int hf_ecmp_program_status_additional_items;
static int hf_ecmp_cyclic_setup_max_mappings;
static int hf_ecmp_cyclic_setup_start_offset;
static int hf_ecmp_cyclic_setup_tx_count;
static int hf_ecmp_cyclic_setup_rx_count;
static int hf_ecmp_udp_alignment;
static int hf_ecmp_udp_scheme;
static int hf_ecmp_cyclic_data;
static int hf_ecmp_version_summary;
static int hf_ecmp_min_param_menu;
static int hf_ecmp_max_param_menu;
static int hf_ecmp_file_length;
static int hf_ecmp_mec_offset;
static int hf_ecmp_sample_period;
static int hf_ecmp_rx_timeout;
static int hf_ecmp_rx_action;
static int hf_ecmp_rx_event_destination;
static int hf_ecmp_rx_event;
static int hf_ecmp_rx_late_handler_action;
static int hf_ecmp_rx_late_handler_event_destination;
static int hf_ecmp_rx_late_handler_event;
static int hf_ecmp_transport_addr_scheme;
static int hf_ecmp_transport_addr;
static int hf_ecmp_mapping_item_offset;
static int hf_ecmp_mapping_item_scheme;
static int hf_ecmp_setup_attribute;
static int hf_ecmp_mec_period;
static int hf_ecmp_interrogate_command;
/************************************************************/

/* These are the ids of the subtrees that we may be creating */

static int ett_ecmp;
static int ett_ecmp_address;
static int ett_ecmp_response_size;
static int ett_ecmp_command;
static int ett_ecmp_category;
static int ett_ecmp_option;
static int ett_ecmp_option_data;
static int ett_ecmp_attribute;
static int ett_ecmp_attribute_data;
static int ett_ecmp_cyclic_scheme;
static int ett_ecmp_info_type;
static int ett_ecmp_info_count;
static int ett_ecmp_interrogate_message;
static int ett_ecmp_param_address;
static int ett_ecmp_access_mode;
static int ett_ecmp_access_file;
static int ett_ecmp_file_read;
static int ett_ecmp_file_write;
static int ett_ecmp_file_info;
static int ett_ecmp_file_info_att;
static int ett_ecmp_file_position;
static int ett_ecmp_file_list_no;
static int ett_ecmp_file_list;
static int ett_ecmp_tunnel_3s_goodframe;
static int ett_ecmp_tunnel_3s_size;
static int ett_ecmp_tunnel_3s_service;
static int ett_cyclic_setup_attribs;
static int ett_cyclic_setup_attrib_item;
static int ett_cyclic_setup_transport_addr;
static int ett_ecmp_cyclic_data_32_bit_display;
static int ett_ecmp_cyclic_data_16_bit_display;
static int ett_ecmp_cyclic_data_8_bit_display;
static int ett_ecmp_modbus_pdu_message;
static int ett_ecmp_program_control_message;
static int ett_ecmp_program_status_message;
static expert_field ei_ecmp_unknown_command;
static expert_field ei_ecmp_color;
static expert_field ei_ecmp_option;
static expert_field ei_ecmp_item_type;
static expert_field ei_ecmp_options_not_implemented;
static expert_field ei_ecmp_info_type;
static expert_field ei_ecmp_attribute_type;
static expert_field ei_ecmp_parameter_addressing_scheme;
static expert_field ei_ecmp_data_type;


/*--------------------------------------------------------------------*/
/* General Commands and Framing Dissectors                            */
/*--------------------------------------------------------------------*/
/*a function to add the initial information about the transport layer (the first bits)*/
static int add_transport_layer_frame(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree, int addr_type)
{
	proto_item *ecmp_address_item = NULL;
	proto_tree *ecmp_address_tree = NULL;
	uint8_t byte_test;

	ecmp_address_item = proto_tree_add_item(ecmp_tree, addr_type, tvb, offset, 1, ENC_BIG_ENDIAN);

	byte_test = tvb_get_uint8(tvb, offset);
	if ((byte_test != 0) && (byte_test != 1)) {
		/* tree to display the data in the address*/
		ecmp_address_tree = proto_item_add_subtree(ecmp_address_item, ett_ecmp_address);

		switch (byte_test)
		{
		case 2: /*  default route scheme*/
			offset++;

			/* displays the values of the addresses*/
			proto_tree_add_item(ecmp_address_tree, hf_ecmp_physical_address, tvb, offset, 1, ENC_NA);
			proto_tree_add_item(ecmp_address_tree, hf_ecmp_logical_address, tvb, offset, 1, ENC_NA);
			break;

		case 3:/*  diagnostic scheme*/
			proto_tree_add_item(ecmp_address_tree, hf_ecmp_diagnostic, tvb, offset, 1, ENC_BIG_ENDIAN);
			offset++;
			break;
		case 4: /* Names scheme */
			/* Calls a function to display the UTF-8 string data*/
			proto_tree_add_item(ecmp_address_tree, hf_ecmp_names_scheme, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_ASCII);
			offset += (tvb_get_ntohs(tvb, offset) + 2);
			break;
		}
	}
	offset++;

	return offset;
}


/* a function to display option codes */
static int add_option_codes(unsigned offset, packet_info *pinfo, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_option_number_item = NULL;
	proto_item* ecmp_option_item;
	proto_tree* ecmp_option_tree;
	proto_tree* ecmp_option_data_tree = NULL;
	uint8_t option_code_display = 0;
	uint16_t count = 0; /* number of times the loop iterates*/
	int start_offset;
	bool more_options = true;

	offset++;

	start_offset = offset;
	ecmp_option_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 1, ett_ecmp_option, &ecmp_option_number_item, "Options" );

	/* Loop to display all options */
	while(more_options) /* loops until option code is 0*/
	{
		option_code_display = tvb_get_uint8(tvb, offset);
		ecmp_option_item = proto_tree_add_item(ecmp_option_tree, hf_ecmp_option, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset++;
		switch(option_code_display)
		{
		case 0:/* end of options*/
			proto_item_append_text(ecmp_option_number_item, ": %d", count);
			proto_item_set_len(ecmp_option_number_item, offset-start_offset);
			more_options = false;
			break;
		case 1:/* dummy - 0 bytes of data */
			break;
		case 2:/* process at - 8 bytes of data */
			ecmp_option_data_tree = proto_item_add_subtree(ecmp_option_item, ett_ecmp_option_data);

			proto_tree_add_item(ecmp_option_data_tree, hf_ecmp_process_time, tvb, offset, 8, ENC_BIG_ENDIAN);
			offset += 8;
			break;
		default: /* Option that is not recognised*/
			proto_item_append_text(ecmp_option_number_item, "%d ", count);
			expert_add_info(pinfo, ecmp_option_number_item, &ei_ecmp_option);
			break;
		}
		count++;
	}
	return offset;
}


/* a function to display attributes */
static void add_attributes(packet_info* pinfo, unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree, bool request)
{
	proto_item* ecmp_attribute_number_item = NULL;
	proto_item* ecmp_attribute_item = NULL, *color_item;
	proto_tree* ecmp_attribute_tree = NULL;
	proto_tree* ecmp_attribute_data_tree = NULL;
	uint8_t no_of_attributes = 0;
	uint8_t a = 0; /*values used for looping*/
	uint8_t b = 0;
	uint8_t c = 0;
	uint8_t check = 0;
	uint16_t att_length = 0;
	uint32_t color;
	wmem_strbuf_t* pStr = NULL; /*char array for version string output*/
	int start_offset = offset;

	/*display the number of attributes*/
	ecmp_attribute_number_item = proto_tree_add_item(ecmp_tree, hf_ecmp_no_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN);
	ecmp_attribute_tree = proto_item_add_subtree(ecmp_attribute_number_item, ett_ecmp_attribute);

	no_of_attributes = tvb_get_uint8(tvb, offset);
	offset++;

	for (a = 0; a < no_of_attributes; a++, offset++) {
		/*attribute header*/
		ecmp_attribute_item = proto_tree_add_item(ecmp_attribute_tree, hf_ecmp_attribute, tvb, offset, 1, ENC_BIG_ENDIAN );
		ecmp_attribute_data_tree = proto_item_add_subtree(ecmp_attribute_item, ett_ecmp_attribute_data);

		if (!request) {
			/*code for dissecting the colour codes attribute*/
			switch(tvb_get_uint8(tvb, offset))
			{
			case 8:
				offset+= 1;
				/*get length of attribute for error checking*/
				offset+= 2;

				/*output primary colour codes- the two bytes representing each colour are output as integers*/
				color = tvb_get_ntohl(tvb, offset);
				color_item = proto_tree_add_uint_format_value(ecmp_attribute_data_tree, hf_ecmp_primary_colour, tvb, offset, 4, color, "(red) %d (green) %d (blue) %d", tvb_get_uint8(tvb, offset+1), tvb_get_uint8(tvb, offset+2), tvb_get_uint8(tvb, offset+3));
				if ((color & 0xFF000000) != 0) {
					/*error check for correct colour code format */
					expert_add_info(pinfo, color_item, &ei_ecmp_color);
				}
				offset+= 4;

				/*output secondary colour codes- the two bytes representing each colour are output as integers*/
				color = tvb_get_ntohl(tvb, offset);
				color_item = proto_tree_add_uint_format_value(ecmp_attribute_data_tree, hf_ecmp_secondary_colour, tvb, offset, 4, color, "(red) %d (green) %d (blue) %d", tvb_get_uint8(tvb, offset+1), tvb_get_uint8(tvb, offset+2), tvb_get_uint8(tvb, offset+3));
				if ((color & 0xFF000000) != 0) {
					/*error check for correct colour code format */
					expert_add_info(pinfo, color_item, &ei_ecmp_color);
				}
				offset+= 4;
				break;
			/*code for dissecting the version summary attribute*/
			case 7:
				offset++;
				att_length = tvb_get_ntohs(tvb, offset);
				pStr = wmem_strbuf_create(pinfo->pool);
				offset+= 2;
				if (pStr != NULL) {
					for (c = 0; c < att_length; c++, offset++) {
						check = tvb_get_uint8(tvb,offset);
						if (check == 'V' || check == '#' || check == '@') {
							wmem_strbuf_append_c(pStr, ' ');
						} else if (check == ';') {
							/*display version summary parameter, e.g 'FW', 'BL', 'HW'*/
							proto_tree_add_string(ecmp_attribute_data_tree, hf_ecmp_version_summary, tvb, offset-b, b, wmem_strbuf_get_str(pStr));
							wmem_strbuf_truncate(pStr, 0);
						} else if (check <= 0x7f) {
							wmem_strbuf_append_c(pStr, check);
						}
						else {
							wmem_strbuf_append_hex(pStr, check);
						}
					}
					/*display last version summary parameter, e.g 'FW', 'BL', 'HW' as no delimiter to check for, just prints out rest of version string*/
					proto_tree_add_string(ecmp_attribute_data_tree, hf_ecmp_version_summary, tvb, offset-b, b, wmem_strbuf_get_str(pStr));
					offset-= 1;
				}
				break;
			default:
				/* displays the data inside the attribute*/
				proto_tree_add_item(ecmp_attribute_data_tree, hf_ecmp_attribute_string, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
				offset += (tvb_get_ntohs(tvb, offset+1) + 2);
				break;
			}
		}
	}

	proto_item_set_len(ecmp_attribute_number_item, offset-start_offset);
}


/* a function to display the category codes */
static int add_category_codes(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item *ecmp_category_item = NULL;
	proto_tree *ecmp_category_tree = NULL;
	uint8_t category_size = 0;
	int start_offset = offset;
	uint8_t category_value = tvb_get_uint8(tvb, offset);

	/* displays the category and creates a tree to display further data*/
	ecmp_category_item = proto_tree_add_item(ecmp_tree, hf_ecmp_category, tvb, offset, 1, ENC_BIG_ENDIAN);
	ecmp_category_tree = proto_item_add_subtree(ecmp_category_item, ett_ecmp_category);
	offset++;

	category_size = tvb_get_uint8(tvb, offset);
	offset++;

	if(category_size==2 && category_value == 1) {
		/*display "option module" and its ID*/
		proto_tree_add_item(ecmp_category_tree, hf_ecmp_category_id, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=category_size;
	} else if(category_size == 4 && category_value == 0) {
		/*display "drive" and its data (product type, drive derivative and ID*/
		proto_tree_add_item(ecmp_category_tree, hf_ecmp_drive_type, tvb, offset, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(ecmp_category_tree, hf_ecmp_drive_derivative, tvb, offset+1, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(ecmp_category_tree, hf_ecmp_drive_factory_fit_category_id, tvb, offset+2, 2, ENC_BIG_ENDIAN);
		offset+=category_size;

	} else {
		/* Display unknown and its hex data */
		proto_tree_add_item(ecmp_category_tree, hf_ecmp_data, tvb, offset, category_size, ENC_NA);
		offset += category_size;
	}

	proto_item_set_len(ecmp_category_item, offset-start_offset);
	return offset;
}


/* a function to display response size data */
static int get_response_size(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_max_response_item = NULL;
	proto_tree* ecmp_response_size_tree = NULL;
	uint8_t chunks = 0;
	uint16_t max_response_size = 0;

	/*get values for number of chunks and max response size*/
	chunks = tvb_get_uint8(tvb, offset)>>4&0x0F;
	max_response_size = tvb_get_ntohs(tvb, offset) & 0x0FFF;

	/*display response subtree */
	ecmp_response_size_tree = proto_tree_add_subtree_format(ecmp_tree, tvb, offset, 2, ett_ecmp_response_size, &ecmp_max_response_item, "Response Size: %X, %X (%d)", chunks, max_response_size, max_response_size);

	/*display chunks and max response size in response subtree*/
	proto_tree_add_item(ecmp_response_size_tree, hf_ecmp_chunking, tvb, offset, 2, ENC_BIG_ENDIAN);
	proto_tree_add_item(ecmp_response_size_tree, hf_ecmp_max_response_size, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset+= 2;

	return offset;
}


/* a function to display the command code and type (request/response) */
static int add_command_codes(packet_info* pinfo, unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree, uint8_t transaction_id_value, uint8_t* command_value)
{
	proto_tree *ecmp_command_tree;
	const char* command_str;
	uint8_t command = tvb_get_uint8(tvb, offset);
	*command_value = command & 0x7F;
	command_str = val_to_str(pinfo->pool, *command_value, command_vals, "Unknown Type (0x%02x)");

	/*display command subtree*/
	ecmp_command_tree = proto_tree_add_subtree_format(ecmp_tree, tvb, offset, 1, ett_ecmp_command, NULL, "Request Response Code: %s", command_str);

	/* Displays the command */
	proto_tree_add_item(ecmp_command_tree, hf_ecmp_command, tvb, offset, 1, ENC_BIG_ENDIAN);
	/* Displays the type (request/response) */
	proto_tree_add_item(ecmp_command_tree, hf_ecmp_type_rr, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Information displayed in the Info column*/
	col_add_fstr(pinfo->cinfo, COL_INFO, "%s, %s. Transaction ID: %d",
				command_str, tfs_get_string(((command & 0x80) >> 7), &tfs_response_request),
				transaction_id_value);

	return offset;
}


/* a function to add a cyclic frame query */
static int add_cyclic_frame_query(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree )
{
	/* display the cyclic link number  */
	proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_link_num, tvb, offset++, 1, ENC_BIG_ENDIAN);
	return offset;
}


/* a function to add a cyclic frame */
static int add_cyclic_frame(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree )
{
	uint32_t scheme;
	proto_item *ecmp_scheme_item = NULL;
	proto_tree *ecmp_scheme_tree = NULL;
	proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_link_num, tvb, offset++, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_align, tvb, offset++, 1, ENC_BIG_ENDIAN);
	ecmp_scheme_item = proto_tree_add_item_ret_uint(ecmp_tree, hf_ecmp_cyclic_scheme, tvb, offset++, 1, ENC_BIG_ENDIAN, &scheme);

	if (scheme == 1) {
		/* Create a new sub tree spawning off the scheme byte for the synchronisation scheme data to be placed. */
		ecmp_scheme_tree = proto_item_add_subtree(ecmp_scheme_item, ett_ecmp_cyclic_scheme);

		/* grandmaster */
		proto_tree_add_item( ecmp_scheme_tree, hf_ecmp_grandmaster, tvb, offset, 8, ENC_BIG_ENDIAN);
		offset += 8;

		proto_tree_add_item(ecmp_scheme_tree, hf_ecmp_cyclic_frame_time, tvb, offset, 8, ENC_BIG_ENDIAN);
		offset += 8;
	}

	proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset, -1, ENC_NA);

	return tvb_reported_length(tvb);
}


/* a function to display cyclic tvb data in byte (8-bit), word (16-bit), and long (32-bit) unsigned formats  */
static int display_raw_cyclic_data(uint8_t display, unsigned offset, uint16_t buffer_size, tvbuff_t *tvb, packet_info *pinfo, proto_tree* ecmp_current_tree )
{
	/****************************************************************************************/
	/*                                                                                      */
	/*     display_raw_cyclic_data - display the cyclic data in various formats.            */
	/*                                                                                      */
	/*     Parameters:   display = selects desired display format.                          */
	/*                             0  =  BYTE_FORMAT (8-bits  1F 20 37 BC ...               */
	/*                             1  =  WORD_FORMAT (16-bits 1F20 37BC 77F1 ...            */
	/*                             2  =  LONG_FORMAT (32-bits  1F2037BC 0013F5CD ...        */
	/*                                                                                      */
	/*                   offset  =  offset within tvb buffer where this data starts.        */
	/*                                                                                      */
	/*                   buffer_size = number of bytes to be converted and displayed.       */
	/*                                                                                      */
	/*                   tvb  =  buffer structure within Wireshark holding this frame.      */
	/*                                                                                      */
	/*                   pinfo  =  packet info for this frame.                              */
	/*                                                                                      */
	/*                   ecmp_current_tree  =  the tree where the data is to be displayed.  */
	/*                                                                                      */
	/*                                                                                      */
	/*   Notes: we only display so many elements on a line (before continuing on next line) */
	/*                                                                                      */
	/*          16 elements per line for byte (8-bit) and word (16-bit)                     */
	/*           8 elements per line for long (32-bit)                                      */
	/*                                                                                      */
	/*  Programmer: Jim Lynch                                                               */
	/****************************************************************************************/

	/* bail out if the buffer size is zero */
	if (buffer_size == 0) {
		proto_tree_add_bytes_format_value(ecmp_current_tree, hf_ecmp_cyclic_data, tvb, offset-1, 0, NULL, "No data");
	} else {
		/* define some variables  */
		wmem_strbuf_t*		pdata = wmem_strbuf_create(pinfo->pool); /* formatted data string */
		uint16_t		num_elements_total = 0; /* contains total number of elements (byte/word/long) to be processed  */
		const uint16_t	num_byte_elements_per_line = 16; /* number of byte (8-bit) elements per line e.g.  "1B " (3 chars per element)  */
		const uint16_t	num_word_elements_per_line = 16; /* number of word (16-bit) elements per line e.g.  "A81B " (5 chars per element) */
		const uint16_t	num_long_elements_per_line = 8; /* number of long (32-bit) elements per line e.g.  "01F4A81B " (9 chars per element) */
		uint16_t		num_elements_per_line = 8; /* counts the current number of elements per line */
		uint16_t		num_elements = 0; /* counts the number of elements in the format string */
		uint16_t		a = 0; /* value used for looping */
		int		start_offset, line_offset;

		/* calculate number of elements                                                     */
		/*                                                                                  */
		if (display == cyclic_display_byte_format) {
			num_elements_per_line = num_byte_elements_per_line; /* num_elements_per_line = 16  */
			num_elements_total = buffer_size;
		} else if (display == cyclic_display_word_format) {
			num_elements_per_line = num_word_elements_per_line; /* num_elements_per_line = 16  */
			num_elements_total = buffer_size >> 1;
		} else if (display == cyclic_display_long_format) {
			num_elements_per_line = num_long_elements_per_line; /* num_elements_per_line = 8  */
			num_elements_total = buffer_size >> 2;
		} else {
			num_elements_per_line = num_byte_elements_per_line; /* num_elements_per_line = 16  */
			num_elements_total = buffer_size;
		}

		/* OK, let's get started */
		num_elements = 0;

		line_offset = start_offset = offset;
		/* work through the display elements, 1 byte\word\long at a time  */
		for (a = 0; a < num_elements_total; a++ ) {
			if (wmem_strbuf_get_len(pdata) > 0) {
				wmem_strbuf_append_c(pdata, ' ');
			}

			/* use Wireshark accessor function to get the next byte, word, or long data  */
			if (display == cyclic_display_byte_format) {
				uint8_t value8 = tvb_get_uint8(tvb, offset);
				wmem_strbuf_append_printf(pdata, "%02x", value8);
				offset++;
			} else if (display == cyclic_display_word_format) {
				uint16_t value16 = tvb_get_ntohs(tvb, offset);
				wmem_strbuf_append_printf(pdata, "%04x", value16);
				offset += 2;
			} else if (display == cyclic_display_long_format) {
				uint32_t value32 = tvb_get_ntohl(tvb, offset);
				wmem_strbuf_append_printf(pdata, "%08x", value32);
				offset += 4;
			}

			/* increment the num_elements we've done on the current line  */
			num_elements++;

			/* check if we hit the max number of byte elements per line  */
			if (num_elements >= num_elements_per_line) {
				/* display the completed line in the sub-tree  */
				proto_tree_add_bytes_format(ecmp_current_tree, hf_ecmp_cyclic_data, tvb, offset, offset-line_offset, NULL, "%s", wmem_strbuf_get_str(pdata));

				/* start the line over */
				wmem_strbuf_truncate(pdata, 0);
				num_elements = 0;
				line_offset = offset;
			}
		}

		/* if we exited the loop, see if there's a partial line to display  */
		if (num_elements > 0) {
			/* display the partial line in the sub-tree  */
			proto_tree_add_bytes_format(ecmp_current_tree, hf_ecmp_cyclic_data, tvb, start_offset, offset-start_offset, NULL, "%s", wmem_strbuf_get_str(pdata));
		}
	}
	return offset;
}


/* a function returning the information requested by the 'info' command */
static void add_info_response(unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_info_address_item = NULL;
	proto_tree* ecmp_info_tree = NULL;
	proto_tree* ecmp_info_address_tree = NULL, *address_tree;
	uint16_t length = 0;
	uint8_t no_of_address = 0;
	uint8_t i = 0; /*for counting */

	length = tvb_reported_length(tvb);

	/*display info response tree */
	ecmp_info_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 6, ett_ecmp_info_type, NULL, "Response Information");

	/*display buffer size */
	proto_tree_add_item(ecmp_info_tree, hf_ecmp_buffer_size, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset+= 2;

	/*display max response time */
	proto_tree_add_item(ecmp_info_tree, hf_ecmp_max_response, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset+= 2;

	/*display max handle period */
	proto_tree_add_item(ecmp_info_tree, hf_ecmp_max_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset+= 2;

	if (length > offset) {
		/*display count of default server addresses */
		ecmp_info_address_item = proto_tree_add_item(ecmp_tree, hf_ecmp_info_address, tvb, offset, 1, ENC_BIG_ENDIAN);
		ecmp_info_address_tree = proto_item_add_subtree(ecmp_info_address_item, ett_ecmp_info_count);
		no_of_address = tvb_get_uint8(tvb, offset);

		if (no_of_address > 0) {
			/*do code to display address data */
			for (i = 0; i < no_of_address; i++) {
				address_tree = proto_tree_add_subtree_format(ecmp_info_address_tree, tvb, offset, 1, ett_ecmp_address, NULL, "Address %d", i+1);
				proto_tree_add_item(address_tree, hf_ecmp_physical_address, tvb, offset, 1, ENC_NA);
				proto_tree_add_item(address_tree, hf_ecmp_logical_address, tvb, offset, 1, ENC_NA);
				offset+= 1;
			}
		}
	}
}


/*--------------------------------------------------------------------*/
/*                Parameter Access Commands                           */
/*--------------------------------------------------------------------*/

/* a function to display data given data_type */
static int get_data_type(packet_info* pinfo, unsigned offset, uint8_t data_type, tvbuff_t *tvb, proto_tree* ecmp_current_tree)
{
	/*switch to decide correct data_type dissection*/
	switch(data_type)
	{
	case 0: /*display boolean*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_boolean, tvb, offset, 1, ENC_NA);
		break;
	case 1: /*display INT8*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_int8, tvb, offset, 1, ENC_NA);
		break;
	case 2: /*display UINT8*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_uint8, tvb, offset, 1, ENC_NA);
		break;
	case 3: /*display INT16*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_int16, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+= 1;
		break;
	case 4: /*display UINT16*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_uint16, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+= 1;
		break;
	case 5: /*display INT32*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_int32, tvb, offset, 4, ENC_BIG_ENDIAN);
		offset+= 3;
		break;
	case 6: /*display UINT32*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_uint32, tvb, offset, 4, ENC_BIG_ENDIAN);
		offset+= 3;
		break;
	case 7: /*display INT64*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_int64, tvb, offset, 8, ENC_BIG_ENDIAN);
		offset+= 7;
		break;
	case 8: /*display UINT64*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_uint64, tvb, offset, 8, ENC_BIG_ENDIAN);
		offset+= 7;
		break;
	case 9:  /*display INT128*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data, tvb, offset, 16, ENC_NA);
		offset += 15;
		break;
	case 10: /*display UINT128*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data, tvb, offset, 16, ENC_NA);
		offset += 15;
		break;
	case 20:/*display single float*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_float, tvb, offset, 4, ENC_BIG_ENDIAN);
		offset+= 3;
		break;
	case 21: /*display double float*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_data_double, tvb, offset, 8, ENC_BIG_ENDIAN);
		offset+= 7;
		break;
	case 30: /*display string ID*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_string_id, tvb, offset, 2, ENC_ASCII);
		offset++;
		break;
	case 32: /*display (ECMP) string*/
		proto_tree_add_item(ecmp_current_tree, hf_ecmp_ecmp_string, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
		offset += (tvb_get_ntohs(tvb, offset+1) + 2);
		break;
	default: /*display untyped size*/
		if (data_type < 128) {
			proto_tree_add_expert_remaining(ecmp_current_tree, pinfo, &ei_ecmp_data_type, tvb, 0);
		} else {
			proto_tree_add_item(ecmp_current_tree, hf_ecmp_data, tvb, offset, (data_type- 127), ENC_NA);
			offset += (data_type- 128);
		}
		break;
	}
	return offset;
}


/* a function to add the parameter address schemes for 'read' command */
static int get_address_scheme(packet_info* pinfo, unsigned offset, uint8_t scheme, tvbuff_t *tvb, proto_tree* ecmp_parameter_tree)
{
	/*if address scheme is standard*/
	switch (scheme)
	{
	case 0:
		/*display Menu no. */
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_menu, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+= 2;

		/*display parameter no. */
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_parameter, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset++;
		break;

	case 1:/*if address scheme is slot specific*/
		/*display slot number*/
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_slot, tvb, offset, 1, ENC_NA);
		offset++;

		/*display Menu no. */
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_menu, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+= 2;

		/*display parameter no. */
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_parameter, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset++;
		break;

	case 3: /*if address scheme is variable*/
		/*display variable name */
		offset--;
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_variable_name, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
		offset += (tvb_get_ntohs(tvb, offset+1) + 2);
		break;

	case 4: /*if address scheme is NULL*/
		/*null size*/
		proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_address_scheme_null_byte_size, tvb, offset, 1, ENC_NA);
		offset++;
		break;

	default:
		proto_tree_add_expert(ecmp_parameter_tree, pinfo, &ei_ecmp_parameter_addressing_scheme, tvb, offset, 1);
	}
	return offset;
}


/* a function to display an array of the read address schemes */
static void get_parameter_definitions(packet_info* pinfo, unsigned offset, uint8_t command_value, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_parameter_item = NULL;
	proto_tree* ecmp_parameter_number_tree = NULL;
	proto_tree* ecmp_parameter_tree = NULL;
	uint8_t count = 0;
	uint8_t a = 0;
	uint8_t data_type = 0;
	int8_t dec = 0;
	uint8_t scheme = 0;
	uint16_t n = 0;

	scheme = tvb_get_uint8(tvb, offset);

	ecmp_parameter_item = proto_tree_add_item(ecmp_tree, hf_ecmp_parameter_address, tvb, offset, 1, ENC_BIG_ENDIAN);
	ecmp_parameter_tree = proto_item_add_subtree(ecmp_parameter_item, ett_ecmp_param_address);

	offset++;
	/* if "GetNextObjects" command */
	if(command_value == ECMP_COMMAND_GETNEXTOBJECTS)
	{
		offset = get_address_scheme(pinfo, offset, scheme, tvb, ecmp_parameter_tree);
		offset++;
		proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_subsequent_object_requests, tvb, offset, 1, ENC_NA);
	}else
	{
		/*display tree with count of definitions */
		ecmp_parameter_item = proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_parameter_definitions, tvb, offset, 1, ENC_BIG_ENDIAN);
		ecmp_parameter_number_tree = proto_item_add_subtree(ecmp_parameter_item, ett_ecmp_param_address);

		count = tvb_get_uint8(tvb,offset);

		offset++;

		switch(scheme)/*sets n so that the tree highlights bytes in scheme specific data*/
		{
			case 0:
				n = 4;
				break;
			case 1:
				n = 5;
				break;
			case 3:
				n = 1 + ((tvb_get_uint8(tvb, offset+1)<<8)|(tvb_get_uint8(tvb, offset+2)));
				break;
			default:
				n = 0;
				break;
		}

		if (command_value == ECMP_COMMAND_OBJECTINFO) {
			n += 1;
		}

		for (a = 0; a < count; a++) {
			ecmp_parameter_tree = proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, n, ett_ecmp_param_address, NULL, "Parameter Definition %d:", (a+1));

			if (command_value == ECMP_COMMAND_OBJECTINFO) {
				proto_tree_add_item(ecmp_parameter_tree, hf_ecmp_info_type, tvb, offset, 1, ENC_BIG_ENDIAN);
				offset++;
				offset = get_address_scheme(pinfo, offset, scheme, tvb, ecmp_parameter_tree);
				offset++;
			} else {
				/*output the address schemes of the parameter requests */
				offset = get_address_scheme(pinfo, offset, scheme, tvb, ecmp_parameter_tree);
				offset++;
				if (command_value == ECMP_COMMAND_WRITE) {
					proto_tree_add_item_ret_uint8(ecmp_parameter_tree, hf_ecmp_data_type, tvb, offset, 1, ENC_BIG_ENDIAN, &data_type);
					offset++;
					dec = tvb_get_int8(tvb, offset);
					if (dec != -1) {
						proto_tree_add_int(ecmp_parameter_tree, hf_ecmp_number_of_decimal_places, tvb, offset, 1, dec);
					} else {
						proto_tree_add_int_format_value(ecmp_parameter_tree, hf_ecmp_number_of_decimal_places, tvb, offset, 1, dec, "0 (Invalid type)");
					}
					offset++;
					offset = get_data_type(pinfo, offset, data_type, tvb, ecmp_parameter_tree);
					offset++;
				}
			}
		}
	}
}


/* a function to show the "objectinfo" command response */
static void get_object_info_response(packet_info* pinfo, unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_response_item = NULL;
	proto_tree* ecmp_parameter_number_tree = NULL;
	proto_tree* ecmp_parameter_response_tree = NULL;
	uint8_t count = 0; /* stores number of parameter read responses */
	uint8_t a = 0; /* counting variables */
	uint8_t n = 0;
	uint8_t info_type0 = 0;
	uint16_t length = 0;
	uint8_t data_type = 0;

	length = tvb_reported_length(tvb);

	ecmp_response_item = proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_parameter_responses, tvb, offset, 1, ENC_BIG_ENDIAN);
	ecmp_parameter_number_tree = proto_item_add_subtree(ecmp_response_item, ett_ecmp_param_address);

	count = tvb_get_uint8(tvb, offset);

	if (count == 0) {
		offset++;
		proto_tree_add_item(ecmp_parameter_number_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);

	} else {
		/*display info data response */
		for (a = 0; a < count; a++) {
			if (a==0) {
				n = (length-offset)/count;
			}
			offset++;
			/*display response header */
			proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, n, ett_ecmp_command, NULL, "Response %d:", (a+1));

			/*display response status */
			proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);
			offset++;

			/*display response data */
			proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_info_type, tvb, offset, 1, ENC_BIG_ENDIAN);
			info_type0 = tvb_get_uint8(tvb, offset);

			switch(info_type0)
			{
				case 0:
					/*no information available */
					proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_no_information_available, tvb, offset, 1, ENC_NA);
					break;
				case 1:
					/*display min parameter in menu */
					offset++;
					proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_min_param_menu, tvb, offset, 2, ENC_BIG_ENDIAN);
					offset++;
					break;
				case 2:
					/*display max parameter in menu */
					offset++;
					proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_max_param_menu, tvb, offset, 2, ENC_BIG_ENDIAN);
					offset++;
					break;
				case 3:
					{
						static int * const fields[] = {
							&hf_ecmp_param_format_bit_default_unipolar,
							&hf_ecmp_param_format_write_allowed,
							&hf_ecmp_param_format_read_not_allowed,
							&hf_ecmp_param_format_protected_from_destinations,
							&hf_ecmp_param_format_parameter_not_visible,
							&hf_ecmp_param_format_not_clonable,
							&hf_ecmp_param_format_voltage_or_current_rating_dependent,
							&hf_ecmp_param_format_parameter_has_no_default,
							&hf_ecmp_param_format_number_of_decimal_places,
							&hf_ecmp_param_format_variable_maximum_and_minimum,
							&hf_ecmp_param_format_string_parameter,
							&hf_ecmp_param_format_destination_set_up_parameter,
							&hf_ecmp_param_format_filtered_when_displayed,
							&hf_ecmp_param_format_pseudo_read_only,
							&hf_ecmp_param_format_display_format,
							&hf_ecmp_param_format_floating_point_value,
							&hf_ecmp_param_format_units,
							NULL
						};

						/*display data for parameter format- UNITS and Display Format need dissecting? */
						offset++;
						proto_tree_add_bitmask_list(ecmp_parameter_response_tree, tvb, offset, 4, fields, ENC_BIG_ENDIAN);
						offset+= 3;
					}
					break;
				case 4:
					/*display minimum allowed value*/
					offset++;
					ecmp_response_item = proto_tree_add_item_ret_uint8(ecmp_parameter_response_tree, hf_ecmp_data_type, tvb, offset, 1, ENC_BIG_ENDIAN, &data_type);
					offset++;
					offset = get_data_type(pinfo, offset, data_type, tvb, ecmp_parameter_response_tree);
					break;
				case 5:
					/*display maximum allowed value*/
					offset++;
					ecmp_response_item = proto_tree_add_item_ret_uint8(ecmp_parameter_response_tree, hf_ecmp_data_type, tvb, offset, 1, ENC_BIG_ENDIAN, &data_type);
					offset++;
					offset = get_data_type(pinfo, offset, data_type, tvb, ecmp_parameter_response_tree);
					break;
				case 6:
					/*display Units- ID string */
					offset++;
					proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_string_id, tvb, offset, 2, ENC_ASCII);
					offset++;
					break;
				case 7:
					/*display data type */
					offset++;
					ecmp_response_item = proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_data_type, tvb, offset, 1, ENC_BIG_ENDIAN);
					break;
				default:
					expert_add_info(pinfo, ecmp_response_item, &ei_ecmp_info_type);
					break;
			}
		}
	}
}


/* a function to display an array of the read responses */
static int get_parameter_responses(packet_info* pinfo, unsigned offset, uint8_t command_value, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_response_item = NULL;
	proto_tree* ecmp_parameter_number_tree = NULL;
	proto_tree* ecmp_parameter_response_tree = NULL;
	uint8_t count = 0; /* stores number of parameter read responses */
	uint8_t a = 0; /* counting variables */
	uint8_t data_type = 0;
	uint8_t unit_id = 0;
	int8_t dec = 0;
	uint16_t n = 0;
	uint8_t st_error = 0;
	uint16_t length = 0;
	uint8_t scheme = 0;
	int start_offset;

	scheme = tvb_get_uint8(tvb, offset);
	length = tvb_reported_length(tvb);

	if (command_value == ECMP_COMMAND_GETNEXTOBJECTS) {
		/*display addressing scheme*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_parameter_address, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset++;
	}

	/*display number of responses*/
	ecmp_response_item = proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_parameter_responses, tvb, offset, 1, ENC_BIG_ENDIAN);
	ecmp_parameter_number_tree = proto_item_add_subtree(ecmp_response_item, ett_ecmp_param_address);

	count = tvb_get_uint8(tvb, offset);

	if (count == 0) {
		offset++;
		if (command_value != ECMP_COMMAND_GETNEXTOBJECTS) {
			/*display parameter status*/
			proto_tree_add_item(ecmp_parameter_number_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);
		}
	} else {
		/*loop for outputting parameter data responses*/
		for (a = 0; a < count; a++) {
			if (command_value == ECMP_COMMAND_WRITE) {
				if (a==0) {
					n = (length-offset)/count; /*set byte highlighting*/
				}
				offset++;
				/*display response: (a+1)*/
				ecmp_parameter_response_tree = proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, n, ett_ecmp_command, NULL, "Response %d:", (a+1));
				ecmp_response_item = proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);

			} else if (command_value == ECMP_COMMAND_GETNEXTOBJECTS) {
				if (a==0) {
					n = (length-offset)/count;
				}
				offset++;
				/*display response: (a+1)*/
				ecmp_parameter_response_tree = proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, n, ett_ecmp_command, NULL, "Response %d:", (a+1));
				offset = get_address_scheme(pinfo, offset, scheme, tvb, ecmp_parameter_response_tree);
			} else {
				/*if status is error */
				if (tvb_get_int8(tvb, offset+1) < 0) {
					/*output status*/
					st_error = 1;
					offset++;
					ecmp_parameter_response_tree = proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, 1, ett_ecmp_command, NULL, "Response %d:", (a+1));
					ecmp_response_item = proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);
					if ((a+1) != count) {
						/*loop to move to next data_type (skips bytes == 0)*/
						while(1) {
							if(tvb_get_uint8(tvb, offset+1)==0) {
								offset++;
							} else {
								break;
							}
						}
					}
				} else {
					offset++;
					/*display response data_byte*/
					start_offset = offset;
					ecmp_parameter_response_tree = proto_tree_add_subtree_format(ecmp_parameter_number_tree, tvb, offset, 0, ett_ecmp_command, &ecmp_response_item, "Response %d:", (a+1));
					proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_parameter_status, tvb, offset, 1, ENC_BIG_ENDIAN);
					offset++;
					proto_tree_add_item_ret_uint8(ecmp_parameter_response_tree, hf_ecmp_data_type, tvb, offset, 1, ENC_BIG_ENDIAN, &data_type);
					offset++;
					offset = get_data_type(pinfo, offset, data_type, tvb, ecmp_parameter_response_tree);

					/*if "ReadWithType" */
					if ((command_value == ECMP_COMMAND_READWITHTYPE) && (st_error!= 1)) {
						offset++;
						/*display decimal places*/
						dec = tvb_get_int8(tvb, offset);
						if (dec != -1) {
							proto_tree_add_int(ecmp_parameter_response_tree, hf_ecmp_number_of_decimal_places, tvb, offset, 1, dec);
						} else {
							proto_tree_add_int_format_value(ecmp_parameter_response_tree, hf_ecmp_number_of_decimal_places, tvb, offset, 1, dec, "0 (Invalid type)");
						}
						offset++;
						/*display unit ID*/
						proto_tree_add_item_ret_uint8(ecmp_parameter_response_tree, hf_ecmp_display_unit_id, tvb, offset, 1, ENC_NA, &unit_id);
						if (unit_id == 255) {
							offset++;
							proto_tree_add_item(ecmp_parameter_response_tree, hf_ecmp_unit_id_string, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_ASCII);
							offset += (tvb_get_ntohs(tvb, offset) + 2);
						}
					}

					proto_item_set_len(ecmp_response_item, offset-start_offset);
				}

			}
		}
	}
	return offset;
}


/*--------------------------------------------------------------------*/
/*                   File Access Commands                             */
/*--------------------------------------------------------------------*/
/* a function to dissect "FileOpen" command */
static void file_open(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_tree* ecmp_scheme_data_tree = NULL;
	uint8_t additional_scheme = 0;
	uint8_t relative = 0;

	if (request) {
		static int * const fields[] = {
			&hf_ecmp_open_in_non_blocking_mode,
			&hf_ecmp_open_file_relative_to_specified_directory_handle,
			&hf_ecmp_file_access_mode,
			NULL
		};

		proto_tree_add_bitmask(ecmp_tree, tvb, offset, hf_ecmp_access_mode, ett_ecmp_access_mode, fields, ENC_BIG_ENDIAN);
		relative = (tvb_get_uint8(tvb, offset) & 0x40) ? 1 : 0;
		offset++;

		/*display additional scheme*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_additional_scheme, tvb, offset, 1, ENC_BIG_ENDIAN);
		additional_scheme= tvb_get_uint8(tvb, offset);

		/*display file name*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_name, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
		offset += (tvb_get_ntohs(tvb, offset+1) + 2);

		/*only show file handle in relative mode*/
		if (relative == 1) {
			offset++;

			/*display file handle*/
			proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		}

		if (additional_scheme == 1) {
			/*display additional data*/
			offset+= 2;
			ecmp_scheme_data_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, -1, ett_ecmp_access_file, NULL, "Additional scheme data");
			proto_tree_add_item(ecmp_scheme_data_tree, hf_ecmp_scheme_data_length, tvb, offset, 1, ENC_NA);
			offset++;
			proto_tree_add_item(ecmp_scheme_data_tree, hf_ecmp_data, tvb, offset, -1, ENC_NA);
		}
	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);

		if (tvb_get_int8(tvb, offset) >= 0) {
			offset++;
			/*display file handle*/
			proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		}
	}
}


/* a function to dissect "FileRead" command */
static void file_read(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	uint16_t req_bytes = 0;

	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=2;

		/*display requested bytes*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_requested_bytes, tvb, offset, 2, ENC_BIG_ENDIAN);

	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);

		if (tvb_get_int8(tvb, offset)>= 0) {
			offset++;

			/*display bytes for reading*/
			req_bytes = tvb_get_ntohs(tvb, offset);
			proto_tree_add_item(ecmp_tree, hf_ecmp_response_data, tvb, offset, req_bytes+2, ENC_NA);
			/*offset += (2+req_bytes);*/
		}
	}
}


/* a function to dissect "FileWrite" command */
static void file_write(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	uint16_t req_bytes;

	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=2;

		/*display bytes for writing*/
		req_bytes = tvb_get_ntohs(tvb, offset);
		proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset+2, req_bytes, ENC_NA);
		/*offset += (2+req_bytes);*/

	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);
		/*offset++;*/
	}
}


/*a function to dissect "FileClose" command*/
static void file_close(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=2;

		/*display number of data bytes transferred*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_bytes_transferred, tvb, offset, 4, ENC_BIG_ENDIAN);
		offset+= 4;

		/*display CRC value*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_crc, tvb, offset, 4, ENC_BIG_ENDIAN);
	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset++;
	}
}


/*a function to display file attributes*/
static int get_file_attribute(packet_info* pinfo, unsigned offset, tvbuff_t *tvb, proto_tree* ecmp_current_tree)
{
	proto_item *ecmp_file_info_att_item;
	proto_tree *ecmp_file_info_att_tree;
	uint32_t attribute0;
	int start_offset = offset;

	ecmp_file_info_att_item = proto_tree_add_item_ret_uint(ecmp_current_tree,
			hf_ecmp_file_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &attribute0);
	offset++;
	ecmp_file_info_att_tree = proto_item_add_subtree(ecmp_file_info_att_item, ett_ecmp_file_info_att);

	switch(attribute0)
	{
		case 0: /*display length of file*/
			proto_tree_add_item(ecmp_file_info_att_tree, hf_ecmp_file_length, tvb, offset, 4, ENC_BIG_ENDIAN);
			offset += 4;
			break;
		case 1: /*display integrity*/
			proto_tree_add_item(ecmp_file_info_att_tree, hf_ecmp_file_integrity, tvb, offset, 1, ENC_BIG_ENDIAN);
			offset++;
			break;
		case 2:	/*display CRC*/
			proto_tree_add_item(ecmp_file_info_att_tree, hf_ecmp_crc, tvb, offset, 4, ENC_BIG_ENDIAN);
			offset += 4;
			break;
		case 3:	/*display attrib*/
			{
				static int * const fields[] = {
					&hf_ecmp_display_attr_read_only,
					&hf_ecmp_display_attr_hidden,
					&hf_ecmp_display_attr_system,
					&hf_ecmp_display_attr_volume_label,
					&hf_ecmp_display_attr_subdirectory,
					&hf_ecmp_display_attr_archive,
					NULL
				};

				proto_tree_add_bitmask_list(ecmp_file_info_att_tree, tvb, offset, 1, fields, ENC_BIG_ENDIAN);
				offset++;
			}
			break;
		case 4:	/*display creation date*/
			proto_tree_add_item(ecmp_file_info_att_tree, hf_ecmp_display_creation, tvb, offset, 4, ENC_TIME_SECS|ENC_BIG_ENDIAN);
			offset += 4;
			break;
		case 5:	/*display modification date*/
			proto_tree_add_item(ecmp_file_info_att_tree, hf_ecmp_display_modification, tvb, offset, 4, ENC_TIME_SECS|ENC_BIG_ENDIAN);
			offset += 4;
			break;
		default: /*display incorrect attribute type error*/
			proto_tree_add_expert(ecmp_file_info_att_tree, pinfo, &ei_ecmp_attribute_type, tvb, offset, 1);
			offset++;
			break;
	}
	proto_item_set_len(ecmp_file_info_att_item, offset - start_offset);
	return offset;
}


/*a function to dissect "FileInfo" command*/
static void file_info(packet_info* pinfo, unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_tree *ecmp_file_info_tree;
	uint32_t a, no_of_att;
	int start_offset;

	if (request) {
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=2;

		start_offset = offset;
		ecmp_file_info_tree = proto_tree_add_subtree(ecmp_tree,
				tvb, offset, -1, ett_ecmp_file_info, NULL, "Requested Attributes");
		proto_tree_add_item_ret_uint(ecmp_file_info_tree,
				hf_ecmp_no_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &no_of_att);
		offset++;

		for (a = 0; a < no_of_att; a++) {
			proto_tree_add_item(ecmp_file_info_tree,
					hf_ecmp_file_attributes, tvb, offset, 1, ENC_BIG_ENDIAN);
			offset++;
		}
		proto_item_set_len(ecmp_file_info_tree, offset - start_offset);
	} else {
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset++;

		start_offset = offset;
		ecmp_file_info_tree = proto_tree_add_subtree(ecmp_tree,
				tvb, offset, -1, ett_ecmp_file_info, NULL, "Received Attributes");

		proto_tree_add_item_ret_uint(ecmp_file_info_tree,
				hf_ecmp_no_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &no_of_att);
		offset++;

		/*display attributes*/
		for (a = 0; a < no_of_att; a++) {
			offset = get_file_attribute(pinfo, offset, tvb, ecmp_file_info_tree);
		}
		proto_item_set_len(ecmp_file_info_tree, offset-start_offset);
	}
}


/*a function to dissect	"FileStatus"/"FileDelete" commands*/
static void file_state_delete(uint16_t offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);

	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);
	}
}


/*a function to dissect "FilePos" command*/
static void file_pos(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_tree* ecmp_file_position_tree = NULL;

	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+=2;

		/*display "position" header*/
		ecmp_file_position_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 5, ett_ecmp_file_position, NULL, "Position");

		/*display reference point*/
		proto_tree_add_item(ecmp_file_position_tree, hf_ecmp_file_ref_point, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset++;

		/*display offset from ref point*/
		proto_tree_add_item(ecmp_file_position_tree, hf_ecmp_ref_offset, tvb, offset, 4, ENC_BIG_ENDIAN);

	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);

		if(tvb_get_int8(tvb,offset) >= 0) {
			offset++;

			/*display offset from ref point*/
			proto_tree_add_item(ecmp_file_position_tree, hf_ecmp_ref_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
		}
	}
}


/*a function to dissect "FileList" command*/
static void file_list(packet_info* pinfo, unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item* ecmp_file_list_item, *ecmp_file_list_item2, *item_type_item;
	proto_tree* ecmp_file_list_no_tree = NULL;
	proto_tree* ecmp_file_list_tree = NULL;
	uint8_t no_of_items = 0;
	uint8_t item_type = 0;
	uint8_t a = 0;
	uint16_t n = 0;
	int start_offset, start_offset2;

	if (request) {
		/*display file handle*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_handle, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset+= 2;

		/*display number of files to list*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_files_to_list, tvb, offset, 1, ENC_NA);
	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);

		if (tvb_get_int8(tvb,offset) >= 0) {
			offset++;

			/*display number of files to list*/
			no_of_items = tvb_get_uint8(tvb, offset);
			proto_tree_add_item(ecmp_tree, hf_ecmp_number_of_files_to_list, tvb, offset, 1, ENC_NA);
			offset++;

			/*display hash value (dissection TBD)*/
			ecmp_file_list_item = proto_tree_add_item(ecmp_tree, hf_ecmp_file_hash, tvb, offset, 2, ENC_BIG_ENDIAN);
			offset++;

			/*display subtree for files*/
			start_offset = offset+1;
			ecmp_file_list_no_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset+1, no_of_items, ett_ecmp_file_list_no, &ecmp_file_list_item, "Files");

			/*display list of file names*/
			for (a = 0; a < no_of_items; a++) {
				start_offset2 = offset;
				offset++;
				item_type = tvb_get_uint8(tvb, offset);
				n = tvb_get_ntohs(tvb, offset+1);
				ecmp_file_list_tree = proto_tree_add_subtree_format(ecmp_file_list_no_tree, tvb, offset, n+2, ett_ecmp_file_list, &ecmp_file_list_item2, "File %d:", a+1);
				item_type_item = proto_tree_add_item(ecmp_file_list_tree, hf_ecmp_item_type, tvb, offset, 1, ENC_NA);

				switch(item_type)
				{
					case 0: /*if item type is "file"*/
						proto_tree_add_item(ecmp_file_list_tree, hf_ecmp_file_name, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
						break;

					case 1: /*if item type is "directory"*/
						proto_tree_add_item(ecmp_file_list_tree, hf_ecmp_directory, tvb, offset+1, 2, ENC_BIG_ENDIAN|ENC_ASCII);
						break;

					default: /*if item type is not "file" or "directory"*/
						expert_add_info(pinfo, item_type_item, &ei_ecmp_item_type);
						break;
				}
				offset+= n;

				proto_item_set_len(ecmp_file_list_item2, offset-start_offset2);
			}

			proto_item_set_len(ecmp_file_list_item, (offset+1)-start_offset);
		}
	}
}


/*a function to dissect "FileExists" command*/
static void file_exists(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	if (request) {
		/*display filename*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_name, tvb, offset, 2, ENC_BIG_ENDIAN|ENC_ASCII);
	} else {
		/*display file status*/
		proto_tree_add_item(ecmp_tree, hf_ecmp_file_status, tvb, offset, 1, ENC_BIG_ENDIAN);
	}
}


static int add_cyclic_setup_attributes(packet_info* pinfo, unsigned offset, uint16_t length, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_item *cyclic_setup_attributes_root = NULL;
	proto_item *cyclic_setup_attributes = NULL;
	proto_item *cyclic_setup_attrib_item_root = NULL;
	proto_tree *cyclic_setup_attrib_item = NULL;
	uint8_t attrib;

	/* num attribs */
	cyclic_setup_attributes_root = proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_attrib_count, tvb, offset++, 1, ENC_BIG_ENDIAN);

	/* attrib list */
	cyclic_setup_attributes = proto_item_add_subtree(cyclic_setup_attributes_root, ett_cyclic_setup_attribs);

	while (offset < length) {
		cyclic_setup_attrib_item_root = proto_tree_add_item_ret_uint8(cyclic_setup_attributes, hf_ecmp_cyclic_setup_attrib, tvb, offset++, 1, ENC_BIG_ENDIAN, &attrib);

		cyclic_setup_attrib_item = proto_item_add_subtree(cyclic_setup_attrib_item_root, ett_cyclic_setup_attrib_item);

		switch (attrib) {
			case 3: /* mec offset */
			{
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_mec_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
				offset += 4;
			}
			break;

			case 4: /* sample period */
			case 5: /* mec delay */
			{
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_sample_period, tvb, offset, 8, ENC_BIG_ENDIAN);
				offset += 8;
			}
			break;

			case 7: /* rx timeout */
			{
				/* tout */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_timeout, tvb, offset, 4, ENC_BIG_ENDIAN);
				offset += 4;

				/* action */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_action, tvb, offset++, 1, ENC_NA);

				/* event dest */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_event_destination, tvb, offset++, 1, ENC_NA);

				/* event */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_event, tvb, offset++, 1, ENC_NA);

			}
			break;

			case 8: /* rx late handler */
			{
				/* action */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_late_handler_action, tvb, offset++, 1, ENC_NA);

				/* event dest */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_late_handler_event_destination, tvb, offset++, 1, ENC_NA);

				/* event */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_rx_late_handler_event, tvb, offset++, 1, ENC_NA);
			}
			break;

			case 9: /* transport addr */
			{
				/* scheme */
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_transport_addr_scheme, tvb, offset++, 1, ENC_NA);

				/* todo - make this check the scheme is actually 0! */

				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_transport_addr, tvb, offset, 4, ENC_BIG_ENDIAN);
				offset += 4;
			}
			break;

			case 12: /* mapping item */
			{
				uint8_t addrScheme;

				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_mapping_item_offset, tvb, offset++, 1, ENC_NA);

				addrScheme = tvb_get_uint8(tvb, offset);
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_mapping_item_scheme, tvb, offset++, 1, ENC_NA);

				offset = get_address_scheme(pinfo, offset, addrScheme, tvb, cyclic_setup_attrib_item);

				/* todo - should this be done in the last function itself??? */
				offset++;
			}
			break;

			case 0: /* state */
			case 1: /* rx/tx */
			case 2: /* synchronised */
			case 6: /* data change */
			case 10: /* max mappings */
			case 11: /* num mappings */
			case 13: /* saveable */
			case 128: /* max rx links */
			case 129: /* max tx links */
			case 130: /* max mappings per link */
			case 131: /* max sync rx links */
			case 132: /* max sync tx links */
			case 133: /* max mappings per sync link */
			case 134: /* process at queue depth */
			{
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_setup_attribute, tvb, offset++, 1, ENC_NA);
			}
			break;

			case 135: /* mec period */
			{
				proto_tree_add_item(cyclic_setup_attrib_item, hf_ecmp_mec_period, tvb, offset, 4, ENC_BIG_ENDIAN);
				offset += 4;
			}
			break;

			default:
			break;

		} /* attribute switch */
	} /* loop through list */

	return offset;
}


static void cyclic_setup(packet_info* pinfo, uint16_t offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	uint16_t length = 0;
	proto_item* cyclic_setup_attributes_root = NULL;
	proto_item* cyclic_setup_attributes = NULL;
	uint8_t Mode;

	length = tvb_reported_length(tvb);

	/* if a request add the check output flag */
	if (request) {
		proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_linkno, tvb, offset++, 1, ENC_BIG_ENDIAN);

		Mode = tvb_get_uint8(tvb, offset);
		proto_tree_add_uint(ecmp_tree, hf_ecmp_cyclic_setup_mode, tvb, offset++, 1, Mode);

		switch (Mode) {
			case 0: /* create  */
			case 10: /* set  */
			{
				/* link direction */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_dir, tvb, offset++, 1, ENC_BIG_ENDIAN);

				/* add the attributes as a tree */
				add_cyclic_setup_attributes(pinfo, offset, length, tvb, ecmp_tree);
			}
			break;

			case 1: /* edit */
			case 2: /* finalise */
			case 3: /* delete */
			case 4: /* exist */
				/* link direction */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_dir, tvb, offset++, 1, ENC_BIG_ENDIAN);
			break;

			case 5: /* list */
				/* tx/rx bits */
				proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA);
			break;

			case 11: /* get */
			case 6: /* info */
			{
				if (Mode == 11) {
					/* link dir */
					proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_dir, tvb, offset++, 1, ENC_BIG_ENDIAN);
				}

				/* num attribs */
				cyclic_setup_attributes_root = proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_attrib_count, tvb, offset++, 1, ENC_BIG_ENDIAN);

				/* attrib list */
				cyclic_setup_attributes = proto_item_add_subtree(cyclic_setup_attributes_root, ett_cyclic_setup_attribs);
				while (offset < length) {
					proto_tree_add_item(cyclic_setup_attributes, hf_ecmp_cyclic_setup_attrib, tvb, offset++, 1, ENC_BIG_ENDIAN);
				}
			}
			break;

			case 12: /* get mappings */
			{
				/* link dir */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_dir, tvb, offset++, 1, ENC_BIG_ENDIAN);

				/* max mappings */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_max_mappings, tvb, offset++, 1, ENC_NA);

				/* start offset */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_start_offset, tvb, offset++, 1, ENC_NA);
			}
			break;

			default:
				/* display payload as hex bytes */
				proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA);
			break;
		}
	} else {
		proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_rsp_status, tvb, offset++, 1, ENC_BIG_ENDIAN);
		proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_rsp_err_idx, tvb, offset++, 1, ENC_BIG_ENDIAN);

		Mode = tvb_get_uint8(tvb, offset);
		proto_tree_add_uint(ecmp_tree, hf_ecmp_cyclic_setup_mode, tvb, offset++, 1, Mode);

		switch (Mode) {
			case 0: /* create */
			case 1: /* edit */
			case 2: /* finalise */
			case 3: /* delete */
				/* no mode specific data */
			break;

			case 4: /* exist */
				proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_link_exists, tvb, offset++, 1, ENC_BIG_ENDIAN);
			break;

			case 5: /* list */
			{
				uint8_t txCount, rxCount, linkno;

				/* num attribs */
				txCount = tvb_get_uint8(tvb, offset);
				cyclic_setup_attributes_root = proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_tx_count, tvb, offset++, 1, ENC_NA);

				/* link list */
				cyclic_setup_attributes = proto_item_add_subtree(cyclic_setup_attributes_root, ett_cyclic_setup_attribs);
				while (txCount > 0) {
					linkno = tvb_get_uint8(tvb, offset);
					proto_tree_add_uint(cyclic_setup_attributes, hf_ecmp_cyclic_setup_linkno, tvb, offset++, 1, linkno);
					txCount--;
				}

				rxCount = tvb_get_uint8(tvb, offset);
				cyclic_setup_attributes_root = proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_rx_count, tvb, offset++, 1, ENC_NA);

				/* link list */
				cyclic_setup_attributes = proto_item_add_subtree(cyclic_setup_attributes_root, ett_cyclic_setup_attribs);
				while (rxCount > 0) {
					linkno = tvb_get_uint8(tvb, offset);
					proto_tree_add_uint(cyclic_setup_attributes, hf_ecmp_cyclic_setup_linkno, tvb, offset++, 1, linkno);
					rxCount--;
				}
			}
			break;

			case 10: /* set */
			{
				/* num attribs */
				cyclic_setup_attributes_root = proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_setup_attrib_count, tvb, offset++, 1, ENC_BIG_ENDIAN);

				/* attrib list */
				cyclic_setup_attributes = proto_item_add_subtree(cyclic_setup_attributes_root, ett_cyclic_setup_attribs);
				while (offset < length) {
					proto_tree_add_item(cyclic_setup_attributes, hf_ecmp_cyclic_setup_attrib, tvb, offset++, 1, ENC_BIG_ENDIAN);
				}
			}
			break;

			case 11: /* get */
			case 12: /* get mappings */
			case 6: /* info */
				/* add the attributes as a tree */
				add_cyclic_setup_attributes(pinfo, offset, length, tvb, ecmp_tree);
			break;

			default:
				/* display payload as hex bytes */
				proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA);
			break;
		}
	}
}


/*a function to dissect "ProgramStatus" command  */
static void program_status(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
/*  Description:function to dissect Program Status command              */
/*                                                                      */
/*  Inputs:                                                             */
/*    offset - current offset of pointer within the ECMP frame          */
/*    command_value - function code of ECMP message                     */
/*    request - (1 = query, 0 = response)                               */
/*    tvb - Wireshark protocol tree                                     */
/*    ecmp_tree - Wireshark protocol tree                               */
/*    tree - Wireshark protocol tree                                    */
/*                                                                      */
/*  Returns: nothing                                                    */
/*                                                                      */
/*  Notes: for queries, the "offset" points to the "target".            */
/*         for responses, the "offset" points to the "status".          */
/*                                                                      */
/*  sample ECMP Request Frame                                           */
/*  0x61 - request code  (program control)                              */
/*  0x00 - option terminator                                            */
/*  0x00 - target   (0 = default program) <======== offset              */
/*                                                                      */
/*  sample ECMP Response Frame                                          */
/*  0xE1 - response code  (program control)                             */
/*  0x00 - option terminator                                            */
/*  0x01 - running state  (0=Stopped, 1=Running ... ) <======== offset  */
/*  0x00 - additional items                                             */

	proto_item*		ecmp_program_status_message_tree = NULL;

	/* differentiate between ECMP query and response  */
	if (request) {
		/*display the program control details sub-tree  */
		ecmp_program_status_message_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 1, ett_ecmp_program_status_message, NULL, "Program Status: (Query)");

		/* read the target  */
		proto_tree_add_item(ecmp_program_status_message_tree, hf_ecmp_program_status_target, tvb, offset, 1, ENC_NA);
	} else {
		/*display the program status details sub-tree  */
		ecmp_program_status_message_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_program_status_message, NULL, "Program Status: (Response)");

		/* read and display the Status */
		proto_tree_add_item(ecmp_program_status_message_tree, hf_ecmp_program_status_status, tvb, offset, 1, ENC_NA);
		offset += 1;

		/* read and display the Additional Items */
		proto_tree_add_item(ecmp_program_status_message_tree, hf_ecmp_program_status_additional_items, tvb, offset, 1, ENC_NA);
	}
}


/*a function to dissect "ProgramControl" command  */
static void program_control(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
/*  Description:function to dissect Program Control command             */
/*                                                                      */
/*  Inputs:                                                             */
/*    offset - current offset of pointer within the ECMP frame          */
/*    command_value - function code of ECMP message                     */
/*    request - (1 = query, 0 = response)	                        */
/*    tvb - Wireshark protocol tree                                     */
/*    ecmp_tree - Wireshark protocol tree                               */
/*    tree - Wireshark protocol tree                                    */
/*                                                                      */
/*  Returns: nothing                                                    */
/*                                                                      */
/* Notes: for queries, the "offset" points to the "target".              */
/*         for responses, the "offset" points to the "status".          */
/*                                                                      */
/*  sample ECMP Request Frame                                           */
/*  0x60 - request code  (program control)                              */
/*  0x00 - option terminator                                            */
/*  0x00 - target   (0 = default program) <======== offset              */
/*  0x01 - command  (1 = start the program)                             */
/*  0x00 - sub command                                                  */
/*                                                                      */
/*  sample ECMP Response Frame                                          */
/*  0xE0 - response code  (program control)                             */
/*  0x00 - option terminator                                            */
/*  0x00 - status  (0 = OK)  <======== offset                           */
/*                                                                      */

	proto_item*		ecmp_program_control_message_tree = NULL;

	/* differentiate between ECMP query and response  */
	if (request) {
		/*display the program control details sub-tree  */
		ecmp_program_control_message_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 3, ett_ecmp_program_control_message, NULL, "Program Control: (Query)");

		/* read the target  */
		proto_tree_add_item(ecmp_program_control_message_tree, hf_ecmp_program_control_target, tvb, offset, 1, ENC_NA);
		offset += 1;

		/* read the command  */
		proto_tree_add_item(ecmp_program_control_message_tree, hf_ecmp_program_control_command, tvb, offset, 1, ENC_NA);
		offset += 1;

		/* read the subcommand  */
		proto_tree_add_item(ecmp_program_control_message_tree, hf_ecmp_program_control_sub_command, tvb, offset, 1, ENC_NA);
	} else {
		/*display the program control details sub-tree  */
		ecmp_program_control_message_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 1, ett_ecmp_program_control_message, NULL, "Program Control: (Response)");

		/* read and display the Status */
		proto_tree_add_item(ecmp_program_control_message_tree, hf_ecmp_program_control_status, tvb, offset, 1, ENC_NA);
	}
}


/*a function to dissect "ModbusPDU" command  */
static void modbus_pdu(unsigned offset, bool request, tvbuff_t *tvb, packet_info* pinfo, proto_tree* ecmp_tree)
{
/*  Description:function to dissect Modbus PDU ECMP transactions        */
/*                                                                      */
/*  Inputs:                                                             */
/*    offset - current offset of pointer within the ECMP frame          */
/*    command_value - function code of ECMP message                     */
/*    request - (1 = query, 0 = response)                               */
/*    tvb - Wireshark protocol tree                                     */
/*    ecmp-tree - Wireshark protocol tree                               */
/*    tree - Wireshark protocol tree                                    */
/*                                                                      */
/*  Returns: nothing                                                    */
/*                                                                      */
/*  Notes: for queries, the "offset" points to the "size".               */
/*         for responses, the "offset" points to the size.              */
/*                                                                      */
/*  sample ECMP Request Frame (Read Holding Registers)                  */
/*  0x74 -  request code  (ModbusMaster)                                */
/*  0x00 - option terminator                                            */
/*  0x00 - size                             msb <======== offset        */
/*  0x05 - size                             lsb                         */
/*  0x03 - function code - read hold reg                                */
/*  0x07 - register address (#20.021)       msb                         */
/*  0xE4 - register address                 lsb                         */
/*  0x00 - number registers                 msb                         */
/*  0x03 - number registers                 lsb                         */
/*                                                                      */
/*  sample ECMP Response Frame                                          */
/*  0xF4 - response code  (ModbusMaster)                                */
/*  0x00 - option terminator                                            */
/*  0x00 - size                             msb <======== offset        */
/*  0x08 - size                             lsb                         */
/*  0x03 - function code - read hold reg                                */
/*  0x06 - byte count                                                   */
/*  0x30 - register #2021 value 12345       msb                         */
/*  0x39 - register #2021 value             lsb                         */
/*  0x03 - register #2022 value  787        msb                         */
/*  0x13 - register #2022 value             lsb                         */
/*  0x00 - register #2023 value  100        msb                         */
/*  0x64 - register #2023 value             lsb                         */

	tvbuff_t*		next_tvb;
	uint16_t			size = 0; /* from Modbus TCP/IP spec: number of bytes that follow */
	modbus_data_t   modbus_data;

	/* differentiate between ECMP query and response  */
	if (request) {
		/* read and display the Size  */
		proto_tree_add_item_ret_uint16(ecmp_tree, hf_ecmp_modbus_pdu_size, tvb, offset, 2, ENC_BIG_ENDIAN, &size);
		offset += 2;

		/* keep packet context */
		modbus_data.packet_type = QUERY_PACKET;
		modbus_data.mbtcp_transid = 0;
		modbus_data.unit_id = 0;
		next_tvb = tvb_new_subset_length(tvb, offset, size);
		call_dissector_with_data(modbus_handle, next_tvb, pinfo, ecmp_tree, &modbus_data);

	} else {
		/* read and display the Size  */
		proto_tree_add_item_ret_uint16(ecmp_tree, hf_ecmp_modbus_pdu_size, tvb, offset, 2, ENC_BIG_ENDIAN, &size);
		offset += 2;

		modbus_data.packet_type = RESPONSE_PACKET;
		modbus_data.mbtcp_transid = 0;
		modbus_data.unit_id = 0;
		next_tvb = tvb_new_subset_length(tvb, offset, size);
		call_dissector_with_data(modbus_handle, next_tvb, pinfo, ecmp_tree, &modbus_data);
	}
}


/*a function to dissect "Interrogate" command  */
static void interrogate(packet_info* pinfo, unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
/*  Description:  function to dissect Interrogate command               */
/*                                                                      */
/*  Inputs:                                                             */
/*    offset - current offset of pointer within the ECMP frame          */
/*    request - (1 = query, 0 = response)                               */
/*    tvb - rather complex structure that has the frame data            */
/*    ecmp-tree - Wireshark protocol tree                               */
/*                                                                      */
/*  Returns: nothing                                                    */
/*                                                                      */
/*  Notes: for queries, the "offset" points to the "item type".          */
/*         for responses, the "offset" points to the "item type".       */
/*                                                                      */
/*  sample ECMP Request Frame                          	                */
/*  0x02 - request code  (interrogate)                                  */
/*  0x00 - option terminator                           	                */
/*  0x00 - item type (0=ECMP command, 1=ECMP option)  <==== offset      */
/*  0x05 - count  (number of commands/options to follow)                */
/*  0x10 - item code #1  ECMP Read command                              */
/*  0x11 - item code #2  ECMP ReadWithType command                      */
/*  0x12 - item code #3  ECMP Write command                             */
/*  0x13 - item code #4  ECMP ObjectInfo command                        */
/*  0x14 - item code #5  ECMP GetNextObject command                     */
/*                                                                      */
/*  sample ECMP Response Frame                                          */
/*  0x82 - response code  (program control)                             */
/*  0x00 - option terminator                                            */
/*  0x00 - item type (0=ECMP command, 1=ECMP option)  <==== offset      */
/*  0x05 - count  (number of commands/options to follow)                */
/*  0x10 - item code #1                                                 */
/*  0x01 - supported    (ECMP Read command)                             */
/*  0x11 - item code #2                                               	*/
/*  0x01 - supported    (ECMP ReadWithType command)                     */
/*  0x12 - item code #3                                                 */
/*  0x01 - supported    (ECMP Write command)                            */
/*  0x13 - item code #4                                                 */
/*  0x01 - supported    (ECMP ObjectInfo command)                       */
/*  0x14 - item code #5                                                 */
/*  0x01 - supported    (ECMP GetNextObject command)                    */
/*                                                                      */
/*                                                                      */
/*  Item Type: 0 = ECMP command                                         */
/*             1 = ECMP option  (not currently implemented)             */
/*                                                                      */
/*  Item Code: 0 .. 0x7F for commands                                   */
/*             0 .. 2    for options                                    */
/*                                                                      */
/*  Item Support:  0 = not supported                                    */
/*                 1 = supported                                        */
/*                                                                      */


	const uint8_t interrogate_type_command = 0;

	proto_tree*		ecmp_interrogate_message_tree = NULL, *ecmp_interrogate_tree;
	proto_item*		ecmp_interrogate_message_item = NULL;
	uint8_t			item_type = 0; /* 0=ECMP command,  1=ECMP option  */
	uint8_t			command_req = 0; /* ECMP command  */
	uint8_t			supported = 0; /* ECMP command support status: 1=supported,  0=not supported  */
	uint32_t			count = 0; /* number of ECMP commands to be checked  */
	uint32_t			j; /* loop counter  */


	/* differentiate between ECMP query and response  */
	if (request) {

		/* identify the ECMP command we're dissecting  */
		ecmp_interrogate_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_interrogate_message, NULL, "Interrogate: (Query)");

		/* read the item_type (command/option setting)  */
		proto_tree_add_item_ret_uint8(ecmp_interrogate_tree, hf_ecmp_interrogate_item_type, tvb, offset, 1, ENC_NA, &item_type);
		offset += 1;

		/* read the count  */
		proto_tree_add_item_ret_uint(ecmp_interrogate_tree, hf_ecmp_interrogate_count, tvb, offset, 1, ENC_NA, &count);
		offset += 1;

		/*create the interrogate details sub-tree  */
		ecmp_interrogate_message_tree = proto_tree_add_subtree(ecmp_interrogate_tree, tvb, offset, count, ett_ecmp_interrogate_message, &ecmp_interrogate_message_item, "ECMP Commands to be Checked");

		/* display the item_codes (commands to be checked)  */
		if (item_type == interrogate_type_command) {
			/* loop on the commands  */
			for (j = 0; j < count; j++) {

				/* display the commands to be checked  */
				proto_tree_add_item(ecmp_interrogate_message_tree, hf_ecmp_interrogate_command, tvb, offset, 1, ENC_NA);
				offset += 1;
			}
			proto_item_set_len(ecmp_interrogate_message_item, count);

		} else {
			expert_add_info(pinfo, ecmp_interrogate_message_item, &ei_ecmp_options_not_implemented);
		}

	} else {
		/* identify the ECMP command we're dissecting  */
		ecmp_interrogate_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_interrogate_message, NULL, "Interrogate: (Response)");

		/* read the item_type (command/option setting)  */
		item_type = tvb_get_uint8(tvb, offset);
		offset += 1;

		/* read the count  */
		count = tvb_get_uint8(tvb, offset);
		offset += 1;

		/* display the item_codes (commands to be checked)  */
		if (item_type == interrogate_type_command) {

			/*create the interrogate details sub-tree  */
			ecmp_interrogate_message_tree = proto_tree_add_subtree(ecmp_interrogate_tree, tvb, offset, 1, ett_ecmp_interrogate_message, &ecmp_interrogate_message_item, "ECMP Commands Supported");

			/* loop on the commands  */
			for (j = 0; j < count; j++) {
				/* get the command code  */
				command_req = tvb_get_uint8(tvb, offset);
				offset += 1;

				/* get the support status  */
				supported = tvb_get_uint8(tvb, offset);
				offset += 1;

				/* display if the command is supported  */
				proto_tree_add_uint_format(ecmp_interrogate_message_tree, hf_ecmp_interrogate_command, tvb, offset, 1, command_req, "%s: %s",
					                try_val_to_str(command_req, command_vals),
					                try_val_to_str(supported, Interrogate_support_state));
			}

		} else {

			expert_add_info(pinfo, ecmp_interrogate_message_item, &ei_ecmp_options_not_implemented);
		}
	}
}


static void tunnel_frame(unsigned offset, bool request, tvbuff_t *tvb, proto_tree* ecmp_tree)
{
	proto_tree_add_item(ecmp_tree, hf_ecmp_tunnel_control, tvb, offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(ecmp_tree, hf_ecmp_tunnel_start_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(ecmp_tree, hf_ecmp_tunnel_end_flag, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* if a request add the check output flag */
	if (request) {
		proto_tree_add_item(ecmp_tree, hf_ecmp_tunnel_check_output_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
	}

	offset+= 1;

	/* Payload length */
	proto_tree_add_item(ecmp_tree, hf_ecmp_tunnel_size, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset+= 2;

	proto_tree_add_item(ecmp_tree, hf_ecmp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA);
	/*offset = tvb_reported_length(tvb);*/
}


/*  dissect_ecmp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
*   -----------------------------------------------------------------
*
*   Purpose:  Wireshark dissector for Emerson Control Techniques ECMP protocol
*
*
*   Inputs:     tvbuff_t        *tvb        - complex buffer structure holding the packet's bytes
*               packet_info     *pinfo      - structure holding information about the packet
*               proto_tree      *tree       - pointer to top-level tree (print lines)
*
*   Outputs:    none
*
*
*    Notes:      if tree = NULL, packet capture is running and dissector will only write into the Summary Line (Info Field)
*
*               if tree is non-NULL, packet capture has stopped because a packet has been selected (clicked)
*               In this case, we will display quite a bit of additional information about the packet.
*
*
*               To inspect the frame buffer (very difficult using the *tvb pointer), it's best to add this little debug
*               code snippet at the top of the program to copy the frame buffer into an array that you can inspect.
*
*                   static uint8_t jimbuf[512];                     // temp buffer for current frame data
*                   static int     lenframe = 0;                    // num bytes in the frame
*                   static int     j = 0;                           // loop counter
*                   static int16_t saved_offset = 0;                // saves offset for later restoration
*
*                   lenframe = tvb_captured_length(tvb);            // get the length of the frame
*                   saved_offset = offset;                          // temporarily save the "offset"
*
*                   for (j = 0; j < lenframe; j++) {                // loop to copy the frame buffer
*                       jimbuf[j] = tvb_get_uint8(tvb, offset);    // Wireshark function to read the frame buffer
*                       offset += 1;
*                   }
*                   offset = saved_offset;                          // restore the offset
*
*
*   Authors:  Sarah Bouremoum, Jim Lynch, Luke Orehawa, Others
*/

static int dissect_ecmp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	/* Initialize the items and trees*/
	proto_item		*ecmp_item = NULL;
	proto_item		*ecmp_transaction_id_item = NULL;
	proto_item		*ecmp_chunk_id_item = NULL;
	proto_tree		*ecmp_tree = NULL;

	/*initialise the values to be used */
	uint8_t	command_value = 0;
	bool request;
	uint8_t	transaction_id_value = 0;
	int	    offset = 0; /* index used to read data from the buffer*/
	int     framelen = 0; /* number of bytes in the frame   */

	/* note length of the UDP frame  */
	framelen = tvb_reported_length(tvb);

	if (framelen < ecmp_min_packet_size) {
		return 0;
	}

	/* this code block processes ECMP TCP messages (most of them)  */
	col_set_str(pinfo->cinfo, COL_PROTOCOL, "ECMP");
	col_clear(pinfo->cinfo,COL_INFO);


	/*declaration of variables*/
	offset = 4;

	/*display the first line of the tree (ECMP data)*/
	ecmp_item = proto_tree_add_item(tree, proto_ecmp, tvb, 0, -1, ENC_NA);
	ecmp_tree = proto_item_add_subtree(ecmp_item, ett_ecmp);

	/* display the information for the destination address */
	offset = add_transport_layer_frame(offset, tvb, ecmp_tree, hf_ecmp_destination_address);

	/* display the information for the source address */
	offset = add_transport_layer_frame(offset, tvb, ecmp_tree, hf_ecmp_source_address);

	/*display the transaction ID*/
	ecmp_transaction_id_item = proto_tree_add_item(ecmp_tree, hf_ecmp_transaction_id, tvb, offset, 1, ENC_BIG_ENDIAN );
	transaction_id_value = tvb_get_uint8(tvb, offset);

	if(transaction_id_value == 0) {
		proto_item_append_text(ecmp_transaction_id_item, "%s", " -> Not initiated by Request");
	}
	offset++;

	request = ((tvb_get_uint8(tvb, offset+2) & 0x80) == 0);
	if (request) {
		/* Calls the function to display the Response size */
		offset = get_response_size(offset, tvb, ecmp_tree);

		/* Calls the function to display the command and request/response */
		offset = add_command_codes(pinfo, offset, tvb, ecmp_tree, transaction_id_value, &command_value);

		/* Calls the function to display the option codes and its data */
		offset = add_option_codes(offset, pinfo, tvb, ecmp_tree);

		/* up til here all code for the request should be the same */
		switch(command_value)
		{
			case ECMP_COMMAND_IDENTIFY:
				/*Calls a method to display the attributes and its data */
				add_attributes(pinfo, offset, tvb, ecmp_tree, request);
				break;
			case ECMP_COMMAND_INFO:
				/* Info command is just the request code, nothing else to display  */
				proto_tree_add_item(ecmp_tree, hf_ecmp_info_command, tvb, 0, -1, ENC_NA);
				/*do nothing, no more data is present */
				break;
			case ECMP_COMMAND_INTERROGATE:
				interrogate(pinfo, offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_READ:
				get_parameter_definitions(pinfo, offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_READWITHTYPE:
				get_parameter_definitions(pinfo, offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_WRITE:
				get_parameter_definitions(pinfo, offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_OBJECTINFO:
				get_parameter_definitions(pinfo, offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_GETNEXTOBJECTS:
				get_parameter_definitions(pinfo, offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEOPEN:
				file_open(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEREAD:
				file_read(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEWRITE:
				file_write(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILECLOSE:
				file_close(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEINFO:
				file_info(pinfo, offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEDELETE:
				file_state_delete(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILESTATE:
				file_state_delete(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEPOS:
				file_pos(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILELIST:
				file_list(pinfo, offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_FILEEXISTS:
				file_exists(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_CYCLICLINK:
				cyclic_setup(pinfo, offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_PROGRAMCONTROL:
				program_control(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_PROGRAMSTATUS:
				program_status(offset, request, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_CYCLICFRAME:
				add_cyclic_frame_query(offset, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_TUNNELFRAME:
				tunnel_frame(offset, command_value, tvb, ecmp_tree);
				break;
			case ECMP_COMMAND_MODBUSPDU:
				modbus_pdu(offset, request, tvb, pinfo, ecmp_tree);
				break;
			default:
				proto_tree_add_expert_remaining(ecmp_tree, pinfo, &ei_ecmp_unknown_command, tvb, 0);
				break;
		}

		/* END of code to be modified */
	} else {
		uint8_t chunk_id_value = 0;
		int8_t status_value = 0;

		status_value = tvb_get_int8(tvb, offset); /*stores a signed value for status */

		proto_tree_add_item(ecmp_tree, hf_ecmp_status, tvb, offset, 1, ENC_BIG_ENDIAN);


		if (status_value >= 0) {
			offset++;
			chunk_id_value = tvb_get_uint8(tvb, offset);
			ecmp_chunk_id_item = proto_tree_add_item(ecmp_tree, hf_ecmp_chunk_id, tvb, offset, 1, ENC_BIG_ENDIAN);

			if(chunk_id_value == 0) {
				proto_item_append_text(ecmp_chunk_id_item, "%s", " -> Response is NOT Chunked");
			}

			offset++;

			/* Calls the function to display the option codes */
			offset = add_command_codes(pinfo, offset, tvb, ecmp_tree, transaction_id_value, &command_value);

			if ((status_value == 0) || (status_value == 1)) {
				/* Calls a method to display option codes */
				offset = add_option_codes(offset, pinfo, tvb, ecmp_tree);

				/* up til here all code for the response should be the same */
				switch(command_value)
				{
					case ECMP_COMMAND_IDENTIFY:
						/*Call a method to add category data */
						offset = add_category_codes(offset, tvb, ecmp_tree);
						/*Call a method to add attributes */
						add_attributes(pinfo, offset, tvb, ecmp_tree, request);
						break;
					case ECMP_COMMAND_INFO:
						add_info_response(offset, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_INTERROGATE:
						interrogate(pinfo, offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_READ:
						get_parameter_responses(pinfo, offset, command_value, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_READWITHTYPE:
						get_parameter_responses(pinfo, offset, command_value, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_WRITE:
						get_parameter_responses(pinfo, offset, command_value, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_OBJECTINFO:
						get_object_info_response(pinfo, offset, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_GETNEXTOBJECTS:
						get_parameter_responses(pinfo, offset, command_value, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEOPEN:
						file_open(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEREAD:
						file_read(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEWRITE:
						file_write(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILECLOSE:
						file_close(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEINFO:
						file_info(pinfo, offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEDELETE:
						file_state_delete(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILESTATE:
						file_state_delete(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEPOS:
						file_pos(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILELIST:
						file_list(pinfo, offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_FILEEXISTS:
						file_exists(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_CYCLICLINK:
						cyclic_setup(pinfo, offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_PROGRAMCONTROL:
						program_control(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_PROGRAMSTATUS:
						program_status(offset, request, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_CYCLICFRAME:
						add_cyclic_frame(offset, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_TUNNELFRAME:
						tunnel_frame(offset, command_value, tvb, ecmp_tree);
						break;
					case ECMP_COMMAND_MODBUSPDU:
						modbus_pdu(offset, request, tvb, pinfo, ecmp_tree);
						break;
					default:
						proto_tree_add_expert_remaining(ecmp_tree, pinfo, &ei_ecmp_unknown_command, tvb, 0);
						break;
				}
/********************************* END of code to be modified ***********************************/
			}
		}
	}

	return framelen;
}

/* this code block processes ECMP UDP messages (cyclic data)  */
static int dissect_ecmp_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	proto_item		*ecmp_item = NULL;
	proto_tree		*ecmp_tree = NULL;
	proto_tree		*ecmp_cyclic_data_32_bit_display_tree = NULL;
	proto_tree		*ecmp_cyclic_data_16_bit_display_tree = NULL;
	proto_tree		*ecmp_cyclic_data_8_bit_display_tree = NULL;
	uint8_t	command_value = 0;
	uint8_t	type_value = 0;
	uint8_t	transaction_id_value = 0;
	uint16_t	offset = 0; /* index used to read data from the buffer*/
	int     framelen = 0; /* number of bytes in the frame   */
	uint8_t	scheme = 0; /* 0=no scheme, 1=grandmaster setup */

	/* note length of the UDP frame  */
	framelen = tvb_reported_length(tvb);

	if (framelen < ecmp_min_packet_size) {
		return 0;
	}

	/* display the "ECMP" protocol indication in the PROTOCOL field  */
	col_set_str(pinfo->cinfo, COL_PROTOCOL, "ECMP");

	/* Clear out stuff in the info column */
	col_clear(pinfo->cinfo,COL_INFO);

	/* adjust offset to point at transaction ID  */
	offset += 2;

	/*getting the information from the buffer*/
	transaction_id_value = tvb_get_uint8(tvb, offset);

	/* adjust offset to point at ECMP query/response code  */
	offset += 3;

	/* calculate if it's a query or response (type_r_r)  */
	type_value = tvb_get_uint8(tvb, offset);

	/* determine the ECMP command code  */
	command_value = type_value & 0x7f;

	/* update offset to point to cyclic link number  */
	offset += 2;

	/* Information displayed in the Info column*/
	col_add_fstr(pinfo->cinfo, COL_INFO, "%s, %s. Transaction ID: %d",
					val_to_str(pinfo->pool, command_value, command_vals, "Unknown Type:0x%02x"),
					tfs_get_string((type_value & 0x80) >> 7, &tfs_response_request),
					transaction_id_value);

	/*display the first line of the tree (ECMP data)*/
	ecmp_item = proto_tree_add_item(tree, proto_ecmp, tvb, 0, -1, ENC_NA); /*item created*/
	ecmp_tree = proto_item_add_subtree(ecmp_item, ett_ecmp); /*tree created*/

	/* indicate cyclic link message  */
	proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_link_req_resp, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* display the cyclic link number  */
	proto_tree_add_item(ecmp_tree, hf_ecmp_cyclic_link_number_display, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	if (type_value & 0x80) {
		/* response data handled here  */
		/* display the alignment  */
		proto_tree_add_item(ecmp_tree, hf_ecmp_udp_alignment, tvb, offset, 1, ENC_NA);
		offset += 1;

		/* display the scheme  */
		proto_tree_add_item_ret_uint8(ecmp_tree, hf_ecmp_udp_scheme, tvb, offset, 1, ENC_NA, &scheme);
		offset += 1;

		/* if the scheme is 1, there is grandmaster data to be printed  */
		if (scheme == 1) {
			proto_tree_add_item(ecmp_tree, hf_ecmp_grandmaster, tvb, offset, 8, ENC_BIG_ENDIAN);
			offset += 8;

			proto_tree_add_item(ecmp_tree, hf_ecmp_process_time, tvb, offset, 8, ENC_BIG_ENDIAN);
			offset += 8;
		}

		/* create the Cyclic Data Display (uint32_t format) sub-tree  */
		ecmp_cyclic_data_32_bit_display_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_cyclic_data_32_bit_display, NULL,
				"Cyclic Data (32-bit hex unsigned format): ");

		/* display the raw hex data for the cyclic data in a 32-bit format  */
		display_raw_cyclic_data(cyclic_display_long_format, offset, framelen - offset, tvb, pinfo, ecmp_cyclic_data_32_bit_display_tree);

		/* create the Cyclic Data Display (uint16_t format) sub-tree  */
		ecmp_cyclic_data_16_bit_display_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_cyclic_data_16_bit_display, NULL,
				"Cyclic Data (16-bit hex unsigned format): ");

		/* display the raw hex data for the cyclic data in a 16-bit format  */
		display_raw_cyclic_data(cyclic_display_word_format, offset, framelen - offset, tvb, pinfo, ecmp_cyclic_data_16_bit_display_tree);

		/* display the raw hex data for the cyclic data in a uint8_t format  */
		ecmp_cyclic_data_8_bit_display_tree = proto_tree_add_subtree(ecmp_tree, tvb, offset, 2, ett_ecmp_cyclic_data_8_bit_display, NULL,
				"Cyclic Data (8-bit hex unsigned format): ");

		/* display the raw hex data for the cyclic data in 8-bit format */
		display_raw_cyclic_data(cyclic_display_byte_format, offset, framelen - offset, tvb, pinfo, ecmp_cyclic_data_8_bit_display_tree);
	}

	return tvb_reported_length(tvb);
}

/* Function to register the protocol */
/* Wireshark literally scans this file (packet-ecmp.c) to find this function  */
/* note: this function MUST start in column 1, due to the scanning mentioned above */
void proto_register_ecmp (void)
{
	/* A header field is something you can search/filter on.
	*
	* We create a structure to register our fields. It consists of an
	* array of hf_register_info structures, each of which are of the format
	* {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}.
	*/

	static hf_register_info hf[] = {

	{ &hf_ecmp_destination_address,
	{ "Destination Address scheme", "ecmp.destination_address", FT_UINT8, BASE_DEC, VALS(address_scheme), 0, NULL, HFILL }},

	{ &hf_ecmp_source_address,
	{ "Source Address scheme", "ecmp.source_address", FT_UINT8, BASE_DEC, VALS(address_scheme), 0, NULL, HFILL }},

	{ &hf_ecmp_diagnostic,
	{ "Diagnostic group", "ecmp.diagnostic", FT_UINT8, BASE_DEC, VALS(diagnostic), 0, NULL, HFILL }},

	{ &hf_ecmp_command,
	{ "Command", "ecmp.command", FT_UINT8, BASE_DEC, VALS(command_vals), 0x7F, NULL, HFILL }},

	{ &hf_ecmp_option,
	{ "Option", "ecmp.option", FT_UINT8, BASE_DEC, VALS(option_code), 0x0, NULL, HFILL }},

	{ &hf_ecmp_type_rr,
	{ "Type", "ecmp.type", FT_BOOLEAN, 8, TFS(&tfs_response_request), 0x80, "ECMP Type (request/response)", HFILL }},

	{ &hf_ecmp_chunking,
	{ "Chunks allowed","ecmp.chunking", FT_UINT16, BASE_DEC, NULL,0xF000, "ECMP number of chunks allowed", HFILL}},

	{ &hf_ecmp_max_response_size,
	{ "Maximum Response Size","ecmp.response_size", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), 0x0FFF, NULL, HFILL}},

	{ &hf_ecmp_category,
	{ "Device", "ecmp.category", FT_UINT8, BASE_DEC, VALS(category), 0x0, "ECMP Category (drive or option module)", HFILL }},

	{ &hf_ecmp_attribute,
	{ "Attribute", "ecmp.attribute", FT_UINT8, BASE_DEC, VALS(attribute), 0x0, NULL, HFILL }},

	{ &hf_ecmp_no_of_attributes,
	{ "Number of attributes", "ecmp.attribute_number", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_status,
	{ "Status", "ecmp.status", FT_INT8, BASE_DEC, VALS(status), 0x0, NULL, HFILL }},

	{ &hf_ecmp_chunk_id,
	{ "Chunk ID", "ecmp.chunkID", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_transaction_id,
	{ "Transaction ID", "ecmp.transactionID", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_drive_type,
	{ "Product Type", "ecmp.drive_type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_drive_derivative,
	{ "Drive Derivative", "ecmp.drive_derivative", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_drive_factory_fit_category_id,
	{ "Factory Fitted Option ID", "ecmp.drive_factory_fit_category_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_category_id,
	{ "Option ID", "ecmp.category_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_cyclic_link_num,
	{ "Cyclic Link Number", "ecmp.link_num", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_cyclic_align,
	{ "Alignment", "ecmp.cyclic_align", FT_UINT8, BASE_DEC, VALS(cyclic_align), 0x0, "ECMP Cyclic Data Alignment", HFILL }},

	{ &hf_ecmp_cyclic_scheme,
	{ "Scheme", "ecmp.cyclic_scheme", FT_UINT8, BASE_DEC, VALS(cyclic_scheme), 0x0, "ECMP Cyclic Scheme", HFILL }},

	{ &hf_ecmp_cyclic_link_number_display,
	{ "Cyclic Link Number Display", "ecmp.link_num_display", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_buffer_size,
	{"Buffer Size", "ecmp.buffer_size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_max_response,
	{"Maximum Response Time", "ecmp.max_response", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_max_handle,
	{"Maximum Handle Period", "ecmp.max_handle", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_info_address,
	{"Number of Default Route Addresses", "ecmp.count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_parameter_address,
	{"Parameter Addressing Scheme", "ecmp.parameter.address", FT_UINT8, BASE_DEC, VALS(parameter_address_scheme), 0x0, NULL, HFILL}},

	{ &hf_ecmp_number_of_parameter_definitions,
	{"Number of Parameter Definitions", "ecmp.parameter.definitions", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_number_of_parameter_responses,
	{"Number of Parameter Responses", "ecmp.parameter.response", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_parameter_status,
	{"Parameter Status", "ecmp.parameter.status", FT_INT8, BASE_DEC, VALS(parameter_access_status), 0x0, NULL, HFILL}},

	{ &hf_ecmp_data_type,
	{"Parameter Data Type", "ecmp.parameter.data_type", FT_UINT8, BASE_DEC, VALS(parameter_data_types), 0x0, NULL, HFILL}},

	{ &hf_ecmp_info_type,
	{"Info Type", "ecmp.info_type", FT_UINT8, BASE_DEC, VALS(info_type), 0x0, NULL, HFILL}},

	{ &hf_ecmp_file_status,
	{"File Status", "ecmp.file.status", FT_INT8, BASE_DEC, VALS(file_status), 0x0, NULL, HFILL}},

	{ &hf_ecmp_file_handle,
	{"File Handle", "ecmp.file.handle", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}},

	{ &hf_ecmp_file_attributes,
	{"Attribute", "ecmp.file.attribute", FT_UINT8, BASE_DEC, VALS(file_attributes), 0x0, "File attributes", HFILL}},

	{ &hf_ecmp_file_ref_point,
	{"Reference Point", "ecmp.file.reference", FT_UINT8, BASE_DEC, VALS(file_ref_point), 0x0, "File reference points", HFILL}},

	{ &hf_ecmp_tunnel_control,
	{"Control", "ecmp.tunnel_control", FT_UINT8, BASE_DEC, NULL, 0x0, "Tunnel frame control field", HFILL}},

	{ &hf_ecmp_tunnel_start_flag,
	{"Start", "ecmp.tunnel_control.start", FT_BOOLEAN, 8, NULL, TUNNEL_START_FLAG, "Tunnel frame control field start flag", HFILL}},

	{ &hf_ecmp_tunnel_end_flag,
	{"End", "ecmp.tunnel_control.end", FT_BOOLEAN, 8, NULL, TUNNEL_END_FLAG, "Tunnel frame control field end flag", HFILL}},

	{ &hf_ecmp_tunnel_check_output_flag,
	{"Check Output", "ecmp.tunnel_control.check", FT_BOOLEAN, 8, NULL, TUNNEL_CHECK_OUTPUT_FLAG, "Tunnel frame control field check output flag", HFILL}},

	{ &hf_ecmp_tunnel_size,
	{"Size", "ecmp.tunnel_size", FT_UINT16, BASE_DEC, NULL, 0x0, "Tunnel frame payload size", HFILL}},

	{ &hf_ecmp_cyclic_setup_mode,
	{"Mode", "ecmp.cyclic_setup.mode", FT_UINT8, BASE_DEC, VALS(cyclic_setup_mode), 0x0, "Cyclic setup mode", HFILL}},

	{ &hf_ecmp_cyclic_setup_linkno,
	{"Link No", "ecmp.cyclic_setup.linkno", FT_UINT8, BASE_DEC, NULL, 0x0, "Cyclic setup link no", HFILL}},

	{ &hf_ecmp_cyclic_setup_dir,
	{"Direction", "ecmp.cyclic_setup.direction", FT_UINT8, BASE_DEC, VALS(cyclic_setup_link_dir), 0x0, "Cyclic setup link direction", HFILL}},

	{ &hf_ecmp_cyclic_setup_attrib_count,
	{"Count", "ecmp.cyclic_setup.attrib_count", FT_UINT8, BASE_DEC, NULL, 0x0, "Cyclic setup attribute count", HFILL}},

	{ &hf_ecmp_cyclic_setup_attrib,
	{"Attribute", "ecmp.cyclic_setup.attrib", FT_UINT8, BASE_DEC, VALS(cyclic_attributes), 0x0, "Cyclic setup attribute", HFILL}},

	{ &hf_ecmp_cyclic_setup_rsp_status,
	{"Status", "ecmp.cyclic_setup.rsp_status", FT_INT8, BASE_DEC, NULL, 0x0, "Cyclic setup status", HFILL}},

	{ &hf_ecmp_cyclic_setup_rsp_err_idx,
	{"Error Index", "ecmp.cyclic_setup.rsp_err_idx", FT_UINT8, BASE_DEC, NULL, 0x0, "Cyclic setup error index", HFILL}},

	{ &hf_ecmp_cyclic_setup_link_exists,
	{"Existence State", "ecmp.cyclic_setup.exists.state", FT_UINT8, BASE_DEC, VALS(cyclic_setup_link_exists), 0x0, "Cyclic setup exists state", HFILL}},

	{ &hf_ecmp_cyclic_link_req_resp,
	{"Cyclic Link - Request-Response", "ecmp.cyclic_link.request.response", FT_BOOLEAN, BASE_NONE, TFS(&tfs_response_request), 0x0, "Cyclic link request - response", HFILL}},

	{ &hf_ecmp_attribute_string,
	{ "Attribute string", "ecmp.attribute_string", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_file_name,
	{ "File name", "ecmp.file_name", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_directory,
	{ "Directory", "ecmp.directory", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_names_scheme,
	{ "Names Scheme", "ecmp.names_scheme", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_variable_name,
	{ "Variable name", "ecmp.variable_name", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_unit_id_string,
	{ "Unit ID String", "ecmp.unit_id_string", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_ecmp_string,
	{ "ECMP string", "ecmp.ecmp_string", FT_UINT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_info_command,
	{ "Info command data", "ecmp.info_command", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_process_time,
	{ "ProcessAt time", "ecmp.processat_time",  FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_cyclic_frame_time,
	{ "Cyclic frame time", "ecmp.cyclic_frame_time",  FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_grandmaster,
	{ "Grandmaster", "ecmp.grandmaster",  FT_EUI64, BASE_NONE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_data,
	{ "Data", "ecmp.data",  FT_BYTES, SEP_SPACE, NULL, 0x0, NULL, HFILL }},

	{ &hf_ecmp_response_data,
	{ "Response Data", "ecmp.response_data",  FT_BYTES, SEP_SPACE, NULL, 0x0, NULL, HFILL }},

	/* Generated from convert_proto_tree_add_text.pl */
	{ &hf_ecmp_physical_address, { "Physical address", "ecmp.physical_address", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL }},
	{ &hf_ecmp_logical_address, { "Logical address", "ecmp.logical_address", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL }},
	{ &hf_ecmp_primary_colour, { "Primary Colour", "ecmp.primary_colour", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_secondary_colour, { "Secondary Colour", "ecmp.secondary_colour", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_number_of_subsequent_object_requests, { "Number of subsequent object requests", "ecmp.number_of_subsequent_object_requests", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_number_of_decimal_places, { "Number of decimal places", "ecmp.number_of_decimal_places", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_no_information_available, { "No Information available", "ecmp.no_information_available", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_param_format_bit_default_unipolar, { "BU- Bit default/Unipolar", "ecmp.param_format.bit_default_unipolar", FT_UINT32, BASE_DEC, NULL, 0x00000001, NULL, HFILL }},
	{ &hf_ecmp_param_format_write_allowed, { "W- Write allowed", "ecmp.param_format.write_allowed", FT_UINT32, BASE_DEC, NULL, 0x00000002, NULL, HFILL }},
	{ &hf_ecmp_param_format_read_not_allowed, { "NR- Read not allowed", "ecmp.param_format.read_not_allowed", FT_UINT32, BASE_DEC, NULL, 0x00000004, NULL, HFILL }},
	{ &hf_ecmp_param_format_protected_from_destinations, { "PT- Protected from destinations", "ecmp.param_format.protected_from_destinations", FT_UINT32, BASE_DEC, NULL, 0x00000008, NULL, HFILL }},
	{ &hf_ecmp_param_format_parameter_not_visible, { "NV- Parameter not visible", "ecmp.param_format.parameter_not_visible", FT_UINT32, BASE_DEC, NULL, 0x00000010, NULL, HFILL }},
	{ &hf_ecmp_param_format_not_clonable, { "NC- Not clonable", "ecmp.param_format.not_clonable", FT_UINT32, BASE_DEC, NULL, 0x00000020, NULL, HFILL }},
	{ &hf_ecmp_param_format_voltage_or_current_rating_dependent, { "RA- Voltage or current rating dependent", "ecmp.param_format.voltage_or_current_rating_dependent", FT_UINT32, BASE_DEC, NULL, 0x00000040, NULL, HFILL }},
	{ &hf_ecmp_param_format_parameter_has_no_default, { "ND- Parameter has no default", "ecmp.param_format.parameter_has_no_default", FT_UINT32, BASE_DEC, NULL, 0x00000080, NULL, HFILL }},
	{ &hf_ecmp_param_format_number_of_decimal_places, { "DP- Number of Decimal places", "ecmp.param_format.number_of_decimal_places", FT_UINT32, BASE_DEC, NULL, 0x00000F00, NULL, HFILL }},
	{ &hf_ecmp_param_format_variable_maximum_and_minimum, { "VM- Variable maximum and minimum", "ecmp.param_format.variable_maximum_and_minimum", FT_UINT32, BASE_DEC, NULL, 0x00001000, NULL, HFILL }},
	{ &hf_ecmp_param_format_string_parameter, { "TE- String parameter", "ecmp.param_format.string_parameter", FT_UINT32, BASE_DEC, NULL, 0x00002000, NULL, HFILL }},
	{ &hf_ecmp_param_format_destination_set_up_parameter, { "DE- destination set-up parameter", "ecmp.param_format.destination_set_up_parameter", FT_UINT32, BASE_DEC, NULL, 0x00004000, NULL, HFILL }},
	{ &hf_ecmp_param_format_filtered_when_displayed, { "FI- Filtered when displayed", "ecmp.param_format.filtered_when_displayed", FT_UINT32, BASE_DEC, NULL, 0x00008000, NULL, HFILL }},
	{ &hf_ecmp_param_format_pseudo_read_only, { "PR- Pseudo read only", "ecmp.param_format.pseudo_read_only", FT_UINT32, BASE_DEC, NULL, 0x00010000, NULL, HFILL }},
	{ &hf_ecmp_param_format_display_format, { "DF- Display Format", "ecmp.param_format.display_format", FT_UINT32, BASE_DEC, VALS(display_format), 0x001E0000, NULL, HFILL }},
	{ &hf_ecmp_param_format_floating_point_value, { "FL- Floating point value", "ecmp.param_format.floating_point_value", FT_UINT32, BASE_DEC, NULL, 0x00200000, NULL, HFILL }},
	{ &hf_ecmp_param_format_units, { "UNITS", "ecmp.param_format.units", FT_UINT32, BASE_DEC, VALS(format_units), 0x0FC00000, NULL, HFILL }},
	{ &hf_ecmp_string_id, { "String ID", "ecmp.string_id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_address_scheme_menu, { "Menu", "ecmp.address_scheme.menu", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_address_scheme_parameter, { "Parameter", "ecmp.address_scheme.parameter", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_address_scheme_slot, { "Slot", "ecmp.address_scheme.slot", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_address_scheme_null_byte_size, { "NULL byte size", "ecmp.address_scheme.null_byte_size", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_display_unit_id, { "Unit ID", "ecmp.display_unit_id", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_boolean, { "Data", "ecmp.data.boolean", FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL }},
	{ &hf_ecmp_data_int8, { "Data", "ecmp.data.int8", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_uint8, { "Data", "ecmp.data.uint8", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_int16, { "Data", "ecmp.data.int16", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_uint16, { "Data", "ecmp.data.uint16", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_int32, { "Data", "ecmp.data.int32", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_uint32, { "Data", "ecmp.data.uint32", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_int64, { "Data", "ecmp.data.int64", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_uint64, { "Data", "ecmp.data.uint64", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_float, { "Data", "ecmp.data.float", FT_FLOAT, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_data_double, { "Data", "ecmp.data.double", FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_access_mode, { "Access Mode", "ecmp.access_mode", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_open_in_non_blocking_mode, { "Open in non-blocking mode", "ecmp.open_in_non_blocking_mode", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }},
	{ &hf_ecmp_open_file_relative_to_specified_directory_handle, { "Open file relative to specified directory handle", "ecmp.open_file_relative_to_specified_directory_handle", FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL }},
	{ &hf_ecmp_file_access_mode, { "File Access Mode", "ecmp.file_access_mode", FT_UINT8, BASE_DEC, VALS(file_status_mode), 0x0F, NULL, HFILL }},
	{ &hf_ecmp_additional_scheme, { "Additional Scheme", "ecmp.additional_scheme", FT_UINT8, BASE_DEC, VALS(additional_scheme_vals), 0x0, NULL, HFILL }},
	{ &hf_ecmp_scheme_data_length, { "Length", "ecmp.scheme_data_length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_number_of_requested_bytes, { "Number of requested bytes", "ecmp.number_of_requested_bytes", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_number_of_bytes_transferred, { "Number of bytes transferred", "ecmp.number_of_bytes_transferred", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_crc, { "CRC", "ecmp.crc", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_ref_offset, { "Offset", "ecmp.ref_offset", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_number_of_files_to_list, { "Number of files to list", "ecmp.number_of_files_to_list", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_file_hash, { "Hash", "ecmp.file_hash", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_item_type, { "Item type", "ecmp.item_type", FT_UINT8, BASE_DEC, VALS(item_type_vals), 0x0, NULL, HFILL }},
	{ &hf_ecmp_file_integrity, { "File Integrity", "ecmp.file_integrity", FT_BOOLEAN, 8, TFS(&tfs_ok_error), 0x01, NULL, HFILL }},
	{ &hf_ecmp_display_attr_read_only, { "Read Only", "ecmp.display_attr.read_only", FT_BOOLEAN, 8, NULL, 0x01, NULL, HFILL }},
	{ &hf_ecmp_display_attr_hidden, { "Hidden", "ecmp.display_attr.hidden", FT_BOOLEAN, 8, NULL, 0x02, NULL, HFILL }},
	{ &hf_ecmp_display_attr_system, { "System", "ecmp.display_attr.system", FT_BOOLEAN, 8, NULL, 0x04, NULL, HFILL }},
	{ &hf_ecmp_display_attr_volume_label, { "Volume Label", "ecmp.display_attr.volume_label", FT_BOOLEAN, 8, NULL, 0x08, NULL, HFILL }},
	{ &hf_ecmp_display_attr_subdirectory, { "Subdirectory", "ecmp.display_attr.subdirectory", FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL }},
	{ &hf_ecmp_display_attr_archive, { "Archive", "ecmp.display_attr.archive", FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL }},
	{ &hf_ecmp_display_creation, { "Display creation", "ecmp.display_creation", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_display_modification, { "Display modification", "ecmp.display_modification", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_interrogate_item_type, { "Item Type", "ecmp.interrogate_item_type", FT_UINT8, BASE_DEC, VALS(Interrogate_command_option_state), 0x0, NULL, HFILL }},
	{ &hf_ecmp_interrogate_count, { "Count", "ecmp.interrogate_count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_modbus_pdu_size, { "Size", "ecmp.modbus_pdu_size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
#if 0
	{ &hf_ecmp_destination_scheme, { "Destination Scheme", "ecmp.destination_scheme", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
#endif
	{ &hf_ecmp_program_control_target, { "Target", "ecmp.program_control_target", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_control_command, { "Command", "ecmp.program_control_command", FT_UINT8, BASE_DEC, VALS(command_code_list), 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_control_sub_command, { "Sub-Command", "ecmp.program_control_sub_command", FT_UINT8, BASE_DEC, VALS(sub_command_code_list), 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_control_status, { "Status", "ecmp.program_control_status", FT_UINT8, BASE_DEC, VALS(status_list), 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_status_target, { "Target", "ecmp.program_status_target", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_status_status, { "Status", "ecmp.program_status_status", FT_UINT8, BASE_DEC, VALS(running_state_list), 0x0, NULL, HFILL }},
	{ &hf_ecmp_program_status_additional_items, { "Additional Items", "ecmp.program_status_additional_items", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_cyclic_setup_max_mappings, { "Max Mappings", "ecmp.cyclic_setup.max_mappings", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_cyclic_setup_start_offset, { "Start Offset", "ecmp.cyclic_setup.start_offset", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_cyclic_setup_tx_count, { "Tx Count", "ecmp.cyclic_setup.tx_count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_cyclic_setup_rx_count, { "Rx Count", "ecmp.cyclic_setup.rx_count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_udp_alignment, { "Alignment", "ecmp.udp_alignment", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_udp_scheme, { "Scheme", "ecmp.udp_scheme", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_cyclic_data, { "Cyclic Data", "ecmp.cyclic_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_version_summary, { "Version summary", "ecmp.version_summary", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_min_param_menu, { "Min parameter in menu", "ecmp.min_param_menu", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_max_param_menu, { "Max parameter in menu", "ecmp.max_param_menu", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_file_length, { "File length", "ecmp.file_length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_mec_offset, { "mec_offset", "ecmp.mec_offset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_sample_period, { "Sample period", "ecmp.sample_period", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_timeout, { "RX Timeout", "ecmp.rx_timeout", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, UNS(&units_microseconds), 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_action, { "Action", "ecmp.rx_action", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_event_destination, { "Event Destination", "ecmp.rx_event_destination", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_event, { "Event", "ecmp.rx_event", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_late_handler_action, { "Action", "ecmp.rx_late_handler_action", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_late_handler_event_destination, { "Event Destination", "ecmp.rx_late_handler_event_destination", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_rx_late_handler_event, { "Event", "ecmp.rx_late_handler_event", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_transport_addr_scheme, { "Scheme", "ecmp.transport_addr_scheme", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_transport_addr, { "Transport address", "ecmp.transport_addr", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_mapping_item_offset, { "Offset", "ecmp.mapping_item_offset", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_mapping_item_scheme, { "Scheme", "ecmp.mapping_item_scheme", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_setup_attribute, { "Attribute", "ecmp.setup_attribute", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_mec_period, { "mec period", "ecmp.mec_period", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
	{ &hf_ecmp_interrogate_command, { "Command", "ecmp.interrogate_command", FT_UINT8, BASE_DEC, VALS(command_vals), 0x0, NULL, HFILL }}
};

/* array to store pointers to the ids of the subtrees that we may be creating */
	static int *ett[] = {
		&ett_ecmp,
		&ett_ecmp_address,
		&ett_ecmp_response_size,
		&ett_ecmp_command,
		&ett_ecmp_category,
		&ett_ecmp_option,
		&ett_ecmp_option_data,
		&ett_ecmp_attribute,
		&ett_ecmp_attribute_data,
		&ett_ecmp_cyclic_scheme,
		&ett_ecmp_interrogate_message,
		&ett_ecmp_info_type,
		&ett_ecmp_info_count,
		&ett_ecmp_param_address,
		&ett_ecmp_access_mode,
		&ett_ecmp_access_file,
		&ett_ecmp_file_read,
		&ett_ecmp_file_write,
		&ett_ecmp_file_info,
		&ett_ecmp_file_info_att,
		&ett_ecmp_file_position,
		&ett_ecmp_file_list_no,
		&ett_ecmp_file_list,
		&ett_ecmp_tunnel_3s_goodframe,
		&ett_ecmp_tunnel_3s_size,
		&ett_ecmp_tunnel_3s_service,
		&ett_cyclic_setup_attribs,
		&ett_cyclic_setup_transport_addr,
		&ett_cyclic_setup_attrib_item,
		&ett_ecmp_cyclic_data_32_bit_display,
		&ett_ecmp_cyclic_data_16_bit_display,
		&ett_ecmp_cyclic_data_8_bit_display,
		&ett_ecmp_modbus_pdu_message,
		&ett_ecmp_program_control_message,
		&ett_ecmp_program_status_message
	};

	static ei_register_info ei[] = {
		{ &ei_ecmp_unknown_command, { "ecmp.unknown_command", PI_PROTOCOL, PI_WARN, "Unknown Command", EXPFILL }},
		{ &ei_ecmp_color, { "ecmp.color_invalid", PI_PROTOCOL, PI_WARN, "Invalid color data value", EXPFILL }},
		{ &ei_ecmp_option, { "ecmp.ecmp_option.unknown", PI_PROTOCOL, PI_WARN, "ERROR - Unrecognised Option Code", EXPFILL }},
		{ &ei_ecmp_data_type, { "ecmp.data_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown Data Type", EXPFILL }},
		{ &ei_ecmp_parameter_addressing_scheme, { "ecmp.incorrect_parameter_addressing_scheme", PI_PROTOCOL, PI_WARN, "Incorrect parameter addressing scheme", EXPFILL }},
		{ &ei_ecmp_info_type, { "ecmp.info_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown info type", EXPFILL }},
		{ &ei_ecmp_attribute_type, { "ecmp.attribute_type.unknown", PI_PROTOCOL, PI_WARN, "Wrong attribute type", EXPFILL }},
		{ &ei_ecmp_item_type, { "ecmp.item_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown item type", EXPFILL }},
		{ &ei_ecmp_options_not_implemented, { "ecmp.options_not_implemented", PI_UNDECODED, PI_WARN, "ECMP Options Not Implemented", EXPFILL }}
	};

	expert_module_t* expert_ecmp;

	proto_ecmp = proto_register_protocol ("ECMP", "ECMP", "ecmp");
	ecmp_tcp_handle = register_dissector("ecmp_tcp", dissect_ecmp_tcp, proto_ecmp);
	ecmp_udp_handle = register_dissector("ecmp_udp", dissect_ecmp_udp, proto_ecmp);


	/* full name short name and abbreviation (display filter name)*/
	proto_register_field_array(proto_ecmp, hf, array_length (hf));
	proto_register_subtree_array (ett, array_length (ett));
	expert_ecmp = expert_register_protocol(proto_ecmp);
	expert_register_field_array(expert_ecmp, ei, array_length(ei));
}

/* Function to initialise the dissector*/
/* Wireshark literally scans this file (packet-ecmp.c) to find this function  */
void proto_reg_handoff_ecmp(void)
{
	/* Cyclic frames are over UDP and non-cyclic are over TCP */
	dissector_add_uint_with_preference("udp.port", ECMP_TCP_PORT, ecmp_udp_handle);
	dissector_add_uint_with_preference("tcp.port", ECMP_TCP_PORT, ecmp_tcp_handle);

	/* Modbus dissector hooks */
	modbus_handle = find_dissector_add_dependency("modbus", proto_ecmp);
	proto_modbus = proto_get_id_by_filter_name( "modbus" );
}


/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */
