/* packet-gsm_a_common.c
 * Common routines for GSM A Interface dissection
 *
 * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
 * In association with Telos Technology Inc.
 *
 * Split from packet-gsm_a.c by Neil Piercy <Neil [AT] littlebriars.co.uk>
 *
 * $Id: packet-gsm_a_common.c 35608 2011-01-21 09:31:25Z etxrab $
 *
 * 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 <stdlib.h>
#include <math.h>

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/tap.h>

#include "packet-bssap.h"
#include "packet-sccp.h"
#include "packet-gsm_a_common.h"
#include "packet-e212.h"

/* nasty globals as a result of the split of packet-gsm_a.c in need of further restructure */
/* nasty static for handling half octet mandatory V IEs */
gboolean lower_nibble=FALSE;

const value_string gsm_common_elem_strings[] = {
	/* Common Information Elements 10.5.1 */
	{ 0x00,	"Cell Identity" },
	{ 0x00,	"Ciphering Key Sequence Number" },
	{ 0x00,	"Location Area Identification (LAI)" },
	{ 0x00,	"Mobile Identity" },
	{ 0x00,	"Mobile Station Classmark 1" },
	{ 0x00,	"Mobile Station Classmark 2" },
	{ 0x00,	"Mobile Station Classmark 3" },
	{ 0x00,	"Spare Half Octet" },
	{ 0x00,	"Descriptive group or broadcast call reference" },
	{ 0x00,	"Group Cipher Key Number" },
	{ 0x00,	"PD and SAPI $(CCBS)$" },
	{ 0x00,	"Priority Level" },
	{ 0x00,	"PLMN List" },
	{ 0, NULL }
};

/* Mobile Station Classmark Value strings
 */

/* Mobile Station Classmark
 * Revision level
 */
static const value_string gsm_a_msc_rev_vals[] = {
	{ 0,	"Reserved for GSM phase 1"},
	{ 1,	"Used by GSM phase 2 mobile stations"},
	{ 2,	"Used by mobile stations supporting R99 or later versions of the protocol"},
	{ 3,	"Reserved for future use"},
	{ 0,	NULL }
};

/* ES IND (octet 3, bit 5) "Controlled Early Classmark Sending" option implementation */
static const value_string ES_IND_vals[] = {
	{ 0,	"Controlled Early Classmark Sending option is not implemented in the MS"},
	{ 1,	"Controlled Early Classmark Sending option is implemented in the MS"},
	{ 0,	NULL }
};
/* A5/1 algorithm supported (octet 3, bit 4 */
static const value_string A5_1_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/1 available"},
	{ 1,	"encryption algorithm A5/1 not available"},
	{ 0,	NULL }
};
/* RF Power Capability (Octet 3) */
static const value_string RF_power_capability_vals[] = {
	{ 0,	"class 1"},
	{ 1,	"class 2"},
	{ 2,	"class 3"},
	{ 3,	"class 4"},
	{ 4,	"class 5"},
	{ 7,	"RF Power capability is irrelevant in this information element"},
	{ 0,	NULL }
};
/* PS capability (pseudo-synchronization capability) (octet 4) */
static const value_string ps_sup_cap_vals[] = {
	{ 0,	"PS capability not present"},
	{ 1,	"PS capability present"},
	{ 0,	NULL }
};
/* SS Screening Indicator (octet 4)defined in 3GPP TS 24.080 */
static const value_string SS_screening_indicator_vals[] = {
	{ 0,	"Default value of phase 1"},
	{ 1,	"Capability of handling of ellipsis notation and phase 2 error handling "},
	{ 2,	"For future use"},
	{ 3,	"For future use"},
	{ 0,	NULL }
};
/* SM capability (MT SMS pt to pt capability) (octet 4)*/
static const value_string SM_capability_vals[] = {
	{ 0,	"Mobile station does not support mobile terminated point to point SMS"},
	{ 1,	"Mobile station supports mobile terminated point to point SMS"},
	{ 0,	NULL }
};
/* VBS notification reception (octet 4) */
static const value_string VBS_notification_rec_vals[] = {
	{ 0,	"no VBS capability or no notifications wanted"},
	{ 1,	"VBS capability and notifications wanted"},
	{ 0,	NULL }
};
/* VGCS notification reception (octet 4) */
static const value_string VGCS_notification_rec_vals[] = {
	{ 0,	"no VGCS capability or no notifications wanted"},
	{ 1,	"VGCS capability and notifications wanted"},
	{ 0,	NULL }
};
/* FC Frequency Capability (octet 4 ) */
static const value_string FC_frequency_cap_vals[] = {
	{ 0,	"The MS does not support the E-GSM or R-GSM band"},
	{ 1,	"The MS does support the E-GSM or R-GSM "},
	{ 0,	NULL }
};
/* CM3 (octet 5, bit 8) */
static const value_string CM3_vals[] = {
	{ 0,	"The MS does not support any options that are indicated in CM3"},
	{ 1,	"The MS supports options that are indicated in classmark 3 IE"},
	{ 0,	NULL }
};
/* LCS VA capability (LCS value added location request notification capability) (octet 5,bit 6) */
static const value_string LCS_VA_cap_vals[] = {
	{ 0,	"LCS value added location request notification capability not supported"},
	{ 1,	"LCS value added location request notification capability supported"},
	{ 0,	NULL }
};
/* UCS2 treatment (octet 5, bit 5) */
static const value_string UCS2_treatment_vals[] = {
	{ 0,	"the ME has a preference for the default alphabet"},
	{ 1,	"the ME has no preference between the use of the default alphabet and the use of UCS2"},
	{ 0,	NULL }
};
/* SoLSA (octet 5, bit 4) */
static const value_string SoLSA_vals[] = {
	{ 0,	"The ME does not support SoLSA"},
	{ 1,	"The ME supports SoLSA"},
	{ 0,	NULL }
};
/* CMSP: CM Service Prompt (octet 5, bit 3) */
static const value_string CMSP_vals[] = {
	{ 0,	"Network initiated MO CM connection request not supported"},
	{ 1,	"Network initiated MO CM connection request supported for at least one CM protocol"},
	{ 0,	NULL }
};
/* A5/4 algorithm supported */
static const value_string A5_7_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/7 not available"},
	{ 1,	"encryption algorithm A5/7 available"},
	{ 0,	NULL }
};
/* A5/4 algorithm supported */
static const value_string A5_6_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/6 not available"},
	{ 1,	"encryption algorithm A5/6 available"},
	{ 0,	NULL }
};
/* A5/5 algorithm supported */
static const value_string A5_5_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/5 not available"},
	{ 1,	"encryption algorithm A5/5 available"},
	{ 0,	NULL }
};
/* A5/4 algorithm supported */
static const value_string A5_4_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/4 not available"},
	{ 1,	"encryption algorithm A5/4 available"},
	{ 0,	NULL }
};

/* A5/3 algorithm supported (octet 5, bit 2) */
static const value_string A5_3_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/3 not available"},
	{ 1,	"encryption algorithm A5/3 available"},
	{ 0,	NULL }
};

/* A5/2 algorithm supported (octet 5, bit 1) */
static const value_string A5_2_algorithm_sup_vals[] = {
	{ 0,	"encryption algorithm A5/2 not available"},
	{ 1,	"encryption algorithm A5/2 available"},
	{ 0,	NULL }
};

static const value_string mobile_identity_type_vals[] = {
	{ 1,	"IMSI"},
	{ 2,	"IMEI"},
	{ 3,	"IMEISV"},
	{ 4,	"TMSI/P-TMSI"},
	{ 5,	"TMGI and optional MBMS Session Identity"}, /* ETSI TS 124 008 V6.8.0 (2005-03) p326 */
	{ 0,	"No Identity"},
	{ 0,	NULL }
};

static const value_string oddevenind_vals[] = {
	{ 0,	"Even number of identity digits"},
	{ 1,	"Odd number of identity digits"},
	{ 0,	NULL }
};

static const value_string true_false_vals[] = {
    { 0, "false" },
    { 1, "true" },
    { 0, NULL}
};

const value_string gsm_a_sms_vals[] = {
    {0, "1/4 timeslot (~144 microseconds)" },
    {1, "2/4 timeslot (~288 microseconds)" },
    {2, "3/4 timeslot (~433 microseconds)" },
    {3, "4/4 timeslot (~577 microseconds)" },
    {4, "5/4 timeslot (~721 microseconds)" },
    {5, "6/4 timeslot (~865 microseconds)" },
    {6, "7/4 timeslot (~1009 microseconds)" },
    {7, "8/4 timeslot (~1154 microseconds)" },
    {8, "9/4 timeslot (~1298 microseconds)" },
    {9, "10/4 timeslot (~1442 microseconds)" },
    {10, "11/4 timeslot (~1586 microseconds)" },
    {11, "12/4 timeslot (~1730 microseconds)" },
    {12, "13/4 timeslot (~1874 microseconds)" },
    {13, "14/4 timeslot (~2019 microseconds)" },
    {14, "15/4 timeslot (~2163 microseconds)" },
    {15, "16/4 timeslot (~2307 microseconds)" },
    { 0, NULL}
};

static const value_string ms_assisted_e_otd_vals[] = {
	{ 0, "MS assisted E-OTD not supported" },
	{ 1, "MS assisted E-OTD supported" },
	{ 0, NULL}
};

static const value_string ms_based_e_otd_vals[] = {
	{ 0, "MS based E-OTD not supported" },
	{ 1, "MS based E-OTD supported" },
	{ 0, NULL}
};

static const value_string ms_assisted_gps_vals[] = {
	{ 0, "MS assisted GPS not supported" },
	{ 1, "MS assisted GPS supported" },
	{ 0, NULL}
};

static const value_string ms_based_gps_vals[] = {
	{ 0, "MS based GPS not supported" },
	{ 1, "MS based GPS supported" },
	{ 0, NULL}
};

static const value_string ms_conventional_gps_vals[] = {
	{ 0, "Conventional GPS not supported" },
	{ 1, "Conventional GPS supported" },
	{ 0, NULL}
};

static const value_string modulation_capability_vals[] = {
	{ 0, "8-PSK supported for downlink reception only" },
	{ 1, "8-PSK supported for uplink transmission and downlink reception" },
	{ 0, NULL}
};

static const value_string eight_psk_rf_power_capability_vals[] = {
	{ 0, "Reserved" },
	{ 1, "Power class E1" },
	{ 2, "Power class E2" },
	{ 3, "Power class E3" },
	{ 0, NULL}
};

static const value_string gsm_400_bands_supported_vals[] = {
    { 1, "GSM 480 supported, GSM 450 not supported" },
    { 2, "GSM 450 supported, GSM 480 not supported" },
    { 3, "GSM 450 supported, GSM 480 supported" },
    { 0, NULL}
};

static const value_string umts_fdd_rat_cap_vals[] = {
	{ 0, "UMTS FDD not supported" },
	{ 1, "UMTS FDD supported" },
	{ 0, NULL}
};

static const value_string umts_384_mcps_tdd_rat_cap_vals[] = {
	{ 0, "UMTS 3.84 Mcps TDD not supported" },
	{ 1, "UMTS 3.84 Mcps TDD supported" },
	{ 0, NULL}
};

static const value_string cdma_2000_rat_cap_vals[] = {
	{ 0, "CDMA 2000 not supported" },
	{ 1, "CDMA 2000 supported" },
	{ 0, NULL}
};

static const value_string dtm_gprs_multi_slot_class_vals[] = {
	{ 0, "Unused. If received, the network shall interpret this as 1" },
	{ 1, "Multislot class 5 supported" },
	{ 2, "Multislot class 9 supported" },
	{ 3, "Multislot class 11 supported" },
	{ 0, NULL}
};

static const value_string single_slot_dtm_vals[] = {
	{ 0, "Single Slot DTM not supported" },
	{ 1, "Single Slot DTM supported" },
	{ 0, NULL}
};

static const value_string gsm_band_vals[] = {
	{ 0, "E-GSM is supported" },
	{ 1, "P-GSM is supported" },
	{ 2, "GSM 1800 is supported" },
	{ 3, "GSM 450 is supported" },
	{ 4, "GSM 480 is supported" },
	{ 5, "GSM 850 is supported" },
	{ 6, "GSM 1900 is supported" },
	{ 7, "GSM 750 is supported" },
	{ 8, "GSM 710 is supported" },
	{ 9, "T-GSM 810 is supported" },
	{ 0, NULL}
};

static const value_string umts_128_mcps_tdd_rat_cap_vals[] = {
	{ 0, "UMTS 1.28 Mcps TDD not supported" },
	{ 1, "UMTS 1.28 Mcps TDD supported" },
	{ 0, NULL}
};

static const value_string geran_feature_package_1_vals[] = {
	{ 0, "GERAN feature package 1 not supported" },
	{ 1, "GERAN feature package 1 supported" },
	{ 0, NULL}
};

static const value_string flo_iu_cap_vals[] = {
	{ 0, "FLO in GERAN Iu Mode not supported" },
	{ 1, "FLO in GERAN Iu Mode supported" },
	{ 0, NULL}
};

static const value_string geran_feature_package_2_vals[] = {
	{ 0, "GERAN feature package 2 not supported" },
	{ 1, "GERAN feature package 2 supported" },
	{ 0, NULL}
};

static const value_string gmsk_multislot_power_prof_vals[] = {
	{ 0, "GMSK_MULTISLOT_POWER_PROFILE 0" },
	{ 1, "GMSK_MULTISLOT_POWER_PROFILE 1" },
	{ 2, "GMSK_MULTISLOT_POWER_PROFILE 2" },
	{ 3, "GMSK_MULTISLOT_POWER_PROFILE 3" },
	{ 0, NULL}
};

static const value_string eight_psk_multislot_power_prof_vals[] = {
	{ 0, "8-PSK_MULTISLOT_POWER_PROFILE 0" },
	{ 1, "8-PSK_MULTISLOT_POWER_PROFILE 1" },
	{ 2, "8-PSK_MULTISLOT_POWER_PROFILE 2" },
	{ 3, "8-PSK_MULTISLOT_POWER_PROFILE 3" },
	{ 0, NULL}
};

static const value_string t_gsm_400_bands_supported_vals[] = {
    { 1, "T-GSM 380 supported, T-GSM 410 not supported" },
    { 2, "T-GSM 410 supported, T-GSM 380 not supported" },
    { 3, "T-GSM 410 supported, T-GSM 380 supported" },
    { 0, NULL}
};

static const value_string downlink_adv_receiver_perf_vals[] = {
    { 0, "Downlink Advanced Receiver Performance not supported" },
    { 1, "Downlink Advanced Receiver Performance - phase I supported" },
    { 2, "Downlink Advanced Receiver Performance - phase II supported" },
    { 0, NULL}
};

static const value_string dtm_enhancements_cap_vals[] = {
	{ 0, "The mobile station does not support enhanced DTM CS establishment and release procedures" },
	{ 1, "The mobile station supports enhanced DTM CS establishment and release procedures" },
	{ 0, NULL}
};

static const value_string offset_required_vals[] = {
	{ 0, "The mobile station does not require the offset" },
	{ 1, "The mobile station requires the offset" },
	{ 0, NULL}
};

static const value_string dtm_gprs_high_multi_slot_class_vals[] = {
	{ 0, "Unused. If received, the network shall interpret this as \"0 0 1\"" },
	{ 1, "Multislot class 31 or 36 supported" },
	{ 2, "Multislot class 32 or 37 supported" },
	{ 3, "Multislot class 33 or 38 supported" },
	{ 4, "Multislot class 41 supported" },
	{ 5, "Multislot class 42 supported" },
	{ 6, "Multislot class 43 supported" },
	{ 7, "Multislot class 44 supported" },
	{ 0, NULL}
};

static const value_string repeated_acch_cap_vals[] = {
	{ 0, "The mobile station does not support Repeated SACCH" },
	{ 1, "The mobile station supports Repeated SACCH and Repeated Downlink FACCH" },
	{ 0, NULL}
};

static const value_string ciphering_mode_setting_cap_vals[] = {
	{ 0, "The mobile station does not support the Ciphering Mode Setting IE in the DTM ASSIGNMENT COMMAND message" },
	{ 1, "The mobile station supports the Ciphering Mode Setting IE in the DTM ASSIGNMENT COMMAND message" },
	{ 0, NULL}
};

static const value_string additional_positioning_caps_vals[] = {
	{ 0, "The mobile station does not support additional positioning capabilities which can be retrieved using RRLP" },
	{ 1, "The mobile station supports additional positioning capabilities which can be retrieved using RRLP" },
	{ 0, NULL}
};

static const value_string e_utra_fdd_support_vals[] = {
	{ 0, "E-UTRA FDD not supported" },
	{ 1, "E-UTRA FDD supported" },
	{ 0, NULL}
};

static const value_string e_utra_tdd_support_vals[] = {
	{ 0, "E-UTRA TDD not supported" },
	{ 1, "E-UTRA TDD supported" },
	{ 0, NULL}
};

static const value_string e_utra_meas_and_report_support_vals[] = {
	{ 0, "E-UTRAN Neighbour Cell measurements and measurement reporting while having an RR connection not supported" },
	{ 1, "E-UTRAN Neighbour Cell measurements and measurement reporting while having an RR connection supported" },
	{ 0, NULL}
};

static const value_string gsm_a_rr_rxlev_vals [] = {
    { 0, "< -110 dBm"},
    { 1, "-110 <= x < -109 dBm"},
    { 2, "-109 <= x < -108 dBm"},
    { 3, "-108 <= x < -107 dBm"},
    { 4, "-107 <= x < -106 dBm"},
    { 5, "-106 <= x < -105 dBm"},
    { 6, "-105 <= x < -104 dBm"},
    { 7, "-104 <= x < -103 dBm"},
    { 8, "-103 <= x < -102 dBm"},
    { 9, "-102 <= x < -101 dBm"},
    {10, "-101 <= x < -100 dBm"},
    {11, "-100 <= x < -99 dBm"},
    {12, "-99 <= x < -98 dBm"},
    {13, "-98 <= x < -97 dBm"},
    {14, "-97 <= x < -96 dBm"},
    {15, "-96 <= x < -95 dBm"},
    {16, "-95 <= x < -94 dBm"},
    {17, "-94 <= x < -93 dBm"},
    {18, "-93 <= x < -92 dBm"},
    {19, "-92 <= x < -91 dBm"},
    {20, "-91 <= x < -90 dBm"},
    {21, "-90 <= x < -89 dBm"},
    {22, "-89 <= x < -88 dBm"},
    {23, "-88 <= x < -87 dBm"},
    {24, "-87 <= x < -86 dBm"},
    {25, "-86 <= x < -85 dBm"},
    {26, "-85 <= x < -84 dBm"},
    {27, "-84 <= x < -83 dBm"},
    {28, "-83 <= x < -82 dBm"},
    {29, "-82 <= x < -81 dBm"},
    {30, "-81 <= x < -80 dBm"},
    {31, "-80 <= x < -79 dBm"},
    {32, "-79 <= x < -78 dBm"},
    {33, "-78 <= x < -77 dBm"},
    {34, "-77 <= x < -76 dBm"},
    {35, "-76 <= x < -75 dBm"},
    {36, "-75 <= x < -74 dBm"},
    {37, "-74 <= x < -73 dBm"},
    {38, "-73 <= x < -72 dBm"},
    {39, "-72 <= x < -71 dBm"},
    {40, "-71 <= x < -70 dBm"},
    {41, "-70 <= x < -69 dBm"},
    {42, "-69 <= x < -68 dBm"},
    {43, "-68 <= x < -67 dBm"},
    {44, "-67 <= x < -66 dBm"},
    {45, "-66 <= x < -65 dBm"},
    {46, "-65 <= x < -64 dBm"},
    {47, "-64 <= x < -63 dBm"},
    {48, "-63 <= x < -62 dBm"},
    {49, "-62 <= x < -61 dBm"},
    {50, "-61 <= x < -60 dBm"},
    {51, "-60 <= x < -59 dBm"},
    {52, "-59 <= x < -58 dBm"},
    {53, "-58 <= x < -57 dBm"},
    {54, "-57 <= x < -56 dBm"},
    {55, "-56 <= x < -55 dBm"},
    {56, "-55 <= x < -54 dBm"},
    {57, "-54 <= x < -53 dBm"},
    {58, "-53 <= x < -52 dBm"},
    {59, "-52 <= x < -51 dBm"},
    {60, "-51 <= x < -50 dBm"},
    {61, "-50 <= x < -49 dBm"},
    {62, "-49 <= x < -48 dBm"},
    {63, ">= -48 dBm"},
    { 0, NULL}
};

value_string_ext gsm_a_rr_rxlev_vals_ext = VALUE_STRING_EXT_INIT(gsm_a_rr_rxlev_vals);

/* Initialize the protocol and registered fields */
static int proto_a_common = -1;

int gsm_a_tap = -1;

int hf_gsm_a_common_elem_id = -1;
static int hf_gsm_a_l_ext = -1;
static int hf_gsm_a_imsi = -1;
int hf_gsm_a_tmsi = -1;
static int hf_gsm_a_imei = -1;
static int hf_gsm_a_imeisv = -1;

static int hf_gsm_a_MSC_rev = -1;
static int hf_gsm_a_ES_IND			= -1;
static int hf_gsm_a_A5_1_algorithm_sup = -1;
static int hf_gsm_a_RF_power_capability = -1;
static int hf_gsm_a_ps_sup_cap		= -1;
static int hf_gsm_a_SS_screening_indicator = -1;
static int hf_gsm_a_SM_capability		 = -1;
static int hf_gsm_a_VBS_notification_rec = -1;
static int hf_gsm_a_VGCS_notification_rec = -1;
static int hf_gsm_a_FC_frequency_cap	= -1;
static int hf_gsm_a_CM3				= -1;
static int hf_gsm_a_LCS_VA_cap		= -1;
static int hf_gsm_a_UCS2_treatment	= -1;
static int hf_gsm_a_SoLSA				= -1;
static int hf_gsm_a_CMSP				= -1;
static int hf_gsm_a_A5_7_algorithm_sup= -1;
static int hf_gsm_a_A5_6_algorithm_sup= -1;
static int hf_gsm_a_A5_5_algorithm_sup= -1;
static int hf_gsm_a_A5_4_algorithm_sup= -1;
static int hf_gsm_a_A5_3_algorithm_sup= -1;
static int hf_gsm_a_A5_2_algorithm_sup = -1;

static int hf_gsm_a_odd_even_ind = -1;
static int hf_gsm_a_mobile_identity_type = -1;
static int hf_gsm_a_tmgi_mcc_mnc_ind = -1;
static int hf_gsm_a_mbs_ses_id_ind = -1;
static int hf_gsm_a_mbs_service_id = -1;
static int hf_gsm_a_mbs_session_id = -1;
int hf_gsm_a_L3_protocol_discriminator = -1;
int hf_gsm_a_call_prio = -1;
int hf_gsm_a_skip_ind = -1;
int hf_gsm_a_spare_bits = -1;
int hf_gsm_a_lac = -1;

static int hf_gsm_a_b7spare = -1;
int hf_gsm_a_b8spare = -1;
static int hf_gsm_a_multi_bnd_sup_fields = -1;
static int hf_gsm_a_pgsm_supported = -1;
static int hf_gsm_a_egsm_supported = -1;
static int hf_gsm_a_gsm1800_supported = -1;
static int hf_gsm_a_ass_radio_cap1 = -1;
static int hf_gsm_a_ass_radio_cap2 = -1;
static int hf_gsm_a_rsupport = -1;
static int hf_gsm_a_r_capabilities = -1;
static int hf_gsm_a_multislot_capabilities = -1;
static int hf_gsm_a_multislot_class = -1;
static int hf_gsm_a_ucs2_treatment = -1;
static int hf_gsm_a_extended_measurement_cap = -1;
static int hf_gsm_a_ms_measurement_capability = -1;
static int hf_gsm_a_sms_value =-1;
static int hf_gsm_a_sm_value =-1;
static int hf_gsm_a_key_seq = -1;
static int hf_gsm_a_ms_pos_method_cap_present	= -1;
static int hf_gsm_a_ms_pos_method				= -1;
static int hf_gsm_a_ms_assisted_e_otd			= -1;
static int hf_gsm_a_ms_based_e_otd				= -1;
static int hf_gsm_a_ms_assisted_gps				= -1;
static int hf_gsm_a_ms_based_gps				= -1;
static int hf_gsm_a_ms_conventional_gps			= -1;
static int hf_gsm_a_ecsd_multi_slot_capability	= -1;
static int hf_gsm_a_ecsd_multi_slot_class		= -1;
static int hf_gsm_a_8_psk_struct_present		= -1;
static int hf_gsm_a_8_psk_struct				= -1;
static int hf_gsm_a_modulation_capability		= -1;
static int hf_gsm_a_8_psk_rf_power_capability_1	= -1;
static int hf_gsm_a_8_psk_rf_power_capability_2	= -1;
static int hf_gsm_a_gsm_400_band_info_present	= -1;
static int hf_gsm_a_gsm_400_bands_supported		= -1;
static int hf_gsm_a_gsm_400_assoc_radio_cap	= -1;
static int hf_gsm_a_gsm_850_assoc_radio_cap_present	= -1;
static int hf_gsm_a_gsm_850_assoc_radio_cap	= -1;
static int hf_gsm_a_gsm_1900_assoc_radio_cap_present	= -1;
static int hf_gsm_a_gsm_1900_assoc_radio_cap	= -1;
static int hf_gsm_a_cm3_A5_bits					= -1;
static int hf_gsm_a_umts_fdd_rat_cap			= -1;
static int hf_gsm_a_umts_384_mcps_tdd_rat_cap	= -1;
static int hf_gsm_a_cdma_2000_rat_cap			= -1;
static int hf_gsm_a_dtm_e_gprs_multi_slot_info_present	= -1;
static int hf_gsm_a_dtm_gprs_multi_slot_class	= -1;
static int hf_gsm_a_single_slot_dtm				= -1;
static int hf_gsm_a_dtm_egprs_multi_slot_class_present		= -1;
static int hf_gsm_a_dtm_egprs_multi_slot_class	= -1;
static int hf_gsm_a_single_band_support			= -1;
static int hf_gsm_a_gsm_band					= -1;
static int hf_gsm_a_gsm_750_assoc_radio_cap_present	= -1;
static int hf_gsm_a_gsm_750_assoc_radio_cap	= -1;
static int hf_gsm_a_umts_128_mcps_tdd_rat_cap	= -1;
static int hf_gsm_a_geran_feature_package_1		= -1;
static int hf_gsm_a_ext_dtm_e_gprs_multi_slot_info_present	= -1;
static int hf_gsm_a_ext_dtm_gprs_multi_slot_class	= -1;
static int hf_gsm_a_ext_dtm_egprs_multi_slot_class	= -1;
static int hf_gsm_a_high_multislot_cap_present	= -1;
static int hf_gsm_a_high_multislot_cap			= -1;
static int hf_gsm_a_geran_iu_mode_support		= -1;
static int hf_gsm_a_geran_iu_mode_cap			= -1;
static int hf_gsm_a_geran_iu_mode_cap_length	= -1;
static int hf_gsm_a_flo_iu_cap					= -1;
static int hf_gsm_a_geran_feature_package_2		= -1;
static int hf_gsm_a_gmsk_multislot_power_prof	= -1;
static int hf_gsm_a_8_psk_multislot_power_prof	= -1;
static int hf_gsm_a_t_gsm_400_band_info_present	= -1;
static int hf_gsm_a_t_gsm_400_bands_supported		= -1;
static int hf_gsm_a_t_gsm_400_assoc_radio_cap	= -1;
static int hf_gsm_a_t_gsm_900_assoc_radio_cap_present	= -1;
static int hf_gsm_a_t_gsm_900_assoc_radio_cap	= -1;
static int hf_gsm_a_downlink_adv_receiver_perf	= -1;
static int hf_gsm_a_dtm_enhancements_cap		= -1;
static int hf_gsm_a_dtm_e_gprs_high_multi_slot_info_present	= -1;
static int hf_gsm_a_dtm_gprs_high_multi_slot_class	= -1;
static int hf_gsm_a_offset_required				= -1;
static int hf_gsm_a_dtm_egprs_high_multi_slot_class_present	= -1;
static int hf_gsm_a_dtm_egprs_high_multi_slot_class	= -1;
static int hf_gsm_a_repeated_acch_cap			= -1;
static int hf_gsm_a_gsm_710_assoc_radio_cap_present	= -1;
static int hf_gsm_a_gsm_710_assoc_radio_cap		= -1;
static int hf_gsm_a_t_gsm_810_assoc_radio_cap_present	= -1;
static int hf_gsm_a_t_gsm_810_assoc_radio_cap	= -1;
static int hf_gsm_a_ciphering_mode_setting_cap	= -1;
static int hf_gsm_a_additional_positioning_caps	= -1;
static int hf_gsm_a_e_utra_fdd_support			= -1;
static int hf_gsm_a_e_utra_tdd_support			= -1;
static int hf_gsm_a_e_utra_meas_and_report_support	= -1;

static int hf_gsm_a_geo_loc_type_of_shape = -1;
static int hf_gsm_a_geo_loc_sign_of_lat	= -1;
static int hf_gsm_a_geo_loc_deg_of_lat =-1;
static int hf_gsm_a_geo_loc_deg_of_long =-1;
static int hf_gsm_a_geo_loc_uncertainty_code = -1;
static int hf_gsm_a_geo_loc_uncertainty_semi_major = -1;
static int hf_gsm_a_geo_loc_uncertainty_semi_minor = -1;
static int hf_gsm_a_geo_loc_orientation_of_major_axis = -1;
static int hf_gsm_a_geo_loc_uncertainty_altitude = -1;
static int hf_gsm_a_geo_loc_confidence = -1;
static int hf_gsm_a_geo_loc_no_of_points = -1;
static int hf_gsm_a_geo_loc_D = -1;
static int hf_gsm_a_geo_loc_altitude = -1;
static int hf_gsm_a_geo_loc_inner_radius = -1;
static int hf_gsm_a_geo_loc_uncertainty_radius = -1;
static int hf_gsm_a_geo_loc_offset_angle = -1;
static int hf_gsm_a_geo_loc_included_angle = -1;

static char a_bigbuf[1024];

sccp_msg_info_t* sccp_msg;
sccp_assoc_info_t* sccp_assoc;

#define	NUM_GSM_COMMON_ELEM (sizeof(gsm_common_elem_strings)/sizeof(value_string))
gint ett_gsm_common_elem[NUM_GSM_COMMON_ELEM];


#define  ELLIPSOID_POINT 0
#define  ELLIPSOID_POINT_WITH_UNCERT_CIRC 1
#define  ELLIPSOID_POINT_WITH_UNCERT_ELLIPSE 3
#define  POLYGON 5
#define  ELLIPSOID_POINT_WITH_ALT 8
#define  ELLIPSOID_POINT_WITH_ALT_AND_UNCERT_ELLIPSOID 9
#define  ELLIPSOID_ARC 10
/*
4 3 2 1
0 0 0 0 Ellipsoid Point
0 0 0 1 Ellipsoid point with uncertainty Circle
0 0 1 1 Ellipsoid point with uncertainty Ellipse
0 1 0 1 Polygon
1 0 0 0 Ellipsoid point with altitude
1 0 0 1 Ellipsoid point with altitude and uncertainty Ellipsoid
1 0 1 0 Ellipsoid Arc
other values reserved for future use
*/

/* TS 23 032 Table 2a: Coding of Type of Shape */
static const value_string type_of_shape_vals[] = {
	{ ELLIPSOID_POINT,		"Ellipsoid Point"},
	{ ELLIPSOID_POINT_WITH_UNCERT_CIRC,		"Ellipsoid point with uncertainty Circle"},
	{ ELLIPSOID_POINT_WITH_UNCERT_ELLIPSE,		"Ellipsoid point with uncertainty Ellipse"},
	{ POLYGON,		"Polygon"},
	{ ELLIPSOID_POINT_WITH_ALT,		"Ellipsoid point with altitude"},
	{ ELLIPSOID_POINT_WITH_ALT_AND_UNCERT_ELLIPSOID,		"Ellipsoid point with altitude and uncertainty Ellipsoid"},
	{ ELLIPSOID_ARC,		"Ellipsoid Arc"},
	{ 0,	NULL }
};

/* 3GPP TS 23.032 7.3.1 */
static const value_string sign_of_latitude_vals[] = {
	{ 0,		"North"},
	{ 1,		"South"},
	{ 0,	NULL }
};

static const value_string dir_of_alt_vals[] = {
	{ 0,		"Altitude expresses height"},
	{ 1,		"Altitude expresses depth"},
	{ 0,	NULL }
};

void
dissect_geographical_description(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree){

	proto_item *lat_item, *long_item, *major_item, *minor_item, *alt_item, *uncer_item;
	/*proto_tree *subtree; */

	guint8 type_of_shape;
	guint8 no_of_points;
	int offset = 0;
	int length;
	guint8 value;
	guint32 value32;

	/*subtree = proto_item_add_subtree(item, ett_gsm_a_geo_desc);*/

	length = tvb_reported_length_remaining(tvb,0);
	/* Geographical Location
	 * The Location Estimate field is composed of 1 or more octets with an internal structure
	 * according to section 7 in [23.032].
	 */
	proto_tree_add_item(tree, hf_gsm_a_geo_loc_type_of_shape, tvb, 0, 1, FALSE);
	if (length<2)
		return;
	type_of_shape = tvb_get_guint8(tvb,offset)>>4;
	switch (type_of_shape){
	case ELLIPSOID_POINT:
		/* Ellipsoid Point */
	case ELLIPSOID_POINT_WITH_UNCERT_CIRC:
		/* Ellipsoid Point with uncertainty Circle */
	case ELLIPSOID_POINT_WITH_UNCERT_ELLIPSE:
		/* Ellipsoid Point with uncertainty Ellipse */
	case ELLIPSOID_POINT_WITH_ALT:
		/* Ellipsoid Point with Altitude */
	case ELLIPSOID_POINT_WITH_ALT_AND_UNCERT_ELLIPSOID:
		/* Ellipsoid Point with altitude and uncertainty ellipsoid */
	case ELLIPSOID_ARC:
		/* Ellipsoid Arc */
		offset++;
		if (length<4)
			return;
		proto_tree_add_item(tree, hf_gsm_a_geo_loc_sign_of_lat, tvb, offset, 1, FALSE);

		value32 = tvb_get_ntoh24(tvb,offset)&0x7fffff;
		/* convert degrees (X/0x7fffff) * 90 = degrees */
		lat_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_deg_of_lat, tvb, offset, 3, FALSE);
		proto_item_append_text(lat_item,"(%.5f degrees)", (((double)value32/8388607) * 90));
		if (length<7)
			return;
		offset = offset + 3;
		value32 = tvb_get_ntoh24(tvb,offset)&0x7fffff;
		long_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_deg_of_long, tvb, offset, 3, FALSE);
		/* (X/0xffffff) *360 = degrees */
		proto_item_append_text(long_item,"(%.5f degrees)", (((double)value32/16777215) * 360));
		offset = offset + 3;
		if(type_of_shape==ELLIPSOID_POINT_WITH_UNCERT_CIRC){
			/* Ellipsoid Point with uncertainty Circle */
			if (length<8)
				return;
			/* Uncertainty code */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			uncer_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_code, tvb, offset, 1, FALSE);
			proto_item_append_text(uncer_item,"(%.1f m)", 10 * (pow(1.1, (double)value) - 1));
		}else if(type_of_shape==ELLIPSOID_POINT_WITH_UNCERT_ELLIPSE){
			/* Ellipsoid Point with uncertainty Ellipse */
			/* Uncertainty semi-major octet 10
			 * To convert to metres 10*(((1.1)^X)-1)
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			major_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_semi_major, tvb, offset, 1, FALSE);
			proto_item_append_text(major_item,"(%.1f m)", 10 * (pow(1.1, (double)value) - 1));
			offset++;
			/* Uncertainty semi-minor Octet 11
			 * To convert to metres 10*(((1.1)^X)-1)
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			minor_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_semi_minor, tvb, offset, 1, FALSE);
			proto_item_append_text(minor_item,"(%.1f m)", 10 * (pow(1.1, (double)value) - 1));
			offset++;
			/* Orientation of major axis octet 12
			 * allowed value from 0-179 to convert
			 * to actual degrees multiply by 2.
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			proto_tree_add_uint(tree, hf_gsm_a_geo_loc_orientation_of_major_axis, tvb, offset, 1, value*2);
			offset++;
			/* Confidence */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_confidence, tvb, offset, 1, FALSE);
			offset++;
		}else if(type_of_shape==ELLIPSOID_POINT_WITH_ALT){
			/* Ellipsoid Point with Altitude */
			/*D: Direction of Altitude */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_D, tvb, offset, 1, FALSE);
			/* Altitude */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_altitude, tvb, offset, 2, FALSE);
		}else if(type_of_shape==ELLIPSOID_POINT_WITH_ALT_AND_UNCERT_ELLIPSOID){
			/* Ellipsoid Point with altitude and uncertainty ellipsoid */
			/*D: Direction of Altitude octet 8,9 */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_D, tvb, offset, 1, FALSE);
			/* Altitude Octet 8,9*/
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_altitude, tvb, offset, 2, FALSE);
			offset = offset +2;
			/* Uncertainty semi-major octet 10
			 * To convert to metres 10*(((1.1)^X)-1)
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			major_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_semi_major, tvb, offset, 1, FALSE);
			proto_item_append_text(major_item,"(%.1f m)", 10 * (pow(1.1, (double)value) - 1));
			offset++;
			/* Uncertainty semi-minor Octet 11
			 * To convert to metres 10*(((1.1)^X)-1)
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			minor_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_semi_minor, tvb, offset, 1, FALSE);
			proto_item_append_text(minor_item,"(%.1f m)", 10 * (pow(1.1, (double)value) - 1));
			offset++;
			/* Orientation of major axis octet 12
			 * allowed value from 0-179 to convert
			 * to actual degrees multiply by 2.
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			proto_tree_add_uint(tree, hf_gsm_a_geo_loc_orientation_of_major_axis, tvb, offset, 1, value*2);
			offset++;
			/* Uncertainty Altitude 13
			 * to convert to metres 45*(((1.025)^X)-1)
			 */
			value = tvb_get_guint8(tvb,offset)&0x7f;
			alt_item = proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_altitude, tvb, offset, 1, FALSE);
			proto_item_append_text(alt_item,"(%.1f m)", 45 * (pow(1.025, (double)value) - 1));
			offset++;
			/* Confidence octet 14
			 */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_confidence, tvb, offset, 1, FALSE);
		}else if(type_of_shape==ELLIPSOID_ARC){
			/* Ellipsoid Arc */
			/* Inner radius */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_inner_radius, tvb, offset, 2, FALSE);
			offset= offset +2;
			/* Uncertainty radius */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_uncertainty_radius, tvb, offset, 1, FALSE);
			offset++;
			/* Offset angle */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_offset_angle, tvb, offset, 1, FALSE);
			offset++;
			/* Included angle */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_included_angle, tvb, offset, 1, FALSE);
			offset++;
			/* Confidence */
			proto_tree_add_item(tree, hf_gsm_a_geo_loc_confidence, tvb, offset, 1, FALSE);
		}

		break;
	case POLYGON:					/* Polygon */
		/* Number of points */
		no_of_points = tvb_get_guint8(tvb,offset)&0x0f;
		proto_tree_add_item(tree, hf_gsm_a_geo_loc_no_of_points, tvb, offset, 1, FALSE);
		/*
		while ( no_of_points > 0){
			offset++;

			no_of_points--;
		}
		*/
		break;
	default:
		break;
	}

}

const char* get_gsm_a_msg_string(int pdu_type, int idx)
{
	const char *msg_string=NULL;

	switch (pdu_type) {
	case GSM_A_PDU_TYPE_BSSMAP:
		msg_string = gsm_bssmap_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_DTAP:
		msg_string = gsm_dtap_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_RP:
		msg_string = gsm_rp_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_RR:
		msg_string = gsm_rr_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_COMMON:
		msg_string = gsm_common_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_GM:
		msg_string = gsm_gm_elem_strings[idx].strptr;
		break;
	case GSM_A_PDU_TYPE_BSSLAP:
		msg_string = gsm_bsslap_elem_strings[idx].strptr;
		break;
	case GSM_PDU_TYPE_BSSMAP_LE:
		msg_string = gsm_bssmap_le_elem_strings[idx].strptr;
		break;
	case NAS_PDU_TYPE_COMMON:
		msg_string = nas_eps_common_elem_strings[idx].strptr;
		break;
	case NAS_PDU_TYPE_EMM:
		msg_string = nas_emm_elem_strings[idx].strptr;
		break;
	case NAS_PDU_TYPE_ESM:
		msg_string = nas_esm_elem_strings[idx].strptr;
		break;
	case SGSAP_PDU_TYPE:
		msg_string = sgsap_elem_strings[idx].strptr;
		break;
	case BSSGP_PDU_TYPE:
		msg_string = bssgp_elem_strings[idx].strptr;
		break;
	default:
		DISSECTOR_ASSERT_NOT_REACHED();
	}

	return msg_string;
}

static int get_hf_elem_id(int pdu_type)
{
	int			hf_elem_id = 0;

	switch (pdu_type) {
	case GSM_A_PDU_TYPE_BSSMAP:
		hf_elem_id = hf_gsm_a_bssmap_elem_id;
		break;
	case GSM_A_PDU_TYPE_DTAP:
		hf_elem_id = hf_gsm_a_dtap_elem_id;
		break;
	case GSM_A_PDU_TYPE_RP:
		hf_elem_id = hf_gsm_a_rp_elem_id;
		break;
	case GSM_A_PDU_TYPE_RR:
		hf_elem_id = hf_gsm_a_rr_elem_id;
		break;
	case GSM_A_PDU_TYPE_COMMON:
		hf_elem_id = hf_gsm_a_common_elem_id;
		break;
	case GSM_A_PDU_TYPE_GM:
		hf_elem_id = hf_gsm_a_gm_elem_id;
		break;
	case GSM_A_PDU_TYPE_BSSLAP:
		hf_elem_id = hf_gsm_a_bsslap_elem_id;
		break;
	case GSM_PDU_TYPE_BSSMAP_LE:
		hf_elem_id = hf_gsm_bssmap_le_elem_id;
		break;
	case NAS_PDU_TYPE_COMMON:
		hf_elem_id = hf_nas_eps_common_elem_id;
		break;
	case NAS_PDU_TYPE_EMM:
		hf_elem_id = hf_nas_eps_emm_elem_id;
		break;
	case NAS_PDU_TYPE_ESM:
		hf_elem_id = hf_nas_eps_esm_elem_id;
		break;
	case SGSAP_PDU_TYPE:
		hf_elem_id = hf_sgsap_elem_id;
		break;
	case BSSGP_PDU_TYPE:
		hf_elem_id = hf_bssgp_elem_id;
		break;
	default:
		DISSECTOR_ASSERT_NOT_REACHED();
	}

	return hf_elem_id;
}

/*
 * Type Length Value (TLV) element dissector
 */
guint16 elem_tlv(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, guint len _U_, const gchar *name_add)
{
	guint8		oct;
	guint16		parm_len;
	guint8		lengt_length = 1;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if (oct == iei){
		parm_len = tvb_get_guint8(tvb, curr_offset + 1);

		item =
		proto_tree_add_text(tree,
			tvb, curr_offset, parm_len + 1 + lengt_length,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		proto_tree_add_uint(subtree,
			get_hf_elem_id(pdu_type), tvb,
			curr_offset, 1, oct);

		proto_tree_add_uint(subtree, hf_gsm_a_length, tvb,
			curr_offset + 1, lengt_length, parm_len);

		if (parm_len > 0)
		{
			if (elem_funcs[idx] == NULL)
			{
				proto_tree_add_text(subtree,
					tvb, curr_offset + 1 + lengt_length, parm_len,
					"Element Value");
				/* See ASSERT above */
				consumed = (guint8)parm_len;
			}
			else
			{
				gchar *a_add_string;

				a_add_string=ep_alloc(1024);
				a_add_string[0] = '\0';
				consumed =
				(*elem_funcs[idx])(tvb, subtree, curr_offset + 2,
					parm_len, a_add_string, 1024);

				if (a_add_string[0] != '\0')
				{
					proto_item_append_text(item, "%s", a_add_string);
				}
			}
		}

		consumed += 1 + lengt_length;
	}

	return(consumed);
}

/*
 * Type Extendable Length Value (TELV) element dissector
 * This is a version where the length field can be one or two octets depending 
 * if the extension bit is set or not (TS 48.016 p 10.1.2).
 *         8        7 6 5 4 3 2 1
 * octet 2 0/1 ext  length
 * octet 2a length
 */
guint16 elem_telv(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, guint len _U_, const gchar *name_add)
{
	guint8		oct;
	guint16		parm_len;
	guint8		lengt_length = 1;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if (oct == iei){
		parm_len = tvb_get_guint8(tvb, curr_offset + 1);
		if((parm_len&0x80)==0){
			/* length in 2 octets */
			parm_len = tvb_get_ntohs(tvb, curr_offset + 1);
			lengt_length = 2;
		}else{
			parm_len = parm_len & 0x7f;
		}

		item =
		proto_tree_add_text(tree,
			tvb, curr_offset, parm_len + 1 + lengt_length,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		proto_tree_add_uint(subtree,
			get_hf_elem_id(pdu_type), tvb,
			curr_offset, 1, oct);

		proto_tree_add_item(subtree, hf_gsm_a_l_ext, tvb, curr_offset+1, 1, FALSE);

		proto_tree_add_uint(subtree, hf_gsm_a_length, tvb,
			curr_offset + 1, lengt_length, parm_len);

		if (parm_len > 0)
		{
			if (elem_funcs[idx] == NULL)
			{
				proto_tree_add_text(subtree,
					tvb, curr_offset + 1 + lengt_length, parm_len,
					"Element Value");
				/* See ASSERT above */
				consumed = parm_len;
			}
			else
			{
				gchar *a_add_string;

				a_add_string=ep_alloc(1024);
				a_add_string[0] = '\0';
				consumed =
				(*elem_funcs[idx])(tvb, subtree, curr_offset + 1 + lengt_length,
					parm_len, a_add_string, 1024);

				if (a_add_string[0] != '\0')
				{
					proto_item_append_text(item, "%s", a_add_string);
				}
			}
		}

		consumed += 1 + lengt_length;
	}

	return(consumed);
}

/*
 * Type Length Value Extended(TLV-E) element dissector
 * TS 24.007
 * information elements of format LV-E or TLV-E with value part consisting of zero,
 * one or more octets and a maximum of 65535 octets (type 6). This category is used in EPS only.
 */
guint16 elem_tlv_e(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, guint len _U_, const gchar *name_add)
{
	guint8		oct;
	guint16		parm_len;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if (oct == iei){
		parm_len = tvb_get_ntohs(tvb, curr_offset + 1);

		item = proto_tree_add_text(tree, tvb, curr_offset, parm_len + 1 + 2,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		proto_tree_add_uint(subtree,
			get_hf_elem_id(pdu_type), tvb,
			curr_offset, 1, oct);

		proto_tree_add_uint(subtree, hf_gsm_a_length, tvb,
			curr_offset + 1, 2, parm_len);

		if (parm_len > 0)
		{
			if (elem_funcs[idx] == NULL)
			{
				proto_tree_add_text(subtree,
					tvb, curr_offset + 1 + 2, parm_len,
					"Element Value");
				/* See ASSERT above */
				consumed = parm_len;
			}
			else
			{
				gchar *a_add_string;

				a_add_string=ep_alloc(1024);
				a_add_string[0] = '\0';
				consumed =
				(*elem_funcs[idx])(tvb, subtree, curr_offset + 1 + 2,
					parm_len, a_add_string, 1024);

				if (a_add_string[0] != '\0')
				{
					proto_item_append_text(item, "%s", a_add_string);
				}
			}
		}

		consumed += 1 + 2;
	}

	return(consumed);
}

/*
 * Type Value (TV) element dissector
 *
 * Length cannot be used in these functions, big problem if a element dissector
 * is not defined for these.
 */
guint16 elem_tv(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, const gchar *name_add)
{
	guint8		oct;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if (oct == iei)
	{
		item =
			proto_tree_add_text(tree,
			tvb, curr_offset, -1,
			"%s%s",
			elem_names[idx].strptr,
				(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		proto_tree_add_uint(subtree,
			get_hf_elem_id(pdu_type), tvb,
			curr_offset, 1, oct);

		if (elem_funcs[idx] == NULL)
		{
			/* BAD THING, CANNOT DETERMINE LENGTH */

			proto_tree_add_text(subtree,
				tvb, curr_offset + 1, 1,
				"No element dissector, rest of dissection may be incorrect");

			consumed = 1;
		}
		else
		{
			gchar *a_add_string;

			a_add_string=ep_alloc(1024);
			a_add_string[0] = '\0';
			consumed = (*elem_funcs[idx])(tvb, subtree, curr_offset + 1, -1, a_add_string, 1024);

			if (a_add_string[0] != '\0')
			{
				proto_item_append_text(item, "%s", a_add_string);
			}
		}

		consumed++;

		proto_item_set_len(item, consumed);
	}

	return(consumed);
}

/*
 * Type Value (TV) element dissector
 * Where top half nibble is IEI and bottom half nibble is value.
 *
 * Length cannot be used in these functions, big problem if a element dissector
 * is not defined for these.
 */
guint16 elem_tv_short(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, const gchar *name_add)
{
	guint8		oct;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);
	char buf[10+1];

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if ((oct & 0xf0) == (iei & 0xf0))
	{
		item =
			proto_tree_add_text(tree,
				tvb, curr_offset, -1,
				"%s%s",
				elem_names[idx].strptr,
				(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		other_decode_bitfield_value(buf, oct, 0xf0, 8);
		proto_tree_add_text(subtree,
			tvb, curr_offset, 1,
			"%s = Element ID",
			buf);

		if (elem_funcs[idx] == NULL)
		{
			/* BAD THING, CANNOT DETERMINE LENGTH */

			proto_tree_add_text(subtree,
				tvb, curr_offset, 1,
				"No element dissector, rest of dissection may be incorrect");

			consumed++;
		}
		else
		{
			gchar *a_add_string;

			a_add_string=ep_alloc(1024);
			a_add_string[0] = '\0';
			consumed = (*elem_funcs[idx])(tvb, subtree, curr_offset, -1, a_add_string, 1024);

			if (a_add_string[0] != '\0')
			{
				proto_item_append_text(item, "%s", a_add_string);
			}
		}

		proto_item_set_len(item, consumed);
	}

	return(consumed);
}

/*
 * Type (T) element dissector
 */
guint16 elem_t(tvbuff_t *tvb, proto_tree *tree, guint8 iei, gint pdu_type, int idx, guint32 offset, const gchar *name_add)
{
	guint8		oct;
	guint32		curr_offset;
	guint16		consumed;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	oct = tvb_get_guint8(tvb, curr_offset);

	if (oct == iei)
	{
		proto_tree_add_uint_format(tree,
			get_hf_elem_id(pdu_type), tvb,
			curr_offset, 1, oct,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

		consumed = 1;
	}

	return(consumed);
}

/*
 * Length Value (LV) element dissector
 */
guint16
elem_lv(tvbuff_t *tvb, proto_tree *tree, gint pdu_type, int idx, guint32 offset, guint len _U_, const gchar *name_add)
{
	guint8		parm_len;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	parm_len = tvb_get_guint8(tvb, curr_offset);

	item =
		proto_tree_add_text(tree,
			tvb, curr_offset, parm_len + 1,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

	subtree = proto_item_add_subtree(item, elem_ett[idx]);

	proto_tree_add_uint(subtree, hf_gsm_a_length, tvb,
		curr_offset, 1, parm_len);

	if (parm_len > 0)
	{
		if (elem_funcs[idx] == NULL)
		{
			proto_tree_add_text(subtree,
				tvb, curr_offset + 1, parm_len,
				"Element Value");

			consumed = parm_len;
		}
		else
		{
			gchar *a_add_string;

			a_add_string=ep_alloc(1024);
			a_add_string[0] = '\0';
			consumed =
				(*elem_funcs[idx])(tvb, subtree, curr_offset + 1,
					parm_len, a_add_string, 1024);

			if (a_add_string[0] != '\0')
			{
				proto_item_append_text(item, "%s", a_add_string);
			}
		}
	}

	return(consumed + 1);
}

/*
 * Length Value Extended(LV-E) element dissector
 */
guint16 elem_lv_e(tvbuff_t *tvb, proto_tree *tree, gint pdu_type, int idx, guint32 offset, guint len _U_, const gchar *name_add)
{
	guint16		parm_len;
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	parm_len = tvb_get_ntohs(tvb, curr_offset);

	item = proto_tree_add_text(tree, tvb, curr_offset, parm_len + 2,
			"%s%s",
			elem_names[idx].strptr,
			(name_add == NULL) || (name_add[0] == '\0') ? "" : name_add);

	subtree = proto_item_add_subtree(item, elem_ett[idx]);

	proto_tree_add_uint(subtree, hf_gsm_a_length, tvb,
		curr_offset, 2, parm_len);

	if (parm_len > 0)
	{
		if (elem_funcs[idx] == NULL)
		{
			proto_tree_add_text(subtree,
				tvb, curr_offset + 2, parm_len,
				"Element Value");

			consumed = parm_len;
		}
		else
		{
			gchar *a_add_string;

			a_add_string=ep_alloc(1024);
			a_add_string[0] = '\0';
			consumed =
				(*elem_funcs[idx])(tvb, subtree, curr_offset + 2,
					parm_len, a_add_string, 1024);

			if (a_add_string[0] != '\0')
			{
				proto_item_append_text(item, "%s", a_add_string);
			}
		}
	}

	return(consumed + 2);
}
/*
 * Value (V) element dissector
 *
 * Length cannot be used in these functions, big problem if a element dissector
 * is not defined for these.
 */
guint16 elem_v(tvbuff_t *tvb, proto_tree *tree, gint pdu_type, int idx, guint32 offset)
{
	guint16		consumed;
	guint32		curr_offset;
	proto_tree		*subtree;
	proto_item		*item;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	if (elem_funcs[idx] == NULL)
	{
		/* BAD THING, CANNOT DETERMINE LENGTH */

		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"No element dissector, rest of dissection may be incorrect");

		consumed = 1;
	}
	else
	{
		gchar *a_add_string;

		item =
			proto_tree_add_text(tree,
				tvb, curr_offset, 0,
				"%s",
				elem_names[idx].strptr);

		subtree = proto_item_add_subtree(item, elem_ett[idx]);

		a_add_string=ep_alloc(1024);
		a_add_string[0] = '\0';
		consumed = (*elem_funcs[idx])(tvb, subtree, curr_offset, -1, a_add_string, 1024);
		if (a_add_string[0] != '\0')
		{
			proto_item_append_text(item, "%s", a_add_string);
		}
		proto_item_set_len(item, consumed);
	}

	return(consumed);
}

/*
 * Short Value (V_SHORT) element dissector
 *
 * Length is (ab)used in these functions to indicate upper nibble of the octet (-2) or lower nibble (-1)
 * noting that the tv_short dissector always sets the length to -1, as the upper nibble is the IEI.
 * This is expected to be used upper nibble first, as the tables of 24.008.
 */

guint16 elem_v_short(tvbuff_t *tvb, proto_tree *tree, gint pdu_type, int idx, guint32 offset)
{
	guint16		consumed;
	guint32		curr_offset;
	const value_string	*elem_names;
	gint		*elem_ett;
	guint16 (**elem_funcs)(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len);

	curr_offset = offset;
	consumed = 0;

	SET_ELEM_VARS(pdu_type, elem_names, elem_ett, elem_funcs);

	if (elem_funcs[idx] == NULL)
	{
		/* NOT A BAD THING - LENGTH IS HALF NIBBLE */

		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"No element dissector");

		consumed = 1;
	}
	else
	{
		gchar *a_add_string;

		a_add_string=ep_alloc(1024);
		a_add_string[0] = '\0';
		consumed = (*elem_funcs[idx])(tvb, tree, curr_offset, (lower_nibble?LOWER_NIBBLE:UPPER_NIBBLE), a_add_string, 1024);
	}
	if (!lower_nibble)	/* is this the first (upper) nibble ? */
	{
		consumed--; /* only half a nibble has been consumed, but all ie dissectors assume they consume 1 octet */
		lower_nibble = TRUE;
	}
	else	/* if it is the second (lower) nibble, move on... */
		lower_nibble = FALSE;

	return(consumed);
}


static dgt_set_t Dgt_tbcd = {
	{
  /*  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e */
	 '0','1','2','3','4','5','6','7','8','9','?','B','C','*','#'
	}
};

static dgt_set_t Dgt1_9_bcd = {
	{
  /*  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e */
	 '0','1','2','3','4','5','6','7','8','9','?','?','?','?','?'
	}
};

/* FUNCTIONS */

/*
 * Unpack BCD input pattern into output ASCII pattern
 *
 * Input Pattern is supplied using the same format as the digits
 *
 * Returns: length of unpacked pattern
 */
int
my_dgt_tbcd_unpack(
	char	*out,		/* ASCII pattern out */
	guchar	*in,		/* packed pattern in */
	int		num_octs,	/* Number of octets to unpack */
	dgt_set_t	*dgt		/* Digit definitions */
	)
{
	int cnt = 0;
	unsigned char i;

	while (num_octs)
	{
		/*
		 * unpack first value in byte
		 */
		i = *in++;
		*out++ = dgt->out[i & 0x0f];
		cnt++;

		/*
		 * unpack second value in byte
		 */
		i >>= 4;

		if (i == 0x0f)	/* odd number bytes - hit filler */
			break;

		*out++ = dgt->out[i];
		cnt++;
		num_octs--;
	}

	*out = '\0';

	return(cnt);
}

/*
 * Decode the MCC/MNC from 3 octets in 'octs'
 */
static void
mcc_mnc_aux(guint8 *octs, gchar *mcc, gchar *mnc)
{
	if ((octs[0] & 0x0f) <= 9)
	{
		mcc[0] = Dgt_tbcd.out[octs[0] & 0x0f];
	}
	else
	{
		mcc[0] = (octs[0] & 0x0f) + 55;
	}

	if (((octs[0] & 0xf0) >> 4) <= 9)
	{
		mcc[1] = Dgt_tbcd.out[(octs[0] & 0xf0) >> 4];
	}
	else
	{
		mcc[1] = ((octs[0] & 0xf0) >> 4) + 55;
	}

	if ((octs[1] & 0x0f) <= 9)
	{
		mcc[2] = Dgt_tbcd.out[octs[1] & 0x0f];
	}
	else
	{
		mcc[2] = (octs[1] & 0x0f) + 55;
	}

	mcc[3] = '\0';

	if (((octs[1] & 0xf0) >> 4) <= 9)
	{
		mnc[2] = Dgt_tbcd.out[(octs[1] & 0xf0) >> 4];
	}
	else
	{
		mnc[2] = ((octs[1] & 0xf0) >> 4) + 55;
	}

	if ((octs[2] & 0x0f) <= 9)
	{
		mnc[0] = Dgt_tbcd.out[octs[2] & 0x0f];
	}
	else
	{
		mnc[0] = (octs[2] & 0x0f) + 55;
	}

	if (((octs[2] & 0xf0) >> 4) <= 9)
	{
		mnc[1] = Dgt_tbcd.out[(octs[2] & 0xf0) >> 4];
	}
	else
	{
		mnc[1] = ((octs[2] & 0xf0) >> 4) + 55;
	}

	if (mnc[1] == 'F')
	{
		/*
		 * only a 1 digit MNC (very old)
		 */
		mnc[1] = '\0';
	}
	else if (mnc[2] == 'F')
	{
		/*
		 * only a 2 digit MNC
		 */
		mnc[2] = '\0';
	}
	else
	{
		mnc[3] = '\0';
	}
}

/* 3GPP TS 24.008
 * [3] 10.5.1.1 Cell Identity
 */
guint16
de_cell_id(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
{
	guint32	curr_offset;

	curr_offset = offset;

	curr_offset +=
	/* 0x02 CI */
	be_cell_id_aux(tvb, tree, offset, len, add_string, string_len, 0x02);

	/* no length check possible */

	return(curr_offset - offset);
}
/*
 * 10.5.1.2 Ciphering Key Sequence Number
 */


/*
 * Key sequence (octet 1)
 * Bits
 * 3 2 1
 * 0 0 0
 * through
 * 1 1 0
 * Possible values for the ciphering key sequence number
 * 1 1 1 No key is available (MS to network);Reserved (network to MS)
 */

static const value_string gsm_a_key_seq_vals[] = {
	{ 0,		"Cipering key sequence number"},
	{ 1,		"Cipering key sequence number"},
	{ 2,		"Cipering key sequence number"},
	{ 3,		"Cipering key sequence number"},
	{ 4,		"Cipering key sequence number"},
	{ 5,		"Cipering key sequence number"},
	{ 6,		"Cipering key sequence number"},
	{ 7,		"No key is available (MS to network)"},
	{ 0,	NULL }
};

guint16
de_ciph_key_seq_num( tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint32	curr_offset;

	curr_offset = offset;

	proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, (curr_offset<<3)+4, 1, FALSE);
	proto_tree_add_item(tree, hf_gsm_a_key_seq, tvb, curr_offset, 1, FALSE);
	curr_offset++;

	return(curr_offset - offset);
}


/*
 * [3] 10.5.1.3
 */

guint16
de_lai(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint8	octs[3];
	guint16	value;
	guint32	curr_offset;
	proto_tree	*subtree;
	proto_item	*item;
	gchar	mcc[4];
	gchar	mnc[4];

	curr_offset = offset;

	item =
		proto_tree_add_text(tree,
			tvb, curr_offset, 5, "%s",
			gsm_common_elem_strings[DE_LAI].strptr);

	subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_LAI]);

	octs[0] = tvb_get_guint8(tvb, curr_offset);
	octs[1] = tvb_get_guint8(tvb, curr_offset + 1);
	octs[2] = tvb_get_guint8(tvb, curr_offset + 2);

	mcc_mnc_aux(octs, mcc, mnc);

	curr_offset = dissect_e212_mcc_mnc(tvb, gsm_a_dtap_pinfo, subtree, curr_offset, TRUE);

	value = tvb_get_ntohs(tvb, curr_offset);

	proto_tree_add_item(subtree, hf_gsm_a_lac, tvb, curr_offset, 2, FALSE);

	proto_item_append_text(item, " - %s/%s/%u", mcc,mnc,value);

	curr_offset += 2;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.4 Mobile Identity
 * 3GPP TS 24.008 version 7.8.0 Release 7
 */
static const true_false_string gsm_a_present_vals = {
	"Present" ,
	"Not present"
};

guint16
de_mid(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
{
	guint8	oct;
	guint32	curr_offset;
	guint8	*poctets;
	guint32	value;
	gboolean	odd;

	curr_offset = offset;
	odd = FALSE;

	oct = tvb_get_guint8(tvb, curr_offset);

	switch (oct & 0x07)
	{
	case 0:	/* No Identity */
		other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"%s = Unused",
			a_bigbuf);

		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);

		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);

		if (add_string)
			g_snprintf(add_string, string_len, " - No Identity Code");

		curr_offset++;

		if (len > 1)
		{
			proto_tree_add_text(tree, tvb, curr_offset, len - 1,
				"Format not supported");
		}

		curr_offset += len - 1;
		break;

	case 3:	/* IMEISV */
		/* FALLTHRU */

	case 1:	/* IMSI */
		other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"%s = Identity Digit 1: %c",
			a_bigbuf,
			Dgt1_9_bcd.out[(oct & 0xf0) >> 4]);

		odd = oct & 0x08;

		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);

		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);

		a_bigbuf[0] = Dgt1_9_bcd.out[(oct & 0xf0) >> 4];
		curr_offset++;

		poctets = tvb_get_ephemeral_string(tvb, curr_offset, len - (curr_offset - offset));

		my_dgt_tbcd_unpack(&a_bigbuf[1], poctets, len - (curr_offset - offset),
			&Dgt1_9_bcd);

		proto_tree_add_string_format(tree,
			((oct & 0x07) == 3) ? hf_gsm_a_imeisv : hf_gsm_a_imsi,
			tvb, curr_offset - 1, len - (curr_offset - offset) + 1,
			a_bigbuf,
			"BCD Digits: %s",
			a_bigbuf);

		if (sccp_assoc && ! sccp_assoc->calling_party) {
			sccp_assoc->calling_party = se_strdup_printf(
				((oct & 0x07) == 3) ? "IMEISV: %s" : "IMSI: %s",
				a_bigbuf );
		}

		if (add_string)
			g_snprintf(add_string, string_len, " - %s (%s)",
				((oct & 0x07) == 3) ? "IMEISV" : "IMSI",
				a_bigbuf);

		curr_offset += len - (curr_offset - offset);

		if (!odd)
		{
			oct = tvb_get_guint8(tvb, curr_offset - 1);

			other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
			proto_tree_add_text(tree,
				tvb, curr_offset - 1, 1,
				"%s = Filler",
				a_bigbuf);
		}
		break;

	case 2:	/* IMEI */
		other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"%s = Identity Digit 1: %c",
			a_bigbuf,
			Dgt1_9_bcd.out[(oct & 0xf0) >> 4]);

		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);

		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);

		a_bigbuf[0] = Dgt1_9_bcd.out[(oct & 0xf0) >> 4];
		curr_offset++;

		poctets = tvb_get_ephemeral_string(tvb, curr_offset, len - (curr_offset - offset));

		my_dgt_tbcd_unpack(&a_bigbuf[1], poctets, len - (curr_offset - offset),
			&Dgt1_9_bcd);

		proto_tree_add_string_format(tree,
			hf_gsm_a_imei,
			tvb, curr_offset, len - (curr_offset - offset),
			a_bigbuf,
			"BCD Digits: %s",
			a_bigbuf);

		if (add_string)
			g_snprintf(add_string, string_len, " - IMEI (%s)", a_bigbuf);

		curr_offset += len - (curr_offset - offset);
		break;

	case 4:	/* TMSI/P-TMSI */
		other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
		proto_tree_add_text(tree,
			tvb, curr_offset, 1,
			"%s = Unused",
			a_bigbuf);

		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);

		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);

		curr_offset++;

		value = tvb_get_ntohl(tvb, curr_offset);

		proto_tree_add_uint(tree, hf_gsm_a_tmsi,
			tvb, curr_offset, 4,
			value);

		if (add_string)
			g_snprintf(add_string, string_len, " - TMSI/P-TMSI (0x%04x)", value);

		curr_offset += 4;
		break;

	case 5: /* TMGI and optional MBMS Session Identity */
		/* Spare bits (octet 3) Bits 8-7 */
		proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, curr_offset<<3, 2, FALSE);
		/* MBMS Session Identity indication (octet 3) Bit 6 */
		proto_tree_add_item(tree, hf_gsm_a_mbs_ses_id_ind, tvb, curr_offset, 1, FALSE);
		/* MCC/MNC indication (octet 3) Bit 5 */
		proto_tree_add_item(tree, hf_gsm_a_tmgi_mcc_mnc_ind, tvb, curr_offset, 1, FALSE);
		/* Odd/even indication (octet 3) Bit 4 */
		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);
		/* Type of identity (octet 3) Bits 3-1 */
		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);
		curr_offset++;
		/* MBMS Service ID (octet 4, 5 and 6) */
		proto_tree_add_item(tree, hf_gsm_a_mbs_service_id, tvb, curr_offset, 3, FALSE);
		curr_offset += 3;
		if((oct&0x10)==0x10){
			/* MCC/MNC*/
			/* MCC, Mobile country code (octet 6a, octet 6b bits 1 to 4)*/
			/* MNC, Mobile network code (octet 6b bits 5 to 8, octet 6c) */
			curr_offset = dissect_e212_mcc_mnc(tvb, gsm_a_dtap_pinfo, tree, curr_offset,TRUE);
		}
		if((oct&0x20)==0x20){
			/* MBMS Session Identity (octet 7)
			 * The MBMS Session Identity field is encoded as the value part
			 * of the MBMS Session Identity IE as specified in 3GPP TS 48.018 [86].
			 */
			proto_tree_add_item(tree, hf_gsm_a_mbs_session_id, tvb, curr_offset, 1, FALSE);
			curr_offset++;
		}
		break;

	default:	/* Reserved */
		proto_tree_add_item(tree, hf_gsm_a_odd_even_ind, tvb, curr_offset, 1, FALSE);
		proto_tree_add_item(tree, hf_gsm_a_mobile_identity_type, tvb, curr_offset, 1, FALSE);
		proto_tree_add_text(tree, tvb, curr_offset, len,
			"Mobile station identity Format %u, Format Unknown",(oct & 0x07));

		if (add_string)
			g_snprintf(add_string, string_len, " - Format Unknown");

		curr_offset += len;
		break;
	}

	EXTRANEOUS_DATA_CHECK_EXPERT(len, curr_offset - offset, gsm_a_dtap_pinfo);

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.5
 */
guint16
de_ms_cm_1(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint8	oct;
	guint32	curr_offset;
	proto_tree	*subtree;
	proto_item	*item;

	curr_offset = offset;

	oct = tvb_get_guint8(tvb, curr_offset);

	item =
	proto_tree_add_text(tree,
		tvb, curr_offset, 1, "%s",
		gsm_common_elem_strings[DE_MS_CM_1].strptr);

	subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_1]);

	proto_tree_add_item(subtree, hf_gsm_a_b8spare, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(subtree, hf_gsm_a_MSC_rev, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(subtree, hf_gsm_a_ES_IND, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(subtree, hf_gsm_a_A5_1_algorithm_sup, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(subtree, hf_gsm_a_RF_power_capability, tvb, curr_offset, 1, FALSE);

	curr_offset++;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.6 Mobile Station Classmark 2
 * 3GPP TS 24.008 version 7.8.0 Release 7
 */
guint16
de_ms_cm_2(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
{
	guint32	curr_offset;
	curr_offset = offset;

	proto_tree_add_item(tree, hf_gsm_a_b8spare, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_MSC_rev, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_ES_IND, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_A5_1_algorithm_sup, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_RF_power_capability, tvb, curr_offset, 1, FALSE);

	curr_offset++;

	NO_MORE_DATA_CHECK(len);

	proto_tree_add_item(tree, hf_gsm_a_b8spare, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_ps_sup_cap, tvb, curr_offset, 1, FALSE);

	proto_tree_add_item(tree, hf_gsm_a_SS_screening_indicator, tvb, curr_offset, 1, FALSE);

	/* SM capability (MT SMS pt to pt capability) (octet 4)*/
	proto_tree_add_item(tree, hf_gsm_a_SM_capability, tvb, curr_offset, 1, FALSE);
	/* VBS notification reception (octet 4) */
	proto_tree_add_item(tree, hf_gsm_a_VBS_notification_rec, tvb, curr_offset, 1, FALSE);
	/*VGCS notification reception (octet 4)*/
	proto_tree_add_item(tree, hf_gsm_a_VGCS_notification_rec, tvb, curr_offset, 1, FALSE);
	/* FC Frequency Capability (octet 4 ) */
	proto_tree_add_item(tree, hf_gsm_a_FC_frequency_cap, tvb, curr_offset, 1, FALSE);

	curr_offset++;

	NO_MORE_DATA_CHECK(len);

	/* CM3 (octet 5, bit 8) */
	proto_tree_add_item(tree, hf_gsm_a_CM3, tvb, curr_offset, 1, FALSE);
	/* spare bit 7 */
	proto_tree_add_item(tree, hf_gsm_a_b7spare, tvb, curr_offset, 1, FALSE);
	/* LCS VA capability (LCS value added location request notification capability) (octet 5,bit 6) */
	proto_tree_add_item(tree, hf_gsm_a_LCS_VA_cap, tvb, curr_offset, 1, FALSE);
	/* UCS2 treatment (octet 5, bit 5) */
	proto_tree_add_item(tree, hf_gsm_a_UCS2_treatment, tvb, curr_offset, 1, FALSE);
	/* SoLSA (octet 5, bit 4) */
	proto_tree_add_item(tree, hf_gsm_a_SoLSA, tvb, curr_offset, 1, FALSE);
	/* CMSP: CM Service Prompt (octet 5, bit 3) */
	proto_tree_add_item(tree, hf_gsm_a_CMSP, tvb, curr_offset, 1, FALSE);
	/* A5/3 algorithm supported (octet 5, bit 2) */
	proto_tree_add_item(tree, hf_gsm_a_A5_3_algorithm_sup, tvb, curr_offset, 1, FALSE);
	/* A5/2 algorithm supported (octet 5, bit 1) */
	proto_tree_add_item(tree, hf_gsm_a_A5_2_algorithm_sup, tvb, curr_offset, 1, FALSE);

	curr_offset++;

	EXTRANEOUS_DATA_CHECK_EXPERT(len, curr_offset - offset, gsm_a_dtap_pinfo);

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.7 Mobile Station Classmark 3
 * 3GPP TS 24.008 version 8.6.0 Release 8
 */
#define AVAILABLE_BITS_CHECK(n) \
	bits_left = ((len + offset) << 3) - bit_offset; \
	if (bits_left < (n)) { \
		if (bits_left) \
			proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, bit_offset, bits_left, FALSE); \
		return(len); \
	}

guint16
de_ms_cm_3(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
{
	guint32	curr_offset;
	guint32 bit_offset; /* Offset in bits */
	guint8	length;
	proto_tree	*subtree;
	proto_item	*item;
	guint32 bits_left, target_bit_offset;
	guint64 multi_bnd_sup_fields, rsupport, multislotCapability, msMeasurementCapability, msPosMethodCapPresent;
	guint64 ecsdMultiSlotCapability, eightPskStructPresent, gsm400BandInfoPresent, gsm850AssocRadioCapabilityPresent;
	guint64 gsm1900AssocRadioCapabilityPresent, dtmEGprsMultiSlotInfoPresent, dtmEgprsMultiSlotClassPresent, singleBandSupport;
	guint64 gsm750AssocRadioCapabilityPresent, extDtmEGprsMultiSlotInfoPresent, highMultislotCapPresent, geranIuModeSupport;
	guint64 tGsm400BandInfoPresent, tGsm900AssocRadioCapabilityPresent, dtmEGprsHighMultiSlotInfoPresent;
	guint64 dtmEgprsHighMultiSlotClassPresent, gsm710AssocRadioCapabilityPresent, tGsm810AssocRadioCapabilityPresent;

	curr_offset = offset;

	bit_offset = curr_offset << 3;

	/* Spare bit */
	proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, bit_offset, 1, FALSE);
	bit_offset++;

	/* Multiband supported field
	 * { < Multiband supported : { 000 } >
	 * < A5 bits >
	 * | < Multiband supported : { 101 | 110 } >
	 * < A5 bits >
	 * < Associated Radio Capability 2 : bit(4) >
	 * < Associated Radio Capability 1 : bit(4) >
	 * | < Multiband supported : { 001 | 010 | 100 } >
	 * < A5 bits >
	 * < spare bit >(4)
	 * < Associated Radio Capability 1 : bit(4) > }
	 */

	item = proto_tree_add_bits_ret_val(tree, hf_gsm_a_multi_bnd_sup_fields, tvb, bit_offset, 3, &multi_bnd_sup_fields, FALSE);
	subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_3]);

	proto_tree_add_bits_item(subtree, hf_gsm_a_gsm1800_supported, tvb, bit_offset, 1, FALSE);
	bit_offset++;

	proto_tree_add_bits_item(subtree, hf_gsm_a_egsm_supported, tvb, bit_offset, 1, FALSE);
	bit_offset++;

	proto_tree_add_bits_item(subtree, hf_gsm_a_pgsm_supported, tvb, bit_offset, 1, FALSE);
	bit_offset++;

	item = proto_tree_add_bits_item(tree, hf_gsm_a_cm3_A5_bits, tvb, bit_offset, 4, FALSE);
	subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_3]);

	/* < A5 bits > */
	proto_tree_add_bits_item(subtree, hf_gsm_a_A5_7_algorithm_sup, tvb, bit_offset, 1, FALSE);
	bit_offset++;
	proto_tree_add_bits_item(subtree, hf_gsm_a_A5_6_algorithm_sup, tvb, bit_offset, 1, FALSE);
	bit_offset++;
	proto_tree_add_bits_item(subtree, hf_gsm_a_A5_5_algorithm_sup, tvb, bit_offset, 1, FALSE);
	bit_offset++;
	proto_tree_add_bits_item(subtree, hf_gsm_a_A5_4_algorithm_sup, tvb, bit_offset, 1, FALSE);
	bit_offset++;

	switch(multi_bnd_sup_fields){
		case 0:
			/* A5 bits dissected is done */
			break;
		/*
		 * | < Multiband supported : { 001 | 010 | 100 } >
		 */
		case 1:
		case 2:
		case 4:
			/* < spare bit >(4) */
			proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, bit_offset, 4, FALSE);
			bit_offset+=4;
			/* < Associated Radio Capability 1 : bit(4) > */
			proto_tree_add_bits_item(tree, hf_gsm_a_ass_radio_cap1, tvb, bit_offset, 4, FALSE);
			bit_offset+=4;
			break;
		/* < Multiband supported : { 101 | 110 } > */
		case 5:
			/* fall trough */
		case 6:
			/* < Associated Radio Capability 2 : bit(4) > */
			proto_tree_add_bits_item(tree, hf_gsm_a_ass_radio_cap2, tvb, bit_offset, 4, FALSE);
			bit_offset+=4;
			/* < Associated Radio Capability 1 : bit(4) > */
			proto_tree_add_bits_item(tree, hf_gsm_a_ass_radio_cap1, tvb, bit_offset, 4, FALSE);
			bit_offset+=4;
			break;
		default:
			break;
	}
	/* Extract R Support */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_rsupport, tvb, bit_offset, 1, &rsupport, FALSE);
	bit_offset++;

	if(rsupport == 1)
	{
		/*
		 * { 0 | 1 < R Support > }
		 * Extract R Capabilities
		 */
		proto_tree_add_bits_item(tree, hf_gsm_a_r_capabilities, tvb, bit_offset, 3, FALSE);
		bit_offset = bit_offset + 3;
	}

	/*
	 * { 0 | 1 < HSCSD Multi Slot Capability > }
	 * Extract Multislot capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_multislot_capabilities, tvb, bit_offset, 1, &multislotCapability, FALSE);
	bit_offset++;

	if(multislotCapability == 1)
	{
		/* Extract Multislot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_multislot_class, tvb, bit_offset, 5, FALSE);
		bit_offset = bit_offset + 5;
	}

	/* < UCS2 treatment: bit > */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_ucs2_treatment, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < Extended Measurement Capability : bit > */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_extended_measurement_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* { 0 | 1 < MS measurement capability > }
	 * Extract MS Measurement capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_ms_measurement_capability, tvb, bit_offset, 1, &msMeasurementCapability, FALSE);
	bit_offset = bit_offset + 1;

	if(msMeasurementCapability == 1)
	{
		/* Extract SMS Value n/4 */
		proto_tree_add_bits_item(tree, hf_gsm_a_sms_value, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;

		/* Extract SM Value n/4 */
		proto_tree_add_bits_item(tree, hf_gsm_a_sm_value, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 < MS Positioning Method Capability > }
	 * Extract MS Positioning Method Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_ms_pos_method_cap_present, tvb, bit_offset, 1, &msPosMethodCapPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(msPosMethodCapPresent == 1)
	{
		/* Extract MS Positioning Method */
		item = proto_tree_add_bits_item(tree, hf_gsm_a_ms_pos_method, tvb, bit_offset, 5, FALSE);
		subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_3]);

		proto_tree_add_bits_item(subtree, hf_gsm_a_ms_assisted_e_otd, tvb, bit_offset, 1, FALSE);
		bit_offset++;

		proto_tree_add_bits_item(subtree, hf_gsm_a_ms_based_e_otd, tvb, bit_offset, 1, FALSE);
		bit_offset++;

		proto_tree_add_bits_item(subtree, hf_gsm_a_ms_assisted_gps, tvb, bit_offset, 1, FALSE);
		bit_offset++;

		proto_tree_add_bits_item(subtree, hf_gsm_a_ms_based_gps, tvb, bit_offset, 1, FALSE);
		bit_offset++;

		proto_tree_add_bits_item(subtree, hf_gsm_a_ms_conventional_gps, tvb, bit_offset, 1, FALSE);
		bit_offset++;
	}

	/* { 0 | 1 < ECSD Multi Slot Capability > }
	 * Extract ECSD Multi Slot Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_ecsd_multi_slot_capability, tvb, bit_offset, 1, &ecsdMultiSlotCapability, FALSE);
	bit_offset = bit_offset + 1;

	if(ecsdMultiSlotCapability == 1)
	{
		/* Extract ECSD Multi Slot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_ecsd_multi_slot_class, tvb, bit_offset, 5, FALSE);
		bit_offset = bit_offset + 5;
	}

	/* { 0 | 1 < 8-PSK Struct > }
	 * Extract 8-PSK struct presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_8_psk_struct_present, tvb, bit_offset, 1, &eightPskStructPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(eightPskStructPresent == 1)
	{
		/* Extract 8-PSK struct */
		item = proto_tree_add_bits_item(tree, hf_gsm_a_8_psk_struct, tvb, bit_offset, 5, FALSE);
		subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_3]);

		/* Extract Modulation Capability */
		proto_tree_add_bits_item(subtree, hf_gsm_a_modulation_capability, tvb, bit_offset, 1, FALSE);
		bit_offset = bit_offset + 1;

		/* Extract 8_PSK RF Power Capability 1 */
		proto_tree_add_bits_item(subtree, hf_gsm_a_8_psk_rf_power_capability_1, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;

		/* Extract 8_PSK RF Power Capability 2 */
		proto_tree_add_bits_item(subtree, hf_gsm_a_8_psk_rf_power_capability_2, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;
	}

	/* { 0 | 1 < GSM 400 Bands Supported : { 01 | 10 | 11 } >
	 *   < GSM 400 Associated Radio Capability: bit(4) > }
	 * Extract GSM 400 Band Information presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_gsm_400_band_info_present, tvb, bit_offset, 1, &gsm400BandInfoPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(gsm400BandInfoPresent == 1)
	{
		/* Extract GSM 400 Bands Supported */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_400_bands_supported, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;

		/* Extract GSM 400 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_400_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 <GSM 850 Associated Radio Capability : bit(4) > }
	 * Extract GSM 850 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_gsm_850_assoc_radio_cap_present, tvb, bit_offset, 1, &gsm850AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(gsm850AssocRadioCapabilityPresent == 1)
	{
		/* Extract GSM 850 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_850_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 <GSM 1900 Associated Radio Capability : bit(4) > }
	 * Extract GSM 1900 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_gsm_1900_assoc_radio_cap_present, tvb, bit_offset, 1, &gsm1900AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(gsm1900AssocRadioCapabilityPresent == 1)
	{
		/* Extract GSM 1900 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_1900_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* < UMTS FDD Radio Access Technology Capability : bit >
	 * Extract UMTS FDD Radio Access Technology Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_umts_fdd_rat_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < UMTS 3.84 Mcps TDD Radio Access Technology Capability : bit >
	 * Extract UMTS 3.84 Mcps TDD Radio Access Technology Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_umts_384_mcps_tdd_rat_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < CDMA 2000 Radio Access Technology Capability : bit >
	 * Extract CDMA 2000 Radio Access Technology Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_cdma_2000_rat_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* { 0 | 1 < DTM GPRS Multi Slot Class : bit(2) >
	 *   < Single Slot DTM : bit >
	 *   {0 | 1< DTM EGPRS Multi Slot Class : bit(2) > } }
	 * Extract DTM E/GPRS Information presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_dtm_e_gprs_multi_slot_info_present, tvb, bit_offset, 1, &dtmEGprsMultiSlotInfoPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(dtmEGprsMultiSlotInfoPresent == 1)
	{
		/* Extract DTM GPRS Multi Slot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_dtm_gprs_multi_slot_class, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;

		/* Extract Single Slot DTM */
		proto_tree_add_bits_item(tree, hf_gsm_a_single_slot_dtm, tvb, bit_offset, 1, FALSE);
		bit_offset = bit_offset + 1;

		/* Extract DTM EGPRS Multi Slot Class Presence */
		proto_tree_add_bits_ret_val(tree, hf_gsm_a_dtm_egprs_multi_slot_class_present, tvb, bit_offset, 1, &dtmEgprsMultiSlotClassPresent, FALSE);
		bit_offset = bit_offset + 1;

		/* Extract DTM EGPRS Multi Slot Class */
		if (dtmEgprsMultiSlotClassPresent == 1)
		{
			proto_tree_add_bits_item(tree, hf_gsm_a_dtm_egprs_multi_slot_class, tvb, bit_offset, 2, FALSE);
			bit_offset = bit_offset + 2;
		}
	}

	/*
	 * Release 4 starts here
	 *
	 * { 0 | 1 < Single Band Support > } -- Release 4 starts here:
	 * Extract Single Band Support
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_single_band_support, tvb, bit_offset, 1, &singleBandSupport, FALSE);
	bit_offset = bit_offset + 1;

	if(singleBandSupport == 1)
	{
		/* Extract Single Band Support */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_band, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 <GSM 750 Associated Radio Capability : bit(4) > }
	 * Extract GSM 750 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_gsm_750_assoc_radio_cap_present, tvb, bit_offset, 1, &gsm750AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(gsm750AssocRadioCapabilityPresent == 1)
	{
		/* Extract GSM 750 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_750_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* < UMTS 1.28 Mcps TDD Radio Access Technology Capability : bit >
	 * Extract UMTS 1.28 Mcps TDD Radio Access Technology Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_umts_128_mcps_tdd_rat_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < GERAN Feature Package 1 : bit >
	 * Extract GERAN Feature Package 1
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_geran_feature_package_1, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* { 0 | 1 < Extended DTM GPRS Multi Slot Class : bit(2) >
	 *   < Extended DTM EGPRS Multi Slot Class : bit(2) > }
	 * Extract Extended DTM E/GPRS Information presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_ext_dtm_e_gprs_multi_slot_info_present, tvb, bit_offset, 1, &extDtmEGprsMultiSlotInfoPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(extDtmEGprsMultiSlotInfoPresent == 1)
	{
		/* Extract Extended DTM GPRS Multi Slot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_ext_dtm_gprs_multi_slot_class, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;

		/* Extract Extended DTM EGPRS Multi Slot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_ext_dtm_egprs_multi_slot_class, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;
	}

	/*
	 * Release 5 starts here
	 *
	 * { 0 | 1 < High Multislot Capability : bit(2) > } -- Release 5 starts here.
	 * Extract High Multislot Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_high_multislot_cap_present, tvb, bit_offset, 1, &highMultislotCapPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(highMultislotCapPresent == 1)
	{
		/* Extract High Multislot Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_high_multislot_cap, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;
	}

	/*
	 * { 0 | 1 < GERAN Iu Mode Capabilities > } -- "1" also means support of GERAN Iu mode
	 * Extract GERAN Iu Mode Capabilities presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_geran_iu_mode_support, tvb, bit_offset, 1, &geranIuModeSupport, FALSE);
	bit_offset = bit_offset + 1;

	if(geranIuModeSupport == 1)
	{
		/* Extract GERAN Iu Mode Capabilities Length */
		length = tvb_get_bits8(tvb, bit_offset, 4);

		/* Extract GERAN Iu Mode Capabilities */
		item = proto_tree_add_bits_item(tree, hf_gsm_a_geran_iu_mode_cap, tvb, bit_offset, length + 4, FALSE);
		subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_MS_CM_3]);

		/* Add GERAN Iu Mode Capabilities Length in subtree */
		proto_tree_add_bits_item(subtree, hf_gsm_a_geran_iu_mode_cap_length, tvb, bit_offset, 4, FALSE);
		bit_offset += 4;
		target_bit_offset = bit_offset + length;

		/* Extract FLO Iu Capability */
		proto_tree_add_bits_item(subtree, hf_gsm_a_flo_iu_cap, tvb, bit_offset, 1, FALSE);
		bit_offset += 1;

		/* If needed, add spare bits */
		if (target_bit_offset > bit_offset)
		{
			proto_tree_add_bits_item(subtree, hf_gsm_a_spare_bits, tvb, bit_offset, target_bit_offset - bit_offset, FALSE);
			bit_offset = target_bit_offset;
		}
	}

	/* < GERAN Feature Package 2 : bit >
	 * Extract GERAN Feature Package 2
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_geran_feature_package_2, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < GMSK Multislot Power Profile : bit (2) >
	 * Extract GMSK Multislot Power Profile
	 */
	AVAILABLE_BITS_CHECK(2);
	proto_tree_add_bits_item(tree, hf_gsm_a_gmsk_multislot_power_prof, tvb, bit_offset, 2, FALSE);
	bit_offset = bit_offset + 2;

	/* < 8-PSK Multislot Power Profile : bit (2) >
	 * Extract GMSK Multislot Power Profile
	 */
	AVAILABLE_BITS_CHECK(2);
	proto_tree_add_bits_item(tree, hf_gsm_a_8_psk_multislot_power_prof, tvb, bit_offset, 2, FALSE);
	bit_offset = bit_offset + 2;

	/*
	 * Release 6 starts here
	 *
	 * { 0 | 1 < T-GSM 400 Bands Supported : { 01 | 10 | 11 } > -- Release 6 starts here.
	 *   < T-GSM 400 Associated Radio Capability: bit(4) > }
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_t_gsm_400_band_info_present, tvb, bit_offset, 1, &tGsm400BandInfoPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(tGsm400BandInfoPresent == 1)
	{
		/* Extract T-GSM 400 Bands Supported */
		proto_tree_add_bits_item(tree, hf_gsm_a_t_gsm_400_bands_supported, tvb, bit_offset, 2, FALSE);
		bit_offset = bit_offset + 2;

		/* Extract T-GSM 400 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_t_gsm_400_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 < T-GSM 900 Associated Radio Capability: bit(4) > }
	 * Extract T-GSM 900 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_t_gsm_900_assoc_radio_cap_present, tvb, bit_offset, 1, &tGsm900AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(tGsm900AssocRadioCapabilityPresent == 1)
	{
		/* Extract T-GSM 900 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_t_gsm_900_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* < Downlink Advanced Receiver Performance : bit (2)>
	 * Extract Downlink Advanced Receiver Performance
	 */
	AVAILABLE_BITS_CHECK(2);
	proto_tree_add_bits_item(tree, hf_gsm_a_downlink_adv_receiver_perf, tvb, bit_offset, 2, FALSE);
	bit_offset = bit_offset + 2;

	/* < DTM Enhancements Capability : bit >
	 * Extract DTM Enhancements Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_dtm_enhancements_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* { 0 | 1 < DTM GPRS High Multi Slot Class : bit(3) >
	 *   < Offset required : bit>
	 *   { 0 | 1 < DTM EGPRS High Multi Slot Class : bit(3) > } }
	 * Extract DTM E/GPRS High Multi Slot Information presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_dtm_e_gprs_high_multi_slot_info_present, tvb, bit_offset, 1, &dtmEGprsHighMultiSlotInfoPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(dtmEGprsHighMultiSlotInfoPresent == 1)
	{
		/* Extract DTM GPRS High Multi Slot Class */
		proto_tree_add_bits_item(tree, hf_gsm_a_dtm_gprs_high_multi_slot_class, tvb, bit_offset, 3, FALSE);
		bit_offset = bit_offset + 3;

		/* Extract Offset Required */
		proto_tree_add_bits_item(tree, hf_gsm_a_offset_required, tvb, bit_offset, 1, FALSE);
		bit_offset = bit_offset + 1;

		/* Extract DTM EGPRS High Multi Slot Class Presence */
		proto_tree_add_bits_ret_val(tree, hf_gsm_a_dtm_egprs_high_multi_slot_class_present, tvb, bit_offset, 1, &dtmEgprsHighMultiSlotClassPresent, FALSE);
		bit_offset = bit_offset + 1;

		/* Extract DTM EGPRS High Multi Slot Class */
		if (dtmEgprsHighMultiSlotClassPresent == 1)
		{
			proto_tree_add_bits_item(tree, hf_gsm_a_dtm_egprs_high_multi_slot_class, tvb, bit_offset, 3, FALSE);
			bit_offset = bit_offset + 3;
		}
	}

	/* < Repeated ACCH Capability : bit >
	 * Extract Repeated ACCH Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_repeated_acch_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/*
	 * Release 7 starts here
	 *
	 * { 0 | 1 <GSM 710 Associated Radio Capability : bit(4) > } -- Release 7 starts here.
	 * Extract GSM 710 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_gsm_710_assoc_radio_cap_present, tvb, bit_offset, 1, &gsm710AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(gsm710AssocRadioCapabilityPresent == 1)
	{
		/* Extract GSM 710 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_gsm_710_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* { 0 | 1 < T-GSM 810 Associated Radio Capability: bit(4) > }
	 * Extract T-GSM 810 Associated Radio Capability presence
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_ret_val(tree, hf_gsm_a_t_gsm_810_assoc_radio_cap_present, tvb, bit_offset, 1, &tGsm810AssocRadioCapabilityPresent, FALSE);
	bit_offset = bit_offset + 1;

	if(tGsm810AssocRadioCapabilityPresent == 1)
	{
		/* Extract T-GSM 810 Associated Radio Capability */
		proto_tree_add_bits_item(tree, hf_gsm_a_t_gsm_810_assoc_radio_cap, tvb, bit_offset, 4, FALSE);
		bit_offset = bit_offset + 4;
	}

	/* < Ciphering Mode Setting Capability : bit >
	 * Extract Ciphering Mode Setting Capability
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_ciphering_mode_setting_cap, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/* < Additional Positioning Capabilities : bit >
	 * Extract Additional Positioning Capabilities
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_additional_positioning_caps, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/*
	 * Release 8 starts here
	 *
	 * { 0 | 1 <E-UTRA FDD support : bit > } -- Release 8 starts here.
	 * Extract E-UTRA FDD support
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_e_utra_fdd_support, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/*
	 * { 0 | 1 <E-UTRA TDD support : bit > }
	 * Extract E-UTRA TDD support
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_e_utra_tdd_support, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/*
	 * { 0 | 1 <E-UTRA Measurement and Reporting support : bit > }
	 * Extract E-UTRA Measurement and Reporting support
	 */
	AVAILABLE_BITS_CHECK(1);
	proto_tree_add_bits_item(tree, hf_gsm_a_e_utra_meas_and_report_support, tvb, bit_offset, 1, FALSE);
	bit_offset = bit_offset + 1;

	/*
	 * Add spare bits until we reach an octet boundary
	 */
	bits_left = (((len + offset) << 3) - bit_offset) & 0x07;
	if (bits_left != 0)
	{
		proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, bit_offset, bits_left, FALSE);
		bit_offset += bits_left;
	}

	/* translate to byte offset (we already know that we are on an octet boundary) */
	curr_offset = bit_offset >> 3;
	EXTRANEOUS_DATA_CHECK_EXPERT(len, curr_offset - offset, gsm_a_dtap_pinfo);

	return(len);
}
/*
 * [3] 10.5.1.8
 */
static guint16
de_spare_nibble(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint32	curr_offset;

	curr_offset = offset;

	proto_tree_add_text(tree,
		tvb, curr_offset, 1,
		"Spare Nibble");

	curr_offset++;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.9 Descriptive group or broadcast call reference
 */
guint16
de_d_gb_call_ref(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint8	oct;
	guint32	value;
	guint32	curr_offset;
	const gchar *str;

	curr_offset = offset;

	value = tvb_get_ntohl(tvb, curr_offset);

	other_decode_bitfield_value(a_bigbuf, value, 0xffffffe0, 32);
	proto_tree_add_text(tree, tvb, curr_offset, 4,
		"%s = Group or Broadcast call reference: %u (0x%04x)",
		a_bigbuf,
		(value & 0xffffffe0) >> 5,
		(value & 0xffffffe0) >> 5);

	other_decode_bitfield_value(a_bigbuf, value, 0x00000010, 32);
	proto_tree_add_text(tree, tvb, curr_offset, 4,
		"%s = SF Service Flag: %s",
		a_bigbuf,
		(value & 0x00000010) ?
		"VGCS (Group call reference)" : "VBS (Broadcast call reference)");

	other_decode_bitfield_value(a_bigbuf, value, 0x00000008, 32);
	proto_tree_add_text(tree, tvb, curr_offset, 4,
		"%s = AF Acknowledgement Flag: acknowledgment is %srequired",
		a_bigbuf,
		(value & 0x00000008) ? "" : "not ");

	switch (value & 0x00000007)
	{
	case 1: str = "call priority level 4"; break;
	case 2: str = "call priority level 3"; break;
	case 3: str = "call priority level 2"; break;
	case 4: str = "call priority level 1"; break;
	case 5: str = "call priority level 0"; break;
	case 6: str = "call priority level B"; break;
	case 7: str = "call priority level A"; break;
	default:
	str = "no priority applied";
	break;
	}

	other_decode_bitfield_value(a_bigbuf, value, 0x00000007, 32);
	proto_tree_add_text(tree, tvb, curr_offset, 4,
		"%s = Call Priority: %s",
		a_bigbuf,
		str);

	curr_offset += 4;

	oct = tvb_get_guint8(tvb, curr_offset);

	other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8);
	proto_tree_add_text(tree, tvb, curr_offset, 1,
		"%s = Ciphering Information",
		a_bigbuf);

	proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, (curr_offset<<3)+4, 4, FALSE);
	curr_offset++;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.10a PD and SAPI $(CCBS)$
 */
static guint16
de_pd_sapi(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint8	oct;
	guint32	curr_offset;
	proto_tree	*subtree;
	proto_item	*item;
	const gchar *str;

	curr_offset = offset;

	oct = tvb_get_guint8(tvb, curr_offset);

	item =
	proto_tree_add_text(tree,
		tvb, curr_offset, 1, "%s",
		gsm_dtap_elem_strings[DE_PD_SAPI].strptr);

	subtree = proto_item_add_subtree(item, ett_gsm_dtap_elem[DE_PD_SAPI]);

	proto_tree_add_bits_item(tree, hf_gsm_a_spare_bits, tvb, curr_offset<<3, 2, FALSE);

	switch ((oct & 0x30) >> 4)
	{
	case 0: str = "SAPI 0"; break;
	case 3: str = "SAPI 3"; break;
	default:
	str = "Reserved";
	break;
	}

	other_decode_bitfield_value(a_bigbuf, oct, 0x30, 8);
	proto_tree_add_text(subtree, tvb, curr_offset, 1,
		"%s = SAPI (Service Access Point Identifier): %s",
		a_bigbuf,
		str);

	proto_tree_add_item(tree, hf_gsm_a_L3_protocol_discriminator, tvb, curr_offset, 1, FALSE);

	curr_offset++;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.11 Priority Level
 */
static const value_string gsm_a_call_prio_vals[] = {
	{ 0x00,	"no priority applied" },
	{ 0x01,	"call priority level 4" },
	{ 0x02,	"call priority level 3" },
	{ 0x03,	"call priority level 2" },
	{ 0x04,	"call priority level 1" },
	{ 0x05,	"call priority level 0" },
	{ 0x06,	"call priority level B" },
	{ 0x07,	"call priority level A" },
	{ 0,			NULL }
};

static guint16
de_prio(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
{
	guint32	curr_offset;

	curr_offset = offset;

	proto_tree_add_item(tree, hf_gsm_a_b8spare, tvb, curr_offset, 1, FALSE);
	proto_tree_add_bits_item(tree, hf_gsm_a_call_prio, tvb, (curr_offset<<3)+5, 3, FALSE);
	curr_offset++;

	/* no length check possible */

	return(curr_offset - offset);
}

/*
 * [3] 10.5.1.13 PLMN list
 */
guint16
de_plmn_list(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
{
	guint8	octs[3];
	guint32	curr_offset;
	gchar	mcc[4];
	gchar	mnc[4];
	guint8	num_plmn;

	curr_offset = offset;

	num_plmn = 0;
	while ((len - (curr_offset - offset)) >= 3)
	{
	octs[0] = tvb_get_guint8(tvb, curr_offset);
	octs[1] = tvb_get_guint8(tvb, curr_offset + 1);
	octs[2] = tvb_get_guint8(tvb, curr_offset + 2);

	mcc_mnc_aux(octs, mcc, mnc);

	proto_tree_add_text(tree,
		tvb, curr_offset, 3,
		"PLMN[%u]  Mobile Country Code (MCC): %s, Mobile Network Code (MNC): %s",
		num_plmn + 1,
		mcc,
		mnc);

	curr_offset += 3;

	num_plmn++;
	}

	if (add_string)
	g_snprintf(add_string, string_len, " - %u PLMN%s",
		num_plmn, plurality(num_plmn, "", "s"));

	EXTRANEOUS_DATA_CHECK_EXPERT(len, curr_offset - offset, gsm_a_dtap_pinfo);

	return(curr_offset - offset);
}

guint16 (*common_elem_fcn[])(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len) = {
	/* Common Information Elements 10.5.1 */
	de_cell_id,				/* Cell Identity */
	de_ciph_key_seq_num,	/* Ciphering Key Sequence Number */
	de_lai,					/* Location Area Identification */
	de_mid,					/* Mobile Identity */
	de_ms_cm_1,				/* Mobile Station Classmark 1 */
	de_ms_cm_2,				/* Mobile Station Classmark 2 */
	de_ms_cm_3,				/* Mobile Station Classmark 3 */
	de_spare_nibble,		/* Spare Half Octet */
	de_d_gb_call_ref,		/* Descriptive group or broadcast call reference */
	NULL					/* handled inline */,	/* Group Cipher Key Number */
	de_pd_sapi,				/* PD and SAPI $(CCBS)$ */
	/* Pos 10 */
	de_prio					/* handled inline */,	/* Priority Level */
	de_plmn_list,			/* PLMN List */
	NULL,					/* NONE */
};

/* Register the protocol with Wireshark */
void
proto_register_gsm_a_common(void)
{
	guint	i;
	guint	last_offset;

	/* Setup list of header fields */
	static hf_register_info hf[] =
	{
	{ &hf_gsm_a_common_elem_id,
		{ "Element ID",	"gsm_a_common.elem_id",
		FT_UINT8, BASE_DEC, NULL, 0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_l_ext,
		{ "ext",	"gsm_a_common.l_ext",
		FT_UINT8, BASE_DEC, NULL, 0x80,
		NULL, HFILL }
	},
	{ &hf_gsm_a_imsi,
		{ "IMSI",	"gsm_a.imsi",
		FT_STRING, BASE_NONE, 0, 0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_tmsi,
		{ "TMSI/P-TMSI",	"gsm_a.tmsi",
		FT_UINT32, BASE_HEX, 0, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_imei,
		{ "IMEI",	"gsm_a.imei",
		FT_STRING, BASE_NONE, 0, 0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_imeisv,
		{ "IMEISV",	"gsm_a.imeisv",
		FT_STRING, BASE_NONE, 0, 0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_MSC_rev,
		{ "Revision Level","gsm_a.MSC2_rev",
		FT_UINT8,BASE_DEC, VALS(gsm_a_msc_rev_vals), 0x60,
		NULL, HFILL }
	},
	{ &hf_gsm_a_ES_IND,
		{ "ES IND","gsm_a.MSC2_rev",
		FT_UINT8,BASE_DEC, VALS(ES_IND_vals), 0x10,
			NULL, HFILL }
	},
	{ &hf_gsm_a_A5_1_algorithm_sup,
		{ "A5/1 algorithm supported","gsm_a.MSC2_rev",
		FT_UINT8,BASE_DEC, VALS(A5_1_algorithm_sup_vals), 0x08,
		NULL, HFILL }
	},
	{ &hf_gsm_a_RF_power_capability,
		{ "RF Power Capability","gsm_a.MSC2_rev",
		FT_UINT8,BASE_DEC, VALS(RF_power_capability_vals), 0x07,
		NULL, HFILL }
	},
	{ &hf_gsm_a_ps_sup_cap,
		{ "PS capability (pseudo-synchronization capability)","gsm_a.ps_sup_cap",
		FT_UINT8,BASE_DEC, VALS(ps_sup_cap_vals), 0x40,
		NULL, HFILL }
	},
	{ &hf_gsm_a_SS_screening_indicator,
		{ "SS Screening Indicator","gsm_a.SS_screening_indicator",
		FT_UINT8,BASE_DEC, VALS(SS_screening_indicator_vals), 0x30,
		NULL, HFILL }
	},
	{ &hf_gsm_a_SM_capability,
		{ "SM capability (MT SMS pt to pt capability)","gsm_a.SM_cap",
		FT_UINT8,BASE_DEC, VALS(SM_capability_vals), 0x08,
		NULL, HFILL }
	},
	{ &hf_gsm_a_VBS_notification_rec,
		{ "VBS notification reception","gsm_a.VBS_notification_rec",
		FT_UINT8,BASE_DEC, VALS(VBS_notification_rec_vals), 0x04,
		NULL, HFILL }
	},
	{ &hf_gsm_a_VGCS_notification_rec,
		{ "VGCS notification reception","gsm_a.VGCS_notification_rec",
		FT_UINT8,BASE_DEC, VALS(VGCS_notification_rec_vals), 0x02,
		NULL, HFILL }
	},
	{ &hf_gsm_a_FC_frequency_cap,
		{ "FC Frequency Capability","gsm_a.FC_frequency_cap",
		FT_UINT8,BASE_DEC, VALS(FC_frequency_cap_vals), 0x01,
		NULL, HFILL }
	},
	{ &hf_gsm_a_CM3,
		{ "CM3","gsm_a.CM3",
		FT_UINT8,BASE_DEC, VALS(CM3_vals), 0x80,
		NULL, HFILL }
	},
	{ &hf_gsm_a_LCS_VA_cap,
		{ "LCS VA capability (LCS value added location request notification capability)","gsm_a.LCS_VA_cap",
		FT_UINT8,BASE_DEC, VALS(LCS_VA_cap_vals), 0x20,
		NULL, HFILL }
	},
	{ &hf_gsm_a_UCS2_treatment,
		{ "UCS2 treatment","gsm_a.UCS2_treatment",
		FT_UINT8,BASE_DEC, VALS(UCS2_treatment_vals), 0x10,
		NULL, HFILL }
	},
	{ &hf_gsm_a_SoLSA,
		{ "SoLSA","gsm_a.SoLSA",
		FT_UINT8,BASE_DEC, VALS(SoLSA_vals), 0x08,
		NULL, HFILL }
	},
	{ &hf_gsm_a_CMSP,
		{ "CMSP: CM Service Prompt","gsm_a.CMSP",
		FT_UINT8,BASE_DEC, VALS(CMSP_vals), 0x04,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_7_algorithm_sup,
		{ "A5/7 algorithm supported","gsm_a.A5_7_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_7_algorithm_sup_vals), 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_6_algorithm_sup,
		{ "A5/6 algorithm supported","gsm_a.A5_6_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_6_algorithm_sup_vals), 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_5_algorithm_sup,
		{ "A5/5 algorithm supported","gsm_a.A5_5_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_5_algorithm_sup_vals), 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_4_algorithm_sup,
		{ "A5/4 algorithm supported","gsm_a.A5_4_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_4_algorithm_sup_vals), 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_3_algorithm_sup,
		{ "A5/3 algorithm supported","gsm_a.A5_3_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_3_algorithm_sup_vals), 0x02,
		NULL, HFILL }
	},
	{ &hf_gsm_a_A5_2_algorithm_sup,
		{ "A5/2 algorithm supported","gsm_a.A5_2_algorithm_sup",
		FT_UINT8,BASE_DEC, VALS(A5_2_algorithm_sup_vals), 0x01,
		NULL, HFILL }
	},
	{ &hf_gsm_a_mobile_identity_type,
		{ "Mobile Identity Type","gsm_a.ie.mobileid.type",
		FT_UINT8, BASE_DEC, VALS(mobile_identity_type_vals), 0x07,
		NULL, HFILL }
	},
	{ &hf_gsm_a_odd_even_ind,
		{ "Odd/even indication","gsm_a.oddevenind",
		FT_UINT8, BASE_DEC, oddevenind_vals, 0x08,
		NULL, HFILL }
	},
	{ &hf_gsm_a_tmgi_mcc_mnc_ind,
		{ "MCC/MNC indication", "gsm_a.tmgi_mcc_mnc_ind",
		FT_BOOLEAN, 8, TFS(&gsm_a_present_vals), 0x10,
		NULL, HFILL}
	},
	{ &hf_gsm_a_mbs_ses_id_ind,
		{ "MBMS Session Identity indication", "gsm_a.mbs_session_id_ind",
		FT_BOOLEAN, 8, TFS(&gsm_a_present_vals), 0x20,
		NULL, HFILL}
	},
	{ &hf_gsm_a_mbs_service_id,
		{ "MBMS Service ID", "gsm_a.mbs_service_id",
		FT_UINT24, BASE_HEX, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_mbs_session_id,
		{ "MBMS Session ID", "gsm_a.mbs_session_id",
		FT_UINT8, BASE_HEX, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_L3_protocol_discriminator,
		{ "Protocol discriminator","gsm_a.L3_protocol_discriminator",
		FT_UINT8,BASE_DEC, VALS(protocol_discriminator_vals), 0x0f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_call_prio,
		{ "Call priority", "gsm_a.call_prio",
		FT_UINT8, BASE_DEC, VALS(gsm_a_call_prio_vals), 0x00,
		NULL, HFILL }
	},
	{ &hf_gsm_a_skip_ind,
		{ "Skip Indicator", "gsm_a.skip.ind",
		FT_UINT8, BASE_DEC, NULL, 0xf0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_b7spare,
		{ "Spare","gsm_a.spareb7",
		FT_UINT8,BASE_DEC, NULL, 0x40,
		NULL, HFILL }
	},
	{ &hf_gsm_a_b8spare,
		{ "Spare","gsm_a.spareb8",
		FT_UINT8,BASE_DEC, NULL, 0x80,
		NULL, HFILL }
	},
	{ &hf_gsm_a_spare_bits,
		{ "Spare bit(s)","gsm_a.spare_bits",
		FT_UINT8,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_multi_bnd_sup_fields,
		{ "Multiband supported field","gsm_a.multi_bnd_sup_fields",
		FT_UINT8,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_pgsm_supported,
		{ "P-GSM Supported", "gsm_a.classmark3.pgsmSupported",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_egsm_supported,
		{ "E-GSM or R-GSM Supported", "gsm_a.classmark3.egsmSupported",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm1800_supported,
		{ "GSM 1800 Supported", "gsm_a.classmark3.gsm1800Supported",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ass_radio_cap1,
		{ "Associated Radio Capability 1", "gsm_a.classmark3.ass_radio_cap1",
		FT_UINT8, BASE_DEC, NULL, 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ass_radio_cap2,
		{ "Associated Radio Capability 2", "gsm_a.classmark3.ass_radio_cap2",
		FT_UINT8, BASE_DEC, NULL, 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_cm3_A5_bits,
		{ "A5 bits", "gsm_a.classmark3.a5_bits",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_rsupport,
		{ "R Support", "gsm_a.classmark3.rsupport",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_r_capabilities,
		{ "R-GSM band Associated Radio Capability", "gsm_a.classmark3.r_capabilities",
		FT_UINT8, BASE_DEC, NULL, 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_multislot_capabilities,
		{ "HSCSD Multi Slot Capability", "gsm_a.classmark3.multislot_capabilities",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_multislot_class,
		{ "HSCSD Multi Slot Class", "gsm_a.classmark3.multislot_cap",
		FT_UINT8, BASE_DEC, NULL, 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ucs2_treatment,
		{ "UCS2 treatment","gsm_a.UCS2_treatment",
		FT_UINT8,BASE_DEC, VALS(UCS2_treatment_vals), 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_extended_measurement_cap,
		{ "Extended Measurement Capability", "gsm_a.classmark3.ext_meas_cap",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_measurement_capability,
		{ "MS measurement capability", "gsm_a.classmark3.ms_measurement_capability",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_sms_value,
		{ "SMS_VALUE (Switch-Measure-Switch)", "gsm_a.classmark3.sms_value",
		FT_UINT8, BASE_DEC, VALS(gsm_a_sms_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_sm_value,
		{ "SM_VALUE (Switch-Measure)", "gsm_a.classmark3.sm_value",
		FT_UINT8, BASE_DEC, VALS(gsm_a_sms_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_pos_method_cap_present,
		{ "MS Positioning Method Capability present", "gsm_a.classmark3.ms_pos_method_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_pos_method,
		{ "MS Positioning Method", "gsm_a.classmark3.ms_pos_method",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_assisted_e_otd,
		{ "MS assisted E-OTD", "gsm_a.classmark3.ms_assisted_e_otd",
		FT_UINT8, BASE_DEC, VALS(ms_assisted_e_otd_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_based_e_otd,
		{ "MS based E-OTD", "gsm_a.classmark3.ms_based_e_otd",
		FT_UINT8, BASE_DEC, VALS(ms_based_e_otd_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_assisted_gps,
		{ "MS assisted GPS", "gsm_a.classmark3.ms_assisted_gps",
		FT_UINT8, BASE_DEC, VALS(ms_assisted_gps_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_based_gps,
		{ "MS based GPS", "gsm_a.classmark3.ms_based_gps",
		FT_UINT8, BASE_DEC, VALS(ms_based_gps_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ms_conventional_gps,
		{ "MS Conventional GPS", "gsm_a.classmark3.ms_conventional_gps",
		FT_UINT8, BASE_DEC, VALS(ms_conventional_gps_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ecsd_multi_slot_capability,
		{ "ECSD Multi Slot Capability present", "gsm_a.classmark3.ecsd_multi_slot_capability",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ecsd_multi_slot_class,
		{ "ECSD Multi Slot Class", "gsm_a.classmark3.ecsd_multi_slot_class",
		FT_UINT8, BASE_DEC, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_8_psk_struct_present,
		{ "8-PSK Struct present", "gsm_a.classmark3.8_psk_struct_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_8_psk_struct,
		{ "8-PSK Struct", "gsm_a.classmark3.8_psk_struct",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_modulation_capability,
		{ "Modulation Capability", "gsm_a.classmark3.modulation_capability",
		FT_UINT8, BASE_DEC, VALS(modulation_capability_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_8_psk_rf_power_capability_1,
		{ "8-PSK RF Power Capability 1", "gsm_a.classmark3.8_psk_rf_power_capability_1",
		FT_UINT8, BASE_HEX, VALS(eight_psk_rf_power_capability_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_8_psk_rf_power_capability_2,
		{ "8-PSK RF Power Capability 2", "gsm_a.classmark3.8_psk_rf_power_capability_2",
		FT_UINT8, BASE_HEX, VALS(eight_psk_rf_power_capability_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_400_band_info_present,
		{ "GSM 400 Band Information present", "gsm_a.classmark3.gsm_400_band_info_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_400_bands_supported,
		{ "GSM 400 Bands Supported", "gsm_a.classmark3.gsm_400_bands_supported",
		FT_UINT8, BASE_HEX, VALS(gsm_400_bands_supported_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_400_assoc_radio_cap,
		{ "GSM 400 Associated Radio Capability", "gsm_a.classmark3.gsm_400_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_850_assoc_radio_cap_present,
		{ "GSM 850 Associated Radio Capability present", "gsm_a.classmark3.gsm_850_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_850_assoc_radio_cap,
		{ "GSM 850 Associated Radio Capability", "gsm_a.classmark3.gsm_850_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_1900_assoc_radio_cap_present,
		{ "GSM 1900 Associated Radio Capability present", "gsm_a.classmark3.gsm_1900_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_1900_assoc_radio_cap,
		{ "GSM 1900 Associated Radio Capability", "gsm_a.classmark3.gsm_1900_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_umts_fdd_rat_cap,
		{ "UMTS FDD Radio Access Technology Capability", "gsm_a.classmark3.umts_fdd_rat_cap",
		FT_UINT8, BASE_DEC, VALS(umts_fdd_rat_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_umts_384_mcps_tdd_rat_cap,
		{ "UMTS 3.84 Mcps TDD Radio Access Technology Capability", "gsm_a.classmark3.umts_384_mcps_tdd_rat_cap",
		FT_UINT8, BASE_DEC, VALS(umts_384_mcps_tdd_rat_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_cdma_2000_rat_cap,
		{ "CDMA 2000 Radio Access Technology Capability", "gsm_a.classmark3.cdma_2000_rat_cap",
		FT_UINT8, BASE_DEC, VALS(cdma_2000_rat_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_e_gprs_multi_slot_info_present,
		{ "DTM E/GPRS Multi Slot Information present", "gsm_a.classmark3.dtm_e_gprs_multi_slot_info_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_gprs_multi_slot_class,
		{ "DTM GPRS Multi Slot Class", "gsm_a.classmark3.dtm_gprs_multi_slot_class",
		FT_UINT8, BASE_DEC, VALS(dtm_gprs_multi_slot_class_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_single_slot_dtm,
		{ "Single Slot DTM", "gsm_a.classmark3.single_slot_dtm_supported",
		FT_UINT8, BASE_DEC, VALS(single_slot_dtm_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_egprs_multi_slot_class_present,
		{ "DTM EGPRS Multi Slot Class present", "gsm_a.classmark3.dtm_egprs_multi_slot_class_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_egprs_multi_slot_class,
		{ "DTM EGPRS Multi Slot Class", "gsm_a.classmark3.dtm_egprs_multi_slot_class",
		FT_UINT8, BASE_DEC, VALS(dtm_gprs_multi_slot_class_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_single_band_support,
		{ "Single Band Support", "gsm_a.classmark3.single_band_support",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_band,
		{ "GSM Band", "gsm_a.classmark3.gsm_band",
		FT_UINT8, BASE_DEC, VALS(gsm_band_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_750_assoc_radio_cap_present,
		{ "GSM 750 Associated Radio Capability present", "gsm_a.classmark3.gsm_750_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_750_assoc_radio_cap,
		{ "GSM 750 Associated Radio Capability", "gsm_a.classmark3.gsm_750_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_umts_128_mcps_tdd_rat_cap,
		{ "UMTS 1.28 Mcps TDD Radio Access Technology Capability", "gsm_a.classmark3.umts_128_mcps_tdd_rat_cap",
		FT_UINT8, BASE_DEC, VALS(umts_128_mcps_tdd_rat_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geran_feature_package_1,
		{ "GERAN Feature Package 1", "gsm_a.classmark3.geran_feature_package_1",
		FT_UINT8, BASE_DEC, VALS(geran_feature_package_1_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ext_dtm_e_gprs_multi_slot_info_present,
		{ "Extended DTM E/GPRS Multi Slot Information present", "gsm_a.classmark3.ext_dtm_e_gprs_info_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ext_dtm_gprs_multi_slot_class,
		{ "Extended DTM GPRS Multi Slot Class", "gsm_a.classmark3.ext_dtm_gprs_multi_slot_class",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ext_dtm_egprs_multi_slot_class,
		{ "Extended DTM EGPRS Multi Slot Class", "gsm_a.classmark3.ext_dtm_egprs_multi_slot_class",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_high_multislot_cap_present,
		{ "High Multislot Capability present", "gsm_a.classmark3.high_multislot_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_high_multislot_cap,
		{ "High Multislot Capability", "gsm_a.classmark3.high_multislot_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geran_iu_mode_support,
		{ "GERAN Iu Mode Support", "gsm_a.classmark3.geran_iu_mode_support",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geran_iu_mode_cap,
		{ "GERAN Iu Mode Capabilities", "gsm_a.classmark3.geran_iu_mode_cap",
		FT_UINT24, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geran_iu_mode_cap_length,
		{ "Length", "gsm_a.classmark3.geran_iu_mode_cap.length",
		FT_UINT8, BASE_DEC, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_flo_iu_cap,
		{ "FLO Iu Capability", "gsm_a.classmark3.geran_iu_mode_cap.flo_iu_cap",
		FT_UINT8, BASE_DEC, VALS(flo_iu_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geran_feature_package_2,
		{ "GERAN Feature Package 2", "gsm_a.classmark3.geran_feature_package_2",
		FT_UINT8, BASE_DEC, VALS(geran_feature_package_2_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gmsk_multislot_power_prof,
		{ "GMSK Multislot Power Profile", "gsm_a.classmark3.gmsk_multislot_power_prof",
		FT_UINT8, BASE_DEC, VALS(gmsk_multislot_power_prof_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_8_psk_multislot_power_prof,
		{ "8-PSK Multislot Power Profile", "gsm_a.classmark3.8_psk_multislot_power_prof",
		FT_UINT8, BASE_DEC, VALS(eight_psk_multislot_power_prof_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_400_band_info_present,
		{ "T-GSM 400 Band Information present", "gsm_a.classmark3.gsm_400_band_info_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_400_bands_supported,
		{ "T-GSM 400 Bands Supported", "gsm_a.classmark3.t_gsm_400_bands_supported",
		FT_UINT8, BASE_HEX, VALS(t_gsm_400_bands_supported_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_400_assoc_radio_cap,
		{ "T-GSM 400 Associated Radio Capability", "gsm_a.classmark3.t_gsm_400_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_900_assoc_radio_cap_present,
		{ "T-GSM 900 Associated Radio Capability present", "gsm_a.classmark3.t_gsm_900_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_900_assoc_radio_cap,
		{ "T-GSM 900 Associated Radio Capability", "gsm_a.classmark3.t_gsm_900_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_downlink_adv_receiver_perf,
		{ "Downlink Advanced Receiver Performance", "gsm_a.classmark3.downlink_adv_receiver_perf",
		FT_UINT8, BASE_DEC, VALS(downlink_adv_receiver_perf_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_enhancements_cap,
		{ "DTM Enhancements Capability", "gsm_a.classmark3.dtm_enhancements_capability",
		FT_UINT8, BASE_DEC, VALS(dtm_enhancements_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_e_gprs_high_multi_slot_info_present,
		{ "DTM E/GPRS High Multi Slot Information present", "gsm_a.classmark3.dtm_e_gprs_high_mutli_slot_info_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_gprs_high_multi_slot_class,
		{ "DTM GPRS Multi Slot Class", "gsm_a.classmark3.dtm_gprs_multi_slot_class",
		FT_UINT8, BASE_DEC, VALS(dtm_gprs_high_multi_slot_class_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_offset_required,
		{ "Offset required", "gsm_a.classmark3.offset_required",
		FT_UINT8, BASE_DEC, VALS(offset_required_vals), 0x0,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_egprs_high_multi_slot_class_present,
		{ "DTM EGPRS High Multi Slot Class present", "gsm_a.classmark3.dtm_egprs_high_multi_slot_class_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_dtm_egprs_high_multi_slot_class,
		{ "DTM EGPRS High Multi Slot Class", "gsm_a.classmark3.dtm_egprs_high_multi_slot_class",
		FT_UINT8, BASE_DEC, VALS(dtm_gprs_high_multi_slot_class_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_repeated_acch_cap,
		{ "Repeated ACCH Capability", "gsm_a.classmark3.repeated_acch_cap",
		FT_UINT8, BASE_DEC, VALS(repeated_acch_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_710_assoc_radio_cap_present,
		{ "GSM 710 Associated Radio Capability present", "gsm_a.classmark3.gsm_710_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_gsm_710_assoc_radio_cap,
		{ "GSM 710 Associated Radio Capability", "gsm_a.classmark3.gsm_710_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_810_assoc_radio_cap_present,
		{ "T-GSM 810 Associated Radio Capability present", "gsm_a.classmark3.t_gsm_810_assoc_radio_cap_present",
		FT_UINT8, BASE_DEC, VALS(true_false_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_t_gsm_810_assoc_radio_cap,
		{ "T-GSM 810 Associated Radio Capability", "gsm_a.classmark3.t_gsm_810_assoc_radio_cap",
		FT_UINT8, BASE_HEX, NULL, 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_ciphering_mode_setting_cap,
		{ "Ciphering Mode Setting Capability", "gsm_a.classmark3.ciphering_mode_setting_cap",
		FT_UINT8, BASE_DEC, VALS(ciphering_mode_setting_cap_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_additional_positioning_caps,
		{ "Additional Positioning Capabilities", "gsm_a.classmark3.additional_positioning_caps",
		FT_UINT8, BASE_DEC, VALS(additional_positioning_caps_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_e_utra_fdd_support,
		{ "E-UTRA FDD support", "gsm_a.classmark3.e_utra_fdd_support",
		FT_UINT8, BASE_DEC, VALS(e_utra_fdd_support_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_e_utra_tdd_support,
		{ "E-UTRA TDD support", "gsm_a.classmark3.e_utra_tdd_support",
		FT_UINT8, BASE_DEC, VALS(e_utra_tdd_support_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_e_utra_meas_and_report_support,
		{ "E-UTRA Measurement and Reporting support", "gsm_a.classmark3.e_utra_meas_and_report_support",
		FT_UINT8, BASE_DEC, VALS(e_utra_meas_and_report_support_vals), 0x00,
		NULL, HFILL}
	},
	{ &hf_gsm_a_geo_loc_type_of_shape,
		{ "Location estimate","gsm_a.gad.location_estimate",
		FT_UINT8,BASE_DEC, VALS(type_of_shape_vals), 0xf0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_sign_of_lat,
		{ "Sign of latitude","gsm_a.gad.sign_of_latitude",
		FT_UINT8,BASE_DEC, VALS(sign_of_latitude_vals), 0x80,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_deg_of_lat,
		{ "Degrees of latitude","gsm_a.gad.sign_of_latitude",
		FT_UINT24,BASE_DEC, NULL, 0x7fffff,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_deg_of_long,
		{ "Degrees of longitude","gsm_a.gad.sign_of_longitude",
		FT_UINT24,BASE_DEC, NULL, 0xffffff,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_uncertainty_code,
		{ "Uncertainty code","gsm_a.gad.uncertainty_code",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_uncertainty_semi_major,
		{ "Uncertainty semi-major","gsm_a.gad.uncertainty_semi_major",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_uncertainty_semi_minor,
		{ "Uncertainty semi-minor","gsm_a.gad.uncertainty_semi_minor",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_orientation_of_major_axis,
		{ "Orientation of major axis","gsm_a.gad.orientation_of_major_axis",
		FT_UINT8,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_uncertainty_altitude,
		{ "Uncertainty Altitude","gsm_a.gad.uncertainty_altitude",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_confidence,
		{ "Confidence(%)","gsm_a.gad.confidence",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_no_of_points,
		{ "Number of points","gsm_a.gad.no_of_points",
		FT_UINT8,BASE_DEC, NULL, 0x0f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_D,
		{ "D: Direction of Altitude","gsm_a.gad.D",
		FT_UINT16,BASE_DEC, VALS(dir_of_alt_vals), 0x8000,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_altitude,
		{ "Altitude in meters","gsm_a.gad.altitude",
		FT_UINT16,BASE_DEC, NULL, 0x7fff,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_inner_radius,
		{ "Inner radius","gsm_a.gad.altitude",
		FT_UINT16,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_uncertainty_radius,
		{ "Uncertainty radius","gsm_a.gad.no_of_points",
		FT_UINT8,BASE_DEC, NULL, 0x7f,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_offset_angle,
		{ "Offset angle","gsm_a.gad.offset_angle",
		FT_UINT8,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_geo_loc_included_angle,
		{ "Included angle","gsm_a.gad.included_angle",
		FT_UINT8,BASE_DEC, NULL, 0x0,
		NULL, HFILL }
	},
	{ &hf_gsm_a_key_seq,
		{ "key sequence","gsm_a.key_seq",
		FT_UINT8,BASE_DEC, VALS(gsm_a_key_seq_vals), 0x07,
		NULL, HFILL }
	},
	{ &hf_gsm_a_lac,
		{ "Location Area Code (LAC)","gsm_a.lac",
		FT_UINT16, BASE_HEX_DEC, NULL, 0x00,
		NULL, HFILL }
	},
	};

	/* Setup protocol subtree array */
#define	NUM_INDIVIDUAL_ELEMS	0
	static gint *ett[NUM_INDIVIDUAL_ELEMS +
			NUM_GSM_COMMON_ELEM];

	last_offset = NUM_INDIVIDUAL_ELEMS;

	for (i=0; i < NUM_GSM_COMMON_ELEM; i++, last_offset++)
	{
		ett_gsm_common_elem[i] = -1;
		ett[last_offset] = &ett_gsm_common_elem[i];
	}

	/* Register the protocol name and description */

	proto_a_common =
	proto_register_protocol("GSM A-I/F COMMON", "GSM COMMON", "gsm_a_common");

	proto_register_field_array(proto_a_common, hf, array_length(hf));

	proto_register_subtree_array(ett, array_length(ett));

	gsm_a_tap = register_tap("gsm_a");
}


void
proto_reg_handoff_gsm_a_common(void)
{
}
