/* packet-gmr1_rr.c
 *
 * Routines for GMR-1 Radio Resource dissection in wireshark.
 * Copyright (c) 2011 Sylvain Munaut <tnt@246tNt.com>
 *
 * References:
 *  [1] ETSI TS 101 376-4-8 V1.3.1 - GMR-1 04.008
 *  [2] ETSI TS 101 376-4-8 V2.2.1 - GMPRS-1 04.008
 *  [3] ETSI TS 101 376-4-8 V3.1.1 - GMR-1 3G 44.008
 *  [4] ETSI TS 100 940 V7.21.0 - GSM 04.08
 *  [5] ETSI TS 101 376-4-12 V3.2.1 - GMR-1 3G 44.060
 *  [6] ETSI TS 101 376-5-6 V1.3.1 - GMR-1 05.008
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

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

#include "packet-gmr1_common.h"

#include "packet-gmr1_rr.h"

void proto_register_gmr1_rr(void);

/* GMR-1 RR and CCCH proto */
static int proto_gmr1_rr = -1;
static int proto_gmr1_ccch = -1;

/* Fallback CCCH sub tree */
static gint ett_msg_ccch = -1;

static gint ett_rr_pd = -1;

static expert_field ei_gmr1_missing_mandatory_element = EI_INIT;


/* ------------------------------------------------------------------------ */
/* RR Information Elements                                                  */
/* ------------------------------------------------------------------------ */

enum gmr1_ie_rr_idx {
	GMR1_IE_RR_CHAN_DESC = 0,		/* [1] 11.5.2.5   */
	GMR1_IE_RR_CHAN_MODE,			/* [1] 11.5.2.6   */
	GMR1_IE_RR_CIPH_MODE_SETTING,		/* [4] 10.5.2.9   */
	GMR1_IE_RR_CIPH_RESP,			/* [4] 10.5.2.10  */
	GMR1_IE_RR_L2_PSEUDO_LEN,		/* [1] 11.5.2.19  */
	GMR1_IE_RR_PAGE_MODE,			/* [1] 11.5.2.26  */
	GMR1_IE_RR_REQ_REF,			/* [1] 11.5.2.30  */
	GMR1_IE_RR_CAUSE,			/* [1] 11.5.2.31  */
	GMR1_IE_RR_TIMING_OFS,			/* [1] 11.5.2.40  */
	GMR1_IE_RR_TMSI_PTMSI,			/* [4] 10.5.2.42  */
	GMR1_IE_RR_WAIT_IND,			/* [4] 10.5.2.43  */
	GMR1_IE_RR_MES_INFO_FLG,		/* [1] 11.5.2.44  */
	GMR1_IE_RR_FREQ_OFS,			/* [1] 11.5.2.49  */
	GMR1_IE_RR_PAGE_INFO,			/* [1] 11.5.2.51  */
	GMR1_IE_RR_POS_DISPLAY,			/* [1] 11.5.2.52  */
	GMR1_IE_RR_POS_UPD_INFO,		/* [1] 11.5.2.54  */
	GMR1_IE_RR_BCCH_CARRIER,		/* [1] 11.5.2.55  */
	GMR1_IE_RR_REJECT_CAUSE,		/* [1] 11.5.2.56  */
	GMR1_IE_RR_GPS_TIMESTAMP,		/* [1] 11.5.2.57  */
	GMR1_IE_RR_PWR_CTRL_PRM,		/* [1] 11.5.2.60  */
	GMR1_IE_RR_TMSI_AVAIL_MSK,		/* [1] 11.5.2.62  */
	GMR1_IE_RR_GPS_ALMANAC,			/* [1] 11.5.2.63  */
	GMR1_IE_RR_MSC_ID,			/* [1] 11.5.2.100 */
	GMR1_IE_RR_GPS_DISCR,			/* [1] 11.5.2.101 */
	GMR1_IE_RR_PKT_IMM_ASS_3_PRM,		/* [3] 11.5.2.105 */
	GMR1_IE_RR_PKT_FREQ_PRM,		/* [3] 11.5.2.106 */
	GMR1_IE_RR_PKT_IMM_ASS_2_PRM,		/* [3] 11.5.2.107 */
	GMR1_IE_RR_USF,				/* [3] 11.5.2.110 */
	GMR1_IE_RR_TIMING_ADV_IDX,		/* [3] 10.1.18.3.4 */
	GMR1_IE_RR_TLLI,			/* [5] 12.16      */
	GMR1_IE_RR_PKT_PWR_CTRL_PRM,		/* [3] 10.1.18.3.3 */
	GMR1_IE_RR_PERSISTENCE_LVL,		/* [3] 10.1.18.4.2 */
	NUM_GMR1_IE_RR	/* Terminator */
};

static const value_string gmr1_ie_rr_strings[] = {
	{ GMR1_IE_RR_CHAN_DESC,
	  "Channel Description" },		/* [1] 11.5.2.5   */
	{ GMR1_IE_RR_CHAN_MODE,
	  "Channel Mode" },			/* [1] 11.5.2.6   */
	{ GMR1_IE_RR_CIPH_MODE_SETTING,
	  "Cipher Mode Setting" },		/* [4] 10.5.2.9   */
	{ GMR1_IE_RR_CIPH_RESP,
	  "Cipher Response" },			/* [4] 10.5.2.10  */
	{ GMR1_IE_RR_L2_PSEUDO_LEN,
	  "L2 Pseudo Length" },			/* [1] 11.5.2.19  */
	{ GMR1_IE_RR_PAGE_MODE,
	  "Page Mode" },			/* [1] 11.5.2.26  */
	{ GMR1_IE_RR_REQ_REF,
	  "Request Reference" },		/* [1] 11.5.2.30  */
	{ GMR1_IE_RR_CAUSE,
	  "RR Cause" },				/* [1] 11.5.2.31  */
	{ GMR1_IE_RR_TIMING_OFS,
	  "Timing Offset" },			/* [1] 11.5.2.40  */
	{ GMR1_IE_RR_TMSI_PTMSI,
	  "TMSI/P-TMSI" },			/* [4] 10.5.2.42  */
	{ GMR1_IE_RR_WAIT_IND,
	  "Wait Indication" },			/* [4] 10.5.2.43  */
	{ GMR1_IE_RR_MES_INFO_FLG,
	  "MES Information Flag" },		/* [1] 11.5.2.44  */
	{ GMR1_IE_RR_FREQ_OFS,
	  "Frequency Offset" },			/* [1] 11.5.2.49  */
	{ GMR1_IE_RR_PAGE_INFO,
	  "Paging Information" },		/* [1] 11.5.2.51  */
	{ GMR1_IE_RR_POS_DISPLAY,
	  "Position Display" },			/* [1] 11.5.2.52  */
	{ GMR1_IE_RR_POS_UPD_INFO,
	  "Position Update Information" },	/* [1] 11.5.2.54  */
	{ GMR1_IE_RR_BCCH_CARRIER,
	  "BCCH Carrier Specification"},	/* [1] 11.5.2.55  */
	{ GMR1_IE_RR_REJECT_CAUSE,
	  "Reject Cause" },			/* [1] 11.5.2.56  */
	{ GMR1_IE_RR_GPS_TIMESTAMP,
	  "GPS timestamp" },			/* [1] 11.5.2.57  */
	{ GMR1_IE_RR_PWR_CTRL_PRM,
	  "Power Control Params" },		/* [1] 11.5.2.60  */
	{ GMR1_IE_RR_TMSI_AVAIL_MSK,
	  "TMSI Availability Mask" },		/* [1] 11.5.2.62  */
	{ GMR1_IE_RR_GPS_ALMANAC,
	  "GPS Almanac Data" },			/* [1] 11.5.2.63  */
	{ GMR1_IE_RR_MSC_ID,
	  "MSC ID" },				/* [1] 11.5.2.100 */
	{ GMR1_IE_RR_GPS_DISCR,
	  "GPS Discriminator" },		/* [1] 11.5.2.101 */
	{ GMR1_IE_RR_PKT_IMM_ASS_3_PRM,
	  "Packet Imm. Ass. Type 3 Params" },	/* [3] 11.5.2.105 */
	{ GMR1_IE_RR_PKT_FREQ_PRM,
	  "Packet Frequency Parameters" },	/* [3] 11.5.2.106 */
	{ GMR1_IE_RR_PKT_IMM_ASS_2_PRM,
	  "Packet Imm. Ass. Type 2 Params" },	/* [3] 11.5.2.107 */
	{ GMR1_IE_RR_USF,
	  "USF" },				/* [3] 11.5.2.110 */
	{ GMR1_IE_RR_TIMING_ADV_IDX,
	  "Timing Advance Index" },		/* [3] 10.1.18.3.4 */
	{ GMR1_IE_RR_TLLI,
	  "TLLI" },				/* [5] 12.16      */
	{ GMR1_IE_RR_PKT_PWR_CTRL_PRM,
	  "Packet Power Control Params" },	/* [3] 10.1.18.3.3 */
	{ GMR1_IE_RR_PERSISTENCE_LVL,
	  "Persistence Level" },		/* [3] 10.1.18.4.2 */
	{ 0, NULL },
};
value_string_ext gmr1_ie_rr_strings_ext = VALUE_STRING_EXT_INIT(gmr1_ie_rr_strings);

gint ett_gmr1_ie_rr[NUM_GMR1_IE_RR];


/* Fields */
static int hf_rr_msg_type = -1;
static int hf_rr_chan_desc_kab_loc = -1;
static int hf_rr_chan_desc_rx_tn = -1;
static int hf_rr_chan_desc_arfcn = -1;
static int hf_rr_chan_desc_tx_tn = -1;
static int hf_rr_chan_desc_chan_type = -1;
static int hf_rr_chan_mode = -1;
static int hf_rr_ciph_mode_setting_sc = -1;
static int hf_rr_ciph_mode_setting_algo = -1;
static int hf_rr_ciph_resp_cr = -1;
static int hf_rr_ciph_resp_spare = -1;
static int hf_rr_l2_pseudo_len = -1;
static int hf_rr_page_mode = -1;
static int hf_rr_page_mode_spare = -1;
static int hf_rr_req_ref_est_cause = -1;
static int hf_rr_req_ref_ra = -1;
static int hf_rr_req_ref_fn = -1;
static int hf_rr_cause = -1;
static int hf_rr_timing_ofs_ti = -1;
static int hf_rr_timing_ofs_value = -1;
static int hf_rr_tmsi_ptmsi = -1;
static int hf_rr_wait_ind_timeout = -1;
static int hf_rr_mif_mes1_ab = -1;
static int hf_rr_mif_mes1_i = -1;
static int hf_rr_mif_mes1_d = -1;
static int hf_rr_mif_mes2 = -1;
static int hf_rr_mif_mes3 = -1;
static int hf_rr_mif_mes4 = -1;
static int hf_rr_mif_pv = -1;
static int hf_rr_freq_ofs_fi = -1;
static int hf_rr_freq_ofs_value = -1;
static int hf_rr_freq_ofs_spare = -1;
static int hf_rr_page_info_msc_id = -1;
static int hf_rr_page_info_chan_needed = -1;
static int hf_rr_pos_display_flag = -1;
static int hf_rr_pos_display_text = -1;
static int hf_rr_pos_upd_info_v = -1;
static int hf_rr_pos_upd_info_dist = -1;
static int hf_rr_pos_upd_info_time = -1;
static int hf_rr_bcch_carrier_arfcn = -1;
static int hf_rr_bcch_carrier_si = -1;
static int hf_rr_bcch_carrier_ri = -1;
static int hf_rr_bcch_carrier_spare = -1;
static int hf_rr_reject_cause = -1;
static int hf_rr_reject_cause_b = -1;
static int hf_rr_gps_timestamp = -1;
static int hf_rr_gps_power_control_params = -1;
static int hf_rr_tmsi_avail_msk_tmsi[4] = { -1, -1, -1, -1 };
static int hf_rr_gps_almanac_pn = -1;
static int hf_rr_gps_almanac_wn = -1;
static int hf_rr_gps_almanac_word = -1;
static int hf_rr_gps_almanac_sfn = -1;
static int hf_rr_gps_almanac_co = -1;
static int hf_rr_gps_almanac_spare = -1;
static int hf_rr_msc_id = -1;
static int hf_rr_msc_id_spare = -1;
static int hf_rr_gps_discr = -1;
static int hf_rr_pkt_imm_ass_3_prm_rlc_mode = -1;
static int hf_rr_pkt_imm_ass_3_prm_spare = -1;
static int hf_rr_pkt_imm_ass_3_prm_dl_tfi = -1;
static int hf_rr_pkt_imm_ass_3_prm_start_fn = -1;
static int hf_rr_pkt_imm_ass_3_prm_mac_slot_alloc = -1;
static int hf_rr_pkt_freq_prm_arfcn = -1;
static int hf_rr_pkt_freq_prm_dl_freq_plan_id = -1;
static int hf_rr_pkt_freq_prm_dl_bw = -1;
static int hf_rr_pkt_freq_prm_ul_freq_dist = -1;
static int hf_rr_pkt_freq_prm_ul_bw = -1;
static int hf_rr_pkt_freq_prm_spare = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_spare1 = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_final_alloc = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_usf_granularity = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_dl_ctl_mac_slot = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_mac_mode = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_start_fn = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_rlc_dblk_gnt = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_mcs = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_tfi = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_spare2 = -1;
static int hf_rr_pkt_imm_ass_2_prm_ac_mac_slot_alloc = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd_pnb512 = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_spare1 = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_rlc_dblk_gnt = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_spare2 = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_tfi = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_usf_granularity = -1;
static int hf_rr_pkt_imm_ass_2_prm_d_mac_slot_alloc = -1;
static int hf_rr_usf_value = -1;
static int hf_rr_usf_spare = -1;
static int hf_rr_timing_adv_idx_value = -1;
static int hf_rr_timing_adv_idx_spare = -1;
static int hf_rr_tlli = -1;
static int hf_rr_pkt_pwr_ctrl_prm_par = -1;
static int hf_rr_pkt_pwr_ctrl_prm_spare = -1;
static int hf_rr_persistence_lvl[4] = { -1, -1, -1, -1 };
static int hf_rr_protocol_discriminator = -1;
static int hf_rr_message_elements = -1;

/* Generic display vals/func */
static const value_string rr_gen_ie_presence_vals[] = {
	{ 0, "IE is absent" },
	{ 1, "IE is present" },
	{ 0, NULL }
};

static void
rr_gen_ie_seconds_fmt(gchar *s, guint32 v)
{
	snprintf(s, ITEM_LABEL_LENGTH, "%u seconds", v);
}



/* [1] 11.5.2.5 - Channel Description */
static const value_string rr_chan_desc_chan_type_vals[] = {
	{  1, "TCH3 No offset" },
	{  3, "TCH3 1/2 symbol offset" },
	{  6, "TCH6 No offset" },
	{  7, "TCH6 1/2 symbol offset" },
	{  4, "TCH9 No offset" },
	{  5, "TCH9 1/2 symbol offset" },
	{ 13, "Reserved for SDCCH frames xx00" },
	{ 14, "Reserved for SDCCH frames xx01" },
	{ 15, "Reserved for SDCCH frames xx10" },
	{ 16, "Reserved for SDCCH frames xx11" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_chan_desc)
{
	gint bit_offset;

	bit_offset = offset << 3;

	/* KAB Location (6 bits)*/
	proto_tree_add_bits_item(tree, hf_rr_chan_desc_kab_loc, tvb,
	                         bit_offset, 6, ENC_BIG_ENDIAN);
	bit_offset += 6;

	/* RX Timeslot (5 bits) */
	proto_tree_add_bits_item(tree, hf_rr_chan_desc_rx_tn, tvb,
	                         bit_offset, 5, ENC_BIG_ENDIAN);
	bit_offset += 5;

	/* ARFCN (11 bits) */
	proto_tree_add_bits_item(tree, hf_rr_chan_desc_arfcn, tvb,
	                         bit_offset, 11, ENC_BIG_ENDIAN);
	bit_offset += 11;

	/* TX Timeslot (5 bits) */
	proto_tree_add_bits_item(tree, hf_rr_chan_desc_tx_tn, tvb,
	                         bit_offset, 5, ENC_BIG_ENDIAN);
	bit_offset += 5;

	/* Channel Type (5 bits) */
	proto_tree_add_bits_item(tree, hf_rr_chan_desc_chan_type, tvb,
	                         bit_offset, 5, ENC_BIG_ENDIAN);
	/*bit_offset += 5;*/

	return 4;
}

/* [1] 11.5.2.6 - Channel Mode */
static const value_string rr_chan_mode_vals[] = {
	{ 0x00, "Signalling only" },
	{ 0x01, "Speech" },
	{ 0x03, "Data, 12,0 kbit/s radio I/F rate" },
	{ 0x0b, "Data, 6,0 kbit/s radio I/F rate" },
	{ 0x13, "Data, 3,6 kbit/s radio I/F rate" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_chan_mode)
{
	/* Channel Mode */
	proto_tree_add_item(tree, hf_rr_chan_mode, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [4] 10.5.2.9 - Cipher Mode Setting */
static const value_string rr_ciph_mode_setting_sc_vals[] = {
	{ 0, "No ciphering"},
	{ 1, "Start ciphering"},
	{ 0, NULL }
};

static const value_string rr_ciph_mode_setting_algo_vals[] = {
	{ 0, "A5/1" },
	{ 1, "A5/2" },
	{ 2, "A5/3" },
	{ 3, "A5/4" },
	{ 4, "A5/5" },
	{ 5, "A5/6" },
	{ 6, "A5/7" },
	{ 7, "Reserved" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_ciph_mode_setting)
{
	/* SC */
	proto_tree_add_item(tree, hf_rr_ciph_mode_setting_sc, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Algo */
	proto_tree_add_item(tree, hf_rr_ciph_mode_setting_algo, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [4] 10.5.2.10 - Cipher Response */
static const value_string rr_ciph_resp_cr_vals[] = {
	{ 0, "IMEISV shall not be included"},
	{ 1, "IMEISV shall be included"},
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_ciph_resp)
{
	/* CR */
	proto_tree_add_item(tree, hf_rr_ciph_resp_cr, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_ciph_resp_spare, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.19 - L2 Pseudo Length */
GMR1_IE_FUNC(gmr1_ie_rr_l2_pseudo_len)
{
	/* L2 Pseudo Length value */
	proto_tree_add_item(tree, hf_rr_l2_pseudo_len, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.26 - Page Mode */
static const value_string rr_page_mode_vals[] = {
	{ 0, "Normal Paging" },
	{ 1, "Reserved (Changed from Extended Paging in GSM)" },
	{ 2, "Paging Reorganization" },
	{ 3, "Same as before" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_page_mode)
{
	/* Page mode */
	proto_tree_add_item(tree, hf_rr_page_mode, tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_page_mode_spare, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.30 - Request Reference */
static const value_string rr_req_ref_est_cause_vals[] = {
	{ 0, "MO call" },
	{ 1, "In response to paging/alerting" },
	{ 2, "Location update/IMSI detach" },
	{ 3, "Emergency call" },
	{ 4, "Supplementary/short message service" },
	{ 5, "Position verification" },
	{ 6, "Any other valid cause" },
	{ 7, "Reserved" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_req_ref)
{
	/* Establishement Cause + RA */
	proto_tree_add_item(tree, hf_rr_req_ref_est_cause, tvb, offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_req_ref_ra, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset++;

	/* Frame number % 256 */
	proto_tree_add_item(tree, hf_rr_req_ref_fn, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 2;
}

/* [1] 11.5.2.31 - RR Cause */
static const value_string rr_cause_vals[] = {
	{ 0x00, "Normal event" },
	{ 0x01, "Abnormal release, unspecified" },
	{ 0x02, "Abnormal release, channel unacceptable" },
	{ 0x03, "Abnormal release, timer expired" },
	{ 0x04, "Abnormal release, no activity on the radio path" },
	{ 0x05, "Preemptive release" },
	{ 0x09, "Channel mode unacceptable" },
	{ 0x0a, "Frequency not implemented" },
	{ 0x0b, "Position unacceptable" },
	{ 0x41, "Call already cleared" },
	{ 0x5f, "Semantically incorrect message" },
	{ 0x60, "Invalid mandatory information" },
	{ 0x61, "Message type nonexistent or not implemented" },
	{ 0x62, "Message type not compatible with protocol state" },
	{ 0x6f, "Protocol error unspecified" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_cause)
{
	/* RR Cause */
	proto_tree_add_item(tree, hf_rr_cause, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.40 - Timing Offset */
static const value_string rr_timing_ofs_ti_vals[] = {
	{ 0, "The timing offset parameter in this IE to be ignored" },
	{ 1, "The timing offset parameter has a valid value" },
	{ 0, NULL }
};

static void
rr_timing_ofs_value_fmt(gchar *s, guint32 v)
{
	gint32 sv = (signed)v;

	snprintf(s, ITEM_LABEL_LENGTH, "%.3f symbols ( ~ %.3f ms )",
		sv / 40.0f, (sv / 40.0f) * (10.0f / 234.0f));
}

GMR1_IE_FUNC(gmr1_ie_rr_timing_ofs)
{
	gint bit_offset;

	bit_offset = offset << 3;

	/* TI */
	proto_tree_add_bits_item(tree, hf_rr_timing_ofs_ti, tvb,
	                         bit_offset, 1, ENC_BIG_ENDIAN);
	bit_offset++;

	/* Value */
	proto_tree_add_bits_item(tree, hf_rr_timing_ofs_value, tvb,
	                         bit_offset, 15, ENC_BIG_ENDIAN);
	/*bit_offset += 15;*/

	return 2;
}

/* [4] 10.5.2.42 - TMSI/P-TMSI */
GMR1_IE_FUNC(gmr1_ie_rr_tmsi_ptmsi)
{
	/* TMSI/P-TMSI value as hex */
	proto_tree_add_item(tree, hf_rr_tmsi_ptmsi, tvb, offset, 4, ENC_BIG_ENDIAN);

	return 4;
}

/* [4] 10.5.2.43 - Wait Indication */
GMR1_IE_FUNC(gmr1_ie_rr_wait_ind)
{
	/* Timeout value */
	proto_tree_add_item(tree, hf_rr_wait_ind_timeout, tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.44 - MES Information Flag */
static const value_string rr_mif_mes1_ab_vals[] = {
	{ 0, "Chan. Assigned: MES1 registered at selected GS" },
	{ 1, "Chan. Assigned: MES1 requires registration at selected GS" },
	{ 2, "Chan. Assigned; MES 1 Extended Channel Req. Reqd" },
	{ 3, "Pause Timer Indication" },
	{ 0, NULL }
};

static const value_string rr_mif_mes234_vals[] = {
	{ 0, "MES doesn't exists" },
	{ 1, "Pause Timer Ind for this MES" },
	{ 0, NULL }
};

static const value_string rr_mif_pv_vals[] = {
	{ 0, "Position Verification not requested" },
	{ 1, "MES1 shall send a Channel Request for Position Verification following the completion of the upcoming call" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_mes_info_flg)
{
	proto_tree_add_item(tree, hf_rr_mif_mes1_ab,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_mes1_i,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_mes1_d,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_mes2,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_mes3,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_mes4,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	proto_tree_add_item(tree, hf_rr_mif_pv,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.49 - Frequency Offset */
static const value_string rr_freq_ofs_fi_vals[] = {
	{ 0, "The frequency offset parameter in this IE to be ignored" },
	{ 1, "The frequency offset parameter has a valid value" },
	{ 0, NULL }
};

static void
rr_freq_ofs_value_fmt(gchar *s, guint32 v)
{
	gint32 sv = (signed)v;

	snprintf(s, ITEM_LABEL_LENGTH, "%d Hz", sv);
}

GMR1_IE_FUNC(gmr1_ie_rr_freq_ofs)
{
	gint bit_offset;

	bit_offset = offset << 3;

	/* FI */
	proto_tree_add_bits_item(tree, hf_rr_freq_ofs_fi, tvb,
	                         bit_offset, 1, ENC_BIG_ENDIAN);
	bit_offset++;

	/* Value */
	proto_tree_add_bits_item(tree, hf_rr_freq_ofs_value, tvb,
	                         bit_offset, 12, ENC_BIG_ENDIAN);
	bit_offset += 12;

	/* Spare */
	proto_tree_add_bits_item(tree, hf_rr_freq_ofs_spare, tvb,
	                         bit_offset, 3, ENC_BIG_ENDIAN);
	/*bit_offset += 3;*/

	return 2;
}

/* [1] 11.5.2.51 - Paging Information */
static const value_string rr_page_info_chan_needed_vals[] = {
	{ 0, "Any" },
	{ 1, "SDCCH" },
	{ 2, "TCH3" },
	{ 3, "PDCCH" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_page_info)
{
	/* MSC ID & Channe needed */
	proto_tree_add_item(tree, hf_rr_page_info_msc_id,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_page_info_chan_needed,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.52 - Position Display */
static const value_string rr_pos_display_flag_vals[] = {
	{ 0, "Position not available" },
	{ 1, "No position display service" },
	{ 2, "Use default 7-bit alphabet (GSM 03.38)" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_pos_display)
{
	const unsigned char *txt_raw;
	gchar *txt_packed, *txt_unpacked;
	tvbuff_t *txt_packed_tvb;
	int i;

	/* Flag */
	proto_tree_add_item(tree, hf_rr_pos_display_flag,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Get text in an aligned tvbuff */
	/* Do not use tvb_new_octet_aligned(), GSM 7bit packing bit parsing
	   goes from LSB to MSB so a trick is applied here for the last byte */
	txt_raw = tvb_get_ptr(tvb, offset, 11);
	txt_packed = (gchar*)wmem_alloc(pinfo->pool, 11);
	for (i=0; i<10; i++)
		txt_packed[i] = (txt_raw[i] << 4) | (txt_raw[i+1] >> 4);
	txt_packed[10] = txt_raw[10];
	txt_packed_tvb = tvb_new_real_data(txt_packed, 11, 11);

	/* Unpack text */
	txt_unpacked = tvb_get_ts_23_038_7bits_string_packed(pinfo->pool, txt_packed_tvb, 0, 12);
	tvb_free(txt_packed_tvb);

	/* Display it */
	proto_tree_add_string(tree, hf_rr_pos_display_text, tvb, offset, 11,
	                      txt_unpacked);

	return 11;
}

/* [1] 11.5.2.54 - Position Update Information */
static const value_string rr_pos_upd_info_v_vals[] = {
	{ 0, "Information in this IE is Invalid and should be ignored" },
	{ 1, "Information in this IE is Valid" },
	{ 0, NULL }
};

static void
rr_pos_upd_info_dist_fmt(gchar *s, guint32 v)
{
	snprintf(s, ITEM_LABEL_LENGTH, "%d km", v);
}

static void
rr_pos_upd_info_time_fmt(gchar *s, guint32 v)
{
	snprintf(s, ITEM_LABEL_LENGTH, "%d minutes", v);
}

GMR1_IE_FUNC(gmr1_ie_rr_pos_upd_info)
{
	gint curr_offset = offset;

	/* Valid & GPS Update Distance */
	proto_tree_add_item(tree, hf_rr_pos_upd_info_v,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_pos_upd_info_dist,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	curr_offset++;

	/* GPS Update Timer */
	proto_tree_add_item(tree, hf_rr_pos_upd_info_time,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	curr_offset++;

	return 2;
}

/* [1] 11.5.2.55 - BCCH Carrier */
static const value_string rr_bcch_carrier_si_vals[] = {
	{ 0, "BCCH carrier is on the same satellite" },
	{ 1, "BCCH carrier is on a different satellite" },
	{ 0, NULL }
};

static const value_string rr_bcch_carrier_ri_vals[] = {
	{ 0, "Spot beam reselection not needed; use the spot beam with given BCCH" },
	{ 1, "Spot beam reselection needed; use the BCCH for spot beam reselection" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_bcch_carrier)
{
	gint bit_offset;

	bit_offset = offset << 3;

	/* ARFCN */
	proto_tree_add_bits_item(tree, hf_rr_bcch_carrier_arfcn,
	                         tvb, bit_offset, 11, ENC_BIG_ENDIAN);
	bit_offset += 11;

	/* Sat ind */
	proto_tree_add_bits_item(tree, hf_rr_bcch_carrier_si,
	                         tvb, bit_offset, 1, ENC_BIG_ENDIAN);
	bit_offset++;

	/* Resel ind */
	proto_tree_add_bits_item(tree, hf_rr_bcch_carrier_ri,
	                         tvb, bit_offset, 1, ENC_BIG_ENDIAN);
	bit_offset++;

	/* Spare */
	proto_tree_add_bits_item(tree, hf_rr_bcch_carrier_spare,
	                         tvb, bit_offset, 3, ENC_BIG_ENDIAN);
	/*bit_offset += 3;*/

	return 2;
}

/* [1] 11.5.2.56 - Reject Cause */
static const value_string rr_reject_cause_vals[] = {
	{ 0x00, "Lack of resources (default)" },
	{ 0x11, "Invalid position for selected LAI" },
	{ 0x12, "Invalid position for selected spot beam" },
	{ 0x13, "Invalid position" },
	{ 0x15, "Position too old" },
	{ 0x16, "Invalid position for service provider" },
	{ 0x17, "Redirect to new satellite" },
	{ 0x3f, "Reported position acceptable" },
	{ 0, NULL }
};

GMR1_IE_FUNC(gmr1_ie_rr_reject_cause)
{
	/* Cause */
	proto_tree_add_item(tree, hf_rr_reject_cause,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* BCCH carrier */
	proto_tree_add_item(tree, hf_rr_reject_cause_b,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.57 - GPS timestamp */
static void
rr_gps_timestamp_fmt(gchar *s, guint32 v)
{
	if (v == 0xffff)
		snprintf(s, ITEM_LABEL_LENGTH, "> 65535 minutes or N/A");
	else
		snprintf(s, ITEM_LABEL_LENGTH, "%d minutes", v);
}

GMR1_IE_FUNC(gmr1_ie_rr_gps_timestamp)
{
	/* GPS timestamp */
	proto_tree_add_item(tree, hf_rr_gps_timestamp,
	                    tvb, offset, 2, ENC_BIG_ENDIAN);

	return 2;
}

/* [1] 11.5.2.60 - Power Control Params */
GMR1_IE_FUNC(gmr1_ie_rr_pwr_ctrl_prm)
{
	/* It's CSN1 encoded and we have no real world sample. Until we do,
	 * we don't pollute the code with a bunch of untested stuff ... */

	proto_tree_add_item(tree, hf_rr_gps_power_control_params, tvb, offset, 5, ENC_NA);

	return 5;
}

/* [1] 11.5.2.62 - Availability Mask */
GMR1_IE_FUNC(gmr1_ie_rr_tmsi_avail_msk)
{
	int i;

	for (i=0; i<4; i++)
		proto_tree_add_item(tree, hf_rr_tmsi_avail_msk_tmsi[i],
				    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.63 - GPS Almanac Data */
static void
rr_gps_almanac_pn_fmt(gchar *s, guint32 v)
{
	snprintf(s, ITEM_LABEL_LENGTH, "%d", v+1);
}

static const value_string rr_gps_almanac_sfn_vals[] = {
	{ 0, "Frame 4" },
	{ 1, "Frame 5" },
	{ 0, NULL }
};


GMR1_IE_FUNC(gmr1_ie_rr_gps_almanac)
{
	gint curr_offset = offset;

	/* Page Number & Word Number */
	proto_tree_add_item(tree, hf_rr_gps_almanac_pn,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_gps_almanac_wn,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	curr_offset++;

	/* Almanac Data */
	proto_tree_add_item(tree, hf_rr_gps_almanac_word,
	                    tvb, curr_offset, 3, ENC_BIG_ENDIAN);
	curr_offset += 3;

	/* SubFrame Number & CO & Spare */
	proto_tree_add_item(tree, hf_rr_gps_almanac_sfn,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_gps_almanac_co,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	proto_tree_add_item(tree, hf_rr_gps_almanac_spare,
	                    tvb, curr_offset, 1, ENC_BIG_ENDIAN);
	curr_offset++;

	return 5;
}

/* [1] 11.5.2.100 - MSC ID */
GMR1_IE_FUNC(gmr1_ie_rr_msc_id)
{
	/* MSC ID */
	proto_tree_add_item(tree, hf_rr_msc_id,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare bits */
	proto_tree_add_item(tree, hf_rr_msc_id_spare,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [1] 11.5.2.101 - GPS Discriminator */
GMR1_IE_FUNC(gmr1_ie_rr_gps_discr)
{
	/* GPS Position CRC value */
	proto_tree_add_item(tree, hf_rr_gps_discr, tvb, offset, 2, ENC_BIG_ENDIAN);

	return 2;
}

/* [3] 11.5.2.105 - Packet Imm. Ass. Type 3 Params */
static const value_string rr_pkt_imm_ass_3_prm_rlc_mode_vals[] = {
	{ 0, "RLC acknowledged mode" },
	{ 1, "RLC unacknowledged mode" },
	{ 0, NULL }
};

static const crumb_spec_t rr_pkt_imm_ass_3_prm_dl_tfi_crumbs[] = {
	{  0, 3 },
	{ 12, 4 },
	{  0, 0 }
};

GMR1_IE_FUNC(gmr1_ie_rr_pkt_imm_ass_3_prm)
{
	/* RLC Mode */
	proto_tree_add_item(tree, hf_rr_pkt_imm_ass_3_prm_rlc_mode,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_pkt_imm_ass_3_prm_spare,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Downlink Tempory Flow Identifier (TFI) */
	proto_tree_add_split_bits_item_ret_val(
		tree, hf_rr_pkt_imm_ass_3_prm_dl_tfi,
		tvb, offset << 3,
		rr_pkt_imm_ass_3_prm_dl_tfi_crumbs,
		NULL);

	/* Starting Frame Number */
	proto_tree_add_item(tree, hf_rr_pkt_imm_ass_3_prm_start_fn,
	                    tvb, offset+1, 1, ENC_BIG_ENDIAN);

	/* MAC Slot allocation */
	proto_tree_add_item(tree, hf_rr_pkt_imm_ass_3_prm_mac_slot_alloc,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	return 3;
}

/* [3] 11.5.2.106 - Packet Frequency Parameters */
static const value_string rr_pkt_freq_prm_dl_freq_plan_id_vals[] = {
	{ 0, "S-Band" },	/* Pretty much a guess ... */
	{ 1, "L-Band" },	/* didn't find exact value in specs */
	{ 0, NULL }
};

static const crumb_spec_t rr_pkt_freq_prm_arfcn_crumbs[] = {
	{  0, 8 },
	{ 13, 3 },
	{  0, 0 }
};

static const crumb_spec_t rr_pkt_freq_prm_ul_freq_dist_crumbs[] = {
	{  0, 1 },
	{ 12, 4 },
	{  0, 0 }
};

static void
rr_pkt_freq_prm_xx_bw_fmt(gchar *s, guint32 v)
{
	snprintf(s, ITEM_LABEL_LENGTH, "%d * 31.25 kHz = %.2f kHz (%d)", v, 31.25f*v, v);
}

GMR1_IE_FUNC(gmr1_ie_rr_pkt_freq_prm)
{
	/* ARFCN */
	proto_tree_add_split_bits_item_ret_val(
		tree, hf_rr_pkt_freq_prm_arfcn,
		tvb, offset << 3,
		rr_pkt_freq_prm_arfcn_crumbs,
		NULL);

	/* DL Freq plan ID */
	proto_tree_add_item(tree, hf_rr_pkt_freq_prm_dl_freq_plan_id,
	                    tvb, offset+1, 1, ENC_BIG_ENDIAN);

	/* DL bandwidth */
	proto_tree_add_item(tree, hf_rr_pkt_freq_prm_dl_bw,
	                    tvb, offset+1, 1, ENC_BIG_ENDIAN);

	/* UL Freq distance */
	proto_tree_add_split_bits_item_ret_val(
		tree, hf_rr_pkt_freq_prm_ul_freq_dist,
		tvb, (offset+1) << 3,
		rr_pkt_freq_prm_ul_freq_dist_crumbs,
		NULL);

	/* UL bandwidth */
	proto_tree_add_item(tree, hf_rr_pkt_freq_prm_ul_bw,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_pkt_freq_prm_spare,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	return 3;
}

/* [3] 11.5.2.107 - Packet Imm. Ass. Type 2 Params */
static const value_string rr_pkt_imm_ass_2_prm_ac_mac_mode_vals[] = {
	{ 0, "Dynamic allocation" },
	{ 1, "Reserved" },
	{ 2, "Reserved" },
	{ 3, "Reserved" },
	{ 0, NULL }
};

static const crumb_spec_t rr_pkt_imm_ass_2_prm_ac_rlc_dblk_gnt_crumbs[] = {
	{  0, 4 },
	{ 13, 3 },
	{  0, 0 }
};

GMR1_IE_FUNC(gmr1_ie_rr_pkt_imm_ass_2_prm)
{
	proto_tree *subtree_ac, *subtree_d;


	/* Terminal AC */
	/* ----------- */

	subtree_ac = proto_tree_add_subtree(tree, tvb, offset, 5,
			ett_gmr1_ie_rr[GMR1_IE_RR_PKT_IMM_ASS_2_PRM], NULL, "GMPRS Terminal type A or C");

	/* Spare */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_spare1,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Final Allocation */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_final_alloc,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* USF Granularity */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_usf_granularity,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Downlink Control MAC slot */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_dl_ctl_mac_slot,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* MAC Mode */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_mac_mode,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Starting Frame Number */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_start_fn,
	                    tvb, offset+1, 1, ENC_BIG_ENDIAN);

	/* RLC Data Blocks Granted */
	proto_tree_add_split_bits_item_ret_val(
		tree, hf_rr_pkt_imm_ass_2_prm_ac_rlc_dblk_gnt,
		tvb, (offset+1) << 3,
		rr_pkt_imm_ass_2_prm_ac_rlc_dblk_gnt_crumbs,
		NULL);

	/* MCS */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_mcs,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	/* TFI */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_tfi,
	                    tvb, offset+3, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_spare2,
	                    tvb, offset+3, 1, ENC_BIG_ENDIAN);

	/* MAC Slot allocation */
	proto_tree_add_item(subtree_ac, hf_rr_pkt_imm_ass_2_prm_ac_mac_slot_alloc,
	                    tvb, offset+4, 1, ENC_BIG_ENDIAN);

	/* Terminal D */
	/* ---------- */

	subtree_d = proto_tree_add_subtree(tree, tvb, offset, 5,
			ett_gmr1_ie_rr[GMR1_IE_RR_PKT_IMM_ASS_2_PRM], NULL, "GMPRS Terminal type D");

	/* Channel MCS command */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Channel MCS command PNB(5,12) */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd_pnb512,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_spare1,
	                    tvb, offset+1, 1, ENC_BIG_ENDIAN);

	/* RLC Data Blocks Granted */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_rlc_dblk_gnt,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_spare2,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	/* TFI */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_tfi,
	                    tvb, offset+3, 1, ENC_BIG_ENDIAN);

	/* USF Granularity */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_usf_granularity,
	                    tvb, offset+3, 1, ENC_BIG_ENDIAN);

	/* MAC Slot allocation */
	proto_tree_add_item(subtree_d, hf_rr_pkt_imm_ass_2_prm_d_mac_slot_alloc,
	                    tvb, offset+4, 1, ENC_BIG_ENDIAN);


	return 5;
}

/* [3] 11.5.2.110 - USF */
GMR1_IE_FUNC(gmr1_ie_rr_usf)
{
	/* Spare */
	proto_tree_add_item(tree, hf_rr_usf_spare,
	                    tvb, offset, 3, ENC_BIG_ENDIAN);

	/* USF */
	proto_tree_add_item(tree, hf_rr_usf_value,
	                    tvb, offset+2, 1, ENC_BIG_ENDIAN);

	return 3;
}

/* [3] 10.1.18.3.4 & [5] 12.29 - Timing Advance Index */
GMR1_IE_FUNC(gmr1_ie_rr_timing_adv_idx)
{
	/* TAI */
	proto_tree_add_item(tree, hf_rr_timing_adv_idx_value,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_timing_adv_idx_spare,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [5] 12.16 - TLLI */
GMR1_IE_FUNC(gmr1_ie_rr_tlli)
{
	/* TLLI value as hex */
	proto_tree_add_item(tree, hf_rr_tlli, tvb, offset, 4, ENC_BIG_ENDIAN);

	return 4;
}

/* [3] 10.1.18.3.3 & [5] 10.4.10a & [6] 5.3.3 - Packet Power Control Params */
static void
rr_pkt_pwr_ctrl_prm_par_fmt(gchar *s, guint32 v)
{
	if (v >= 61) {
		snprintf(s, ITEM_LABEL_LENGTH, "Escape %d (%d)", v-60, v);
		return;
	}

	snprintf(s, ITEM_LABEL_LENGTH, "%.1f dB (%d)", v*0.4f, v);
}

GMR1_IE_FUNC(gmr1_ie_rr_pkt_pwr_ctrl_prm)
{
	/* Power Attenuation Request (PAR) */
	proto_tree_add_item(tree, hf_rr_pkt_pwr_ctrl_prm_par,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	/* Spare */
	proto_tree_add_item(tree, hf_rr_pkt_pwr_ctrl_prm_spare,
	                    tvb, offset, 1, ENC_BIG_ENDIAN);

	return 1;
}

/* [3] 10.1.18.4.2 & [5] 12.14 - Persistence Level */
GMR1_IE_FUNC(gmr1_ie_rr_persistence_lvl)
{
	int i;

	for (i=0; i<4; i++)
		proto_tree_add_item(tree, hf_rr_persistence_lvl[i],
				    tvb, offset + (i>>1), 1, ENC_BIG_ENDIAN);

	return 2;
}


elem_fcn gmr1_ie_rr_func[NUM_GMR1_IE_RR] = {
	gmr1_ie_rr_chan_desc,		/* Channel Description */
	gmr1_ie_rr_chan_mode,		/* Channel Mode */
	gmr1_ie_rr_ciph_mode_setting,	/* Cipher Mode Setting */
	gmr1_ie_rr_ciph_resp,		/* Cipher Response */
	gmr1_ie_rr_l2_pseudo_len,	/* L2 Pseudo Length */
	gmr1_ie_rr_page_mode,		/* Page Mode */
	gmr1_ie_rr_req_ref,		/* Request Reference */
	gmr1_ie_rr_cause,		/* RR Cause */
	gmr1_ie_rr_timing_ofs,		/* Timing Offset */
	gmr1_ie_rr_tmsi_ptmsi,		/* TMSI/P-TMSI */
	gmr1_ie_rr_wait_ind,		/* Wait Indication */
	gmr1_ie_rr_mes_info_flg,	/* MES Information Flag */
	gmr1_ie_rr_freq_ofs,		/* Frequency Offset */
	gmr1_ie_rr_page_info,		/* Paging Information */
	gmr1_ie_rr_pos_display,		/* Position Display */
	gmr1_ie_rr_pos_upd_info,	/* Position Update Information */
	gmr1_ie_rr_bcch_carrier,	/* BCCH Carrier */
	gmr1_ie_rr_reject_cause,	/* Reject Cause */
	gmr1_ie_rr_gps_timestamp,	/* GPS timestamp */
	gmr1_ie_rr_pwr_ctrl_prm,	/* Power Control Params */
	gmr1_ie_rr_tmsi_avail_msk,	/* TMSI Availability Mask */
	gmr1_ie_rr_gps_almanac,		/* GPS Almanac Data */
	gmr1_ie_rr_msc_id,		/* MSC ID */
	gmr1_ie_rr_gps_discr,		/* GPS Discriminator */
	gmr1_ie_rr_pkt_imm_ass_3_prm,	/* Packet Imm. Ass. Type 3 Params */
	gmr1_ie_rr_pkt_freq_prm,	/* Packet Frequency Parameters */
	gmr1_ie_rr_pkt_imm_ass_2_prm,	/* Packet Imm. Ass. Type 2 Params */
	gmr1_ie_rr_usf,			/* USF */
	gmr1_ie_rr_timing_adv_idx,	/* Timing Advance Index */
	gmr1_ie_rr_tlli,		/* TLLI */
	gmr1_ie_rr_pkt_pwr_ctrl_prm,	/* Packet Power Control Params */
	gmr1_ie_rr_persistence_lvl,	/* Persistence Level */
};


/* ------------------------------------------------------------------------ */
/* RR Messages                                                              */
/* ------------------------------------------------------------------------ */

/* [1] 10.1.18 - Immediate Assignment */
GMR1_MSG_FUNC(gmr1_rr_msg_imm_ass)
{
	guint8 mif;

	GMR1_MSG_FUNC_BEGIN

	/* MES Information Flag			[1] 11.5.2.44	- M V 1 */
	mif = tvb_get_guint8(tvb, curr_offset);

	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_MES_INFO_FLG, NULL, ei_gmr1_missing_mandatory_element);

	/* Request Reference 1 (MES1)		[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES1", ei_gmr1_missing_mandatory_element);

	/* GPS Discriminator			[1] 11.5.2.101	- C V 2 */
	if ((mif & 0x03) != 0x02) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_DISCR, " - MES1", ei_gmr1_missing_mandatory_element);
	}

	/* Channel Description			[1] 11.5.2.5	- C V 4 */
	if ((mif & 0x03) != 0x03) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_DESC, " - MES1", ei_gmr1_missing_mandatory_element);
	}

	/* Timing Offset			[1] 11.5.2.40	- C V 2 */
	if ((mif & 0x03) != 0x03) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TIMING_OFS, " - MES1", ei_gmr1_missing_mandatory_element);
	}

	/* Frequency Offset			[1] 11.5.2.49	- C V 2 */
	if ((mif & 0x03) != 0x03) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_FREQ_OFS, " - MES1", ei_gmr1_missing_mandatory_element);
	}

	/* Idle Mode Pos. Upd. Info.		[1] 11.5.2.54	- C V 2 */
	if (mif & 0x04) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_POS_UPD_INFO, " - Idle Mode", ei_gmr1_missing_mandatory_element);
	}

	/* Ded. Mode Pos. Upd. Info.		[1] 11.5.2.54	- C V 2 */
	if (mif & 0x08) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_POS_UPD_INFO, " - Dedicated Mode", ei_gmr1_missing_mandatory_element);
	}

	/* Request Reference 2 (MES2)		[1] 11.5.2.30	- C V 2 */
	if (mif & 0x10) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES2", ei_gmr1_missing_mandatory_element);
	}

	/* Request Reference 3 (MES3)		[1] 11.5.2.30	- C V 2 */
	if (mif & 0x20) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES3", ei_gmr1_missing_mandatory_element);
	}

	/* Request Reference 4 (MES4)		[1] 11.5.2.30	- C V 2 */
	if (mif & 0x40) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES4", ei_gmr1_missing_mandatory_element);
	}

	/* IA Rest Octets			[1] 11.5.2.16	- M V 0..18 */
		/* FIXME */

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.20.1 - Immediate Assignment Reject Type 1 */
GMR1_MSG_FUNC(gmr1_rr_msg_imm_ass_rej_1)
{
	guint8 rej_cause;

	GMR1_MSG_FUNC_BEGIN

	/* Request Reference 1 (MES1) 		[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES1", ei_gmr1_missing_mandatory_element);

	/* GPS Discriminator			[1] 11.5.2.101	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_DISCR, NULL, ei_gmr1_missing_mandatory_element);

	/* Reject Cause				[1] 11.5.2.56	- M V 1 */
	rej_cause = tvb_get_guint8(tvb, curr_offset);

	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REJECT_CAUSE, NULL, ei_gmr1_missing_mandatory_element);

	/* Wait Indication 1 (MES1)		[4] 10.5.2.43	- C V 1 */
	if ((rej_cause & 0xfc) == 0x00) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_WAIT_IND, " - MES1", ei_gmr1_missing_mandatory_element);
	}

	/* Request Reference 2 (MES2) 		[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES2", ei_gmr1_missing_mandatory_element);

	/* Wait Indication 2 (MES2)		[4] 10.5.2.43	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_WAIT_IND, " - MES2", ei_gmr1_missing_mandatory_element);

	/* Request Reference 3 (MES3)		[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES3", ei_gmr1_missing_mandatory_element);

	/* Wait Indication 3 (MES3)		[4] 10.5.2.43	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_WAIT_IND, " - MES3", ei_gmr1_missing_mandatory_element);

	/* Request Reference 4 (MES4)		[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, " - MES4", ei_gmr1_missing_mandatory_element);

	/* Wait Indication 4 (MES4)		[4] 10.5.2.43	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_WAIT_IND, " - MES4", ei_gmr1_missing_mandatory_element);

	/* Idle Mode Position Update Info.	[1] 11.5.2.54	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_POS_UPD_INFO, " - Idle Mode", ei_gmr1_missing_mandatory_element);

	/* BCCH Carrier Specification		[1] 11.5.2.55	- C V 2 */
	if (rej_cause & 1) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_BCCH_CARRIER, NULL, ei_gmr1_missing_mandatory_element);
	}

	/* MSC ID				[1] 11.5.2.100	- C V 1 */
	if ((rej_cause & 0xfc) == 0x5c) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_MSC_ID, NULL, ei_gmr1_missing_mandatory_element);
	}

	/* IAR Rest Octets			[1] 11.5.2.17	- M V 1..4 */
		/* FIXME */

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.20.4 - Position Verification Notify */
GMR1_MSG_FUNC(gmr1_rr_msg_pos_verif_notify)
{
	GMR1_MSG_FUNC_BEGIN

	/* Request Reference 			[1] 11.5.2.30	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_REQ_REF, NULL, ei_gmr1_missing_mandatory_element);

	/* GPS Discriminator			[1] 11.5.2.101	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_DISCR, NULL, ei_gmr1_missing_mandatory_element);

	/* Position Display 			[1] 11.5.2.52	- M V 11 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_POS_DISPLAY, NULL, ei_gmr1_missing_mandatory_element);

	/* 78 Idle Mode Position Update Info.	[1] 11.5.2.54	- O TV 3 */
	ELEM_OPT_TV(0x78, GMR1_IE_RR, GMR1_IE_RR_POS_UPD_INFO, NULL);

	/* IAR Rest Octets			[1] 11.5.2.17	- M V 3..6 */
		/* FIXME */

	GMR1_MSG_FUNC_END
}

/* [3] 10.1.18.3 - Immediate Assignment Type 2 */
GMR1_MSG_FUNC(gmr1_rr_msg_imm_ass_2)
{
	GMR1_MSG_FUNC_BEGIN

	/* USF					[3] 11.5.2.110	- M V 3 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_USF, NULL, ei_gmr1_missing_mandatory_element);

	/* Timing Advance Index			[3] 10.1.18.3.4	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TIMING_ADV_IDX, NULL, ei_gmr1_missing_mandatory_element);

	/* TLLI					[5] 12.16	- M V 4 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TLLI, NULL, ei_gmr1_missing_mandatory_element);

	/* Timing Offset			[1] 11.5.2.40	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TIMING_OFS, NULL, ei_gmr1_missing_mandatory_element);

	/* Frequency Offset			[1] 11.5.2.49	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_FREQ_OFS, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Imm. Ass. Type 2 Params.	[3] 11.5.2.107	- M V 5 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_IMM_ASS_2_PRM, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Frequency Parameters		[3] 11.5.2.106	- M V 3 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_FREQ_PRM, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Power Control Parameters	[3] 10.1.18.3.3	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_PWR_CTRL_PRM, NULL, ei_gmr1_missing_mandatory_element);

	GMR1_MSG_FUNC_END
}

/* [3] 10.1.18.4 - Immediate Assignment Type 3 */
GMR1_MSG_FUNC(gmr1_rr_msg_imm_ass_3)
{
	GMR1_MSG_FUNC_BEGIN

	/* Page Mode				[1] 11.5.2.26	- M V 1/2 */
	/* Spare Half Octet			[1] 11.5.1.8	- M V 1/2 */
	ELEM_MAND_VV_SHORT(GMR1_IE_RR, GMR1_IE_RR_PAGE_MODE,
	                   GMR1_IE_COMMON, GMR1_IE_COM_SPARE_NIBBLE, ei_gmr1_missing_mandatory_element);

	/* Persistence Level			[3] 10.1.18.4.2	- M V 2 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PERSISTENCE_LVL, NULL, ei_gmr1_missing_mandatory_element);

	/* Timing Advance Index			[3] 10.1.18.3.4	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TIMING_ADV_IDX, NULL, ei_gmr1_missing_mandatory_element);

	/* TLLI					[5] 12.16	- M V 4 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TLLI, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Imm. Ass. Type 3 Params	[3] 11.5.2.105	- M V 3 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_IMM_ASS_3_PRM, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Frequency Parameters		[3] 11.5.2.106	- M V 3 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_FREQ_PRM, NULL, ei_gmr1_missing_mandatory_element);

	/* Packet Power Control Parameters	[3] 10.1.18.3.3	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PKT_PWR_CTRL_PRM, NULL, ei_gmr1_missing_mandatory_element);

	/* P1 Rest Octets			[1] 11.5.2.23	- M V 6 */
		/* FIXME */

	GMR1_MSG_FUNC_END
}

/* [3] 10.1.9 - Ciphering Mode Command */
GMR1_MSG_FUNC(gmr1_rr_msg_ciph_mode_cmd)
{
	GMR1_MSG_FUNC_BEGIN

	/* Cipher Mode Setting			[4] 10.5.2.9	- M V 1/2 */
	/* Cipher Response			[4] 10.5.2.10	- M V 1/2 */
	ELEM_MAND_VV_SHORT(GMR1_IE_RR, GMR1_IE_RR_CIPH_MODE_SETTING,
	                   GMR1_IE_RR, GMR1_IE_RR_CIPH_RESP, ei_gmr1_missing_mandatory_element);

	/* 75  Position Display			[1] 11.5.2.52	- O TV 12 */
	ELEM_OPT_TV(0x75, GMR1_IE_RR, GMR1_IE_RR_POS_DISPLAY, NULL);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.10 - Ciphering Mode Complete */
GMR1_MSG_FUNC(gmr1_rr_msg_ciph_mode_complete)
{
	GMR1_MSG_FUNC_BEGIN

	/* 17  Mobile Identity			[1] 11.5.1.4	- O TLV 3-11 */
	ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, NULL);

	/* 76  GPS Timestamp			[1] 11.5.2.57	- O TV 3 */
	ELEM_OPT_TV(0x76, GMR1_IE_RR, GMR1_IE_RR_GPS_TIMESTAMP, NULL);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.2.1 - Assignment Command 1 */
GMR1_MSG_FUNC(gmr1_rr_msg_ass_cmd_1)
{
	GMR1_MSG_FUNC_BEGIN

	/* Channel Description			[1] 11.5.2.5	- M V 4 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_DESC, NULL, ei_gmr1_missing_mandatory_element);

	/* 7D  Timing Offset			[1] 11.5.2.40	- O TV 3 */
	ELEM_OPT_TV(0x7D, GMR1_IE_RR, GMR1_IE_RR_TIMING_OFS, NULL);

	/* 7F  Frequency Offset			[1] 11.5.2.49	- O TV 3 */
	ELEM_OPT_TV(0x7F, GMR1_IE_RR, GMR1_IE_RR_FREQ_OFS, NULL);

	/* 63  Channel Mode			[1] 11.5.2.6	- O TV 2 */
	ELEM_OPT_TV(0x63, GMR1_IE_RR, GMR1_IE_RR_CHAN_MODE, NULL);

	/* 71  Power Control Parameters		[1] 11.5.2.60	- O TV 6 */
	ELEM_OPT_TV(0x71, GMR1_IE_RR, GMR1_IE_RR_PWR_CTRL_PRM, NULL);

	/* 9-  Cipher Mode Setting		[4] 10.5.2.9	- O TV 1 */
	ELEM_OPT_TV_SHORT(0x90, GMR1_IE_RR, GMR1_IE_RR_CIPH_MODE_SETTING, NULL);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.7 - Channel Release */
GMR1_MSG_FUNC(gmr1_rr_msg_chan_release)
{
	GMR1_MSG_FUNC_BEGIN

	/* RR Cause				[1] 11.5.2.31	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CAUSE, NULL, ei_gmr1_missing_mandatory_element);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.24 - Paging Request Type 3 */
GMR1_MSG_FUNC(gmr1_rr_msg_pag_req_3)
{
	guint8 tam;

	GMR1_MSG_FUNC_BEGIN

	/* Page Mode				[1] 11.5.2.26	- M V 1/2 */
	/* TMSI Availability Mask		[1] 11.5.2.62	- M V 1/2 */
	tam = (tvb_get_guint8(tvb, curr_offset) & 0xf0) >> 4;

	ELEM_MAND_VV_SHORT(GMR1_IE_RR, GMR1_IE_RR_PAGE_MODE,
	                   GMR1_IE_RR, GMR1_IE_RR_TMSI_AVAIL_MSK, ei_gmr1_missing_mandatory_element);

	/* Mobile Identity 1 (TMSI)		[4] 10.5.2.42	- C V 4 */
	if (tam & 0x01) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TMSI_PTMSI, " - 1", ei_gmr1_missing_mandatory_element);
	}

	/* GPS Almanac Data 1			[1] 11.5.2.63	- C V 5 */
	if (!(tam & 0x01)) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_ALMANAC, " - 1", ei_gmr1_missing_mandatory_element);
	}

	/* Mobile Identity 2 (TMSI)		[4] 10.5.2.42	- C V 4 */
	if (tam & 0x02) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TMSI_PTMSI, " - 2", ei_gmr1_missing_mandatory_element);
	}

	/* GPS Almanac Data 2			[1] 11.5.2.63	- C V 5 */
	if (!(tam & 0x02)) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_ALMANAC, " - 2", ei_gmr1_missing_mandatory_element);
	}

	/* Mobile Identity 3 (TMSI)		[4] 10.5.2.42	- C V 4 */
	if (tam & 0x04) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TMSI_PTMSI, " - 3", ei_gmr1_missing_mandatory_element);
	}

	/* GPS Almanac Data 3			[1] 11.5.2.63	- C V 5 */
	if (!(tam & 0x04)) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_ALMANAC, " - 3", ei_gmr1_missing_mandatory_element);
	}

	/* Mobile Identity 4 (TMSI)		[4] 10.5.2.42	- C V 4 */
	if (tam & 0x08) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_TMSI_PTMSI, " - 4", ei_gmr1_missing_mandatory_element);
	}

	/* GPS Almanac Data 4			[1] 11.5.2.63	- C V 5 */
	if (!(tam & 0x08)) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_GPS_ALMANAC, " - 4", ei_gmr1_missing_mandatory_element);
	}

	/* Paging Information 1			[1] 11.5.2.51	- C V 1 */
	if (tam & 0x01) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PAGE_INFO, " - 1", ei_gmr1_missing_mandatory_element);
	}

	/* Paging Information 2			[1] 11.5.2.51	- C V 1 */
	if (tam & 0x02) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PAGE_INFO, " - 2", ei_gmr1_missing_mandatory_element);
	}

	/* Paging Information 3			[1] 11.5.2.51	- C V 1 */
	if (tam & 0x04) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PAGE_INFO, " - 3", ei_gmr1_missing_mandatory_element);
	}

	/* Paging Information 4			[1] 11.5.2.51	- C V 1 */
	if (tam & 0x08) {
		ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_PAGE_INFO, " - 4", ei_gmr1_missing_mandatory_element);
	}

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.25 - Paging Response */
GMR1_MSG_FUNC(gmr1_rr_msg_pag_resp)
{
	GMR1_MSG_FUNC_BEGIN

	/* Ciphering Key Sequence Number	[4] 10.5.1.2	- M V 1/2 */
	/* Spare Half Octet			[1] 11.5.1.8	- M V 1/2 */
	ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_COMMON, DE_CIPH_KEY_SEQ_NUM,
	                   GMR1_IE_COMMON, GMR1_IE_COM_SPARE_NIBBLE, ei_gmr1_missing_mandatory_element);

	/* Mobile Earth Station Classmark 2	[1] 11.5.1.6	- M L V 4 */
	ELEM_MAND_LV(GMR1_IE_COMMON, GMR1_IE_COM_CM2, NULL, ei_gmr1_missing_mandatory_element);

	/* Mobile Identity			[4] 10.5.1.4	- M L V 2-9 */
	ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MID, NULL, ei_gmr1_missing_mandatory_element);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.5 - Channel Mode Modify */
GMR1_MSG_FUNC(gmr1_rr_msg_chan_mode_modify)
{
	GMR1_MSG_FUNC_BEGIN

	/* Channel Description			[1] 11.5.2.5	- M V 4 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_DESC, NULL, ei_gmr1_missing_mandatory_element);

	/* Channel Mode				[1] 11.5.2.6	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_MODE, NULL, ei_gmr1_missing_mandatory_element);

	GMR1_MSG_FUNC_END
}

/* [1] 10.1.6 - Channel Mode Modify Acknowledge */
GMR1_MSG_FUNC(gmr1_rr_msg_chan_mode_mod_ack)
{
	GMR1_MSG_FUNC_BEGIN

	/* Channel Description			[1] 11.5.2.5	- M V 4 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_DESC, NULL, ei_gmr1_missing_mandatory_element);

	/* Channel Mode				[1] 11.5.2.6	- M V 1 */
	ELEM_MAND_V(GMR1_IE_RR, GMR1_IE_RR_CHAN_MODE, NULL, ei_gmr1_missing_mandatory_element);

	GMR1_MSG_FUNC_END
}


/* See [3] 11.4.1 - Table 11.1 */
static const value_string gmr1_msg_rr_strings[] = {
	/* Channel establishment messages */
	{ 0x3f, "Immediate Assignment" },
	{ 0x3a, "Immediate Assignment Reject Type 1" },
	{ 0x3b, "Immediate Assignment Reject Type 2" },
	/*
	 * XXX - [3] 11.4.1 - Table 11.1 ([3] is ETSI TS 101 376-4-8
	 * V3.1.1, which is GMR-1 3G 44.008) has 0x3e meaning both
	 * "Extended Immediate Assignment" and "Immediate Assignment Type 2"
	 * and has 0x3b meaning both "Immediate Asignment Reject Type 2"
	 * and "Extended Imm. Assignment Reject".
	 *
	 * [3] makes some references to [1] ([1] is ETSI TS 101 376-4-8
	 * V1.3.1, which is GMR-1 04.008, not to be confused with GMR-1
	 * 3G 44.008), saying some messages are the same as in GMR-1
	 * 04.008.  [1]:
	 *
	 *    says, in 10.1.18.2, that "Extended immediate assignment"
	 *    "is sent on the main signalling link by the network to
	 *    the MES in response to an EXTENDED CHANNEL REQUEST message
	 *    by the MES";
	 *
	 *    says, in 10.1.20.2, that "Immediate assignment reject type 2"
	 *    "may be sent to the MES by the network on the CCCH to indicate
	 *    that no channel is available for assignment or that the MES
	 *    cannot be allowed access";
	 *
	 *    says, in 10.1.20.3, that "Extended immediate assignment
	 *    reject" "message is sent by the network on the DCCH to
	 *    indicate that no channel is available for assignment or
	 *    that the MES cannot be allowed access";
	 *
	 *    does not mention "Immediate assignment type 2" anywhere
	 *    that I can see.
	 *
	 * [3]:
	 *
	 *    says, in 10.1.18.2, that "Extended immediate assignment
	 *    (A/Gb mode only)" is "Same as clause 10.1.18.2 of
	 *    GMR-1 04.008";
	 *
	 *    says, in 10.1.18.3, that "Intermediate assignment Type
	 *    2 (A/Gb mode only)" "is sent on the CCCH by the network
	 *    to the MES to change the channel configuration to a
	 *    dedicated configuration while staying in the same cell";
	 *
	 *    says, in 10.1.20.2, that "Immediate assignment reject
	 *    type 2" is "Same as clause 10.1.20.2 of GMR-1 04.008";
	 *
	 *    says, in 10.1.20.3, that "Extended immediate assignment
	 *    reject" is "Same as clause 10.1.20.3 of GMR-1 04.008".
	 *
	 * This is currently handled by ORing 0x100 into the values
	 * for the DCCH, and, in gmr1_get_msg_rr_params(), if the dcch
	 * parameter is set (which it is if called from gmr1_get_msg_params()
	 * in packet-gmr1_common.c), 0x100 is ORed into the value
	 * passed to try_val_to_str_idx().  If that returns NULL, or
	 * isn't called in the first placee, it calls try_val_to_str_idx()
	 * without ORing in 0x100.
	 *
	 * So that's why a 1-byte field has, in a value_string used with
	 * it, two values that don't fit into 1 byte.
	 *
	 * It Would Be Nice if this could be done in a somewhat less
	 * hackish and opaque fashion.
	 */
	{ 0x13e, "Extended Immediate Assignment" },
	{ 0x13b, "Extended Imm. Assignment Reject" },
	{ 0x39, "Position Verification Notify" },
	{ 0x3c, "Immediate Assignment Reject Type 3" },
	{ 0x3e, "Immediate Assignment Type 2" },
	{ 0x3d, "Immediate Assignment Type 3" },

	/* Ciphering messages */
	{ 0x35, "Ciphering Mode Command" },
	{ 0x32, "Ciphering Mode Complete" },

	/* Channel assignment/handover messages */
	{ 0x2e, "Assignment Command 1" },
	{ 0x2a, "Assignment Command 2" },
	{ 0x29, "Assignment Complete" },
	{ 0x2f, "Assignment Failure" },
	{ 0x2b, "Handover Command" },
	{ 0x2c, "Handover Complete" },

	/* Channel release messages */
	{ 0x0d, "Channel Release" },
	{ 0x0e, "TtT Signalling Link Failure" },

	/* Paging messages */
	{ 0x21, "Paging Request Type 1" },
	{ 0x22, "Paging Request Type 2" },
	{ 0x24, "Paging Request Type 3" },
	{ 0x27, "Paging Response" },

	/* Miscellaneous messages */
	{ 0x10, "Channel Mode Modify" },
	{ 0x12, "RR Status" },
	{ 0x17, "Channel Mode Modify Acknowledge" },
	{ 0x16, "Classmark Change" },
	{ 0x13, "Classmark Enquiry" },
	{ 0x14, "Position Update Request" },
	{ 0x15, "Position Update Accept" },
	{ 0x11, "Link Correction Message" },

	{ 0x01, "Power Control Parameters Update" },
	{ 0x02, "Guard Time Violation" },
	{ 0x04, "Extended Channel Request" },

	/* Status and Diagnostic Messages */
	{ 0x40, "Information Request" },
	{ 0x41, "Information Response Position" },
	{ 0x42, "Information Response Version" },
	{ 0x43, "Information Response Spot Beam Selection" },
	{ 0x44, "Information Response Power Control" },
	{ 0x45, "Information Response Vendor Specific" },
	{ 0x46, "Information Response Current Beam" },
	{ 0x4f, "Information Response Error" },

	/* End */
	{ 0, NULL }
};


#define NUM_GMR1_MSG_RR (sizeof(gmr1_msg_rr_strings) / sizeof(value_string))
static gint ett_msg_rr[NUM_GMR1_MSG_RR];

	/* same order as gmr1_msg_rr_strings */
static const gmr1_msg_func_t gmr1_msg_rr_func[NUM_GMR1_MSG_RR] = {
	/* Channel establishment messages */
	gmr1_rr_msg_imm_ass,		/* Imm. Ass.*/
	gmr1_rr_msg_imm_ass_rej_1,	/* Imm. Ass. Reject Type 1 */
	NULL,				/* Imm. Ass. Reject Type 2 */
	NULL,				/* Extended Imm. Ass. */
	NULL,				/* Extended Imm. Ass. Reject */
	gmr1_rr_msg_pos_verif_notify,	/* Position Verification Notify */
	NULL,				/* Imm. Ass. Reject Type 3 */
	gmr1_rr_msg_imm_ass_2,		/* Imm. Ass. Type 2 */
	gmr1_rr_msg_imm_ass_3,		/* Imm. Ass. Type 3 */

	/* Ciphering messages */
	gmr1_rr_msg_ciph_mode_cmd,	/* Ciphering Mode Command */
	gmr1_rr_msg_ciph_mode_complete,	/* Ciphering Mode Complete */

	/* Channel assignment/handover messages */
	gmr1_rr_msg_ass_cmd_1,		/* Assignment Command 1 */
	NULL,				/* Assignment Command 2 */
	NULL,				/* Assignment Complete */
	NULL,				/* Assignment Failure */
	NULL,				/* Handover Command */
	NULL,				/* Handover Complete */

	/* Channel release messages */
	gmr1_rr_msg_chan_release,	/* Channel Release */
	NULL,				/* TtT Signalling Link Failure */

	/* Paging messages */
	NULL,				/* Paging Request Type 1 */
	NULL,				/* Paging Request Type 2 */
	gmr1_rr_msg_pag_req_3,		/* Paging Request Type 3 */
	gmr1_rr_msg_pag_resp,		/* Paging Response */

	/* Miscellaneous messages */
	gmr1_rr_msg_chan_mode_modify,	/* Channel Mode Modify */
	NULL,				/* RR Status */
	gmr1_rr_msg_chan_mode_mod_ack,	/* Channel Mode Modify Acknowledge */
	NULL,				/* Classmark Change */
	NULL,				/* Classmark Enquiry */
	NULL,				/* Position Update Request */
	NULL,				/* Position Update Accept */
	NULL,				/* Link Correction Message */

	NULL,				/* Power Control Parameters Update */
	NULL,				/* Guard Time Violation */
	NULL,				/* Extended Channel Request */

	/* Status and Diagnostic Messages */
	NULL,				/* Info. Req. */
	NULL,				/* Info. Resp. Position */
	NULL,				/* Info. Resp. Version */
	NULL,				/* Info. Resp. Spot Beam Selection */
	NULL,				/* Info. Resp. Power Control */
	NULL,				/* Info. Resp. Vendor Specific */
	NULL,				/* Info. Resp. Current Beam */
	NULL,				/* Info. Resp. Error */

	NULL,
};


void
gmr1_get_msg_rr_params(guint8 oct, int dcch, const gchar **msg_str,
		       int *ett_tree, int *hf_idx, gmr1_msg_func_t *msg_func_p)
{
	const gchar *m = NULL;
	gint idx;

	/*
	 * See the large comment in gmr1_msg_rr_strings[] for an
	 * explanation of why we're doing this.
	 */
	if (dcch)
		m = try_val_to_str_idx((guint32)oct | 0x100, gmr1_msg_rr_strings, &idx);

	if (!m)
		m = try_val_to_str_idx((guint32)oct, gmr1_msg_rr_strings, &idx);

	*msg_str = m;
	*hf_idx = hf_rr_msg_type;
	if (m != NULL) {
		*ett_tree  = ett_msg_rr[idx];
		*msg_func_p = gmr1_msg_rr_func[idx];
	} else {
		*ett_tree = -1;
		*msg_func_p = NULL;
	}
}


/* ------------------------------------------------------------------------ */
/* Dissector code                                                           */
/* ------------------------------------------------------------------------ */

static int
dissect_gmr1_ccch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	guint32 len, offset;
	gmr1_msg_func_t msg_func;
	const gchar *msg_str;
	gint ett_tree;
	int hf_idx;
	proto_item *ccch_item = NULL, *pd_item = NULL;
	proto_tree *ccch_tree = NULL, *pd_tree = NULL;
	guint32 oct[3];
	guint8 pd;
	gint ti = -1;

	/* Scan init */
	len = tvb_reported_length(tvb);
	offset = 0;

	/* Safety */
	if (len < 3) {
		/* Can't be a CCCH */
		goto err;
	}

	col_append_str(pinfo->cinfo, COL_INFO, "(CCCH) ");

	/* First octed with pseudo len */
	oct[0] = tvb_get_guint8(tvb, offset++);

	/* Check protocol descriptor */
	oct[1] = tvb_get_guint8(tvb, offset++);

	if ((oct[1] & GMR1_PD_EXT_MSK) == GMR1_PD_EXT_VAL)
		pd = oct[1] & 0xff;
	else
		pd = oct[1] & 0x0f;

	col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ",
		val_to_str(pd, gmr1_pd_short_vals, "Unknown (%u)"));

	if (pd != GMR1_PD_RR)
		goto err;	/* CCCH is only RR */

	/* Get message parameters */
	oct[2] = tvb_get_guint8(tvb, offset);

	gmr1_get_msg_rr_params(oct[2], 0, &msg_str, &ett_tree, &hf_idx, &msg_func);

	/* Create protocol tree */
	if (msg_str == NULL)
	{
		ccch_item = proto_tree_add_protocol_format(
			tree, proto_gmr1_ccch, tvb, 0, len,
			"GMR-1 CCCH - Message Type (0x%02x)", oct[2]);
		ccch_tree = proto_item_add_subtree(ccch_item, ett_msg_ccch);

		col_append_fstr(pinfo->cinfo, COL_INFO, "Message Type (0x%02x) ", oct[2]);
	}
	else
	{
		ccch_item = proto_tree_add_protocol_format(
			tree, proto_gmr1_ccch, tvb, 0, -1,
			"GMR-1 CCCH - %s", msg_str);
		ccch_tree = proto_item_add_subtree(ccch_item, ett_tree);

		col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", msg_str);
	}

	/* Start over */
	offset = 0;

	/* L2 Pseudo Length - [1] 11.5.2.19 */
	offset += elem_v(tvb, ccch_tree, pinfo, GMR1_IE_RR, GMR1_IE_RR_L2_PSEUDO_LEN, offset, NULL);

	/* Protocol discriminator item */
	pd_item = proto_tree_add_uint(
		ccch_tree, hf_rr_protocol_discriminator, tvb, 1, 1, pd);

	pd_tree = proto_item_add_subtree(pd_item, ett_rr_pd);

		/* Skip indicator / Transaction indicator */
	if (ti == -1) {
		proto_tree_add_item(pd_tree, hf_gmr1_skip_ind, tvb, 1, 1, ENC_BIG_ENDIAN);
	} else {
		/* FIXME !!! */
	}

		/* Protocol discriminator value */
	proto_tree_add_item(pd_tree, hf_gmr1_l3_pd, tvb, 1, 1, ENC_BIG_ENDIAN);


		/* Move on */
	offset++;

	/* Message type - [1] 11.4 */
	proto_tree_add_uint_format(
		ccch_tree, hf_idx, tvb, offset, 1, oct[2],
		"Message Type: %s", msg_str ? msg_str : "(Unknown)"
	);

	offset++;

	/* Decode elements */
	if (msg_func) {
		(*msg_func)(tvb, ccch_tree, pinfo, offset, len - offset);
	} else {
		proto_tree_add_item(ccch_tree, hf_rr_message_elements, tvb, offset, len - offset, ENC_NA);
	}

	/* Error handling */
err:
	call_data_dissector(tvb, pinfo, tree);
	return tvb_captured_length(tvb);
}

void
proto_register_gmr1_rr(void)
{
	static hf_register_info hf[] = {
		{ &hf_rr_msg_type,
		  { "Radio Resources Management Message Type", "gmr1.rr.msg_type",
		    FT_UINT8, BASE_HEX, VALS(gmr1_msg_rr_strings), 0x00,
		    NULL, HFILL }	/* FIXME handle CCCH/DCCH conflicts */
		},
		{ &hf_rr_chan_desc_kab_loc,
		  { "KAB Location", "gmr1.rr.chan_desc.kab_loc",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_chan_desc_rx_tn,
		  { "RX Timeslot", "gmr1.rr.chan_desc.rx_tn",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_chan_desc_arfcn,
		  { "ARFCN", "gmr1.rr.chan_desc.arfcn",
		    FT_UINT16, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_chan_desc_tx_tn,
		  { "TX Timeslot", "gmr1.rr.chan_desc.tx_tn",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_chan_desc_chan_type,
		  { "Channel Type", "gmr1.rr.chan_desc.chan_type",
		    FT_UINT8, BASE_DEC, VALS(rr_chan_desc_chan_type_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_chan_mode,
		  { "Channel Mode", "gmr1.rr.chan_mode",
		    FT_UINT8, BASE_DEC, VALS(rr_chan_mode_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_ciph_mode_setting_sc,
		  { "SC", "gmr1.rr.ciph_mode_setting.sc",
		    FT_UINT8, BASE_DEC, VALS(rr_ciph_mode_setting_sc_vals), 0x01,
		    NULL, HFILL }
		},
		{ &hf_rr_ciph_mode_setting_algo,
		  { "Algorithm", "gmr1.rr.ciph_mode_setting.algo",
		    FT_UINT8, BASE_DEC, VALS(rr_ciph_mode_setting_algo_vals), 0x0e,
		    NULL, HFILL }
		},
		{ &hf_rr_ciph_resp_cr,
		  { "CR", "gmr1.rr.ciph_resp.cr",
		    FT_UINT8, BASE_DEC, VALS(rr_ciph_resp_cr_vals), 0x10,
		    NULL, HFILL }
		},
		{ &hf_rr_ciph_resp_spare,
		  { "Spare", "gmr1.rr.ciph_resp.spare",
		    FT_UINT8, BASE_DEC, NULL, 0xe0,
		    NULL, HFILL }
		},
		{ &hf_rr_l2_pseudo_len,
		  { "L2 Pseudo Length value", "gmr1.rr.l2_pseudo_len",
		    FT_UINT8, BASE_DEC, NULL, 0xfc,
		    NULL, HFILL }
		},
		{ &hf_rr_page_mode,
		  { "Page Mode", "gmr1.rr.page_mode.mode",
		    FT_UINT8, BASE_DEC, VALS(rr_page_mode_vals), 0x03,
		    NULL, HFILL }
		},
		{ &hf_rr_page_mode_spare,
		  { "Spare", "gmr1.rr.page_mode.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x0c,
		    NULL, HFILL }
		},
		{ &hf_rr_req_ref_est_cause,
		  { "Establishment cause group ID", "gmr1.rr.req_ref.est_cause",
		    FT_UINT8, BASE_DEC, VALS(rr_req_ref_est_cause_vals), 0xe0,
		    NULL, HFILL }
		},
		{ &hf_rr_req_ref_ra,
		  { "Random Access Information", "gmr1.rr.req_ref.ra",
		    FT_UINT8, BASE_HEX, NULL, 0x1f,
		    NULL, HFILL }
		},
		{ &hf_rr_req_ref_fn,
		  { "Frame Number mod 256", "gmr1.rr.req_ref.fn",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_cause,
		  { "RR Cause", "gmr1.rr.cause",
		    FT_UINT8, BASE_DEC, VALS(rr_cause_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_timing_ofs_ti,
		  { "TI", "gmr1.rr.timing_offset.ti",
		    FT_UINT8, BASE_DEC, VALS(rr_timing_ofs_ti_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_timing_ofs_value,
		  { "Timing Offset value", "gmr1.rr.timing_offset.value",
		    FT_INT16, BASE_CUSTOM, CF_FUNC(rr_timing_ofs_value_fmt), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_tmsi_ptmsi,
		  { "TMSI/P-TMSI Value","gmr1.rr.tmsi_ptmsi",
		    FT_UINT32,BASE_HEX,  NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_wait_ind_timeout,
		  { "T3122/T3142 timeout", "gmr1.rr.wait_ind.timeout",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_gen_ie_seconds_fmt), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes1_ab,
		  { "MES1 - Assignment Type", "gmr1.rr.mes_info_flag.1.ab",
		    FT_UINT8, BASE_DEC, VALS(rr_mif_mes1_ab_vals), 0x03,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes1_i,
		  { "MES1 - Idle mode position update", "gmr1.rr.mes_info_flag.1.i",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x04,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes1_d,
		  { "MES1 - Dedicated mode position update", "gmr1.rr.mes_info_flag.1.d",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x08,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes2,
		  { "MES2", "gmr1.rr.mes_info_flag.2",
		    FT_UINT8, BASE_DEC, VALS(rr_mif_mes234_vals), 0x10,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes3,
		  { "MES3", "gmr1.rr.mes_info_flag.3",
		    FT_UINT8, BASE_DEC, VALS(rr_mif_mes234_vals), 0x20,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_mes4,
		  { "MES4", "gmr1.rr.mes_info_flag.4",
		    FT_UINT8, BASE_DEC, VALS(rr_mif_mes234_vals), 0x40,
		    NULL, HFILL }
		},
		{ &hf_rr_mif_pv,
		  { "Position Verification indicator", "gmr1.rr.mes_info_flag.pv",
		    FT_UINT8, BASE_DEC, VALS(rr_mif_pv_vals), 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_freq_ofs_fi,
		  { "FI", "gmr1.rr.frequency_offset.fi",
		    FT_UINT8, BASE_DEC, VALS(rr_freq_ofs_fi_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_freq_ofs_value,
		  { "Frequency Offset value", "gmr1.rr.frequency_offset.value",
		    FT_INT16, BASE_CUSTOM, CF_FUNC(rr_freq_ofs_value_fmt), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_freq_ofs_spare,
		  { "Spare", "gmr1.rr.frequency_offset.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_page_info_msc_id,
		  { "MSC ID", "gmr1.rr.paging_info.msc_id",
		    FT_UINT8, BASE_DEC, NULL, 0xfc,
		    NULL, HFILL }
		},
		{ &hf_rr_page_info_chan_needed,
		  { "Channel Needed", "gmr1.rr.paging_info.chan_needed",
		    FT_UINT8, BASE_DEC, VALS(rr_page_info_chan_needed_vals), 0x03,
		    NULL, HFILL }
		},
		{ &hf_rr_pos_display_flag,
		  { "Display Information Flag", "gmr1.rr.pos_display.flag",
		    FT_UINT8, BASE_DEC, VALS(rr_pos_display_flag_vals), 0xf0,
		    NULL, HFILL }
		},
		{ &hf_rr_pos_display_text,
		  { "Country and Region name", "gmr1.rr.pos_display.text",
		    FT_STRING, BASE_NONE, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pos_upd_info_v,
		  { "Valid", "gmr1.rr.pos_upd_info.valid",
		    FT_UINT8, BASE_DEC, VALS(rr_pos_upd_info_v_vals), 0x01,
		    NULL, HFILL }
		},
		{ &hf_rr_pos_upd_info_dist,
		  { "GPS Update Distance", "gmr1.rr.pos_upd_info.distance",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_pos_upd_info_dist_fmt), 0xfe,
		    NULL, HFILL }
		},
		{ &hf_rr_pos_upd_info_time,
		  { "GPS Update Timer", "gmr1.rr.pos_upd_info.time",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_pos_upd_info_time_fmt), 0xff,
		    NULL, HFILL }
		},
		{ &hf_rr_bcch_carrier_arfcn,
		  { "ARFCN", "gmr1.rr.bcch_carrier.arfcn",
		    FT_UINT16, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_bcch_carrier_si,
		  { "Satellite Indication", "gmr1.rr.bcch_carrier.si",
		    FT_UINT8, BASE_DEC, VALS(rr_bcch_carrier_si_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_bcch_carrier_ri,
		  { "Reselection Indication", "gmr1.rr.bcch_carrier.ri",
		    FT_UINT8, BASE_DEC, VALS(rr_bcch_carrier_ri_vals), 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_bcch_carrier_spare,
		  { "Spare", "gmr1.rr.bcch_carrier.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_reject_cause,
		  { "Cause", "gmr1.rr.reject_cause.cause",
		    FT_UINT8, BASE_DEC, VALS(rr_reject_cause_vals), 0xfc,
		    NULL, HFILL }
		},
		{ &hf_rr_reject_cause_b,
		  { "BCCH Carrier IE presence", "gmr1.rr.reject_cause.b",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x01,
		    NULL, HFILL }
		},
		{ &hf_rr_gps_timestamp,
		  { "GPS timestamp", "gmr1.rr.gps_timestamp",
		    FT_UINT16, BASE_CUSTOM, CF_FUNC(rr_gps_timestamp_fmt), 0xffff,
		    NULL, HFILL }
		},
		{ &hf_rr_gps_power_control_params,
		  { "Power Control Parameters", "gmr1.rr.power_control_params",
		    FT_BYTES, BASE_NONE, NULL, 0x0,
		    NULL, HFILL }
		},
		{ &hf_rr_tmsi_avail_msk_tmsi[0],
		  { "TMSI 1 Presence", "gmr1.rr.tmsi_avail_msk.tmsi1",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x10,
		    NULL, HFILL }
		},
		{ &hf_rr_tmsi_avail_msk_tmsi[1],
		  { "TMSI 2 Presence", "gmr1.rr.tmsi_avail_msk.tmsi2",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x20,
		    NULL, HFILL }
		},
		{ &hf_rr_tmsi_avail_msk_tmsi[2],
		  { "TMSI 3 Presence", "gmr1.rr.tmsi_avail_msk.tmsi3",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x40,
		    NULL, HFILL }
		},
		{ &hf_rr_tmsi_avail_msk_tmsi[3],
		  { "TMSI 4 Presence", "gmr1.rr.tmsi_avail_msk.tmsi4",
		    FT_UINT8, BASE_DEC, VALS(rr_gen_ie_presence_vals), 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_gps_almanac_pn,
		  { "Page Number", "gmr1.rr.gps_almanac.pn",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_gps_almanac_pn_fmt), 0xf8,
		    "See ICD-GPS-200", HFILL }
		},
		{ &hf_rr_gps_almanac_wn,
		  { "Word Number", "gmr1.rr.gps_almanac.wn",
		    FT_UINT8, BASE_DEC, NULL, 0x07,
		    "See ICD-GPS-200", HFILL }
		},
		{ &hf_rr_gps_almanac_word,
		  { "GPS Almanac Word", "gmr1.rr.gps_almanac.word",
		    FT_UINT24, BASE_HEX, NULL, 0x00,
		    "See ICD-GPS-200", HFILL }
		},
		{ &hf_rr_gps_almanac_sfn,
		  { "Sub Frame Number", "gmr1.rr.gps_almanac.sfn",
		    FT_UINT8, BASE_DEC, VALS(rr_gps_almanac_sfn_vals), 0x80,
		    "See ICD-GPS-200", HFILL }
		},
		{ &hf_rr_gps_almanac_co,
		  { "CO", "gmr1.rr.gps_almanac.co",
		    FT_UINT8, BASE_DEC, NULL, 0x40,
		    NULL, HFILL }
		},
		{ &hf_rr_gps_almanac_spare,
		  { "Spare", "gmr1.rr.gps_almanac.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x3f,
		    NULL, HFILL }
		},
		{ &hf_rr_msc_id,
		  { "MSC ID", "gmr1.rr.msc_id",
		    FT_UINT8, BASE_DEC, NULL, 0xfc,
		    NULL, HFILL }
		},
		{ &hf_rr_msc_id_spare,
		  { "Spare", "gmr1.rr.msc_id.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x03,
		    NULL, HFILL }
		},
		{ &hf_rr_gps_discr,
		  { "GPS Position field CRC-16", "gmr1.rr.gps_discriminator",
		    FT_UINT16, BASE_HEX, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_3_prm_rlc_mode,
		  { "RLC Mode", "gmr1.rr.pkt_imm_ass_3_prm",
		    FT_UINT8, BASE_DEC, VALS(rr_pkt_imm_ass_3_prm_rlc_mode_vals), 0x01,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_3_prm_spare,
		  { "Spare", "gmr1.rr.pkt_imm_ass_3_prm.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x1e,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_3_prm_dl_tfi,
		  { "Downlink TFI", "gmr1.rr.pkt_imm_ass_3_prm.tfi",
		    FT_UINT8, BASE_HEX, NULL, 0x00,
		    "Temporary Flow Identifier", HFILL }
		},
		{ &hf_rr_pkt_imm_ass_3_prm_start_fn,
		  { "Start Framenumber", "gmr1.rr.pkt_imm_ass_3_prm.start_fn",
		    FT_UINT8, BASE_DEC, NULL, 0xf0,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_3_prm_mac_slot_alloc,
		  { "MAC-slot Allocation", "gmr1.rr.pkt_imm_ass_3_prm.mac_slot_alloc",
		    FT_UINT8, BASE_HEX, NULL, 0xff,
		    "LSB=slot 0, MSB=slot 7", HFILL }
		},
		{ &hf_rr_pkt_freq_prm_arfcn,
		  { "ARFCN", "gmr1.rr.pkt_freq_prm.arfcn",
		    FT_UINT16, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_freq_prm_dl_freq_plan_id,
		  { "Downlink Freq. Plan ID", "gmr1.rr.pkt_freq_prm.dl_freq_plan_id",
		    FT_UINT8, BASE_DEC, VALS(rr_pkt_freq_prm_dl_freq_plan_id_vals), 0x08,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_freq_prm_dl_bw,
		  { "Downlink Bandwidth", "gmr1.rr.pkt_freq_prm.dl_bw",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_pkt_freq_prm_xx_bw_fmt), 0x70,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_freq_prm_ul_freq_dist,
		  { "Uplink Freq. Distance", "gmr1.rr.pkt_freq_prm.ul_freq_dist",
		    FT_INT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_freq_prm_ul_bw,
		  { "Uplink Bandwidth", "gmr1.rr.pkt_freq_prm.ul_bw",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_pkt_freq_prm_xx_bw_fmt), 0x70,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_freq_prm_spare,
		  { "Spare", "gmr1.rr.pkt_freq_prm.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_spare1,
		  { "Spare", "gmr1.rr.pkt_imm_ass_2_prm.ac.spare1",
		    FT_UINT8, BASE_DEC, NULL, 0x01,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_final_alloc,
		  { "Final Allocation", "gmr1.rr.pkt_imm_ass_2_prm.ac.final_alloc",
		    FT_UINT8, BASE_DEC, NULL, 0x02,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_usf_granularity,
		  { "USF Granularity", "gmr1.rr.pkt_imm_ass_2_prm.ac.usf_granularity",
		    FT_UINT8, BASE_DEC, NULL, 0x04,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_dl_ctl_mac_slot,
		  { "Downlink Control MAC-slot", "gmr1.rr.pkt_imm_ass_2_prm.ac.dl_ctl_mac_slot",
		    FT_UINT8, BASE_DEC, NULL, 0x38,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_mac_mode,
		  { "MAC mode", "gmr1.rr.pkt_imm_ass_2_prm.ac.mac_mode",
		    FT_UINT8, BASE_DEC, VALS(rr_pkt_imm_ass_2_prm_ac_mac_mode_vals), 0xc0,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_start_fn,
		  { "Starting Frame Number", "gmr1.rr.pkt_imm_ass_2_prm.ac.start_fn",
		    FT_UINT8, BASE_DEC, NULL, 0x0f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_rlc_dblk_gnt,
		  { "RLC Data Blocks Granted", "gmr1.rr.pkt_imm_ass_2_prm.ac.rlc_dblk_gnt",
		    FT_UINT8, BASE_DEC, NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_mcs,
		  { "MCS", "gmr1.rr.pkt_imm_ass_2_prm.ac.mcs",
		    FT_UINT8, BASE_DEC, NULL, 0xf8,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_tfi,
		  { "TFI", "gmr1.rr.pkt_imm_ass_2_prm.ac.tfi",
		    FT_UINT8, BASE_HEX, NULL, 0x7f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_spare2,
		  { "Spare", "gmr1.rr.pkt_imm_ass_2_prm.ac.spare2",
		    FT_UINT8, BASE_HEX, NULL, 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_ac_mac_slot_alloc,
		  { "MAC-slot Allocation", "gmr1.rr.pkt_imm_ass_2_prm.ac.mac_slot_alloc",
		    FT_UINT8, BASE_HEX, NULL, 0xff,
		    "LSB=slot 0, MSB=slot 7", HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd,
		  { "Channel MCS Command", "gmr1.rr.pkt_imm_ass_2_prm.d.chan_mcs_cmd",
		    FT_UINT8, BASE_HEX, NULL, 0x0f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_chan_mcs_cmd_pnb512,
		  { "Channel MCS Command PNB 5,12", "gmr1.rr.pkt_imm_ass_2_prm.d.chan_mcs_cmd_pnb512",
		    FT_UINT8, BASE_HEX, NULL, 0xf0,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_spare1,
		  { "Spare", "gmr1.rr.pkt_imm_ass_2_prm.d.spare1",
		    FT_UINT8, BASE_HEX, NULL, 0xff,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_rlc_dblk_gnt,
		  { "RLC Data Blocks Granted", "gmr1.rr.pkt_imm_ass_2_prm.d.rlc_dblk_gnt",
		    FT_UINT8, BASE_DEC, NULL, 0x7f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_spare2,
		  { "Spare", "gmr1.rr.pkt_imm_ass_2_prm.d.spare2",
		    FT_UINT8, BASE_HEX, NULL, 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_tfi,
		  { "TFI", "gmr1.rr.pkt_imm_ass_2_prm.d.tfi",
		    FT_UINT8, BASE_HEX, NULL, 0x7f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_usf_granularity,
		  { "USF Granularity", "gmr1.rr.pkt_imm_ass_2_prm.ac.usf_granularity",
		    FT_UINT8, BASE_DEC, NULL, 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_imm_ass_2_prm_d_mac_slot_alloc,
		  { "MAC-slot Allocation", "gmr1.rr.pkt_imm_ass_2_prm.d.mac_slot_alloc",
		    FT_UINT8, BASE_HEX, NULL, 0xff,
		    "LSB=slot 0, MSB=slot 7", HFILL }
		},
		{ &hf_rr_usf_value,
		  { "Uplink state flag (USF)", "gmr1.rr.usf.value",
		    FT_UINT8, BASE_HEX, NULL, 0x3f,
		    NULL, HFILL }
		},
		{ &hf_rr_usf_spare,
		  { "Spare", "gmr1.rr.usf.spare",
		    FT_UINT24, BASE_DEC, NULL, 0xffffc0,
		    NULL, HFILL }
		},
		{ &hf_rr_timing_adv_idx_value,
		  { "TAI Value", "gmr1.rr.timing_adv_idx.tai",
		    FT_UINT8, BASE_DEC, NULL, 0x7f,
		    NULL, HFILL }
		},
		{ &hf_rr_timing_adv_idx_spare,
		  { "Spare", "gmr1.rr.timing_adv_idx.spare",
		    FT_UINT8, BASE_DEC, NULL, 0x80,
		    NULL, HFILL }
		},
		{ &hf_rr_tlli,
		  { "TLLI", "gmr1.rr.tlli",
		    FT_UINT32, BASE_HEX,  NULL, 0x00,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_pwr_ctrl_prm_par,
		  { "Power Attenuation Request (PAR)", "gmr1.rr.pkt_pwr_ctrl_prm.par",
		    FT_UINT8, BASE_CUSTOM, CF_FUNC(rr_pkt_pwr_ctrl_prm_par_fmt), 0x3f,
		    NULL, HFILL }
		},
		{ &hf_rr_pkt_pwr_ctrl_prm_spare,
		  { "Spare", "gmr1.rr.pkt_pwr_ctrl_prm.spare",
		    FT_UINT8, BASE_DEC, NULL, 0xc0,
		    NULL, HFILL }
		},
		{ &hf_rr_persistence_lvl[0],
		  { "for Radio priority 1", "gmr1.rr.persistence_lvl.p1",
		    FT_UINT8, BASE_DEC, NULL, 0xf0,
		    NULL, HFILL }
		},
		{ &hf_rr_persistence_lvl[1],
		  { "for Radio priority 2", "gmr1.rr.persistence_lvl.p2",
		    FT_UINT8, BASE_DEC, NULL, 0x0f,
		    NULL, HFILL }
		},
		{ &hf_rr_persistence_lvl[2],
		  { "for Radio priority 3", "gmr1.rr.persistence_lvl.p3",
		    FT_UINT8, BASE_DEC, NULL, 0xf0,
		    NULL, HFILL }
		},
		{ &hf_rr_persistence_lvl[3],
		  { "for Radio priority 4", "gmr1.rr.persistence_lvl.p4",
		    FT_UINT8, BASE_DEC, NULL, 0x0f,
		    NULL, HFILL }
		},
		{ &hf_rr_protocol_discriminator,
		  { "Protocol Discriminator", "gmr1.rr.protocol_discriminator",
			FT_UINT8, BASE_DEC, VALS(gmr1_pd_vals), 0x0,
			NULL, HFILL }
		},
		{ &hf_rr_message_elements,
		  { "Message elements", "gmr1.rr.message_elements",
			FT_BYTES, BASE_NONE, NULL, 0x0,
			NULL, HFILL }
		},
	};

	static ei_register_info ei[] = {
		{ &ei_gmr1_missing_mandatory_element, { "gmr1.rr.missing_mandatory_element", PI_PROTOCOL, PI_ERROR, "Missing Mandatory element, rest of dissection is suspect", EXPFILL }},
	};

	expert_module_t* expert_gmr1_rr;

#define NUM_INDIVIDUAL_ELEMS 2
	static gint *ett[NUM_INDIVIDUAL_ELEMS +
	                 NUM_GMR1_IE_RR +
	                 NUM_GMR1_MSG_RR];

	unsigned int last_offset, i;

	/* Setup protocol subtree array */
	ett[0] = &ett_msg_ccch;
	ett[1] = &ett_rr_pd;

	last_offset = NUM_INDIVIDUAL_ELEMS;

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

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

	proto_register_subtree_array(ett, array_length(ett));

	/* Register the protocol name and field description */
	proto_gmr1_rr = proto_register_protocol("GEO-Mobile Radio (1) RR", "GMR-1 RR", "gmr1.rr");

	proto_register_field_array(proto_gmr1_rr, hf, array_length(hf));

	expert_gmr1_rr = expert_register_protocol(proto_gmr1_rr);
	expert_register_field_array(expert_gmr1_rr, ei, array_length(ei));

	/* Register the protocol name and field description */
	proto_gmr1_ccch = proto_register_protocol("GEO-Mobile Radio (1) CCCH", "GMR-1 CCCH", "gmr1.ccch");

	/* Register dissector */
	register_dissector("gmr1_ccch", dissect_gmr1_ccch, proto_gmr1_ccch);
}

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