/* packet-ua3g.c
 * Routines for UA/UDP (Universal Alcatel over UDP) packet dissection.
 * Copyright 2012, Alcatel-Lucent Enterprise <lars.ruoff@alcatel-lucent.com>
 *
 * $Id: packet-ua3g.c 41899 2012-04-03 01:25:24Z morriss $
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib.h>
#include "epan/packet.h"
#include "epan/prefs.h"
#include "epan/tap.h"
#include "epan/value_string.h"
#include "epan/emem.h"
#include "packet-uaudp.h"

extern int isprint( int ch );

/*-----------------------------------------------------------------------------
    Globals
    ---------------------------------------------------------------------------*/
static int  proto_ua3g			= -1;
static gint ett_ua3g			= -1;
static gint ett_ua3g_body		= -1;
static gint ett_ua3g_param		= -1;
static gint ett_ua3g_option		= -1;
static int  hf_ua3g_length		= -1;
static int  hf_ua3g_opcode		= -1;
static int  hf_ua3g_ip			= -1;
static int  hf_ua3g_command		= -1;

extern e_ua_direction message_direction;

/* Definition of opcodes */
/* System To Terminal */
#define NOP				0x00
#define PRODUCTION_TEST			0x01	/* IP Phone */
#define SUBDEVICE_ESCAPE		0x02	/* IP Phone */
#define SOFT_RESET			0x03
#define IP_PHONE_WARMSTART		0x04	/* IP Phone */
#define HE_ROUTING			0x05	/* IP Phone - NOT EXPECTED */
#define SUBDEVICE_RESET			0x06
#define LOOPBACK_ON			0x07	/* IP Phone & UA NOE */
#define LOOPBACK_OFF			0x08	/* IP Phone & UA NOE */
#define VIDEO_ROUTING			0x09	/* IP Phone - NOT EXPECTED */
#define SUPER_MSG			0x0B
#define SEGMENT_MSG			0x0C
#define REMOTE_UA_ROUTING		0x0D	/* IP Phone - NOT EXPECTED */
#define VERY_REMOTE_UA_ROUTING		0x0E	/* IP Phone - NOT EXPECTED */
#define OSI_ROUTING			0x0F	/* IP Phone - NOT EXPECTED */
#define ABC_A_ROUTING			0x11	/* IP Phone - NOT EXPECTED */
#define IBS_ROUTING			0x12	/* IP Phone - NOT EXPECTED */
#define IP_DEVICE_ROUTING		0x13
#define M_REFLEX_HUB_ROUTING		0x14	/* IP Phone - NOT EXPECTED */
/*#define NOE_CS_ROUTING		0x15	// Decoded by packet-noe.c */
/*#define NOE_PS_ROUTING		0x16	// Decoded by packet-noe.c */
#define SUPER_MSG_2			0x17
#define DEBUG_IN_LINE			0x18
#define LED_COMMAND			0x21	/* IP Phone */
#define START_BUZZER			0x22	/* VTA */
#define STOP_BUZZER			0x23	/* VTA */
#define ENABLE_DTMF			0x24	/* Only IP NOE */
#define DISABLE_DTMF			0x25	/* Only IP NOE */
#define CLEAR_LCD_DISP			0x26	/* IP Phone */
#define LCD_LINE_1_CMD			0x27	/* IP Phone */
#define LCD_LINE_2_CMD			0x28	/* IP Phone */
#define MAIN_VOICE_MODE			0x29
#define VERSION_INQUIRY			0x2A
#define ARE_YOU_THERE			0x2B	/* IP Phone & UA NOE */
#define SUBDEVICE_METASTATE		0x2C
#define VTA_STATUS_INQUIRY		0x2D	/* IP Phone */
#define SUBDEVICE_STATE			0x2E
#define DWL_DTMF_CLCK_FORMAT		0x30	/* IP Phone */
#define SET_CLCK			0x31	/* IP Phone */
#define VOICE_CHANNEL			0x32	/* IP Phone & UA NOE */
#define EXTERNAL_RINGING		0x33
#define LCD_CURSOR			0x35	/* IP Phone */
#define DWL_SPECIAL_CHAR		0x36	/* IP Phone */
#define SET_CLCK_TIMER_POS		0x38	/* IP Phone */
#define SET_LCD_CONTRAST		0x39	/* IP Phone */
#define AUDIO_IDLE			0x3A
#define SET_SPEAKER_VOL			0x3B	/* IP Phone */
#define BEEP				0x3C
#define SIDETONE			0x3D
#define RINGING_CADENCE			0x3E
#define	MUTE				0x3F
#define FEEDBACK			0x40
#define KEY_RELEASE			0x41	/* IP Phone */
#define TRACE_ON			0x42	/* IP Phone - NOT EXPECTED */
#define TRACE_OFF			0x43	/* IP Phone - NOT EXPECTED */
#define READ_PERIPHERAL			0x44	/* IP Phone - NOT EXPECTED */
#define WRITE_PERIPHERAL		0x45	/* IP Phone - NOT EXPECTED */
#define ALL_ICONS_OFF			0x46	/* IP Phone */
#define ICON_CMD			0x47	/* IP Phone */
#define AMPLIFIED_HANDSET		0x48	/* IP Phone */
#define AUDIO_CONFIG			0x49
#define AUDIO_PADDED_PATH		0x4A	/* IP Phone */
#define RELEASE_RADIO_LINK		0x4B	/* IP Phone - NOT EXPECTED */
#define DECT_HANDOVER			0x4C	/* IP Phone - NOT EXPECTED */
#define LOUDSPEAKER			0x4D
#define ANNOUNCE			0x4E
#define RING				0x4F
#define UA_DWL_PROTOCOL			0x50	/* Only UA NOE */
/* Terminal To System */
#define CS_NOP_ACK			0x00
#define CS_HANDSET_OFFHOOK		0x01	/* IP Phone */
#define CS_HANDSET_ONHOOK		0x02	/* IP Phone */
#define CS_DIGIT_DIALED			0x03	/* IP Phone */
#define CS_SUBDEVICE_MSG		0x04
#define CS_HE_ROUTING			0x05	/* IP Phone - NOT EXPECTED */
#define CS_LOOPBACK_ON			0x06	/* IP Phone & UA NOE */
#define CS_LOOPBACK_OFF			0x07	/* IP Phone & UA NOE */
#define CS_VIDEO_ROUTING		0x09	/* IP Phone - NOT EXPECTED */
#define CS_WARMSTART_ACK		0x0A	/* IP Phone */
#define CS_SUPER_MSG			0x0B	/* IP Phone - NOT EXPECTED */
#define CS_SEGMENT_MSG			0x0C
#define CS_REMOTE_UA_ROUTING		0x0D	/* IP Phone - NOT EXPECTED */
#define CS_VERY_REMOTE_UA_R		0x0E	/* IP Phone - NOT EXPECTED */
#define CS_OSI_ROUTING			0x0F	/* IP Phone - NOT EXPECTED */
#define CS_ABC_A_ROUTING		0x11	/* IP Phone - NOT EXPECTED */
#define CS_IBS_ROUTING			0x12	/* IP Phone - NOT EXPECTED */
#define CS_IP_DEVICE_ROUTING		0x13
/*#define CS_NOE_CS_ROUTING		0x15	// Decoded by packet-noe.c */
/*#define CS_NOE_PS_ROUTING		0x16	// Decoded by packet-noe.c */
#define CS_SUPER_MSG_2			0x17
#define CS_DEBUG_IN_LINE		0x18
#define CS_UNSOLICITED_MSG		0x9F
#define CS_NON_DIGIT_KEY_PUSHED		0x20	/* IP Phone */
#define CS_VERSION_RESPONSE		0x21
#define CS_I_M_HERE			0x22
#define CS_RSP_STATUS_INQUIRY		0x23	/* IP Phone */
#define CS_SUBDEVICE_STATE		0x24
#define CS_DIGIT_KEY_RELEASED		0x26	/* IP Phone */
#define CS_TRACE_ON_ACK			0x27	/* IP Phone */
#define CS_TRACE_OFF_ACK		0x28	/* IP Phone */
#define CS_SPECIAL_KEY_STATUS		0x29	/* IP Phone */
#define CS_KEY_RELEASED			0x2A	/* IP Phone */
#define CS_PERIPHERAL_CONTENT		0x2B	/* IP Phone */
#define CS_TM_KEY_PUSHED		0x2D	/* IP Phone */
#define CS_UA_DWL_PROTOCOL		0x50	/* Only UA NOE */


static const value_string *opcodes_vals;

/* System To Terminal Opcodes */
static const value_string opcodes_vals_sys[] =
{
	{NOP				, "NOP"},
	{PRODUCTION_TEST		, "Production Test"},	/* IP Phone */
	{SUBDEVICE_ESCAPE		, "Subdevice Escape To Subdevice"},	/* IP Phone */
	{SOFT_RESET			, "Software Reset"},
	{IP_PHONE_WARMSTART		, "IP-Phone Warmstart"},	/* IP Phone */
	{HE_ROUTING			, "HE Routing Code"},	/* IP Phone - NOT EXPECTED */
	{SUBDEVICE_RESET		, "Subdevice Reset"},
	{LOOPBACK_ON			, "Loopback On"},
	{LOOPBACK_OFF			, "Loopback Off"},
	{VIDEO_ROUTING			, "Video Routing Code"},	/* IP Phone - NOT EXPECTED */
	{SUPER_MSG			, "Super Message"},
	{SEGMENT_MSG			, "Segment Message"},
	{REMOTE_UA_ROUTING		, "Remote UA Routing Code"},	/* IP Phone - NOT EXPECTED */
	{VERY_REMOTE_UA_ROUTING		, "Very Remote UA Routing Code"},	/* IP Phone - NOT EXPECTED */
	{OSI_ROUTING			, "OSI Routing Code"},	/* IP Phone - NOT EXPECTED */
	{ABC_A_ROUTING			, "ABC-A Routing Code"},	/* IP Phone - NOT EXPECTED */
	{IBS_ROUTING			, "IBS Routing Code"},	/* IP Phone - NOT EXPECTED */
	{IP_DEVICE_ROUTING		, "IP Device Routing"},
	{M_REFLEX_HUB_ROUTING		, "Mutli-Reflex Hub Routing Code"},	/* IP Phone - NOT EXPECTED */
	{SUPER_MSG_2			, "Super Message 2"},
	{DEBUG_IN_LINE			, "Debug In Line"},
	{LED_COMMAND			, "Led Command"},	/* IP Phone */
	{START_BUZZER			, "Start Buzzer"},	/* VTA */
	{STOP_BUZZER			, "Stop Buzzer"},	/* VTA */
	{ENABLE_DTMF			, "Enable DTMF"},
	{DISABLE_DTMF			, "Disable DTMF"},
	{CLEAR_LCD_DISP			, "Clear LCD Display"},	/* IP Phone */
	{LCD_LINE_1_CMD			, "LCD Line 1 Commands"},	/* IP Phone */
	{LCD_LINE_2_CMD			, "LCD Line 2 Commands"},	/* IP Phone */
	{MAIN_VOICE_MODE		, "Main Voice Mode"},
	{VERSION_INQUIRY		, "Version Inquiry"},
	{ARE_YOU_THERE			, "Are You There?"},
	{SUBDEVICE_METASTATE		, "Subdevice Metastate"},
	{VTA_STATUS_INQUIRY		, "VTA Status Inquiry"},	/* IP Phone */
	{SUBDEVICE_STATE		, "Subdevice State?"},
	{DWL_DTMF_CLCK_FORMAT		, "Download DTMF & Clock Format"},	/* IP Phone */
	{SET_CLCK			, "Set Clock"},	/* IP Phone */
	{VOICE_CHANNEL			, "Voice Channel"},	/* IP Phone & UA NOE */
	{EXTERNAL_RINGING		, "External Ringing"},
	{LCD_CURSOR			, "LCD Cursor"},	/* IP Phone */
	{DWL_SPECIAL_CHAR		, "Download Special Character"},	/* IP Phone */
	{SET_CLCK_TIMER_POS		, "Set Clock/Timer Position"},	/* IP Phone */
	{SET_LCD_CONTRAST		, "Set LCD Contrast"},	/* IP Phone */
	{AUDIO_IDLE			, "Audio Idle"},
	{SET_SPEAKER_VOL		, "Set Speaker Volume"},	/* IP Phone */
	{BEEP				, "Beep"},
	{SIDETONE			, "Sidetone"},
	{RINGING_CADENCE		, "Set Programmable Ringing Cadence"},
	{MUTE				, "Mute"},
	{FEEDBACK			, "Feedback"},
	{KEY_RELEASE			, "Key Release"},	/* IP Phone */
	{TRACE_ON			, "Trace On"},	/* IP Phone - NOT EXPECTED */
	{TRACE_OFF			, "Trace Off"},	/* IP Phone - NOT EXPECTED */
	{READ_PERIPHERAL		, "Read Peripheral"},	/* IP Phone - NOT EXPECTED */
	{WRITE_PERIPHERAL		, "Write Peripheral"},	/* IP Phone - NOT EXPECTED */
	{ALL_ICONS_OFF			, "All Icons Off"},	/* IP Phone */
	{ICON_CMD			, "Icon Command"},	/* IP Phone */
	{AMPLIFIED_HANDSET		, "Amplified Handset (Boost)"},	/* IP Phone */
	{AUDIO_CONFIG			, "Audio Config"},
	{AUDIO_PADDED_PATH		, "Audio Padded Path"},	/* IP Phone */
	{RELEASE_RADIO_LINK		, "Release Radio Link"},	/* IP Phone - NOT EXPECTED */
	{DECT_HANDOVER			, "DECT External Handover Routing Code"},	/* IP Phone - NOT EXPECTED */
	{LOUDSPEAKER			, "Loudspeaker"},
	{ANNOUNCE			, "Announce"},
	{RING				, "Ring"},
	{UA_DWL_PROTOCOL		, "UA Download Protocol"},
	{0, NULL}
};

/* Terminal To System Opcodes */
static const value_string opcodes_vals_term[] =
{
	{CS_NOP_ACK			, "NOP Acknowledge"},
	{CS_HANDSET_OFFHOOK		, "Handset Offhook"},	/* IP Phone */
	{CS_HANDSET_ONHOOK		, "Hansdet Onhook"},	/* IP Phone */
	{CS_DIGIT_DIALED		, "Digital Dialed"},	/* IP Phone */
	{CS_SUBDEVICE_MSG		, "Subdevice Message"},
	{CS_HE_ROUTING			, "HE Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_LOOPBACK_ON			, "Loopback On Acknowledge"},	/* Same as CS To Terminal */
	{CS_LOOPBACK_OFF		, "Loopback Off Acknowledge"},	/* Same as CS To Terminal */
	{CS_VIDEO_ROUTING		, "Video Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_WARMSTART_ACK		, "Warmstart Acknowledge"},	/* IP Phone */
	{CS_SUPER_MSG			, "Super Message"},	/* IP Phone - NOT EXPECTED */
	{CS_SEGMENT_MSG			, "Segment Message"},	/* Same as CS To Terminal */
	{CS_REMOTE_UA_ROUTING		, "Remote UA Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_VERY_REMOTE_UA_R		, "Very Remote UA Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_OSI_ROUTING			, "OSI Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_ABC_A_ROUTING		, "ABC-A Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_IBS_ROUTING			, "IBS Routing Response Code"},	/* IP Phone - NOT EXPECTED */
	{CS_IP_DEVICE_ROUTING		, "IP Device Routing"},
	{CS_SUPER_MSG_2			, "Super Message 2"},	/* Same as CS To Terminal */
	{CS_DEBUG_IN_LINE		, "Debug Message"},
	{CS_UNSOLICITED_MSG		, "Unsolicited Message"},
	{CS_NON_DIGIT_KEY_PUSHED	, "Non-Digit Key Pushed"},	/* IP Phone */
	{CS_VERSION_RESPONSE		, "Version Information"},
	{CS_I_M_HERE			, "I'm Here Response"},
	{CS_RSP_STATUS_INQUIRY		, "Response To Status Inquiry"},	/* IP Phone */
	{CS_SUBDEVICE_STATE		, "Subdevice State Response"},
	{CS_DIGIT_KEY_RELEASED		, "Digit Key Released"},	/* IP Phone */
	{CS_TRACE_ON_ACK		, "Trace On Acknowledge"},	/* IP Phone - NOT EXPECTED */
	{CS_TRACE_OFF_ACK		, "Trace Off Acknowledge"},	/* IP Phone - NOT EXPECTED */
	{CS_SPECIAL_KEY_STATUS		, "Special Key Status"},	/* IP Phone */
	{CS_KEY_RELEASED		, "Key Released"},	/* IP Phone */
	{CS_PERIPHERAL_CONTENT		, "Peripheral Content"},	/* IP Phone - NOT EXPECTED */
	{CS_TM_KEY_PUSHED		, "TM Key Pushed"},	/* IP Phone - NOT EXPECTED */
	{CS_UA_DWL_PROTOCOL		, "Download Protocol"},
	{0, NULL}
};

static const value_string str_on_off[] = {
	{0x00, "Off"},
	{0x01, "On"},
	{0, NULL}
};
static const value_string str_yes_no[] = {
	{0x00, "No"},
	{0x01, "Yes"},
	{0, NULL}
};
static const value_string str_device_type[] = {
	{0x00, "Voice Terminal Adaptor"},
	{0, NULL}
};


/*-----------------------------------------------------------------------------
    VERSION NUMBER COMPUTER - This function computes a version number (S.SZ.AB) from a 16 bits number
    ---------------------------------------------------------------------------*/
static char *
version_number_computer(int hexa_version)
{
	static char version[10];
	int release, vers, fix;

	release = (int)(hexa_version / 10000);
	vers = (int)((hexa_version % 10000) / 100);
	fix = (hexa_version % 10000) % 100;
	g_snprintf(version, 10, "%d.%02d.%02d", release, vers, fix);
	return version;
}


/*-----------------------------------------------------------------------------
    Function for UA3G message with opcode and one parameter

    PRODUCTION TEST - 01h (MESSAGE FROM THE SYSTEM)
    SUBDEVICE RESET - 06h (MESSAGE FROM THE SYSTEM)
    ARE YOU THERE - 2Bh - IPhone & UA NOE (MESSAGE FROM THE SYSTEM)
    SET SPEAKER VOLUME - 3Bh (MESSAGE FROM THE SYSTEM)
    TRACE ON - 42h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_with_one_parameter(proto_tree *tree _U_, tvbuff_t *tvb,
			  packet_info *pinfo _U_, guint offset, guint length,
			  guint8 opcode, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_first_parameter[] = {
		{0x01, "Production Test Command"},
		{0x06, "Reserved For Compatibility"},
		{0x2B, "Temporization"},
		{0x3B, "Volmue"},
		{0x42, "Subdevice Address"},
		{0, NULL}
	};

	if(length > 0) {
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
		proto_tree_add_text(ua3g_body_tree, tvb, offset, length, "%s: %d",
			val_to_str(opcode, str_first_parameter, "Unknown"), tvb_get_guint8(tvb, offset));
		length--;
	}
}


/*-----------------------------------------------------------------------------
    SUBDEVICE ESCAPE TO SUBDEVICE - 02h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_subdevice_escape(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
			guint offset, guint length, guint8 opcode _U_,
			proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int j = 0;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subdevice Address: %d", (tvb_get_guint8(tvb, offset) & 0x0F));
	offset++;
	length--;

	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subdevice Opcode: 0x%02x", (tvb_get_guint8(tvb, offset) & 0x7F));
	offset++;
	length--;

	while(length > 0) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Parameter Byte %2d: %d", j++,
			tvb_get_guint8(tvb, offset));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    SOFTWARE RESET - 03h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_software_reset(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		      guint offset, guint length, guint8 opcode _U_,
		      proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_verswitch[] = {
		{0x00, "Reset Without Version Switch"},
		{0x01, "Reset With Version Switch"},
		{0, NULL}
	};

	if(length > 0) {
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
			val_to_str(tvb_get_guint8(tvb, offset), str_verswitch, "Unknown"));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    IP-PHONE WARMSTART - 04h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_ip_phone_warmstart(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
			  guint offset, guint length, guint8 opcode _U_,
			  proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_command[] = {
		{0x00, "Run In UA2G Emulation Mode"},
		{0x01, "Run In Full UA3G Mode"},
		{0, NULL}
	};

	if(length > 0) {
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
		proto_tree_add_text(ua3g_body_tree,
			tvb,
			offset,
			1,
			"Command: %s",
			val_to_str(tvb_get_guint8(tvb, offset), str_command, "Unknown"));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    SUPER MESSAGE - 0Bh (MESSAGE FROM THE SYSTEM)
    SUPER MESSAGE 2 - 17h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_super_msg(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		 guint offset, guint length, guint8 opcode,
		 proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i, parameter_length;
	int j = 0;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	while(length > 0) {
		if(opcode == 0x17) {
			parameter_length = tvb_get_ntohs(tvb, offset);
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
				"Length %d: %d", j++, parameter_length);
			offset += 2;
			length -= 2;
		} else {
			parameter_length = tvb_get_guint8(tvb, offset);
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Length %d: %d", j++, parameter_length);
			offset++;
			length--;
		}

		for(i = 1; i <= parameter_length; i++) {
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"L%d Byte %2d: %d",
				j, i, tvb_get_guint8(tvb, offset));
			offset++;
			length--;
		}
	}
}


/*-----------------------------------------------------------------------------
    SEGMENT MESSAGE - 0Ch (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_segment_msg(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		   guint offset, guint length, guint8 opcode _U_,
		   proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int first_sub_segment;
	int j = 0;
	static const value_string str_first_sub_segment[] = {
		{0x00, "Subsequent Segment"},
		{0x80, "Fisrt Segment"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	first_sub_segment = (tvb_get_guint8(tvb, offset) & 0x80);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "F/S: %s (%d)",
		val_to_str(first_sub_segment, str_first_sub_segment, "Unknown"),
		first_sub_segment);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Number Of Remaining Segments: %d",
		(tvb_get_guint8(tvb, offset) & 0x7F));
	offset++;
	length--;

	if(first_sub_segment == 0x80) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 2, "Length: %d",
			tvb_get_ntohs(tvb, offset));
		offset += 2;
		length -= 2;
	}

	while(length > 0) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Segment Message byte %d: %d",
			j++, tvb_get_guint8(tvb, offset));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    IP DEVICE ROUTING - 13h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_ip_device_routing(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
			 guint offset, guint length, guint8 opcode _U_,
			 proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	gint *lengthp = 0;
	proto_tree *ua3g_body_tree;
	proto_item *ua3g_param_item;
	proto_tree *ua3g_param_tree;
	static const value_string str_command[] = {
		{0x00, "Reset"},
		{0x01, "Start RTP"},
		{0x02, "Stop RTP"},
		{0x03, "Redirect"},
		{0x04, "Tone Definition"},
		{0x05, "Start Tone"},
		{0x06, "Stop Tone"},
		{0x07, "Start Listen RTP"},
		{0x08, "Stop Listen RTP"},
		{0x09, "Get Parameters Value"},
		{0x0A, "Set Parameters Value"},
		{0x0B, "Send Digit"},
		{0x0C, "Pause RTP"},
		{0x0D, "Restart RTP"},
		{0x0E, "Start Record RTP"},
		{0x0F, "Stop Record RTP"},
		{0, NULL}
	};

	emem_strbuf_t *strbuf = ep_strbuf_new_label("");

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
		val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip, tvb, offset, 1,
		command, "Command: %s",
		val_to_str(command, str_command, "Unknown"));
	offset++;
	length--;

	switch(command) {
	case 0x00: /* RESET */
		{
			int i, parameter_id, parameter_length;
			static const value_string str_parameter_id[] = {
				{0x00, "Update Mode"},
				{0x01, "Bad Sec Mode"},
				{0x02, "Customization Name"},
				{0x03, "Localization Name"},
				{0, NULL}
			};

			if(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x00: /* Update Mode */
						{
							static const value_string str_update_mode[] = {
								{0x00, "NOE Update Mode"},
								{0x01, "Bootloader"},
								{0x02, "Data"},
								{0x04, "Customization"},
								{0x08, "Localization"},
								{0x10, "Code"},
								{0x20, "SIP"},
								{0, NULL}
							};
							static const value_string str_disable_enable[] = {
								{0, "Disable"},
								{1, "Enable"},
								{0, NULL}
							};

							if((tvb_get_guint8(tvb, offset + 2) & 0x80) == 0x00) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s: ",
									val_to_str(tvb_get_guint8(tvb, offset + 2) & 0x80, str_update_mode, "Unknown"));

								for(i = 0; i < 6; i++) {
									ep_strbuf_append_printf(strbuf, "%s: %s, ",
										val_to_str(0x01 << i, str_update_mode, "Unknown"),
										val_to_str((tvb_get_guint8(tvb, offset + 2) >> i) & 0x01, str_disable_enable, ""));
								}
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s",
									val_to_str(tvb_get_guint8(tvb, offset + 2) & 0x80, str_update_mode, "Unknown"));
							}

							break;
						}
					case 0x01: /* Bad_Sec_Mode */
						{
							static const value_string str_bad_sec_mode[] = {
								{0x01, "Binary is full, CS is secured, but terminal running in clear mode"},
								{0, NULL}
							};

							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf,
								val_to_str(tvb_get_guint8(tvb, offset + 2), str_bad_sec_mode, "Unknown"));
							break;
						}
					case 0x02: /* Cust_Name */
						{
							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, "\"");
							for(i = 1; i <= parameter_length; i++) {
								if(isprint(tvb_get_guint8(tvb, offset + 1 + i)))
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
								else
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
							}

							ep_strbuf_append(strbuf, "\"");
							break;
						}
					case 0x03: /* L10N_Name */
						{
							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, "\"");
							for(i = 1; i <= parameter_length; i++) {
								if(isprint(tvb_get_guint8(tvb, offset + 1 + i)))
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
								else
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
							}

							ep_strbuf_append(strbuf, "\"");
							break;
						}
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s", val_to_str(parameter_id, str_parameter_id, "Unknown"));
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					/*Fix Clang Warning*/
					/*offset += parameter_length;*/
					/*length -= parameter_length;*/
				}
			}
			break;
		}
	case 0x01: /* START RTP */
		{
			int i, parameter_length, parameter_id;
			static const value_string str_direction[] = {
				{0x00, "Terminal Input"},
				{0x01, "Terminal Output"},
				{0x02, "Terminal Input/Output (Both Directions)"},
				{0, NULL}
			};
			static const value_string str_parameter_id[] = {
				{0x00, "Local UDP Port"},
				{0x01, "Remote IP Address"},
				{0x02, "Remote UDP Port"},
				{0x03, "Type Of Service"},
				{0x04, "Compressor"},
				{0x05, "Payload Concatenation (ms)"},
				{0x06, "Echo Cancelation Enabler"},
				{0x07, "Silence Suppression Enabler"},
				{0x08, "802.1 Q User Priority"},
				{0x09, "Reserved"},
				{0x0a, "Post Filtering Enabler"},
				{0x0b, "High Pass Filtering Enabler"},
				{0x0c, "Remote SSRC"},
				{0x0d, "Must Send QOS Tickets"},
				{0x0e, "Local Identifier"},
				{0x0f, "Distant Identifier"},
				{0x10, "Destination For RTCP Sender Reports - Port Number"},
				{0x11, "Destination For RTCP Sender Reports - IP Address"},
				{0x12, "Destination For RTCP Receiver Reports - Port Number"},
				{0x13, "Destination For RTCP Receiver Reports - IP Address"},
				{0x14, "Channel Number"},
				{0x15, "DTMF Sending"},
				{0x16, "Payload Type Of Redundancy"},
				{0x17, "Payload Type Of DTMF Events"},
				{0x18, "Enable / Disable RFC 2198"},
				{0x31, "SRTP Encryption Enable For This Communication"},
				{0x32, "Master Key For SRTP Session"},
				{0x33, "Master Salt Key For SRTP Session"},
				{0x34, "Master key for output stream of SRTP session"},
				{0x35, "Master salt key for output stream of SRTP session"},
				{0x36, "Integrity checking enabled for this communication"},
				{0x37, "MKI value for SRTP packets in input stream"},
				{0x38, "MKI value for SRTP packets in output stream"},
				{0x50, "MD5 Authentication"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Direction: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_direction, "Unknown"));
			offset++;
			length--;

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x01: /* Remote IP Address */
					case 0x11: /* Destination For RTCP Sender Reports - IP Address */
					case 0x13: /* Destination For RTCP Receiver Reports - IP Address */
						{
							if(parameter_length > 0) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
							}

							for(i = 2; i <= parameter_length; i++) {
								ep_strbuf_append(strbuf, ".");
								ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
							}
							break;
						}
					case 0x04: /* Compressor */
						{
							static const value_string str_compressor[] = {
								{0x00, "G.711 A-law"},
								{0x01, "G.711 mu-law"},
								{0x0F, "G.723.1 5.3kbps"},
								{0x10, "G.723.1 6.3kbps"},
								{0x11, "G.729A 8 kbps"},
								{0, NULL}
							};

							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;
								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_compressor, "Default Codec"));
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x06: /* Echo Cancelation Enabler */
					case 0x07: /* Silence Suppression Enabler */
					case 0x0A: /* Post Filtering Enabler */
					case 0x0B: /* High Pass Filtering Enabler */
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_on_off, "On"));
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x0D: /* Must Send QOS Tickets */
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_yes_no, "No"));
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x0E: /* Local Identifier */
					case 0x0F: /* Distant Identifier */
						{
							break;
						}
					case 0x15: /* DTMF Sending */
						{
							static const value_string str_dtmf_sending[] = {
								{0x00, "Don't Send DTMF"},
								{0, NULL}
							};
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--)
								{
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_dtmf_sending, "Send DTMF"));
							}
							else
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x18: /* Enable / Disable RFC 2198 */
						{
							static const value_string str_enable_00[] = {
								{0x00, "Enable"},
								{0, NULL}
							};

							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_enable_00, "Disable"));
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x31: /* SRTP Encryption Enable For This Communication */
						{
							static const value_string str_enable_10[] = {
								{0x10, "Enable"},
								{0, NULL}
							};

							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--)
								{
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, val_to_str((guint8)(param_value), str_enable_10, "Disable"));
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					case 0x00: /* Local UDP Port */
					case 0x02: /* Remote UDP Port */
					case 0x03: /* Type Of Service */
					case 0x05: /* Payload Concatenation */
					case 0x08: /* 802.1 Q User Priority */
					case 0x09: /* Reserved */
					case 0x0C: /* Remote SSRC */
					case 0x10: /* Destination For RTCP Sender Reports - Port Number */
					case 0x12: /* Destination For RTCP Receiver Reports - Port Number */
					case 0x14: /* Channel Number */
					case 0x16: /* Payload Type For Redundancy */
					case 0x17: /* Payload Type For DTMF Events */
					case 0x32: /* Master Key For SRTP Session */
					case 0x33: /* Master Salt Key For SRTP Session */
					case 0x34: /* Master key for output stream of SRTP session */
					case 0x35: /* Master salt key for output stream of SRTP session */
					case 0x36: /* Integrity checking enabled for this communication */
					case 0x37: /* MKI value for SRTP packets in input stream */
					case 0x38: /* MKI value for SRTP packets in output stream */
					case 0x50: /* MD5 Authentication */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--)
								{
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x02: /* STOP_RTP */
		{
			int i, parameter_id, parameter_length;
			static const value_string str_parameter_id[] = {
				{0x0E, "Local Identifier"},
				{0x0F, "Distant Identifier"},
				{0x14, "Canal Identifier"},
				{0, NULL}
			};

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x0E: /* Local Identifier */
					case 0x0F: /* Distant Identifier */
						{
							break;
						}
					case 0x14: /* Canal Identifier */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--)
								{
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x03: /* REDIRECT */
		{
			int i, parameter_length, parameter_id;
			static const value_string str_parameter_id[] = {
				{0x00, "Remote MainCPU Server IP Adress"},
				{0x01, "Remote MainCPU Server Port"},
				{0, NULL}
			};

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x00: /* Remote MainCPU Server IP Adress */
						{
							if(parameter_length > 0) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
							}

							for(i = 2; i <= parameter_length; i++) {
								ep_strbuf_append(strbuf, ".");
								ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
							}
							break;
						}
					case 0x01: /* Remote MainCPU Server Port */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
						}
						break;
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, parameter_length + 2,
						"%s: %s", val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, parameter_length + 2,
						"%s", val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x04: /* DEF_TONES */
		{
			int i, tone_nb_entries;
			guint16 frequency_1, frequency_2;
			signed char level_1, level_2;

			tone_nb_entries = tvb_get_guint8(tvb, offset);

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Number Of Entries: %d", tone_nb_entries);
			offset++;
			length--;

			while(length > 0 && tone_nb_entries) {
				for(i = 1; i <= tone_nb_entries; i++) {
					frequency_1 = tvb_get_ntohs(tvb, offset);
					level_1 = (signed char)(tvb_get_guint8(tvb, offset + 2)) / 2;
					frequency_2 = tvb_get_ntohs(tvb, offset + 3);
					level_2 = (signed char)(tvb_get_guint8(tvb, offset + 5)) / 2;

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6,
						"Tone Pair %d: %d Hz at %d dB / %d Hz at %d dB",
						i, frequency_1, level_1, frequency_2, level_2);
					ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 2,
						"Frequency 1: %d Hz", frequency_1);
					offset += 2;
					length -= 2;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Level: %d dB", level_1);
					offset++;
					length--;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 2, "Frequency 2: %d Hz", frequency_2);
					offset += 2;
					length -= 2;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Level: %d dB", level_2);
					offset++;
					length--;
				}
			}
			break;
		}
	case 0x05: /* START TONE */
		{
			guint8 i, tone_nb_entries, tone_direction, tone_id;
/*			guint8 i, tone_nb_entries, tone_direction, tone_id, tone_duration tone_silence; */
			int tone_duration;
			static const value_string str_tone_direction[] = {
				{0x00, "On The Phone"},
				{0x40, "To The Network"},
				{0x80, "On The Phone and To The Network"},
				{0, NULL}
			};

			tone_direction = tvb_get_guint8(tvb, offset) & 0xC0;
			tone_nb_entries = tvb_get_guint8(tvb, offset);

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Direction: %s - Number Of Entries: %d",
				val_to_str(tone_direction, str_tone_direction, "Unknown"), tone_nb_entries);
			offset++;
			length--;

			while(length > 0 && tone_nb_entries) {
				for(i = 1; i <= tone_nb_entries; i++) {
					tone_id = tvb_get_guint8(tvb, offset);
					tone_duration = tvb_get_ntohs(tvb, offset + 1);
					/*tone_duration = tvb_get_guint8(tvb, offset + 1);
					tone_silence = tvb_get_guint8(tvb, offset + 2);*/

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 6,
						"Tone Pair %d: Id: %d, Duration: %d ms",
/*						"Tone Pair %d: Id: %d, Duration: %d ms, Silence: %d ms", */
						i, tone_id, tone_duration);
/*						i, tone_id, tone_duration, tone_silence); */
					ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
						"Identification: %d", tone_id);
					offset++;
					length--;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 2,
						"Duration: %d ms", tone_duration);
					offset += 2;
					length -= 2;

					/*proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
						"Duration: %d ms", tone_duration);
					offset++;
					length--;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
						"Silence: %d ms", tone_silence);
					offset++;
					length--;*/
				}
			}
			break;
		}
	case 0x07: /* START LISTEN RTP */
	case 0x08: /* STOP LISTEN RTP */
		{
			int i, parameter_length, parameter_id;
			static const value_string str_parameter_id[] = {
				{0x00, "Remote IP Adress     "},
				{0x01, "Remote UDP Port In   "},
				{0x02, "Remote UDP Port Out  "},
				{0x03, "Remote IP Address Out"},
				{0x04, "Canal Number"},
				{0, NULL}
			};

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x00: /* Remote IP Adress - Not for start listening rtp */
					case 0x03: /* Remote IP Adress Out - Not for start listening rtp */
						{
							if(parameter_length > 0) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
							}

							for(i = 2; i <= parameter_length; i++) {
								ep_strbuf_append(strbuf, ".");
								ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
							}
							break;
						}
					case 0x01: /* Remote UDP Port In - Not for start listening rtp */
					case 0x02: /* Remote UDP Port Out - Not for start listening rtp */
					case 0x04: /* Canal Number */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
						}
						break;
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				}
				else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x09: /* GET_PARAM_REQ */
		{
			guint8 parameter;
			static const value_string str_parameter[] = {
				{0x00	, "Firmware Version"},
				{0x01	, "Firmware Version"},
				{0x02	, "DHCP IP Address"},
				{0x03	, "Local IP Address"},
				{0x04	, "Subnetwork Mask"},
				{0x05	, "Router IP Address"},
				{0x06	, "TFTP IP Address"},
				{0x07	, "MainCPU IP Address"},
				{0x08	, "Default Codec"},
				{0x09	, "Ethernet Drivers Config"},
				{0x0A	, "MAC Address"},
				{0, NULL}
			};

			while(length > 0) {
				parameter = tvb_get_guint8(tvb, offset);
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
					val_to_str(parameter, str_parameter, "Unknown"));
				offset++;
				length--;
			}
			break;
		}
	case 0x0A: /* SET_PARAM_REQ */
		{
			int i, parameter_id, parameter_length;
			static const value_string str_parameter_id[] = {
				{0x00	, "QOS IP TOS"},
				{0x01	, "QOS 8021 VLID"},
				{0x02	, "QOS 8021 PRI"},
				{0x03	, "SNMP MIB2 SysContact"},
				{0x04	, "SNMP MIB2 SysName"},
				{0x05	, "SNMP MIB2 SysLocation"},
				{0x06	, "Default Compressor"},
				{0x07	, "Error String Net Down"},
				{0x08	, "Error String Cable PB"},
				{0x09	, "Error String Try Connect"},
				{0x0A	, "Error String Connected"},
				{0x0B	, "Error String Reset"},
				{0x0C	, "Error String Duplicate IP Address"},
				{0x0D	, "SNMP MIB Community"},
				{0x0E	, "TFTP Backup Sec Mode"},
				{0x0F	, "TFTP Backup IP Address"},
				{0x10	, "Set MMI Password"},
				{0x11	, "Set PC Port Status"},
				{0x12	, "Record RTP Authorization"},
				{0x13	, "Security Flags"},
				{0x14	, "ARP Spoofing"},
				{0x15	, "Session Param"},
				{0x30	, "MD5 Authentication"},
				{0, NULL}
			};

			while(length > 0) {
				guint64 param_value = 0;

				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x06: /* Compressor */
						{
							static const value_string str_compressor[] = {
								{0x00, "G.711 A-law"},
								{0x01, "G.711 mu-law"},
								{0x0F, "G.723.1 5.3kbps"},
								{0x10, "G.723.1 6.3kbps"},
								{0x11, "G.729A 8 kbps"},
								{0, NULL}
							};

							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, val_to_str(tvb_get_guint8(tvb, offset + 2),
								str_compressor, "Default Codec"));
							break;
						}
					case 0x07: /* ERR STRING NET DOWN */
					case 0x08: /* ERR STRING CABLE PB */
					case 0x09: /* ERR STRING TRY CONNECT */
					case 0x0A: /* ERR STRING CONNECTED */
					case 0x0B: /* ERR STRING RESET */
					case 0x0C: /* ERR STRING DUPLICATE IP ADDRESS */
						{
							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, "\"");
							for(i = 1; i <= parameter_length; i++) {
								if(isprint(tvb_get_guint8(tvb, offset + 1 + i)))
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
								else
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
							}
							ep_strbuf_append(strbuf, "\"");
							break;
						}
					case 0x0F: /* TFTP BACKUP IP ADDR */
						{
							if(parameter_length > 0) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
							}

							for(i = 2; i <= parameter_length; i++) {
								ep_strbuf_append(strbuf, ".");
								ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
							}
							break;
						}
					case 0x11: /* Set PC Port status */
						{
							static const value_string str_set_pc_port_status[] = {
								{0x00, "No PC Port Security"},
								{0x01, "Block PC Port"},
								{0x02, "Filter VLAN"},
								{0, NULL}
							};
							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, val_to_str(tvb_get_guint8(tvb, offset + 2),
								str_set_pc_port_status, "Unknown"));
							break;
						}
					case 0x12: /* Record RTP Authorization */
						{
							static const value_string str_enable_feature[] = {
								{0x00, "Disable Feature"},
								{0x01, "Enable Feature"},
								{0, NULL}
							};

							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, val_to_str(tvb_get_guint8(tvb, offset + 2),
								str_enable_feature, "Unknown"));
							break;
						}
					case 0x13: /* Security Flags */
						{
							static const value_string str_set_pc_port_status[] = {
								{0x00, "Filtering Not Active"},
								{0x01, "Filtering Activated"},
								{0, NULL}
							};
							ep_strbuf_truncate(strbuf, 0);
							ep_strbuf_append(strbuf, val_to_str((tvb_get_guint8(tvb, offset + 2) & 0x01),
								str_set_pc_port_status, "Unknown"));
							break;
						}
					case 0x00: /* QOS IP TOS */
					case 0x01: /* QOS 8021 VLID */
					case 0x02: /* QOS 8021 PRI */
					case 0x03: /* SNMP MIB2 SYSCONTACT */
					case 0x04: /* SNMP MIB2 SYSNAME */
					case 0x05: /* SNMP MIB2 SYSLOCATION */
					case 0x0D: /* SNMP MIB COMMUNITY */
					case 0x0E: /* TFTP BACKUP SEC MODE */
					case 0x10: /* SET MMI PASSWORD */
					case 0x14: /* ARP Spoofing */
					case 0x15: /* Session Param */
					case 0x30: /* MD5 Authentication */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else if(parameter_length > 8) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
							break;
						}
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x0B: /* SEND_DIGIT */
		{
			static const value_string str_digit[] = {
				{0, "0"},
				{1, "1"},
				{2, "2"},
				{3, "3"},
				{4, "4"},
				{5, "5"},
				{6, "6"},
				{7, "7"},
				{8, "8"},
				{9, "9"},
				{10, "*"},
				{11, "#"},
				{12, "A"},
				{13, "B"},
				{14, "C"},
				{15, "D"},
				{16, "Flash"},
				{0, NULL}
			};
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Digit Value: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_digit, "Unknown"));
		break;
		}
	case 0x0C: /* PAUSE_RTP */
	case 0x0D: /* RESTART_RTP */
		{
			int i, parameter_length, parameter_id;
			static const value_string str_parameter_id[] = {
				{0x14, "Canal Identifier"},
				{0, NULL}
			};

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					if((parameter_length > 0) && (parameter_length <= 8)) {
						guint64 param_value = 0;

						for(i = parameter_length; i > 0; i--) {
							param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
						}
						ep_strbuf_truncate(strbuf, 0);
						ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
					} else {
						ep_strbuf_truncate(strbuf, 0);
						ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
							tvb_get_guint8(tvb, offset + 2),
							tvb_get_guint8(tvb, offset + 3),
							tvb_get_guint8(tvb, offset + parameter_length),
							tvb_get_guint8(tvb, offset + 1 + parameter_length));
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				}
				else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x0E: /* START_RECORD_RTP */
	case 0x0F: /* STOP RECORD RTP */
		{
			int i, parameter_length, parameter_id;
			static const value_string str_parameter_id[] = {
				{0x00	, "Recorder Index"},
				{0x01	, "Remote IP Address"},
				{0x02	, "Remote UDP Port In"},
				{0x03	, "Remote UDP Port Out"},
				{0x04	, "Remote IP Address Out"},
				{0x05	, "Local UDP Port In"},
				{0x06	, "Local UDP Port Out"},
				{0x07	, "Type Of Service"},
				{0x08	, "Master Key For SRTP Session"},
				{0x09	, "Master Salt Key For SRTP Session"},
				{0x30	, "MD5 Authentication"},
				{0, NULL}
			};

			while(length > 0) {
				parameter_id = tvb_get_guint8(tvb, offset);
				parameter_length = tvb_get_guint8(tvb, offset + 1);

				if(parameter_length > 0) {
					switch(parameter_id) {
					case 0x01: /* Remote IP Address */
					case 0x04: /* Remote IP Address Out */
						{
							if(parameter_length > 0) {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
							}

							for(i = 2; i <= parameter_length; i++) {
								ep_strbuf_append(strbuf, ".");
								ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
							}
							break;
						}
					case 0x00: /* Recorder Index */
					case 0x02: /* Remote UDP Port In */
					case 0x03: /* Remote UDP Port Out */
					case 0x05: /* Local UDP Port In */
					case 0x06: /* Local UDP Port Out */
					case 0x07: /* Type Of Service */
					case 0x08: /* Master Key For SRTP Session */
					case 0x09: /* Master Salt Key For SRTP Session */
					case 0x30: /* MD5 Authentication */
					default:
						{
							if((parameter_length > 0) && (parameter_length <= 8)) {
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
							} else {
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
									tvb_get_guint8(tvb, offset + 2),
									tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + parameter_length),
									tvb_get_guint8(tvb, offset + 1 + parameter_length));
							}
						}
						break;
					}

					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s: %s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
				} else
					ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
						parameter_length + 2, "%s",
						val_to_str(parameter_id, str_parameter_id, "Unknown"));

				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Parameter: %s (0x%02x)",
					val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
				offset++;
				length--;

				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
					"Length: %d", parameter_length);
				offset++;
				length--;

				if(parameter_length > 0) {
					proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
						"Value: %s", strbuf->str);
					offset += parameter_length;
					length -= parameter_length;
				}
			}
			break;
		}
	case 0x06: /* STOP TONE */
	default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    DEBUG IN LINE - 18h (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_debug_in_line(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		     guint offset, guint length, guint8 opcode _U_,
		     proto_item *ua3g_body_item)
{
	gint *lengthp = 0;
	proto_tree *ua3g_body_tree;
	int i, parameter_length;
	emem_strbuf_t *strbuf = ep_strbuf_new_label("");

	parameter_length = length;
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	ep_strbuf_truncate(strbuf, 0);
	ep_strbuf_append(strbuf, "\"");
	for(i = 0; i < parameter_length; i++) {
		if(isprint(tvb_get_guint8(tvb, offset + i)))
			ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+i, lengthp));
		else
			ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+i, lengthp));
	}
	ep_strbuf_append(strbuf, "\"");

	proto_tree_add_text(ua3g_body_tree, tvb, offset, length,
		"Text String With Debug: %s", strbuf->str);
}


/*-----------------------------------------------------------------------------
    LED COMMAND - 21h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_led_command(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		   guint offset, guint length, guint8 opcode _U_,
		   proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int command = tvb_get_guint8(tvb, offset);
	static const value_string str_command[] = {
		{0x00, "Led Off"},
		{0x01, "Led On"},
		{0x02, "Red Led Fast Flash"},
		{0x03, "Red Led Slow Flash"},
		{0x04, "Green Led On"},
		{0x05, "Green Led Fast Flash"},
		{0x06, "Green Led Slow Flash"},
		{0x07, "All Led Off"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, "s - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_command, "Unknown"));
	offset++;
	length--;

	if(command != 0x07) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Led Number: %d", tvb_get_guint8(tvb, offset));
	}
}


/*-----------------------------------------------------------------------------
    LCD LINE 1 COMMANDS - 27h (MESSAGE FROM THE SYSTEM)
    LCD LINE 2 COMMANDS - 28h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_lcd_line_cmd(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		    guint offset, guint length, guint8 opcode _U_,
		    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 lcd_options, command, column_n;
	guint i;
	proto_tree *ua3g_body_tree;
	proto_item *ua3g_param_item;
	proto_tree *ua3g_param_tree;
	proto_item *ua3g_option_item;
	proto_tree *ua3g_option_tree;
	static char str_ascii[40];
/*	static char lcd_options_tab[6];*/
	static const value_string str_command[] = {
		{0, "Clear Line & Write From Column"},
		{1, "Write From Column"},
		{2, "Append To Current Line"},
		{0, NULL}
	};
	static const value_string str_enable_disable[] = {
		{0, "Disable"},
		{0, NULL}
	};
	static const value_string str_lcd_option[] = {
		{7, "Suspend Display Refresh"},
		{6, "Time Of Day Display    "},
		{5, "Call Timer Display     "},
		{4, "Call Timer Control     "},
		{2, "Blink                  "},
		{0, NULL}
	};
	static const value_string str_call_timer_ctrl[] = {
		{0x00, "Call Timer Status Not Changed"},
		{0x08, "Stop Call Timer"},
		{0x10, "Start Call Timer From Current Value"},
		{0x18, "Initialize And Call Timer"},
		{0, NULL}
	};
	emem_strbuf_t *strbuf = ep_strbuf_new_label("");

	lcd_options = tvb_get_guint8(tvb, offset) & 0xFC;
	command = tvb_get_guint8(tvb, offset) & 0x03;
	column_n = tvb_get_guint8(tvb, offset + 1);

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s %d",
		val_to_str(command, str_command, "Unknown"),
		column_n);
	proto_item_append_text(ua3g_body_item, " %s %d",
		val_to_str(command, str_command, "Unknown"),
		column_n);
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s %d",
			val_to_str(command, str_command, "Unknown"),
			column_n);

	ep_strbuf_truncate(strbuf, 0);
	ep_strbuf_append(strbuf, "\"");
	for(i = 0; i < length - 2; i++) {
		if(isprint(tvb_get_guint8(tvb, offset + 2 + i)))
			ep_strbuf_append_printf(strbuf, "%c", tvb_get_guint8(tvb, offset + 2 + i));
		else
			ep_strbuf_append_printf(strbuf, "'0x%02x'", tvb_get_guint8(tvb, offset + 2 + i));
	}
	ep_strbuf_append(strbuf, "\"");

	ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
		length, "%s %d: %s",
		val_to_str(command, str_command, "Unknown"), column_n, str_ascii);
	ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_option_item = proto_tree_add_text(ua3g_param_tree, tvb, offset,
		1, "LCD Options: 0x%x", lcd_options);
	ua3g_option_tree = proto_item_add_subtree(ua3g_option_item, ett_ua3g_option);

	for(i = 2; i <= 7; i++) {
		int shift_value = 1 << i;
		if(i != 3) {
			proto_tree_add_text(ua3g_option_tree, tvb, offset, 1, "%s: %s",
				val_to_str(i, str_lcd_option, "Unknown"),
				val_to_str((tvb_get_guint8(tvb, offset) & shift_value), str_enable_disable, "Enable"));
		} else {
			i++;
			proto_tree_add_text(ua3g_option_tree, tvb, offset, 1, "%s: %s",
				val_to_str(i, str_lcd_option, "Unknown"),
				val_to_str((tvb_get_guint8(tvb, offset) & 0x18), str_call_timer_ctrl, "Unknown"));
		}
	}
	offset++;
	length--;

	if(command != 3)
		proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Starting Column: %d", column_n);
	else
		proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Unused");

	offset++;
	length--;
	proto_tree_add_text(ua3g_param_tree, tvb, offset, length, "ASCII Char: %s", str_ascii);
}


/*-----------------------------------------------------------------------------
    MAIN VOICE MODE - 29h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_main_voice_mode(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		       guint offset, guint length, guint8 opcode _U_,
		       proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 mode = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_voice_mode[] = {
		{0x00, "Idle"},
		{0x01, "Handset"},
		{0x02, "Group Listening"},
		{0x03, "On Hook Dial"},
		{0x04, "Handsfree"},
		{0x05, "Announce Loudspeaker"},
		{0x06, "Ringing"},
		{0x10, "Idle"},
		{0x11, "Handset"},
		{0x12, "Headset"},
		{0x13, "Handsfree"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(mode, str_voice_mode, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(mode, str_voice_mode, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(mode, str_voice_mode, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, mode, "Voice Mode: %s",
		val_to_str(mode, str_voice_mode, "Unknown"));
	offset++;
	length--;

	switch(mode) {
	case 0x06: /* Ringing */
		{
			static const value_string str_cadence[] = {
				{0x00, "Standard Ringing"},
				{0x01, "Double Burst"},
				{0x02, "Triple Burst"},
				{0x03, "Continuous Ringing"},
				{0x04, "Priority Attendant Ringing"},
				{0x05, "Regular Attendant Ringing"},
				{0x06, "Programmable Cadence"},
				{0x07, "Programmable Cadence"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Tune: %d", tvb_get_guint8(tvb, offset));
			offset++;
			length--;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Cadence: %s (%d)",
				val_to_str(tvb_get_guint8(tvb, offset), str_cadence, "Unknown"),
				tvb_get_guint8(tvb, offset));
			offset++;
			length--;
		}
		/* FALLTHROUGH */
	case 0x02: /* Group Listening */
	case 0x03: /* On Hook Dial */
	case 0x04: /* Handsfree */
	case 0x05: /* Announce Loudspeaker */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Speaker Volume: %d",
				tvb_get_guint8(tvb, offset));
			offset++;
			length--;

			if(length > 0) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset,
					1, "Microphone Volume: %d",
					tvb_get_guint8(tvb, offset));
			}
			break;
		}
	case 0x11: /* Handset */
	case 0x12: /* Headset */
	case 0x13: /* Handsfree */
		{
			signed char level;
			static const value_string str_receiving_level[] = {
				{0x11, "Receiving Level "},
				{0x12, "Receiving Level "},
				{0x13, "Speaker Level   "},
				{0, NULL}
			};

			level = (signed char)(tvb_get_guint8(tvb, offset)) / 2;
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"%s: %d dB",
				val_to_str(mode, str_receiving_level, "Unknown"), level);
			offset++;
			length--;

			level = (signed char)(tvb_get_guint8(tvb, offset)) / 2;
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Sending Level	: %d dB", level);
			break;
		}
	case 0x00: /* Idle */
	case 0x01: /* Handset */
	case 0x10: /* Idle */
	default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    SUBDEVICE METASTATE - 2Ch (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_subdevice_metastate(proto_tree *tree _U_, tvbuff_t *tvb,
			   packet_info *pinfo _U_, guint offset, guint length,
			   guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_new_metastate[] = {
		{0x00, "Disable"},
		{0x01, "Active"},
		{0x02, "Wake Up"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subchannel Address: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "New Metastate: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_new_metastate, "Unknown"));
}


/*-----------------------------------------------------------------------------
    Download DTMF & CLOCK FORMAT - 30h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_dwl_dtmf_clck_format(proto_tree *tree _U_, tvbuff_t *tvb,
			    packet_info *pinfo _U_, guint offset, guint length,
			    guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_clock_format[] = {
		{0, "Europe"},
		{1, "US"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Minimum 'ON' Time: %d ms", (tvb_get_guint8(tvb, offset) * 10));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Inter-Digit Pause Time: %d ms",
		(tvb_get_guint8(tvb, offset) * 10));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Clock Time Format: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_clock_format, "Unknown"));
	offset++;
	length--;

	if(length > 0)
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"DTMF Country Adaptation: %d", tvb_get_guint8(tvb, offset));
}


/*-----------------------------------------------------------------------------
    SET CLOCK - 31h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_set_clck(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		guint offset, guint length, guint8 opcode _U_,
		proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	proto_item *ua3g_param_item;
	proto_tree *ua3g_param_tree;
	int hour, minute, second, call_timer;
	static const value_string str_command[] = {
		{0x00, "Set Current Time/Call Timer"},
		{0x01, "Set Current Time"},
		{0x02, "Set Call Timer"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_command, "Unknown"));
	offset++;
	length--;
	call_timer = 0;

	switch(command) {
	case 0x02: /* Timer Form */
		{
			call_timer = 1;
		}
		/* FALLTHROUGH */
	case 0x00: /* Set Current Time/Call Timer */
	case 0x01: /* Set Current Time */
		{
			static const value_string str_call_timer[] = {
				{1, "Call Timer "},
				{0, NULL}
			};

			while(length > 0) {
				hour = tvb_get_guint8(tvb, offset);
				minute = tvb_get_guint8(tvb, offset + 1);
				second = tvb_get_guint8(tvb, offset + 2);

				ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset, 3,
					"%s: %d:%d:%d",
					val_to_str(call_timer, str_call_timer, "Current Time"), hour, minute, second);
				ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "%sHour: %d",
					val_to_str(call_timer, str_call_timer, ""), hour);
				offset++;
				length--;
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "%sMinute: %d",
					val_to_str(call_timer, str_call_timer, ""), minute);
				offset++;
				length--;
				proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "%sSecond: %d",
					val_to_str(call_timer, str_call_timer, ""), second);
				offset++;
				length--;

				call_timer = 1;
			}
		}
	default:
		{
			break;
		}
	}

}


/*-----------------------------------------------------------------------------
    VOICE CHANNEL - 32h - (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_voice_channel(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		     guint offset, guint length, guint8 opcode _U_,
		     proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_v[] = {
		{0x00, "Normal Voice Channel Mode"},
		{0x01, "Write 00 to Voice Channel"},
		{0, NULL}
	};
	static const value_string str_c[] = {
		{0x00, "Normal Codec Operation"},
		{0x02, "Write Quiet To Codec"},
		{0, NULL}
	};
	static const value_string str_b[] = {
		{0x00, "Use B1 As Voice Channel"},
		{0x04, "Use B3 As Voice Channel"},
		{0, NULL}
	};
	static const value_string str_voice_channel[] = {
		{0x00, "No"},
		{0x01, "B1"},
		{0x02, "B2"},
		{0x03, "B3"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	if(length == 1) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_v, "Unknown"));
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_c, "Unknown"));
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x04), str_b, "Unknown"));
		offset++;
		length--;
	} else if(length == 2) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Main Voice: %s",
			val_to_str(tvb_get_guint8(tvb, offset), str_voice_channel, "Unknown"));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Announce: %s",
			val_to_str(tvb_get_guint8(tvb, offset), str_voice_channel, "Unknown"));
		offset++;
		length--;
	} else if(length == 4) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "B General: %d",
			tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "B Loud Speaker: %d",
			tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "B Ear Piece: %d",
			tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "B Microphones: %d",
			tvb_get_guint8(tvb, offset));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    EXTERNAL RINGING - 33h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_external_ringing(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
			guint offset, guint length _U_, guint8 opcode _U_,
			proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_ext_ring_cmd[] = {
		{0x00, "Turn Off"},
		{0x01, "Turn On"},
		{0x02, "Follow The Normal Ringing"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_ext_ring_cmd, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_ext_ring_cmd, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(tvb_get_guint8(tvb, offset), str_ext_ring_cmd, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset, 1,
		tvb_get_guint8(tvb, offset), "External Ringing Command: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_ext_ring_cmd, "Unknown"));
}


/*-----------------------------------------------------------------------------
    LCD CURSOR - 35h - (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_lcd_cursor(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		  guint offset, guint length, guint8 opcode _U_,
		  proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str((tvb_get_guint8(tvb, offset + 1) & 0x02), str_on_off, "On"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str((tvb_get_guint8(tvb, offset + 1) & 0x02), str_on_off, "On"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str((tvb_get_guint8(tvb, offset + 1) & 0x02), str_on_off, "On"));

	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Line Number: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset, 1,
		tvb_get_guint8(tvb, offset), "Cursor %s",
		val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_on_off, "On"));
}


/*-----------------------------------------------------------------------------
    DOWNLOAD SPECIAL CHARACTER - 36h - (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_dwl_special_char(proto_tree *tree _U_, tvbuff_t *tvb,
			packet_info *pinfo _U_, guint offset, guint length,
			guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i, j;
	emem_strbuf_t *strbuf = ep_strbuf_new_label("");

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	while(length > 0) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Character Number: %d", tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		for(i = 1; i <= 8; i++) {
			int byte = tvb_get_guint8(tvb, offset);
			char byte_char[20] = "  ";

			/* The following loop will draw a picture of the character with "spaces" and "o" */
			for(j = 7; j >= 0; j--) {
				if(((byte >> j) & 0x01) == 0)
					ep_strbuf_append_printf(strbuf, "  ");
				else
					ep_strbuf_append_printf(strbuf, "o ");
			}

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Byte %d: 0x%02x   %s", i, byte, byte_char);
			offset++;
			length--;
		}
	}
}


/*-----------------------------------------------------------------------------
    SET CLOCK/TIMER POSITION - 38h - (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_set_clck_timer_pos(proto_tree *tree _U_, tvbuff_t *tvb,
			  packet_info *pinfo _U_, guint offset, guint length,
			  guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Clock Line Number: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Clock Column Number: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Call Timer Line Number: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Call Timer Column Number: %d", tvb_get_guint8(tvb, offset));
}


/*-----------------------------------------------------------------------------
    SET LCD CONTRAST - 39h - (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_set_lcd_contrast(proto_tree *tree _U_, tvbuff_t *tvb,
			packet_info *pinfo _U_, guint offset, guint length,
			guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_driver_number[] = {
		{0x00, "Display"},
		{0x01, "Icon"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Driver Number: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_driver_number, "Unknown"));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Contrast Value: %d", tvb_get_guint8(tvb, offset));
}


/*-----------------------------------------------------------------------------
    BEEP - 3Ch (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_beep(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
	    guint offset, guint length, guint8 opcode _U_,
	    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	emem_strbuf_t *strbuf = ep_strbuf_new_label("");
	if(length > 0) { /* All cases except classical beep */
		guint8 command = tvb_get_guint8(tvb, offset);
		proto_tree *ua3g_body_tree;
		static const value_string str_command[] = {
			{0x01, "Beep Once"},
			{0x02, "Beep Start"},
			{0x03, "Stop Beep"},
			{0x04, "Start Beep"},
			{0x05, "Define Beep"},
			{0, NULL}
		};

		/* add text to the frame tree */
		proto_item_append_text(ua3g_item, ", %s",
			val_to_str(command, str_command, "Unknown"));
		proto_item_append_text(ua3g_body_item, " - %s",
			val_to_str(command, str_command, "Unknown"));
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

		/* add text to the frame "INFO" column */
		if (check_col(pinfo->cinfo, COL_INFO))
			col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
				val_to_str(command, str_command, "Unknown"));

		proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb,
			offset, 1, command, "Beep: %s",
			val_to_str(command, str_command, "Unknown"));
		offset++;
		length--;

		switch(command) {
		case 0x01: /* Beep Once */
		case 0x02: /* Beep Start */
			{
				int i =  0;
				static const value_string str_destination[] = {
					{0x01, "Ear-Piece"},
					{0x02, "Loudspeaker"},
					{0x02, "Ear-Piece and Loudspeaker"},
					{0, NULL}
				};
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Destination: %s",
					val_to_str(tvb_get_guint8(tvb, offset), str_destination, "Unknown"));
				offset++;
				length--;

				while(length > 0) {
					i++;
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "On / Off: %s",
						val_to_str((tvb_get_guint8(tvb, offset) & 0x80), str_on_off, "On"));
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Cadence T%d: %d ms",
						i, ((tvb_get_guint8(tvb, offset) & 0x7F) * 10));
					offset++;
					length--;
				}
				break;
			}
		case 0x04: /* Start Beep */
			{
				int i;
				static const value_string str_destination[] = {
					{0x01, "Handset"},
					{0x02, "Headset"},
					{0x04, "Loudspeaker"},
					{0x08, "Announce Loudspeaker"},
					{0x10, "Handsfree"},
					{0, NULL}
				};

				ep_strbuf_append_printf(strbuf, "%s",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_destination, ""));
				for(i = 1; i < 5; i++) {
					ep_strbuf_append(strbuf,
						val_to_str((tvb_get_guint8(tvb, offset) & (0x01 << i)), str_destination, ""));
				}

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Destination: %s", strbuf->str);
				offset++;

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Beep Number: %x", tvb_get_guint8(tvb, offset));
				break;
			}
		case 0x05:
			{
				int i, nb_of_notes, beep_number;
				static const value_string str_freq_sample_nb[] = {
					{0x00, "Frequency"},
					{0xFF, "Audio Sample Number"},
					{0, NULL}
				};
				static const value_string str_duration[] = {
					{0x00, "Duration "},
					{0xFF, "Duration (Ignored)"},
					{0, NULL}
				};
				static const value_string str_terminator[] = {
					{0xFD, "Stop"},
					{0xFE, "Loop"},
					{0xFF, "Infinite"},
					{0, NULL}
				};

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Beep Number: %x", beep_number = tvb_get_guint8(tvb, offset));
				offset++;
				length--;

				if(beep_number <= 0x44)
					beep_number = 0x00;
				else beep_number = 0xFF;

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Number Of Notes: %x", nb_of_notes = tvb_get_guint8(tvb, offset));
				offset++;
				length--;

				while(length > 0) {
					for(i = 1; i <= nb_of_notes; i++) {
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %d",
							val_to_str(beep_number, str_freq_sample_nb, "Unknown"),
							i, tvb_get_guint8(tvb, offset));
						offset++;
						length--;
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Level %d: %d",
							i, tvb_get_guint8(tvb, offset));
						offset++;
						length--;
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s %d: %x",
							val_to_str(beep_number, str_duration, "Unknown"),
							i, tvb_get_guint8(tvb, offset));
						offset++;
						length--;
					}
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Terminator: %d (%s)",
						tvb_get_guint8(tvb, offset),
						val_to_str(tvb_get_guint8(tvb, offset), str_terminator, "Unknown"));
					offset++;
					length--;
				}
				break;
			}
		case 0x03: /* Stop Beep */
		default:
			{
				break;
			}
		}
	} else { /* Classical Beep */
		/* add text to the frame tree */
		proto_item_append_text(ua3g_item, ", Classical Beep");
		proto_item_append_text(ua3g_body_item, " - Classical Beep");

		/* add text to the frame "INFO" column */
		if (check_col(pinfo->cinfo, COL_INFO))
			col_append_fstr(pinfo->cinfo, COL_INFO, ": Classical Beep");
	}
}


/*-----------------------------------------------------------------------------
    SIDETONE ON / OFF - 3Dh (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_sidetone(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		guint offset, guint length _U_, guint8 opcode _U_,
		proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_on_off, "On"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_on_off, "On"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_on_off, "On"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_on_off, "On"));
	offset++;

	if(command == 0x01) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Level: %d dB",
			(signed char)(tvb_get_guint8(tvb, offset) / 2));
	}
}


/*-----------------------------------------------------------------------------
    SET PROGRAMMABLE RINGING CADENCE - 3Eh (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_ringing_cadence(proto_tree *tree _U_, tvbuff_t *tvb,
		       packet_info *pinfo _U_, guint offset, guint length,
		       guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i = 0;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Cadence: %d",
		tvb_get_guint8(tvb, offset));
	offset++;
	length--;

	while(length > 0) {
		i++;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "On / Off	: %s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x80), str_on_off, "On"));
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Length %d	: %d ms",
			i, ((tvb_get_guint8(tvb, offset) & 0x7F) * 10));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    MUTE ON / OFF - 3Fh (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_mute(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
	    guint offset, guint length _U_, guint8 opcode _U_,
	    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_mute[] = {
		{0x00, "Microphone Disable"},
		{0x01, "Microphone Enable"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_mute, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_mute, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_mute, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "%s",
		val_to_str(command, str_mute, "Unknown"));
}


/*-----------------------------------------------------------------------------
	FEEDBACK ON / OFF - 40h (MESSAGE FROM THE SYSTEM)
	---------------------------------------------------------------------------*/
static void
decode_feedback(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		guint offset, guint length, guint8 opcode _U_,
		proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_on_off, "On"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_on_off, "On"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_on_off, "On"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_on_off, "On"));
	offset++;
	length--;

	if(command == 0x01) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Level: %d dB",
			(signed char)(tvb_get_guint8(tvb, offset) / 2));
		offset++;
		length--;

		if(length > 0) {
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Duration: %d ms",
				(tvb_get_guint8(tvb, offset) * 10));
		}
	}
}


/*-----------------------------------------------------------------------------
    READ PERIPHERAL - 44h (MESSAGE FROM THE SYSTEM)
    WRITE PERIPHERAL - 45h (MESSAGE FROM THE SYSTEM)
    PERIPHERAL CONTENT - 2Bh (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_r_w_peripheral(proto_tree *tree _U_, tvbuff_t *tvb,
		      packet_info *pinfo _U_, guint offset, guint length,
		      guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 2, "Address: %d",
		tvb_get_ntohs(tvb, offset));
	offset += 2;
	length -= 2;

	if(length > 0) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Content: %d", tvb_get_guint8(tvb, offset));
	}
}


/*-----------------------------------------------------------------------------
    ICON COMMAND - 47h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_icon_cmd(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		guint offset, guint length, guint8 opcode _U_,
		proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i;
	static const value_string str_state[] = {
		{0x00, "Off"},
		{0x01, "Slow Flash"},
		{0x02, "Not Used"},
		{0x03, "Steady On"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Icon Number: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;

	for(i = 0; i < 8; i++) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
			"Segment %d: %s (%d)", i,
			val_to_str((((tvb_get_guint8(tvb, offset) << 1) >> i) & 0x02) + ((tvb_get_guint8(tvb, offset + 1) >> i) & 0x01),str_state, "Unknown"),
			(((tvb_get_guint8(tvb, offset) << 1) >> i) & 0x02) + ((tvb_get_guint8(tvb, offset + 1) >> i) & 0x01));
	}
}


/*-----------------------------------------------------------------------------
    AUDIO CONFIGURATION - 49h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_audio_config(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		    guint offset, guint length, guint8 opcode _U_,
		    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_command[] = {
		{0x00, "Audio Coding"},
		{0x01, "DPI Channel Allocations"},
		{0x02, "Loudspeaker Volume Adjust"},
		{0x03, "Audio Circuit Configuration"},
		{0x04, "Handsfree Parameters"},
		{0x05, "Loudspeaker Acoustic Parameters"},
		{0x06, "Device Congiguration"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb,
		offset, 1, command, "Command	: %s (%d)",
		val_to_str(command, str_command, "Unknown"), command);
	offset++;
	length--;

	switch(command) {
	case 0x00: /* Audio Coding */
		{
			static const value_string str_law[] = {
				{0x00, "A Law"},
				{0x01, "m Law"},
				{0, NULL}
			};
			proto_tree_add_text(ua3g_body_tree, tvb, offset,
				1, "Ignored	: %d",
				tvb_get_guint8(tvb, offset));
			offset++;
			length--;

			proto_tree_add_text(ua3g_body_tree, tvb, offset,
				1, "Law	: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_law, "Unknown"));
			break;
		}
	case 0x01: /* DPI Channel Allocations */
		{
			int i;
			static const value_string str_body[] = {
				{0, "UA Channel UA-TX1   "},
				{1, "UA Channel UA-TX2   "},
				{2, "GCI Channel GCI-TX1 "},
				{3, "GCI Channel GCI-TX2 "},
				{4, "Codec Channel COD-TX"},
				{0, NULL}
			};
			for(i = 0; i < 5; i++) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset,
					1, "%s: %d",
					val_to_str(i, str_body, "Unknown"), tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}
			break;
		}
	case 0x02: /* Loudspeaker Volume Adjust */
		{
			int i;
			for(i = 1; i < 8; i++) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset,
					1, "Volume Level %d: %d",
					i, tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}
			break;
		}
	case 0x03: /* Audio Circuit Configuration */
		{
			int i;
			static const value_string str_body[] = {
				{0, "Anti-Distortion Coeff 1(DTH)"},
				{1, "Anti-Distortion Coeff 2(DTR)"},
				{2, "Anti-Distortion Coeff 3(DTF)"},
				{3, "Sidetone Attenuation (STR)  "},
				{4, "Anti-Larsen Coeff 1 (AHP1)  "},
				{5, "Anti-Larsen Coeff 2 (AHP2)  "},
				{6, "Anti-Larsen Coeff 3 (ATH)   "},
				{7, "Anti-Larsen Coeff 4 (ATR)   "},
				{8, "Anti-Larsen Coeff 5 (ATF)   "},
				{9, "Anti-Larsen Coeff 6 (ALM)   "},
				{0, NULL}
			};

			for(i = 0; i < 10; i++) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset,
					1, "%s: %d",
					val_to_str(i, str_body, "Unknown"), tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}
			break;
		}
	case 0x04: /* Handsfree Parameters */
		{
			static const value_string str_return_loss[] = {
				{0x00, "Return Loss Normal"},
				{0x01, "Return Loss Active"},
				{0, NULL}
			};
			static const value_string str_full_duplex[] = {
				{0x00, "Handsfree Normal"},
				{0x02, "More Full Duplex"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
				val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_return_loss, "Unknown"));
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s",
				val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_full_duplex, "Unknown"));
			break;
		}
	case 0x05: /* Loudspeaker Acoustic Parameters */
		{
			int i;
			static const value_string str_body[] = {
				{0, "Group Listening Attenuation Constant                                      "},
				{1, "Handsfree Attenuation Constant                                            "},
				{2, "Handsfree Number Of ms To Stay In Send State Before Going To Another State"},
				{3, "Handsfree Number Of Positions To Shift Right MTx                          "},
				{4, "Handsfree Number Of Positions To Shift Right MRc                          "},
				{5, "Handsfree Idle Transmission Threshold                                     "},
				{6, "Handsfree Low Transmission Threshold                                      "},
				{7, "Handsfree Idle Reception Threshold                                        "},
				{8, "Handsfree Low Reception Threshold                                         "},
				{9, "Handsfree Medium Reception Threshold                                      "},
				{10, "Handsfree High Reception Threshold                                        "},
				{0, NULL}
			};

			for(i = 0; i < 11; i++) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s: %d",
					val_to_str(i, str_body, "Unknown"), tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}
			break;
		}
	case 0x06: /* Device Configuration */
		{
			static const value_string str_device[] = {
				{0, "Handset Device             "},
				{1, "Headset Device             "},
				{2, "Loudspeaker Device         "},
				{3, "Announce Loudspeaker Device"},
				{4, "Handsfree Device           "},
				{0, NULL}
			};
			static const value_string str_device_values[] = {
				{0x01, " Internal"},
				{0x02, " Rj9 Plug"},
				{0x04, " Jack Plug"},
				{0x08, " Bluetooth Link"},
				{0x10, " USB Link"},
				{0, NULL}
			};
			emem_strbuf_t *strbuf = ep_strbuf_new_label("");
			int j;
			int i = 0;

			while(length > 0) {
				ep_strbuf_truncate(strbuf, 0);
				ep_strbuf_append_printf(strbuf, "%s",
					val_to_str(tvb_get_guint8(tvb, offset) & 0x01, str_device_values, ""));

				for(j = 1; j < 4; j++) {
					ep_strbuf_append_printf(strbuf, "%s",
						val_to_str((tvb_get_guint8(tvb, offset) & (0x01 << j)), str_device_values, ""));
				}
				if(strcmp(strbuf->str, "") <= 0) {
					ep_strbuf_truncate(strbuf, 0);
					ep_strbuf_append(strbuf, " None");
				}

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s:%s",
					val_to_str(i, str_device, "Unknown"), strbuf->str);
				offset++;
				length--;
				i++;
			}
			break;
		}
	default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    AUDIO PADDED PATH - 4Ah (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_audio_padded_path(proto_tree *tree _U_, tvbuff_t *tvb,
			 packet_info *pinfo _U_, guint offset, guint length,
			 guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Emission Padded Level: %d", tvb_get_guint8(tvb, offset));
	offset++;
	length--;
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Reception Padded Level: %d", tvb_get_guint8(tvb, offset));
}


/*-----------------------------------------------------------------------------
    KEY RELEASE ON / OFF - 41h (MESSAGE FROM THE SYSTEM)
    AMPLIFIED HANDSET (BOOST) - 48h (MESSAGE FROM THE SYSTEM)
    LOUDSPEAKER ON / OFF - 4Dh (MESSAGE FROM THE SYSTEM)
    ANNOUNCE ON / OFF - 4Eh (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_on_off_level(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		    guint offset, guint length, guint8 opcode _U_,
		    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_on_off, "On"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_on_off, "On"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_on_off, "On"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_on_off, "On"));
	offset++;
	length--;

	if(length > 0) {
		if(command == 0x01) {
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Level on Loudspeaker: %d dB",
				(signed char)(tvb_get_guint8(tvb, offset)));
		}
	}
}


/*-----------------------------------------------------------------------------
    RING ON / OFF - 4Fh (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_ring(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
	    guint offset, guint length, guint8 opcode _U_,
	    proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_cadence[] = {
		{0x00, "Standard Ringing"},
		{0x01, "Double Burst"},
		{0x02, "Triple Burst"},
		{0x03, "Continuous"},
		{0x04, "Priority Attendant Ringing"},
		{0x05, "Regular Attendant Ringing"},
		{0x06, "Programmable Cadence"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_on_off, "On"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_on_off, "On"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_on_off, "On"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_on_off, "On"));
	offset++;
	length--;

	if(command == 0x01) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Melody: %d", tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Cadence: %s",
			val_to_str(tvb_get_guint8(tvb, offset), str_cadence, "Unknown"));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Speaker level: %d dB",
			(signed char)(tvb_get_guint8(tvb, offset)));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Beep number: %d", tvb_get_guint8(tvb, offset));
		offset++;
		length--;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Silent: %s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x80), str_on_off, "On"));
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Progressive: %d",
			(tvb_get_guint8(tvb, offset) & 0x03));
	}
}


/*-----------------------------------------------------------------------------
    UA DOWNLOAD PROTOCOL - 50h - Only for UA NOE (MESSAGE FROM THE TERMINAL AND FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_ua_dwl_protocol(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		       guint offset, guint length, guint8 opcode _U_,
		       proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_command[] = {
		{0x00, "Downloading Suggest"},
		{0x01, "Downloading Request"},
		{0x02, "Downloading Acknowledge"},
		{0x03, "Downloading Data"},
		{0x04, "Downloading End"},
		{0x05, "Downloading End Acknowledge"},
		{0x06, "Downloading ISO Checksum"},
		{0x07, "Downloading ISO Checksum Acknowledge"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb,
		offset, 1, command, "Command	: %s",
		val_to_str(command, str_command, "Unknown"));
	offset++;
	length--;

	switch(command) {
	case 0x00:	/* Downloading Suggest (MESSAGE FROM THE TERMINAL) */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Item Identifier: %d", tvb_get_guint8(tvb, offset));
			offset++;
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Item Version: %s",
				version_number_computer(tvb_get_letohs(tvb, offset)));
			offset += 2;
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Cause: %d", tvb_get_guint8(tvb, offset));
			break;
		}
	case 0x01:	/* Downloading Request (MESSAGE FROM THE SYSTEM) */
		{
			static const value_string str_force_mode[] = {
				{0x00, "System Accept All Refusals"},
				{0x01, "Force Software Lock"},
				{0, NULL}
			};
			static const value_string str_item_id[] = {
				{0x00, "Patches File"},
				{0x01, "Application Binary"},
				{0x02, "Datas Binary"},
				{0, NULL}
			};
			static const value_string str_mode_selection_country[] = {
				{0x00, "No Check"},
				{0x01, "For All Countries Except Chinese"},
				{0x02, "For Chinese"},
				{0, NULL}
			};
			static const value_string str_mem_size[] = {
				{0x00, "No Check"},
				{0x01, "128 Kbytes"},
				{0x02, "256 Kbytes"},
				{0x03, "512 Kbytes"},
				{0x04, "1 Mbytes"},
				{0x05, "2 Mbytes"},
				{0x06, "4 Mbytes"},
				{0x07, "8 Mbytes"},
				{0, NULL}
			};
			static const value_string str_bin_info[] = {
				{0x00, "Uncompressed Binary"},
				{0x01, "LZO Compressed Binary"},
				{0, NULL}
			};

			if(length > 7) { /* Not R1 */
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Force Mode: %s",
					val_to_str(tvb_get_guint8(tvb, offset), str_force_mode, "Unknown"));
				offset++;
				length--;
			}

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Item Identifier: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_item_id, "Unknown"));
			offset++;
			length--;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
				"Item Version: %d", tvb_get_ntohs(tvb, offset));
			offset += 2;
			length -= 2;

			if(length > 2) { /* Not R1 */
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Files Included: %s Boot Binary Included, %s Loader Binary Included, %s Appli Binary Included, %s Datas Binary Included",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_yes_no, ""),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_yes_no, ""),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x04), str_yes_no, ""),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x08), str_yes_no, ""));
				offset++;
				length--;

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Model Selection: For A Model: %s, For B Model: %s, For C Model %s, Country Version: %s",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_yes_no, "Yes"),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_yes_no, "Yes"),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x04), str_yes_no, "Yes"),
					val_to_str(((tvb_get_guint8(tvb, offset) & 0xE0) >> 5), str_mode_selection_country, "Unknown"));
				offset++;
				length--;

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Hardware Selection: For Ivanoe 1: %s, For Ivanoe 2: %s",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_yes_no, "Yes"),
					val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_yes_no, "Yes"));
				offset++;
				length--;

				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Memory Sizes Required: Flash Min Size: %s, External Ram Min Size: %s",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x07), str_mem_size, "Unknown"),
					val_to_str(((tvb_get_guint8(tvb, offset) & 0x38) >> 3), str_mem_size, "Unknown"));
				offset++;
				length--;
			} else { /* R1 */
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Binary Information: %s, Country/Operator/CLient Identifier ?",
					val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_bin_info, ""));
				offset++;
				length--;
			}

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 3,
				"Binary Length: %d", tvb_get_ntoh24(tvb, offset));
			break;
		}
	case 0x02:	/* Downloading Acknowledge (MESSAGE FROM THE TERMINAL) */
		{
			static const value_string str_status[] = {
				{0x00, "Ok (Binary Item Downloading In \"Normal\" Progress)"},
				{0x01, "Hardware Failure: Flash Failure"},
				{0x02, "Not Enough Place To Store The Downloaded Binary"},
				{0x03, "Wrong Seq Number On Latest Received Download_Data Message"},
				{0x04, "Wrong Packet Number On Latest Received Download_Data Message"},
				{0x05, "Download Refusal Terminal (Validation Purpose)"},
				{0x06, "Download Refusal Terminal (Development Purpose)"},
				{0x10, "Download Refusal: Hardware Cause (Unknown Flash Device, Incompatible Hardware)"},
				{0x11, "Download Refusal: No Loader Available Into The Terminal"},
				{0x12, "Download Refusal: Software Lock"},
				{0x13, "Download Refusal: Wrong Parameter Into Download Request"},
				{0x20, "Wrong Packet Number On Latest Received Downloading_Data Message"},
				{0x21, "Compress Header Invalid"},
				{0x22, "Decompress Error"},
				{0x23, "Binary Header Invalid"},
				{0x24, "Binary Check Error: Flash Write Error Or Binary Is Invalid"},
				{0x25, "Error Already Signaled - No More Data Accepted"},
				{0x26, "No Downloading In Progress"},
				{0x27, "Too Many Bytes Received (More Than Size Given Into The Download_Req Message)"},
				{0xFF, "Undefined Error"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
				"Packet Number: %d", tvb_get_ntohs(tvb, offset));
			offset += 2;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Status: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_status, "Unknown"));
			break;
		}
	case 0x03:	/* Downloading Data (MESSAGE FROM THE SYSTEM) */
		{
			int i = 1;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
				"Packet Number: %d", tvb_get_ntohs(tvb, offset));
			offset += 2;
			length -= 2;

			while(length > 0) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Packet Number %3d: %d", i, tvb_get_guint8(tvb, offset));
				offset++;
				length--;
				i++;
			}
			break;
		}
	case 0x05:	/* Downloading End Acknowledge (MESSAGE FROM THE TERMINAL) */
		{
			static const value_string str_ok[] = {
				{0x00, "Ok"},
				{0x01, "Hardware Failure: Flash Problems"},
				{0x02, "Not Enough Place To Store The Downloaded Binary"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Status: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_ok, "Not Ok"));
			break;
		}
	case 0x06:	/* Downloading Iso Checksum (MESSAGE FROM THE SYSTEM) */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 4,
				"Checksum: %d", tvb_get_ntohl(tvb, offset));
			/*Fix Clang Warning*/
			/*offset += 4;*/
			/*length -= 4;*/
			break;
		}
	case 0x07:	/* Downloading ISO Checksum Acknowledge (MESSAGE FROM THE TERMINAL) */
		{
			static const value_string str_ack_status[] = {
				{0x00, "The Checksum Matches"},
				{0x25, "Error Detected And Already Signaled"},
				{0x30, "Checksum Error (All Bytes Received)"},
				{0x31, "Checksum Error (Bytes Missing)"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Acknowledge: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_ack_status, "Unknown"));
			break;
		}
	case 0x04:	/* Downloading End (MESSAGE FROM THE SYSTEM) */
	default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    DIGIT DIALED - 03h (MESSAGE FROM THE SYSTEM)
    ---------------------------------------------------------------------------*/
static void
decode_digit_dialed(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		    guint offset, guint length _U_, guint8 opcode _U_,
		    proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	static const value_string str_digit[] = {
		{0, "0"},
		{1, "1"},
		{2, "2"},
		{3, "3"},
		{4, "4"},
		{5, "5"},
		{6, "6"},
		{7, "7"},
		{8, "8"},
		{9, "9"},
		{10, "*"},
		{11, "#"},
		{12, "A"},
		{13, "B"},
		{14, "C"},
		{15, "D"},
		{16, "Flash"},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Digit Value: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_digit, "Unknown"));
}


/*-----------------------------------------------------------------------------
    SUBDEVICE_MSG - 04h (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_subdevice_msg(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		     guint offset, guint length, guint8 opcode _U_,
		     proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i = 0;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subdev Type: %d", (tvb_get_guint8(tvb, offset) & 0xF0));
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subdev Address: %d", (tvb_get_guint8(tvb, offset) & 0x0F));
	offset++;
	length--;

	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
		"Subdevice Opcode: %d", (tvb_get_guint8(tvb, offset) & 0x7F));
	offset++;
	length--;

	while(length > 0) {
		i++;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Parameter Byte %2d: %d", i, tvb_get_guint8(tvb, offset));
		offset++;
		length--;
	}
}


/*-----------------------------------------------------------------------------
    IP DEVICE ROUTING - 13h (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_cs_ip_device_routing(proto_tree *tree _U_, tvbuff_t *tvb,
			    packet_info *pinfo, guint offset, guint length,
			    guint8 opcode _U_, proto_item *ua3g_item,
			    proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	gint *lengthp = 0;
	proto_tree *ua3g_body_tree;
	proto_item *ua3g_param_item;
	proto_tree *ua3g_param_tree;
	emem_strbuf_t *strbuf = ep_strbuf_new_label("");
	static const value_string str_command[] = {
		{0x00, "Init"},
		{0x01, "Incident"},
		{0x02, "Get Parameters Value Response"},
		{0x03, "QOS Ticket RSP"},
		{0, NULL}
	};

	/* add text to the frame tree */
	proto_item_append_text(ua3g_item, ", %s",
		val_to_str(command, str_command, "Unknown"));
	proto_item_append_text(ua3g_body_item, " - %s",
		val_to_str(command, str_command, "Unknown"));
	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	/* add text to the frame "INFO" column */
	if (check_col(pinfo->cinfo, COL_INFO))
		col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
			val_to_str(command, str_command, "Unknown"));

	proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_ip, tvb, offset,
		1, command, "Command: %s",
		val_to_str(command, str_command, "Unknown"));
	offset++;
	length--;

	switch(command) {
		case 0x00:
			{
				static const value_string str_vta_type[] = {
					{0x20, "NOE A"},
					{0x21, "NOE B"},
					{0x22, "NOE C"},
					{0x23, "NOE D"},
					{0, NULL}
				};
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "VTA Type: %s",
					val_to_str(tvb_get_guint8(tvb, offset), str_vta_type, "Unknown"));
				offset++;
				length--;

				proto_tree_add_text(ua3g_body_tree, tvb, offset,
					1, "Characteristic Number: %d", tvb_get_guint8(tvb, offset));
				break;
			}
		case 0x01:
			{
				int i=0;
				if(length == 1) {
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
						"Incident 0: %d", tvb_get_guint8(tvb, offset));
				} else {
					while(length >0) {
						i++;
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
							"Parameter %d Identifier: %d",
							i, tvb_get_guint8(tvb, offset));
						offset++;
						length--;
					}
				}
				break;
			}
		case 0x02:
			{
				int i, parameter_id, parameter_length;
				static const value_string str_parameter_id[] = {
					{0x00, "Firmware Version"},
					{0x01, "Firmware Version"},
					{0x02, "DHCP IP Address"},
					{0x03, "Local IP Address"},
					{0x04, "Subnetwork Mask"},
					{0x05, "Router IP Address"},
					{0x06, "TFTP IP Address"},
					{0x07, "Main CPU Address"},
					{0x08, "Default Codec"},
					{0x09, "Ethernet Drivers Config"},
					{0x0A, "MAC Address"},
					{0, NULL}
				};

				while(length > 0) {
					parameter_id = tvb_get_guint8(tvb, offset);
					parameter_length = tvb_get_guint8(tvb, offset + 1);

					if(parameter_length > 0) {
						switch(parameter_id) {
						case 0x00: /* Firmware Version */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s",
									version_number_computer(tvb_get_ntohs(tvb, offset + 2)));
								break;
							}
						case 0x01: /* Firmware Version */
						case 0x02: /* DHCP IP Address */
						case 0x03: /* Local IP Address */
						case 0x04: /* Subnetwork Mask */
						case 0x05: /* Router IP Address */
						case 0x06: /* TFTP IP Address */
						case 0x07: /* Main CPU Address */
							{
								if(parameter_length > 0) {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 2));
								}

								for(i = 2; i <= parameter_length; i++) {
									ep_strbuf_append(strbuf, ".");
									ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+1+i));
								}
								break;
							}
						case 0x08: /* Default Codec */
							{
								static const value_string str_compressor[] = {
									{0x00, "G.711 A-law"},
									{0x01, "G.711 mu-law"},
									{0x0F, "G.723.1 5.3kbps"},
									{0x10, "G.723.1 6.3kbps"},
									{0x11, "G.729A 8 kbps"},
									{0, NULL}
								};

								if((parameter_length > 0) && (parameter_length <= 8)) {
									guint64 param_value = 0;

									for(i = parameter_length; i > 0; i--) {
										param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
									}
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append(strbuf,
										val_to_str((guint8)(param_value), str_compressor, "Default Codec"));
								} else {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
										tvb_get_guint8(tvb, offset + 2),
										tvb_get_guint8(tvb, offset + 3),
										tvb_get_guint8(tvb, offset + parameter_length),
										tvb_get_guint8(tvb, offset + 1 + parameter_length));
								}
								break;
							}
						case 0x09: /* Ethernet Drivers Config */
							{
								if(parameter_length == 2) {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf,
										"Port Lan Speed: %d - Port Lan Duplex: %d",
										tvb_get_guint8(tvb, offset + 2),
										tvb_get_guint8(tvb, offset + 3));
								} else if(parameter_length == 4) {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf,
										"Port Lan Speed: %d - Port Lan Duplex: %d - Port PC Speed: %d - Port PC Duplex: %d",
										tvb_get_guint8(tvb, offset + 2),
										tvb_get_guint8(tvb, offset + 3),
										tvb_get_guint8(tvb, offset + 4),
										tvb_get_guint8(tvb, offset + 5));
								} else {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
										tvb_get_guint8(tvb, offset + 2),
										tvb_get_guint8(tvb, offset + 3),
										tvb_get_guint8(tvb, offset + parameter_length),
										tvb_get_guint8(tvb, offset + 1 + parameter_length));
								}
								break;
							}
						case 0x0A: /* MAC Address */
							{
								if(parameter_length > 0) {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "%02x", tvb_get_guint8(tvb, offset + 2));
								}

								for(i = 2; i <= parameter_length; i++) {
									ep_strbuf_append(strbuf, ":");
									ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+1+i, lengthp));
								}
								break;
							}
						default:
							{
								if((parameter_length > 0) && (parameter_length <= 8)) {
									guint64 param_value = 0;

									for(i = parameter_length; i > 0; i--) {
										param_value += (tvb_get_guint8(tvb, offset + 1 + i) << (8 * (parameter_length - i)));
									}
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
								} else {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append_printf(strbuf, "0x%02x 0x%02x ... 0x%02x 0x%02x",
										tvb_get_guint8(tvb, offset + 2),
										tvb_get_guint8(tvb, offset + 3),
										tvb_get_guint8(tvb, offset + parameter_length),
										tvb_get_guint8(tvb, offset + 1 + parameter_length));
								}
							}
							break;
						}

						ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
							parameter_length + 2, "%s: %s",
							val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
					} else
						ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
							parameter_length + 2, "%s",
							val_to_str(parameter_id, str_parameter_id, "Unknown"));

					ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);
					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Parameter: %s (0x%02x)",
						val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
					offset++;
					length--;

					proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
						"Length: %d", parameter_length);
					offset++;
					length--;

					if(parameter_length > 0) {
						proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
							"Value: %s", strbuf->str);
						offset += parameter_length;
						length -= parameter_length;
					}
				}
				break;
			}
		case 0x03:
			{
				int i, parameter_id, parameter_length;
				int element_length = 1;
				int framing_rtp = 0;
				char parameter_value_tab[15][50];
				static const value_string str_parameter_id[] = {
					{0x01, "Date Of End Of Communication"},
					{0x02, "Node Number"},
					{0x03, "Ticket Protocol Version"},
					{0x06, "Equiment Type"},
					{0x08, "Local IP Address"},
					{0x09, "Distant IP Address"},
					{0x0A, "Local ID"},
					{0x0B, "Distant ID"},
					{0x0C, "Call Duration (second)"},
					{0x0D, "Local SSRC"},
					{0x0E, "Distant SSRC"},
					{0x0F, "Codec"},
					{0x10, "VAD"},
					{0x11, "ECE"},
					{0x12, "Voice Mode"},
					{0x13, "Transmitted Framing (ms)"},
					{0x14, "Received Framing (ms)"},
					{0x15, "Framing Changes"},
					{0x16, "Number Of RTP Packets Received"},
					{0x17, "Number Of RTP Packets Sent"},
					{0x18, "Number Of RTP Packets Lost"},
					{0x19, "Total Silence Detected (second)"},
					{0x1A, "Number Of SID Received"},
					{0x1B, "Delay Distribution"},
					{0x1C, "Maximum Delay (ms)"},
					{0x1D, "Number Of DTMF Received"},
					{0x1E, "Consecutive BFI"},
					{0x1F, "BFI Distribution"},
					{0x20, "Jitter Depth Distribution"},
					{0x21, "Number Of ICMP Host Unreachable"},
					{0x26, "Firmware Version"},
					{0x29, "DSP Framing (ms)"},
					{0x2A, "Transmitter SID"},
					{0x2D, "Minimum Delay (ms)"},
					{0x2E, "802.1 Q Used"},
					{0x2F, "802.1p Priority"},
					{0x30, "VLAN Id"},
					{0x31, "DiffServ"},
					{0x3D, "200 ms BFI Distribution"},
					{0x3E, "Consecutive RTP Lost"},
					{0, NULL}
				};
				static const value_string str_parameter_id_tab[] = {
					{0x1B, "Range: Value"},
					{0x1F, "Range: Value"},
					{0x20, "Jitter: Value"},
					{0x3D, "Contents: Value"},
					{0x3E, "Contents: Value"},
					{0, NULL}
				};

				while(length > 0) {
					parameter_id = tvb_get_guint8(tvb, offset);
					parameter_length = tvb_get_ntohs(tvb, offset + 1);

					if(parameter_length > 0) {
						switch(parameter_id) {
						case 0x06: /* Type Of Equipment */
							{
								static const value_string str_first_byte[] = {
									{0x01, "IP-Phone"},
									{0x02, "Appli-PC"},
									{0x03, "Coupler OmniPCX Enterprise"},
									{0x04, "Coupler OmniPCX Office"},
									{0, NULL}
								};
								static const value_string str_second_byte[] = {
									{0x0101, "IP-Phone V2"},
									{0x0102, "NOE-IP"},
									{0x0200, "4980 Softphone (PCMM2)"},
									{0x0201, "WebSoftphoneIP"},
									{0x0300, "INTIP"},
									{0x0301, "GD"},
									{0x0302, "eVA"},
									{0, NULL}
								};
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s, %s",
									val_to_str(tvb_get_guint8(tvb, offset + 3), str_first_byte, "Unknown"),
									val_to_str(tvb_get_ntohs(tvb, offset + 3), str_second_byte, "Unknown"));
								break;
							}
						case 0x08: /* Local IP Address */
						case 0x09: /* Distant IP Address */
						case 0x26: /* Firmware Version */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", tvb_get_guint8(tvb, offset + 3));
								for(i = 2; i <= parameter_length; i++) {
									ep_strbuf_append(strbuf, ".");
									ep_strbuf_append_printf(strbuf, "%u", tvb_get_guint8(tvb, offset+2+i));
								}
								break;
							}
						case 0x0A:
						case 0x0B:
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append(strbuf, "\"");
								for(i = 1; i <= parameter_length; i++) {
									if(isprint(tvb_get_guint8(tvb, offset + 2 + i)))
										ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+2+i, lengthp));
									else
										ep_strbuf_append(strbuf, tvb_get_const_stringz(tvb, offset+2+i, lengthp));
								}
								ep_strbuf_append(strbuf, "\"");
								break;
							}
						case 0x0F: /* Default Codec */
							{
								static const value_string str_compressor[] = {
									{0x00, "G.711 A-law"},
									{0x01, "G.711 mu-law"},
									{0x02, "G.723.1 6.3kbps"},
									{0x03, "G.729"},
									{0x04, "G.723.1 5.3kbps"},
									{0, NULL}
								};

								if((parameter_length > 0) && (parameter_length <= 8)) {
									guint64 param_value = 0;

									for(i = parameter_length; i > 0; i--) {
										param_value += (tvb_get_guint8(tvb, offset + 2 + i) << (8 * (parameter_length - i)));
									}
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append(strbuf,
										val_to_str((guint8)(param_value), str_compressor, "Default Codec"));
								} else {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)");
								}

								break;
							}
						case 0x10: /* VAD */
						case 0x11: /* ECE */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s",
									val_to_str(tvb_get_guint8(tvb, offset + 3), str_on_off, "On"));
								break;
							}
						case 0x12: /* Voice Mode */
							{
								static const value_string str_voice_mode[] = {
									{0x50, "Idle"},
									{0x51, "Handset"},
									{0x52, "Group Listening"},
									{0x53, "On Hook Dial"},
									{0x54, "Handsfree"},
									{0x55, "Headset"},
									{0, NULL}
								};

								if((parameter_length > 0) && (parameter_length <= 8)) {
									guint64 param_value = 0;

									for(i = parameter_length; i > 0; i--) {
										param_value += (tvb_get_guint8(tvb, offset + 2 + i) << (8 * (parameter_length - i)));
									}
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append(strbuf,
										val_to_str((guint8)(param_value), str_voice_mode, "Unknown"));
								} else {
									ep_strbuf_truncate(strbuf, 0);
									ep_strbuf_append(strbuf, "Parameter Value Too Long (more than 64 bits)");
								}

								break;
							}
						case 0x1B: /* Delay Distribution */
							{
								static const value_string str_range[] = {
									{0, "0-40     "},
									{1, "40-80    "},
									{2, "80-150   "},
									{3, "150-250  "},
									{4, "250 and +"},
									{0, NULL}
								};
								element_length = 2;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "%s: %d",
										val_to_str(i, str_range, "Unknown"),
										tvb_get_ntohs(tvb, offset + 3 + element_length * i));
								}
								break;
							}
						case 0x1E: /* Consecutive BFI */
							{
								static const value_string str_range[] = {
									{0, "0"},
									{1, "1"},
									{2, "2"},
									{3, "3"},
									{4, "4"},
									{5, "5"},
									{6, "5"},
									{7, "7"},
									{8, "8"},
									{9, "9"},
									{0, NULL}
								};
								element_length = 2;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "%s: %d",
										val_to_str(i, str_range, "Unknown"),
										tvb_get_ntohs(tvb, offset + 3 + element_length * i));
								}
								break;
							}
						case 0x1F: /* BFI Distribution */
							{
								static const value_string str_range[] = {
									{0, "0      "},
									{1, "0-1    "},
									{2, "1-2    "},
									{3, "2-3    "},
									{4, "3 and +"},
									{0, NULL}
								};
								element_length = 2;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "%s: %d",
										val_to_str(i, str_range, "Unknown"),
										tvb_get_ntohs(tvb, offset + 3 + element_length * i));
								}
								break;
							}
						case 0x20: /* Jitter Depth Distribution */
							{
								element_length = 4;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "+/- %3d ms: %d",
										((2 * i) + 1) * framing_rtp / 2,
										tvb_get_ntohl(tvb, offset + 3 + 4 * i));
								}
								break;
							}
						case 0x2E: /* 802.1 Q Used */
							{
								static const value_string str_bool[] = {
									{0x00, "False"},
									{0, NULL}
								};
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%s", val_to_str(tvb_get_guint8(tvb, offset + 3), str_bool, "True"));
								break;
							}
						case 0x2F: /* 802.1p Priority */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", (tvb_get_guint8(tvb, offset + 3) & 0x07));
								break;
							}
						case 0x30: /* VLAN Id */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d", (tvb_get_ntohs(tvb, offset + 3) & 0x0FFF));
								break;
							}
						case 0x31: /* DiffServ */
							{
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%d (%d)", tvb_get_guint8(tvb, offset + 3),
									tvb_get_guint8(tvb, offset + 3)>>2);
								break;
							}
						case 0x3D: /* 200 ms BFI Distribution */
							{
								static const value_string str_range[] = {
									{0, "< 10 %  "},
									{1, "< 20 %  "},
									{2, "< 40 %  "},
									{3, "< 60 %  "},
									{4, ">= 60 % "},
									{0, NULL}
								};
								element_length = 2;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "%s: %d",
										val_to_str(i, str_range, "Unknown"),
										tvb_get_ntohs(tvb, offset + 3 + element_length * i));
								}
								break;
							}
						case 0x3E: /* Consecutive RTP Lost */
							{
								static const value_string str_range[] = {
									{0, "1         "},
									{1, "2         "},
									{2, "3         "},
									{3, "4         "},
									{4, "5 and more"},
									{0, NULL}
								};
								element_length = 2;
								for(i = 0; i < (parameter_length / element_length); i++) {
									g_snprintf(parameter_value_tab[i], 50, "%s: %d",
										val_to_str(i, str_range, "Unknown"),
										tvb_get_ntohs(tvb, offset + 3 + element_length * i));
								}
								break;
							}
						case 0x14: /* Received Framing (ms) */
							{
								framing_rtp = tvb_get_guint8(tvb, offset + 3);
							}
						case 0x01: /* Date Of End Of Communication */
						case 0x02: /* Node Number */
						case 0x03: /* Ticket Protocol Version */
						case 0x0C: /* Call Duration (second) */
						case 0x0D: /* Local SSRC */
						case 0x0E: /* Distant SSRC */
						case 0x13: /* Transmitted Framing (ms) */
						case 0x15: /* Framing Changes */
						case 0x16: /* Number Of RTP Packets Received */
						case 0x17: /* Number Of RTP Packets Sent */
						case 0x18: /* Number Of RTP Packets Lost */
						case 0x19: /* Total Silence Detected (second) */
						case 0x1A: /* Number Of SID Received */
						case 0x1C: /* Maximum Delay (ms) */
						case 0x1D: /* Number Of DTMF Received */
						case 0x21: /* Number Of ICMP Host Unreachable */
						case 0x29: /* DSP Framing (ms) */
						case 0x2A: /* Transmitter SID */
						case 0x2D: /* Minimum Delay (ms) */
						default:
							{
								guint64 param_value = 0;

								for(i = parameter_length; i > 0; i--) {
									param_value += (tvb_get_guint8(tvb, offset + 2 + i) << (8 * (parameter_length - i)));
								}
								ep_strbuf_truncate(strbuf, 0);
								ep_strbuf_append_printf(strbuf, "%" G_GINT64_MODIFIER "u", param_value);
								break;
							}
						}
					}

					switch(parameter_id)
					{
					/* Case of values in table */
					case 0x1B:
					case 0x1E:
					case 0x1F:
					case 0x20:
					case 0x3D:
					case 0x3E:
						{
							ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
								parameter_length + 3, "%s:",
								val_to_str(parameter_id, str_parameter_id, "Unknown"));
							proto_tree_add_text(ua3g_body_tree, tvb, offset + 3,
								parameter_length, "          %s",
								val_to_str(parameter_id, str_parameter_id_tab, "Unknown"));
							ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

							proto_tree_add_text(ua3g_param_tree, tvb, offset, 1,
								"Parameter: %s (0x%02x)",
								val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
							offset++;
							length--;

							proto_tree_add_text(ua3g_param_tree, tvb, offset, 2,
								"Length: %d", parameter_length);
							offset += 2;
							length -= 2;

							for(i = 0; i < (parameter_length / element_length); i++) {
								proto_tree_add_text(ua3g_body_tree, tvb, offset,
									element_length, "          %s", parameter_value_tab[i]);
								offset += element_length;
								length -= element_length;
							}
							break;
						}
					default:
						{
							if(parameter_length > 0)
								ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
									parameter_length + 3, "%s: %s",
									val_to_str(parameter_id, str_parameter_id, "Unknown"), strbuf->str);
							else
								ua3g_param_item = proto_tree_add_text(ua3g_body_tree, tvb, offset,
									parameter_length + 3, "%s",
									val_to_str(parameter_id, str_parameter_id, "Unknown"));

							ua3g_param_tree = proto_item_add_subtree(ua3g_param_item, ett_ua3g_param);

							proto_tree_add_text(ua3g_param_tree, tvb, offset, 1, "Parameter: %s (0x%02x)",
								val_to_str(parameter_id, str_parameter_id, "Unknown"), parameter_id);
							offset++;
							length--;

							proto_tree_add_text(ua3g_param_tree, tvb, offset, 2, "Length: %d", parameter_length);
							offset += 2;
							length -= 2;

							if(parameter_length > 0) {
								proto_tree_add_text(ua3g_param_tree, tvb, offset, parameter_length,
									"Value: %s", strbuf->str);
								offset += parameter_length;
								length -= parameter_length;
							}
							break;
						}
					}

				}
				break;
			}
		default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    UNSOLICITED MESSAGE - 9Fh/1Fh (MESSAGE FROM THE TERMINAL)
    VERSION RESPONSE - 21h (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_unsolicited_msg(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo,
		       guint offset, guint length, guint8 opcode,
		       proto_item *ua3g_item, proto_item *ua3g_body_item)
{
	guint8 command = tvb_get_guint8(tvb, offset);
	proto_tree *ua3g_body_tree;
	static const value_string str_command[] = {
		{0x00, "Hardware Reset Acknowledge"},
		{0x01, "Software Reset Acknowledge"},
		{0x02, "Illegal Command Received"},
		{0x05, "Subdevice Down"},
		{0x06, "Segment Failure"},
		{0x07, "UA Device Event"},
		{0, NULL}
	};

	if(opcode != 0x21) {
		/* add text to the frame tree */
		proto_item_append_text(ua3g_item, ", %s",
			val_to_str(command, str_command, "Unknown"));
		proto_item_append_text(ua3g_body_item, " - %s",
			val_to_str(command, str_command, "Unknown"));
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

		/* add text to the frame "INFO" column */
		if (check_col(pinfo->cinfo, COL_INFO))
			col_append_fstr(pinfo->cinfo, COL_INFO, ": %s",
				val_to_str(command, str_command, "Unknown"));

		proto_tree_add_uint_format(ua3g_body_tree, hf_ua3g_command, tvb,
			offset, 1, command, "Command: %s",
			val_to_str(command, str_command, "Unknown"));
		offset++;
		length--;
	} else {
		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
		command = 0xFF; /* Opcode = 0x21 */
	}

	switch(command)
	{
	case 0x00: /* Hardware Reset Acknowledge */
	case 0x01: /* Software Reset Acknowledge */
	case 0xFF: /* Opcode = 0x21 : Version Response */
		{
			int link, vta_type;
			static const value_string str_vta_type[] = {
				{0x03, "4035"},
				{0x04, "4020"},
				{0x05, "4010"},
				{0x20, "NOE A"},
				{0x21, "NOE B"},
				{0x22, "NOE C"},
				{0x23, "NOE D"},
				{0, NULL}
			};
			static const value_string str_other_info_1[] = {
				{0x00, "Link Is TDM"},
				{0x01, "Link Is IP"},
				{0, NULL}
			};
			static const value_string str_other_info_2[] = {
				{0x00, "Download Allowed"},
				{0x01, "Download Refused"},
				{0, NULL}
			};
			static const value_string str_hard_config_ip[] = {
				{0x00, "Export Binary (No Thales)"},
				{0x01, "Full Binary (Thales)"},
				{0, NULL}
			};
			static const value_string str_hard_config_chip[] = {
				{0x00, "Chip Id: Unknown"},
				{0x01, "Chip Id: Ivanoe 1"},
				{0x02, "Chip Id: Ivanoe 2"},
				{0x03, "Chip Id: Reserved"},
				{0, NULL}
			};
			static const value_string str_hard_config_flash[] = {
				{0x00, "Flash Size: No Flash"},
				{0x01, "Flash Size: 128 Kbytes"},
				{0x02, "Flash Size: 256 Kbytes"},
				{0x03, "Flash Size: 512 Kbytes"},
				{0x04, "Flash Size: 1 Mbytes"},
				{0x05, "Flash Size: 2 Mbytes"},
				{0x06, "Flash Size: 4 Mbytes"},
				{0x07, "Flash Size: 8 Mbytes"},
				{0, NULL}
			};
			static const value_string str_hard_config_ram[] = {
				{0x00, "External RAM Size: No External RAM"},
				{0x01, "External RAM Size: 128 Kbytes"},
				{0x02, "External RAM Size: 256 Kbytes"},
				{0x03, "External RAM Size: 512 Kbytes"},
				{0x04, "External RAM Size: 1 Mbytes"},
				{0x05, "External RAM Size: 2 Mbytes"},
				{0x06, "External RAM Size: 4 Mbytes"},
				{0x07, "External RAM Size: 8 Mbytes"},
				{0, NULL}
			};

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Device Type: %s",
				val_to_str(tvb_get_guint8(tvb, offset), str_device_type, "Unknown"));
			offset++;
			length--;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 2, "Firmware Version: %s",
				version_number_computer(tvb_get_ntohs(tvb, offset)));
			offset += 2;
			length -= 2;

			if(opcode != 0x21) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Self-Test Result: %d", tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}

			vta_type = tvb_get_guint8(tvb, offset);

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "VTA Type: %s",
				val_to_str(vta_type, str_vta_type, "Unknown"));
			offset++;
			length--;

			switch(vta_type)
			{
			case 0x03:
			case 0x04:
			case 0x05:
				{
					static const value_string str_subtype[] = {
						{0x03, "2x40"},
						{0x04, "1x20"},
						{0x05, "1x20"},
						{0, NULL}
					};
					static const value_string str_generation[] = {
						{0x02, "3"},
						{0, NULL}
					};
					static const value_string str_design[] = {
						{0x00, "Alpha"},
						{0, NULL}
					};
					static const value_string str_hard_vta_type[] = {
						{0x03, "MR2 (4035)"},
						{0x05, "VLE (4010)"},
						{0x07, "LE (4020)"},
						{0, NULL}
					};
					static const value_string str_hard_design[] = {
						{0x06, "Alpha"},
						{0, NULL}
					};
					static const value_string str_hard_subtype[] = {
						{0x06, "2x40"},
						{0x07, "1x20"},
						{0x08, "1x20"},
						{0, NULL}
					};

					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
						"Characteristic Number: VTA SubType: %s, Generation: %s, Design: %s",
						val_to_str((((tvb_get_guint8(tvb, offset) & 0xC0) >> 6) + vta_type), str_subtype, "Unknown"),
						val_to_str(((tvb_get_guint8(tvb, offset) & 0x38) >> 3), str_generation, "Unknown"),
						val_to_str((tvb_get_guint8(tvb, offset) & 0x07), str_design, "Unknown"));
					offset++;
					length--;
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
						"Other Information: %s",
						val_to_str(tvb_get_guint8(tvb, offset), str_other_info_2, "Unknown"));
					offset++;
					length--;

					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
						"Hardware Configuration: VTA Type: %s, Design: %s, VTA SubType: %s",
						val_to_str((((tvb_get_guint8(tvb, offset) & 0xE0) >> 5) + vta_type), str_hard_vta_type, "Unknown"),
						val_to_str(((tvb_get_guint8(tvb, offset) & 0x1C) >> 2), str_hard_design, "Unknown"),
						val_to_str((tvb_get_guint8(tvb, offset) & 0x03), str_hard_subtype, "Unknown"));
					offset++;
					length--;

					if(opcode != 0x21) {
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
							"Hook Status/BCM Version: %s Hook",
							val_to_str(tvb_get_guint8(tvb, offset), str_on_off, "On"));
						offset++;
						length--;

					}
					break;
				}
			case 0x20:
			case 0x21:
			case 0x22:
			case 0x23:
			default:
				{
					link = tvb_get_guint8(tvb, offset);
					proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
						"Other Information 1: %s",
						val_to_str(link, str_other_info_1, "Unknown"));
					offset++;
					length--;

					if(link == 0x00) {
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
							"Hardware Version: %d", tvb_get_guint8(tvb, offset));
						offset++;
						length--;

						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
							"Hardware Configuration: %s, %s, %s",
							val_to_str((tvb_get_guint8(tvb, offset) & 0x03), str_hard_config_chip, "Unknown"),
							val_to_str(((tvb_get_guint8(tvb, offset) & 0x1C) >> 2), str_hard_config_flash, "Unknown"),
							val_to_str(((tvb_get_guint8(tvb, offset) & 0xE0) >> 5), str_hard_config_ram, "Unknown"));
						offset++;
						length--;
					} else {
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Other Information 2: %s",
							val_to_str(tvb_get_guint8(tvb, offset), str_other_info_2, "Unknown"));
						offset++;
						length--;

						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hardware Configuration: %s",
							val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_hard_config_ip, "Unknown"));
						offset++;
						length--;
					}

					if(opcode != 0x21) {
						proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Hook Status: %s Hook",
							val_to_str(tvb_get_guint8(tvb, offset), str_on_off, "On"));
						offset++;
						length--;

						if(length > 0) {
							if(link == 0x00) {
								proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
									"Firmware Datas Patch Version: %s",
									version_number_computer(tvb_get_ntohs(tvb, offset)));
								offset += 2;
								length -= 2;

								if(length > 0) {
									proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
										"Firmware Version (Loader): %s",
										version_number_computer(tvb_get_ntohs(tvb, offset)));
								}
							} else {
								proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
									"Datas Version: %s",
									version_number_computer(tvb_get_ntohs(tvb, offset)));
								offset += 2;
								length -= 2;

								if(length > 0) {
									proto_tree_add_text(ua3g_body_tree, tvb, offset, 2,
										"Firmware Version (Bootloader): %s",
										version_number_computer(tvb_get_ntohs(tvb, offset)));
								}
							}
						}
					}
					break;
				}
			}
			break;
		}
	case 0x02: /* Illegal Command Received */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Opcode Of Bad  Command: %d", tvb_get_guint8(tvb, offset));
			offset++;
			length--;

			while(length >0) {
				proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
					"Next Byte Of Bad Command: %d", tvb_get_guint8(tvb, offset));
				offset++;
				length--;
			}
			break;
		}
	case 0x05: /* Subdevice Down */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Subdevice Address: %d", tvb_get_guint8(tvb, offset));
			break;
		}
	case 0x06: /* Segment Failure */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "T: %d",
				(tvb_get_guint8(tvb, offset) & 0x01));
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Num: %d",
				(tvb_get_guint8(tvb, offset) & 0x02));
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "/S: %d",
				(tvb_get_guint8(tvb, offset) & 0x04));
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "L: %d",
				(tvb_get_guint8(tvb, offset) & 0x08));
			offset++;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Opcode Bad Segment: %d", tvb_get_guint8(tvb, offset));
			offset++;

			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Next Byte Of Bad Segment: %d",
				tvb_get_guint8(tvb, offset));
			break;
		}
	case 0x07: /* UA Device Event */
		{
			proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
				"Device Event: %d", tvb_get_guint8(tvb, offset));
			break;
		}
	default:
		{
			break;
		}
	}
}


/*-----------------------------------------------------------------------------
    NON-DIGIT KEY PUSHED - 20h (MESSAGE FROM THE TERMINAL)
    DIGIT KEY RELEASED - 26h (MESSAGE FROM THE TERMINAL)
    KEY RELEASED - 2Ah (MESSAGE FROM THE TERMINAL)
    TM KEY PUSHED - 2Dh (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_key_number(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_,
		  guint offset, guint length, guint8 opcode _U_,
		  proto_item *ua3g_body_item _U_)
{
/*    proto_tree *ua3g_body_tree;*/
/*
	static const value_string str_first_parameter[] = {
		{0x01, "Production Test Command"},
		{0x06, "Reserved For Compatibility"},
		{0x3B, "Volmue"},
		{0x42, "Subdevice Address"},
		{0, NULL}
	};
*/
	if(length > 0) {
/*		ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);*/
		proto_tree_add_text(tree, tvb, offset, length,
			"Key Number: Row %d, Column %d",
			(tvb_get_guint8(tvb, offset) & 0xF0), (tvb_get_guint8(tvb, offset) & 0x0F));
	}
}


/*-----------------------------------------------------------------------------
    I'M HERE - 22h - Only for UA NOE (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_i_m_here(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		guint offset, guint length _U_, guint8 opcode _U_,
		proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "Id Code: %s",
		val_to_str(tvb_get_guint8(tvb, offset), str_device_type, "Unknown"));
}


/*-----------------------------------------------------------------------------
    RESPONSE STATUS INQUIRY - 23h (MESSAGE FROM THE TERMINAL)
    SPECIAL KEY STATUS - 29h (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_special_key(proto_tree *tree _U_, tvbuff_t *tvb, packet_info *pinfo _U_,
		   guint offset, guint length, guint8 opcode,
		   proto_item *ua3g_body_item)
{
/*	guint8 command = tvb_get_guint8(tvb, offset);*/
	proto_tree *ua3g_body_tree;
	int i;
	static const value_string str_parameters[] = {
		{0x00, "Not Received Default In Effect"},
		{0x02, "Downloaded Values In Effect"},
		{0, NULL}
	};
	static const value_string str_special_key_status[] = {
		{0x00, "Released"},
		{0, NULL}
	};
	static const value_string str_special_key[] = {
		{0, "Shift "},
		{1, "Ctrl  "},
		{2, "Alt   "},
		{3, "Cmd   "},
		{4, "Shift'"},
		{5, "Ctrl' "},
		{6, "Alt'  "},
		{7, "Cmd'  "},
		{0, NULL}
	};

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);
	if(opcode == 0x23) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Parameters Received for DTMF: %s",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x02), str_parameters, "Unknown"));
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Hookswitch Status: %shook",
			val_to_str((tvb_get_guint8(tvb, offset) & 0x01), str_on_off, "On"));
		offset++;
		length--;
	}

	for(i = 0; i < 8; i++) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1, "%s: %s",
			val_to_str(i, str_special_key, "Unknown"),
			val_to_str((tvb_get_guint8(tvb, offset) & (0x01 << i)),
			str_special_key_status, "Pressed"));
	}
}


/*-----------------------------------------------------------------------------
    SUBDEVICE STATE ENQUIRY - 24h (MESSAGE FROM THE TERMINAL)
    ---------------------------------------------------------------------------*/
static void
decode_subdevice_state(proto_tree *tree _U_, tvbuff_t *tvb,
		       packet_info *pinfo _U_, guint offset, guint length _U_,
		       guint8 opcode _U_, proto_item *ua3g_body_item)
{
	proto_tree *ua3g_body_tree;
	int i;

	ua3g_body_tree = proto_item_add_subtree(ua3g_body_item, ett_ua3g_body);

	for(i = 0; i <= 7; i++) {
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Subdevice %d State: %d",
			i, (tvb_get_guint8(tvb, offset) & 0x0F));
		i++;
		proto_tree_add_text(ua3g_body_tree, tvb, offset, 1,
			"Subdevice %d State: %d",
			i, ((tvb_get_guint8(tvb, offset) & 0xF0) >> 4));
		offset++;
	}
}


/*-----------------------------------------------------------------------------
    UA3G DISSECTOR
    ---------------------------------------------------------------------------*/
static void
dissect_ua3g(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	proto_item *ua3g_item = NULL;
	proto_tree *ua3g_tree = NULL;
	proto_item *ua3g_body_item = NULL;
	gint length = 0;
	gint offset = 0;
	guint8 opcode = 0x00;

	if(tree) {
		ua3g_item = proto_tree_add_item(tree, proto_ua3g, tvb, 0, -1, ENC_NA);
		ua3g_tree = proto_item_add_subtree(ua3g_item, ett_ua3g);

		if(message_direction == SYS_TO_TERM) {
			opcodes_vals = opcodes_vals_sys;
		} else
			opcodes_vals = opcodes_vals_term;

		/* Length of the UA Message */
		length = tvb_get_letohs(tvb, offset);

		/* add text to the frame "INFO" column */
		if (check_col(pinfo->cinfo, COL_INFO))
			col_append_fstr(pinfo->cinfo, COL_INFO, " - UA3G Message:");

		proto_tree_add_uint(ua3g_tree, hf_ua3g_length, tvb, offset, 2, length);
		offset += 2;

		/* Opcode of the UA Message */
		opcode = tvb_get_guint8(tvb, offset);
		if(opcode != 0x9f)
			opcode = (opcode & 0x7f);

		/* Usefull for a research in wireshark */
		proto_tree_add_uint_format(ua3g_tree, hf_ua3g_opcode, tvb, offset,
			1, opcode, "Opcode: %s (0x%02x)",
			val_to_str(opcode, opcodes_vals, "Unknown"), opcode);
		offset++;
		length--;

		/* add text to the frame "INFO" column */
		if (check_col(pinfo->cinfo, COL_INFO))
			col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
				val_to_str(opcode, opcodes_vals, "Unknown"));

		proto_item_append_text(ua3g_item, ", %s", val_to_str(opcode, opcodes_vals, "Unknown"));

		if(length > 0)
			ua3g_body_item = proto_tree_add_text(ua3g_tree, tvb, offset,
				length, "UA3G Body - %s",
				val_to_str(opcode, opcodes_vals, "Unknown"));

		if(message_direction == SYS_TO_TERM) {
			switch(opcode) {
			case PRODUCTION_TEST: /* 0x01 */
			case SUBDEVICE_RESET: /* 0x06 */
			case ARE_YOU_THERE: /* 0x2B */
			case SET_SPEAKER_VOL: /* 0x3B */
			case TRACE_ON: /* 0x42 */
				{
					decode_with_one_parameter(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SUBDEVICE_ESCAPE: /* 0x02 */
				{
					decode_subdevice_escape(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SOFT_RESET: /* 0x03 */
				{
					decode_software_reset(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case IP_PHONE_WARMSTART: /* 0x04 */
				{
					decode_ip_phone_warmstart(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SUPER_MSG: /* 0x0B */
			case SUPER_MSG_2: /* 0x17 */
				{
					decode_super_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SEGMENT_MSG: /* 0x0C */
				{
					decode_segment_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case IP_DEVICE_ROUTING: /* 0x13 */
				{
					decode_ip_device_routing(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case DEBUG_IN_LINE: /* 0x18 */
				{
					decode_debug_in_line(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case LED_COMMAND: /* 0x21 */
				{
					decode_led_command(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case LCD_LINE_1_CMD: /* 0x27 */
			case LCD_LINE_2_CMD: /* 0x28 */
				{
					decode_lcd_line_cmd(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case MAIN_VOICE_MODE: /* 0x29 */
				{
					decode_main_voice_mode(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case SUBDEVICE_METASTATE: /* 0x2C */
				{
					decode_subdevice_metastate(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case DWL_DTMF_CLCK_FORMAT: /* 0x30 */
				{
					decode_dwl_dtmf_clck_format(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SET_CLCK: /* 0x31 */
				{
					decode_set_clck(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case VOICE_CHANNEL: /* 0x32 */
				{
					decode_voice_channel(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case EXTERNAL_RINGING: /* 0x33 */
				{
					decode_external_ringing(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case LCD_CURSOR: /* 0x35 */
				{
					decode_lcd_cursor(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case DWL_SPECIAL_CHAR: /* 0x36 */
				{
					decode_dwl_special_char(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SET_CLCK_TIMER_POS: /* 0x38 */
				{
					decode_set_clck_timer_pos(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case SET_LCD_CONTRAST: /* 0x39 */
				{
					decode_set_lcd_contrast(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case BEEP: /* 0x3C */
				{
					decode_beep(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case SIDETONE: /* 0x3D */
				{
					decode_sidetone(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case RINGING_CADENCE: /* 0x3E */
				{
					decode_ringing_cadence(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case MUTE: /* 0x3F */
				{
					decode_mute(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case FEEDBACK: /* 0x40 */
				{
					decode_feedback(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case READ_PERIPHERAL: /* 0x44 */
			case WRITE_PERIPHERAL: /* 0x45 */
				{
					decode_r_w_peripheral(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case ICON_CMD: /* 0x47 */
				{
					decode_icon_cmd(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case AUDIO_CONFIG: /* 0x49 */
				{
					decode_audio_config(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case AUDIO_PADDED_PATH: /* 0x4A */
				{
					decode_audio_padded_path(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case KEY_RELEASE: /* 0x41 */
			case AMPLIFIED_HANDSET: /* 0x48 */
			case LOUDSPEAKER: /* 0x4D */
			case ANNOUNCE: /* 0x4E */
				{
					decode_on_off_level(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case RING: /* 0x4F */
				{
					decode_ring(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case UA_DWL_PROTOCOL: /* 0x50 */
				{
					decode_ua_dwl_protocol(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			/* Case for UA3G message with only opcode (No body) */
			case NOP: /* 0x00 */
			case HE_ROUTING:	/* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case LOOPBACK_ON: /* 0x07 */
			case LOOPBACK_OFF: /* 0x08 */
			case VIDEO_ROUTING:	/* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case REMOTE_UA_ROUTING:	/* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case VERY_REMOTE_UA_ROUTING:	/* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case OSI_ROUTING:	/* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case ABC_A_ROUTING:	/* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case IBS_ROUTING:	/* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case M_REFLEX_HUB_ROUTING:	/* 0x14 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case START_BUZZER: /* 0x22 */
			case STOP_BUZZER: /* 0x23 */
			case ENABLE_DTMF: /* 0x24 */
			case DISABLE_DTMF: /* 0x25 */
			case CLEAR_LCD_DISP: /* 0x26 */
			case VERSION_INQUIRY: /* 0x2A */
			case VTA_STATUS_INQUIRY: /* 0x2D */
			case SUBDEVICE_STATE: /* 0x2E */
			case AUDIO_IDLE: /* 0x3A */
			case TRACE_OFF: /* 0x43 */
			case ALL_ICONS_OFF: /* 0x46 */
			case RELEASE_RADIO_LINK: /* 0x4B */
			case DECT_HANDOVER:	/* 0x4C NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			default:
				{
					break;
				}
			}
		}
		if(message_direction == TERM_TO_SYS) {
			switch(opcode) {
			case CS_DIGIT_DIALED: /* 0x03 */
				{
					decode_digit_dialed(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_SUBDEVICE_MSG: /* 0x04 */
				{
					decode_subdevice_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_SUPER_MSG: /* 0x0B */
			case CS_SUPER_MSG_2: /* 0x17 */
				{
					decode_super_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_SEGMENT_MSG: /* 0x0C */
				{
					decode_segment_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_IP_DEVICE_ROUTING: /* 0x13 */
				{
					decode_cs_ip_device_routing(ua3g_tree, tvb, pinfo, offset, length, opcode,ua3g_item, ua3g_body_item);
					break;
				}
			case CS_DEBUG_IN_LINE: /* 0x18 */
				{
					decode_debug_in_line(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_NON_DIGIT_KEY_PUSHED:	/* 0x20 Key translation not sure */
			case CS_DIGIT_KEY_RELEASED:	/* 0x26 Key translation not sure */
			case CS_KEY_RELEASED: /* 0x2A */
			case CS_TM_KEY_PUSHED:	/* 0x2D Key translation not sure */
				{
					decode_key_number(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_UNSOLICITED_MSG: /* 0x9F (0x1F) */
			case CS_VERSION_RESPONSE: /* 0x21 */
				{
					decode_unsolicited_msg(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			case CS_I_M_HERE: /* 0x22 */
				{
					decode_i_m_here(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_RSP_STATUS_INQUIRY: /* 0x23 */
			case CS_SPECIAL_KEY_STATUS: /* 0x29 */
				{
					decode_special_key(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_SUBDEVICE_STATE: /* 0x24 */
				{
					decode_subdevice_state(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_PERIPHERAL_CONTENT: /* 0x2B */
				{
					decode_r_w_peripheral(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_body_item);
					break;
				}
			case CS_UA_DWL_PROTOCOL: /* 0x50 */
				{
					decode_ua_dwl_protocol(ua3g_tree, tvb, pinfo, offset, length, opcode, ua3g_item, ua3g_body_item);
					break;
				}
			/* Case for UA3G message with only opcode (No body) */
			case CS_NOP_ACK: /* 0x00 */
			case CS_HANDSET_OFFHOOK: /* 0x01 */
			case CS_HANDSET_ONHOOK: /* 0x02 */
			case CS_HE_ROUTING:	/* 0x05 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_LOOPBACK_ON: /* 0x06 */
			case CS_LOOPBACK_OFF: /* 0x07 */
			case CS_VIDEO_ROUTING:	/* 0x09 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_WARMSTART_ACK: /* 0x0A */
			case CS_REMOTE_UA_ROUTING:	/* 0x0D NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_VERY_REMOTE_UA_R:	/* 0x0E NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_OSI_ROUTING:	/* 0x0F NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_ABC_A_ROUTING:	/* 0x11 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_IBS_ROUTING:	/* 0x12 NOT DECODED - No description in 3AK 29000 0556 DSZZA */
			case CS_TRACE_ON_ACK: /* 0x27 */
			case CS_TRACE_OFF_ACK: /* 0x28 */
			default:
				{
					break;
				}
			}
		}
	}
}


/*-----------------------------------------------------------------------------
    DISSECTORS REGISTRATION FUNCTIONS
    ---------------------------------------------------------------------------*/
void proto_register_ua3g(void)
{
	static hf_register_info hf_ua3g[] =
		{
			{ &hf_ua3g_length,
			  { "Length", "ua3g.length", FT_UINT16, BASE_DEC,
			    NULL, 0x00, "Decimal Value", HFILL }
			},
			{ &hf_ua3g_opcode,
			  { "Opcode", "ua3g.opcode", FT_UINT8, BASE_HEX,
			    NULL, 0x00, "Hexa Value", HFILL }
			},
			{ &hf_ua3g_ip,
			  { "IP Device Routing", "ua3g.ip", FT_UINT8, BASE_HEX,
			    NULL, 0x00, "Hexa Value - 2nd Command For IP Device Routing Opcode", HFILL }
			},
			{ &hf_ua3g_command,
			  { "Command", "ua3g.command", FT_UINT8, BASE_HEX,
				NULL, 0x00, "Hexa Value - 2nd Command (Excepted IP Device Routing Opcode)", HFILL }
			},
		};

	static gint *ett[] =
	{
		&ett_ua3g,
		&ett_ua3g_body,
		&ett_ua3g_param,
		&ett_ua3g_option,
	};

	/* UA3G dissector registration */
	proto_ua3g = proto_register_protocol("UA3G Message", "UA3G", "ua3g");

	proto_register_field_array(proto_ua3g, hf_ua3g, array_length(hf_ua3g));

	register_dissector("ua3g", dissect_ua3g, proto_ua3g);

	/* Common subtree array registration */
	proto_register_subtree_array(ett, array_length(ett));
}


void proto_reg_handoff_ua3g(void)
{
	dissector_handle_t handle_ua3g = find_dissector("ua3g");

	/* hooking of UA3G on UDP */
	register_dissector_table("ua3g.opcode", "ua3g.opcode",
		FT_UINT8, BASE_HEX);

	dissector_add_uint("ua3g.opcode", 0x15, handle_ua3g);
}
