WIP: BSSGP related hepler functions; towards tests
diff --git a/gprs_gb/BSSGP_Helper.cc b/gprs_gb/BSSGP_Helper.cc
new file mode 100644
index 0000000..8cf97d2
--- /dev/null
+++ b/gprs_gb/BSSGP_Helper.cc
@@ -0,0 +1,96 @@
+
+#include "Octetstring.hh"
+#include "Error.hh"
+#include "Logger.hh"
+
+#include <stdint.h>
+
+namespace BSSGP__Helper__Functions {
+
+/* convert a buffer filled with TLVs that have variable-length "length" fields (Osmocom TvLV) into a
+ * buffer filled with TLVs that have fixed 16-bit length values (TL16V format) */
+static OCTETSTRING transcode_tlv_part(OCTETSTRING const &in)
+{
+	const unsigned char *in_ptr = (const unsigned char *)in;
+	int in_len = in.lengthof();
+	int ofs = 0;
+	uint16_t data_len;
+	OCTETSTRING out(0, (const unsigned char *)"");
+
+	while (ofs < in_len) {
+		int remain_len = in_len - ofs;
+		int tl_length;
+
+		if (remain_len < 2) {
+			TTCN_error("Remaining input length (%d) insufficient for Tag+Length", remain_len);
+			break;
+		}
+
+		/* copy over tag */
+		if (in_ptr[ofs+1] & 0x80) {
+			/* E bit is set, 7-bit length field */
+			data_len = in_ptr[ofs+1] & 0x7F;
+			tl_length = 2;
+		} else {
+			/* E bit is not set, 15 bit length field */
+			if (in_len < 3) {
+				TTCN_error("Remaining input length insufficient for 2-octet length");
+				break;
+			}
+			data_len = in_ptr[ofs+1] << 8 | in_ptr[ofs+2];
+			tl_length = 3;
+		}
+		if (in_len < tl_length + data_len) {
+			TTCN_error("Remaining input length insufficient for TLV value length");
+			break;
+		}
+
+		/* Tag + 16bit length */
+		uint8_t hdr_buf[3];
+		hdr_buf[0] = in_ptr[ofs+0];
+		hdr_buf[1] = data_len >> 8;
+		hdr_buf[2] = data_len & 0xff;
+
+		OCTETSTRING tlv_hdr(3, hdr_buf);
+		out += tlv_hdr;
+
+		if (data_len) {
+			/* append octet string of current TLV to output octetstring */
+			OCTETSTRING tlv_val(data_len, in_ptr + ofs + tl_length);
+			out += tlv_val;
+		}
+
+		/* advance input offset*/
+		ofs += data_len + tl_length;
+	}
+
+	return out;
+}
+
+#define BSSGP_PDUT_DL_UNITDATA	0x00
+#define BSSGP_PDUT_UL_UNITDATA	0x01
+
+/* expand all the variable-length "length" fields of a BSSGP message (Osmocom TvLV) into 
+ * statlc TL16V format */
+OCTETSTRING f__BSSGP__preprocess__pdu(OCTETSTRING const &in)
+{
+	const unsigned char *in_ptr = (const unsigned char *)in;
+	int in_len = in.lengthof();
+	uint8_t pdu_type = in_ptr[0];
+	uint8_t static_hdr_len = 1;
+
+	if (pdu_type == BSSGP_PDUT_DL_UNITDATA || pdu_type == BSSGP_PDUT_UL_UNITDATA)
+		static_hdr_len = 8;
+
+	if (in_len < static_hdr_len)
+		TTCN_error("BSSGP message is shorter (%u bytes) than minimum header length (%u bytes) for msg_type 0x%02x",
+				in_len, static_hdr_len, pdu_type);
+
+	/* prefix = non-TLV section of header */
+	OCTETSTRING prefix(static_hdr_len, in_ptr);
+	OCTETSTRING tlv_part_in(in_len - static_hdr_len, in_ptr + static_hdr_len);
+
+	return prefix + transcode_tlv_part(tlv_part_in);
+}
+
+}
diff --git a/gprs_gb/BSSGP_Helper_Functions.ttcn b/gprs_gb/BSSGP_Helper_Functions.ttcn
new file mode 100644
index 0000000..9c8af92
--- /dev/null
+++ b/gprs_gb/BSSGP_Helper_Functions.ttcn
@@ -0,0 +1,3 @@
+module BSSGP_Helper_Functions {
+	external function f_BSSGP_preprocess_pdu(in octetstring inp) return octetstring;
+};
diff --git a/gprs_gb/BSSGP_Types.ttcn b/gprs_gb/BSSGP_Types.ttcn
new file mode 100644
index 0000000..94d9cd0
--- /dev/null
+++ b/gprs_gb/BSSGP_Types.ttcn
@@ -0,0 +1,279 @@
+module BSSGP_Types {
+
+	import from General_Types all;
+	import from Osmocom_Types all;
+	import from GSM_Types all;
+
+	type enumerated BssgpPduType {
+		DL_UNITDATA		('00'H),
+		UL_UNITDATA		('01'H),
+		RA_CAPABILITY		('02'H),
+		DL_MBMS_UNITDATA	('04'H),
+		UL_MBMS_UNITDATA	('05'H),
+		/* between GMM SAPs */
+		PAGING_PS		('06'H),
+		PAGING_CS		('07'H),
+		RA_CAPABILITY_UPDATE	('08'H),
+		RA_CAPABILITY_UPDATE_ACK ('09'H),
+		RADIO_STATUS		('0A'H),
+		SUSPEND			('0B'H),
+		SUSPEND_ACK		('0C'H),
+		SUSPEND_NACK		('0D'H),
+		RESUME			('0E'H),
+		RESUME_ACK		('0F'H),
+		RESUME_NACK		('10'H),
+		/* between NM SAPs */
+		BVC_BLOCK		('20'H),
+		BVC_BLOCK_ACK		('21'H),
+		BVC_RESET		('22'H),
+		BVC_RESET_ACK		('23'H),
+		BVC_UNBLOCK		('24'H),
+		BVC_UNBLOCK_ACK		('25'H),
+		FLOW_CONTROL_BVC	('26'H),
+		FLOW_CONTROL_BVC_ACK	('27'H),
+		FLOW_CONTROL_MS		('28'H),
+		FLOW_CONTROL_MS_ACK	('29'H),
+		FLUSH_LL		('2A'H),
+		FLUSH_LL_ACK		('2B'H),
+		LLC_DISCARDED		('2C'H),
+		FLOW_CONTROL_PFC	('2D'H),
+		FLOW_CONTROL_PFC_ACK	('2E'H),
+		SGSN_INVOKE_TRACE	('40'H),
+		STATUS			('41'H)
+		/* between PFM SAPs : TODO */
+		/* between LCS SAPs : TODO */
+		/* between RIM SAPs : TODO */
+		/* between MBMS SAPs : TODO */
+	} with { variant "FIELDLENGTH(8)" };
+
+	type enumerated BssgpIEI {
+		ALIGNMENT_OCTETS		('00'H),
+		BMAX_DEFAULT_MS			('01'H),
+		BSS_AREA_INDICATION		('02'H),
+		BUCKET_LEAK_RATE		('03'H),
+		BVCI				('04'H),
+		BVC_BUCKET_SIZE			('05'H),
+		BVC_MEASUREMENT			('06'H),
+		CAUSE				('07'H),
+		CELL_ID				('08'H),
+		CHENNEL_NEEDED			('09'H),
+		DRX_PARAMETERS			('0A'H),
+		EMLPP_PRIORITY			('0B'H),
+		FLUSH_ACTION			('0C'H),
+		IMSI				('0D'H),
+		LLC_PDU				('0E'H),
+		LLC_FRAMES_DISCARDED		('0F'H),
+		LOCATION_AREA			('10'H),
+		MOBILE_IDENTITY			('11'H),
+		MS_BUCKET_SIZE			('12'H),
+		MS_RADIO_ACCESS_CAPABILITY	('13'H),
+		OMC_ID				('14'H),
+		PDU_IN_ERROR			('15'H),
+		PDU_LIFETIME			('16'H),
+		PRIORITY			('17'H),
+		QOS_PROFILE			('18'H),
+		RADIO_CAUSE			('19'H),
+		RA_CAP_UPD_CAUSE		('1A'H),
+		ROUTEING_AREA			('1B'H),
+		R_DEFAULT_MS			('1C'H),
+		SUSPE_DN_REFERENCE_NR		('1D'H),
+		TAG				('1E'H),
+		TLLI				('1F'H),
+		TMSI				('20'H),
+		TRACE_REFERENCE			('21'H),
+		TRACE_TYPE			('22'H),
+		TRANSACTION_ID			('23'H),
+		TRIGGER_ID			('24'H),
+		NUMBER_OF_OCTETS_AFFECTED	('25'H),
+		LSA_IDENTIFIER_LIST		('26'H),
+		LSA_INFORMATION			('27'H),
+		PACKET_FLOW_IDENTIFIER		('28'H),
+		PACKET_FLOW_TIMER		('29'H),
+		AGGREGATE_BSS_QOS_PROFILE	('3a'H),
+		FEATURE_BITMAP			('3b'H),
+		BUCKET_FILL_RATIO		('3c'H),
+		SERVICE_UTRAN_CCO		('3d'H),
+		NSEI				('3e'H),
+		RRLP_APDU			('3f'H),
+		LCS_QOS				('40'H),
+		LCS_CLIENT_TYPE			('41'H),
+		REQUESTED_GPS_ASSIST_DATA	('42'H),
+		LOCATION_TYPE			('43'H),
+		LOCATION_ESTIMATE		('44'H),
+		POSITIONING_DATA		('45'H),
+		DECIPHERING_KEYS		('46'H),
+		LCS_PRIORITY			('47'H),
+		LCS_CAUSE			('48'H),
+		LCS_CAPABILITY			('49'H),
+		RRLP_FLAGS			('4a'H),
+		RIM_APPLICATION_IDENTITY	('4b'H),
+		RIM_SEQUENCE_NUMBER		('4c'H),
+		RAN_INFO_REUEST_AC		('4d'H),
+		RAN_INFO_AC			('4e'H),
+		RIM_PDU_INDICATIONS		('4f'H),
+		PFC_FLOC_CONTROL_PARAMETERS	('52'H),
+		GLOBAL_CN_ID			('53'H),
+		RIM_ROUTING_INFORMATION		('54'H),
+		RIM_PROTOCOL_VERSION_NUMBER	('55'H),
+		APP_ERROR_CONTAINER		('56'H),
+		/* FIXME */
+		EXTENDED_FEATURE_BITMAP		('69'H)
+	} with { variant "FIELDLENGTH(8)" };
+
+	/* 11.3.28 */
+	type record BssgpQosProfile {
+		uint16_t	r,
+		BIT2		spare,
+		boolean		c_r,
+		boolean		t,
+		boolean		a,
+		uint3_t		precedence
+	} with { variant (c_r) "FIELDLENGTH(1)"
+		 variant (t) "FIELDLENGTH(1)"
+		 variant (a) "FIELDLENGTH(1)"
+	};
+
+	/* 11.3.84 */
+	type record BssgpFeatureBitmap {
+		boolean		mbms,
+		boolean		enh_radio_status,
+		boolean		pfc_fc,
+		boolean		rim,
+		boolean		lcs,
+		boolean		inr,
+		boolean		cbl,
+		boolean		pfc
+	} with { variant "" };
+
+	/* 11.3.47 */
+	type record BssgpServiceUtranCco {
+		uint5_t		spare,
+		uint3_t		value_part
+	} with { variant "" };
+
+	/* 11.3.84 */
+	type record BssgpExtendedFeatureBitmap {
+		BIT7		spare,
+		BIT1		ps_handover
+	} with { variant "" };
+
+	type uint16_t BssgpPduLifetime;
+
+	/* TS 48.008 3.2.2.18 */
+	type record BssmapPriority {
+		BIT1		spare,
+		boolean		pci,
+		uint4_t		level,
+		boolean		qa,
+		boolean		pvi
+	} with { variant "" };
+
+	type BssmapPriority BssgpPriority;
+
+	type uint32_t BssgpTlli;
+
+	type uint16_t BssgpBvci;
+	type uint8_t BssgpCause;
+
+	type record BssgpCellId {
+		RoutingAreaIdentification	ra_id,
+		CellIdentity			cell_id
+	} with { variant "" };
+
+	type union BssgpIeUnion {
+		uint16_t		bmax_default_ms,  /* 11.3.2 */
+		uint16_t		bucket_leak_rate, /* 11.3.4 */
+		uint16_t		bvc_bucket_size,  /* 11.3.5 */
+		BssgpBvci		bvci,		/* 11.3.6 */
+		uint16_t		bvc_measurement, /* 11.3.7 */
+		BssgpCause		cause,		/* 11.3.8 */
+		BssgpCellId		cell_id,	/* 11.3.9 */
+		DrxParameter		drx_parameter,	/* 11.3.11 */
+		LocationAreaIdentification lai,		/* 11.3.17 */
+		MobileIdentity		mobile_id,	/* 11.3.20 */
+		BssgpPduLifetime	pdu_lifetime,	/* 11.3.25 */
+		BssgpPriority		priority,	/* 11.3.27 */
+		BssgpQosProfile		qos_profile,	/* 11.3.28 */
+		BssgpTlli		tlli,		/* 11.3.25 */
+		uint16_t		r_default_ms,	/* 11.3.32 */
+		BssgpServiceUtranCco	svc_utran_cco,	/* 11.3.47 */
+		BssgpFeatureBitmap	feature_bitmap,	/* 11.3.40 */
+		BssgpExtendedFeatureBitmap ext_feature_bitmap,	/* 11.3.84 */
+		octetstring		other
+	};
+
+	type record BssgpTLV {
+		BssgpIEI	iei,
+		/* we cannot express a variable-length "length" field with extension octets in the TTCN-3
+		 * syntax, so we simply assume a plain 16 bit length value here and have a 'pseudl-BSSGP'
+		 * translator in front which explands all variable-length "length" fields to 16bits */
+		uint16_t	len,
+		BssgpIeUnion	u
+	} with {
+		variant (u) "CROSSTAG(
+					bmax_default_ms,	iei = BMAX_DEFAULT_MS;
+					bucket_leak_rate,	iei = BUCKET_LEAK_RATE;
+					bvc_bucket_size,	iei = BVC_BUCKET_SIZE;
+					bvci,			iei = BVCI;
+					bvc_measurement,	iei = BVC_MEASUREMENT;
+					cause,			iei = CAUSE;
+					cell_id,		iei = CELL_ID;
+					drx_parameter,		iei = DRX_PARAMETERS;
+					lai,			iei = LOCATION_AREA;
+					priority,		iei = PRIORITY;
+					mobile_id,		iei = MOBILE_IDENTITY;
+					pdu_lifetime,		iei = PDU_LIFETIME;
+					qos_profile,		iei = QOS_PROFILE;
+					tlli,			iei = TLLI;
+					r_default_ms,		iei = R_DEFAULT_MS;
+					svc_utran_cco,		iei = SERVICE_UTRAN_CCO;
+					feature_bitmap,		iei = FEATURE_BITMAP;
+					ext_feature_bitmap,	iei = EXTENDED_FEATURE_BITMAP;
+					other, OTHERWISE)"
+		variant (len) "LENGTHTO(u)"
+	};
+
+	type record of BssgpTLV BssgpTLVs;
+
+	/* 10.2.1 */
+	type record BssgpDlUnitdata {
+		BssgpTlli		tlli,
+		BssgpQosProfile		qos_profile,
+		BssgpTLV		pdu_lifetime,
+		/* optional parts */
+		BssgpTLVs		tlvs
+	} with { variant "" };
+
+	/* 10.2.2 */
+	type record BssgpUlUnitdata {
+		BssgpTlli		tlli,
+		BssgpQosProfile		qos_profile,
+		BssgpTLV		cell_id,
+		/* optional parts */
+		BssgpTLVs		tlvs
+	} with { variant "" };
+
+	type record BssgpNormalPdu {
+		BssgpTLVs		tlvs optional
+	} with { variant "" };
+
+	type union BssgpPduUnion {
+		BssgpDlUnitdata		dl_unitdata,
+		BssgpUlUnitdata		ul_unitdata,
+		BssgpNormalPdu		other
+	};
+
+	type record BssgpPdu {
+		BssgpPduType		pdu_type,
+		BssgpPduUnion		u
+	} with {
+		variant (u) "CROSSTAG(
+					dl_unitdata,	pdu_type = DL_UNITDATA;
+					ul_unitdata,	pdu_type = UL_UNITDATA;
+					other,		OTHERWISE)"
+	}
+
+	external function dec_BssgpPdu(in octetstring stream) return BssgpPdu
+		with { extension "prototype(convert) decode(RAW)" };
+
+} with { encode "RAW" };
diff --git a/gprs_gb/Test.ttcn b/gprs_gb/Test.ttcn
new file mode 100644
index 0000000..88a78a8
--- /dev/null
+++ b/gprs_gb/Test.ttcn
@@ -0,0 +1,79 @@
+module Test {
+
+	import from BSSGP_Helper_Functions all;
+	import from BSSGP_Types all;
+
+	type component dummy_CT {
+	}
+
+	function f_assert_prepr(in octetstring a, in octetstring b) {
+		log ("Input: ", a);
+		log ("Expected: ", b);
+		var octetstring a_preprocessed := f_BSSGP_preprocess_pdu(a);
+		log ("Preprocessed: ", a_preprocessed);
+
+		if (a_preprocessed != b) {
+			setverdict(fail);
+		} else {
+			setverdict(pass);
+		}
+	}
+
+	function f_dec_and_log(in octetstring inp) {
+		log("Input: ", inp);
+		var octetstring inp_p := f_BSSGP_preprocess_pdu(inp);
+		log ("Preprocessed: ", inp_p);
+		var BssgpPdu dec := dec_BssgpPdu(inp_p);
+		log("Decoded: ", dec);
+	}
+
+	testcase TC_selftest() runs on dummy_CT {
+		const octetstring c_bvc_reset_pcu := '2204820000078108088832f44000c80051e0'O;
+		const octetstring c_bvc_reset_q := '2204820000078100'O;
+		const octetstring c_status_pcu := '4107810515882204820000078103'O;
+		const octetstring c_reset_ack_q := '2304820000'O;
+		const octetstring c_reset_ack_pcu := '23048200c4'O;
+		const octetstring c_unblock_pcu := '24048200c4'O;
+		const octetstring c_unblock_ack_q := '25048200c4'O;
+		const octetstring c_fc_bvc_pcu := '261e8101058200fa038200c8018200fa1c8200c806820000'O;
+		const octetstring c_fc_bvc_ack_q := '271e8101'O;
+		const octetstring c_gmm_mo_att_req := '01bb146ddd000004088832f44000c80051e000800e003b01c001080103e5e000110a0005f4fb146ddd32f44000c8001d1b53432b37159ef9090070000dd9c6321200e00019b32c642401c0002017057bf0ec'O;
+		const octetstring c_gmm_mt_ac_req := '00bb146ddd0050001682ffff0a8204030e9c41c001081200102198c72477ea104895e8b959acc58b108182f4d045'O;
+		const octetstring c_gmm_mo_ac_resp := '01bb146ddd000004088832f44000c80051e000800e000e01c00508130122fa361f5fdd623d'O;
+		const octetstring c_gmm_mt_att_acc := '00bb146ddd0050001682ffff0a8204030e9841c005080201340432f44000c8001805f4fb146ddd0967d0'O;
+		const octetstring c_gmm_mt_det_req := '00bb146ddd0050001682ffff0a8204030e8941c00908050215f0b6'O;
+		const octetstring c_gmm_mo_att_cpl := '01fb146ddd000004088832f44000c80051e000800e000801c009080339d7bc'O;
+
+		/* single byte length to two byte length */
+		f_assert_prepr('04058101'O, '0405000101'O);
+		f_assert_prepr('040589000102030405060708'O, '04050009000102030405060708'O);
+		/* two byte length to two byte length */
+		f_assert_prepr('0405000101'O, '0405000101'O);
+		/* special case: DL-UD + UL-UD */
+		f_assert_prepr('00aabbccddeeffaa29822342'O, '00aabbccddeeffaa2900022342'O);
+		f_assert_prepr('01aabbccddeeffaa29822342'O, '01aabbccddeeffaa2900022342'O);
+		/* multiple TLVs */
+		f_assert_prepr('234281aa4382bbbb'O, '23420001aa430002bbbb'O);
+		f_assert_prepr('230080'O, '23000000'O);
+
+		f_dec_and_log(c_bvc_reset_pcu);
+		f_dec_and_log(c_bvc_reset_q);
+		f_dec_and_log(c_status_pcu);
+		f_dec_and_log(c_reset_ack_q);
+		f_dec_and_log(c_reset_ack_pcu);
+		f_dec_and_log(c_unblock_pcu);
+		f_dec_and_log(c_unblock_ack_q);
+		f_dec_and_log(c_fc_bvc_pcu);
+		f_dec_and_log(c_fc_bvc_ack_q);
+		f_dec_and_log(c_gmm_mo_att_req);
+		f_dec_and_log(c_gmm_mt_ac_req);
+		f_dec_and_log(c_gmm_mo_ac_resp);
+		f_dec_and_log(c_gmm_mt_att_acc);
+		f_dec_and_log(c_gmm_mt_det_req);
+		f_dec_and_log(c_gmm_mo_att_cpl);
+	}
+
+	control {
+		execute(TC_selftest());
+	}
+};
diff --git a/gprs_gb/gen_links.sh b/gprs_gb/gen_links.sh
new file mode 100755
index 0000000..e8aa176
--- /dev/null
+++ b/gprs_gb/gen_links.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+BASEDIR=~/projects/git
+
+gen_links() {
+	DIR=$1
+	FILES=$*
+	for f in $FILES; do
+		echo "Linking $f"
+		ln -sf $DIR/$f $f
+	done
+}
+
+#DIR=$BASEDIR/titan.TestPorts.UNIX_DOMAIN_SOCKETasp/src
+#FILES="UD_PT.cc  UD_PT.hh  UD_PortType.ttcn  UD_Types.ttcn"
+#gen_links $DIR $FILES
+
+
+
+DIR=../library
+FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn"
+gen_links $DIR $FILES
diff --git a/gprs_gb/regen_makefile.sh b/gprs_gb/regen_makefile.sh
new file mode 100755
index 0000000..9a9abb8
--- /dev/null
+++ b/gprs_gb/regen_makefile.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+FILES="*.ttcn BSSGP_Helper.cc"
+
+ttcn3_makefilegen -f Test.ttcn $FILES
+sed -i -e 's/# TTCN3_DIR = /TTCN3_DIR = \/usr/' Makefile
+sed -i -e 's/LDFLAGS = /LDFLAGS = -L \/usr\/lib\/titan `pkg-config --libs libnetfilter_conntrack`/' Makefile
+sed -i -e 's/TTCN3_LIB = ttcn3-parallel/TTCN3_LIB = ttcn3/' Makefile
+sed -i -e 's/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include -I\/usr\/include\/titan/' Makefile