first steps towards a L1CTL / LAPD test

The idea here is to implement the L1CTL protocol in TTCN-3 so we can
speak it over a unix domain socket (test port) for simple tasks such as
activating dedicated mode.

This can then subsequently be used for LAPDm testing
diff --git a/lapd/L1CTL_Test.ttcn b/lapd/L1CTL_Test.ttcn
new file mode 100644
index 0000000..b0486fe
--- /dev/null
+++ b/lapd/L1CTL_Test.ttcn
@@ -0,0 +1,31 @@
+module L1CTL_Test {
+	import from GSM_Types all;
+	import from Osmocom_Types all;
+	import from L1CTL_Types all;
+
+	const octetstring c_ul_param_req := '1300000000000000001d0000'O;
+	const octetstring c_ul_data_req := '060a0128284018001d000103490615004001c0000000000000000000000000'O;
+	const octetstring c_ul_ccch_mode_req := '1000000002000000'O;
+	const octetstring c_ul_reset_req := '0d00000002000000'O;
+	const octetstring c_ul_dm_est_req := '050000002800000007000367000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005'O;
+	const octetstring c_ul_rach_req := '040000000000000012010008'O;
+
+	const octetstring c_dl_data_ind := '03000000900003670015f5613f3f00002d063f0328e36712ead000002b2b2b2b2b2b2b2b2b2b2b'O;
+
+	type component dummy_CT {
+	};
+
+	testcase TC_si1() runs on dummy_CT {
+		log("L1CTL_PARAM_REQ: ", dec_L1ctlUlMessage(c_ul_param_req));
+		log("L1CTL_DATA_REQ: ", dec_L1ctlUlMessage(c_ul_data_req));
+		log("L1CTL_CCCH_MODE_REQ: ", dec_L1ctlUlMessage(c_ul_ccch_mode_req));
+		log("L1CTL_RESET_REQ: ", dec_L1ctlUlMessage(c_ul_reset_req));
+		log("L1CTL_DM_EST_REQ: ", dec_L1ctlUlMessage(c_ul_dm_est_req));
+		log("L1CTL_RACH_REQ: ", dec_L1ctlUlMessage(c_ul_rach_req));
+		log("L1CTL_DATA_IND: ", dec_L1ctlDlMessage(c_dl_data_ind));
+		setverdict(pass);
+	}
+	control {
+		execute(TC_si1());
+	}
+}
diff --git a/lapd/L1CTL_Types.ttcn b/lapd/L1CTL_Types.ttcn
new file mode 100644
index 0000000..2a0aa4e
--- /dev/null
+++ b/lapd/L1CTL_Types.ttcn
@@ -0,0 +1,271 @@
+/* Data Types / Encoding / Decoding for OsmocomBB L1CTL interface */
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org>, derived from l1ctl_proto.h
+ * (C) 2010 by Harald Welte + Holger Hans Peter Freyther */
+module L1CTL_Types {
+
+	import from General_Types all;
+	import from GSM_Types all;
+	import from Osmocom_Types all;
+
+	type enumerated L1ctlMsgType {
+		L1CTL_NONE,
+		L1CTL_FBSB_REQ,
+		L1CTL_FBSB_CONF,
+		L1CTL_DATA_IND,
+		L1CTL_RACH_REQ,
+		L1CTL_DM_EST_REQ,
+		L1CTL_DATA_REQ,
+		L1CTL_RESET_IND,
+		L1CTL_PM_REQ,		/* power measurement */
+		L1CTL_PM_CONF,		/* power measurement */
+		L1CTL_ECHO_REQ,
+		L1CTL_ECHO_CONF,
+		L1CTL_RACH_CONF,
+		L1CTL_RESET_REQ,
+		L1CTL_RESET_CONF,
+		L1CTL_DATA_CONF,
+		L1CTL_CCCH_MODE_REQ,
+		L1CTL_CCCH_MODE_CONF,
+		L1CTL_DM_REL_REQ,
+		L1CTL_PARAM_REQ,
+		L1CTL_DM_FREQ_REQ,
+		L1CTL_CRYPTO_REQ,
+		L1CTL_SIM_REQ,
+		L1CTL_SIM_CONF,
+		L1CTL_TCH_MODE_REQ,
+		L1CTL_TCH_MODE_CONF,
+		L1CTL_NEIGH_PM_REQ,
+		L1CTL_NEIGH_PM_IND,
+		L1CTL_TRAFFIC_REQ,
+		L1CTL_TRAFFIC_CONF,
+		L1CTL_TRAFFIC_IND
+	} with { variant "FIELDLENGTH(8)" };
+
+	type enumerated L1ctlCcchMode {
+		CCCH_MODE_NONE (0),
+		CCCH_MODE_NON_COMBINED,
+		CCCH_MODE_COMBINED
+	} with { variant "FIELDLENGTH(8)" };
+
+	type enumerated L1ctlNeighMode {
+		NEIGH_MODE_NONE (0),
+		NEIGH_MODE_PM,
+		NEIGH_MODE_SB
+	} with { variant "FIELDLENGTH(8)" };
+
+	type enumerated L1ctlResetType {
+		L1CTL_RES_T_BOOT (0),
+		L1CTL_RES_T_FULL,
+		L1CTL_RES_T_SCHED
+	} with { variant "FIELDLENGTH(8)" };
+
+	const integer TRAFFIC_DATA_LEN := 40;
+
+	type record L1ctlHdrFlags {
+		BIT7	padding,
+		boolean f_done
+	} with { variant "" };
+
+	type record L1ctlHeader {
+		L1ctlMsgType	msg_type,
+		L1ctlHdrFlags	flags,
+		OCT2		padding
+	} with { variant "" };
+
+	type uint8_t RslChanNr;
+	type uint8_t RslLinkId;
+
+	type record L1ctlDlInfo {
+		RslChanNr	chan_nr,
+		RslLinkId	link_id,
+		Arfcn		arfcn,
+		GsmFrameNumber	frame_nr,
+		GsmRxLev	rx_level,
+		uint8_t		snr,
+		uint8_t		num_biterr,
+		uint8_t		fire_crc
+	} with { variant "" };
+
+	type record L1ctlFbsbConf {
+		int16_t		initial_freq_err,
+		uint8_t		result,
+		uint8_t		bsic
+	} with { variant "" };
+
+	type record L1ctlCcchModeConf {
+		L1ctlCcchMode	ccch_mode,
+		OCT3		padding
+	} with { variant "" };
+
+	/* gsm48_chan_mode */
+	type uint8_t L1ctlTchMode;
+
+	type record L1ctlAudioMode {
+		BIT4		padding,
+		boolean		tx_microphone,
+		boolean		tx_traffic_req,
+		boolean		rx_speaker,
+		boolean		rx_traffic_ind
+	} with { variant "" };
+
+	type record L1ctlTchModeConf {
+		L1ctlTchMode	tch_mode,
+		L1ctlAudioMode	audio_mode,
+		OCT2		padding
+	} with { variant "" };
+
+	type record L1ctlDataInd {
+		octetstring	payload length(23)
+	} with { variant "" };
+
+	type union L1ctlDlPayload {
+		L1ctlFbsbConf		fbsb_conf,
+		L1ctlCcchModeConf	ccch_mode_conf,
+		L1ctlTchModeConf	tch_mode_conf,
+		L1ctlDataInd		data_ind,
+		L1ctlTrafficReq		traffic_ind,
+		octetstring		other
+	} with { variant "" };
+
+	type record L1ctlDlMessage {
+		L1ctlHeader	header,
+		L1ctlDlInfo	dl_info optional,
+		L1ctlDlPayload	payload
+	} with { variant (dl_info) "PRESENCE(header.msg_type = L1CTL_FBSB_CONF,
+					     header.msg_type = L1CTL_RACH_CONF,
+					     header.msg_type = L1CTL_DATA_IND,
+					     header.msg_type = L1CTL_DATA_CONF,
+					     header.msg_type = L1CTL_TRAFFIC_IND)"
+		 variant (payload) "CROSSTAG(fbsb_conf, header.msg_type = L1CTL_FBSB_CONF;
+					     ccch_mode_conf, header.msg_type = L1CTL_CCCH_MODE_CONF;
+					     tch_mode_conf, header.msg_type = L1CTL_TCH_MODE_CONF;
+					     data_ind, header.msg_type = L1CTL_DATA_IND;
+					     traffic_ind, header.msg_type = L1CTL_TRAFFIC_IND;
+					     other, OTHERWISE;
+				)" };
+
+	external function enc_L1ctlDlMessage(in L1ctlDlMessage msg) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_L1ctlDlMessage(in octetstring stream) return L1ctlDlMessage
+		with { extension "prototype(convert) decode(RAW)" };
+
+
+	type record L1ctlUlInfo {
+		RslChanNr	chan_nr,
+		RslLinkId	link_id,
+		OCT2		padding
+	} with { variant "" };
+
+	type record L1ctlFbsbFlags {
+		BIT5		padding,
+		boolean		sb,
+		boolean		fb1,
+		boolean		fb0
+	} with { variant "" };
+
+	type record L1ctlFbsbReq {
+		Arfcn		arfcn,
+		uint16_t	timeout_tdma_frames,
+		uint16_t	freq_err_thresh1,
+		uint16_t	freq_err_thresh2,
+		uint8_t		num_freqerr_avg,
+		L1ctlFbsbFlags	flags,
+		uint8_t		sync_info_idx,
+		L1ctlCcchMode	ccch_mode,
+		GsmRxLev	rxlev_exp
+	} with { variant "" };
+
+	type record L1ctlCcchModeReq {
+		L1ctlTchMode	tch_mode,
+		L1ctlAudioMode	audio_mode,
+		OCT2		padding
+	} with { variant "" };
+
+	type record L1ctlTchModeReq {
+		L1ctlTchMode	tch_mode,
+		L1ctlAudioMode	audio_mode,
+		OCT2		padding
+	} with { variant "" };
+
+	type record L1ctlRachReq {
+		uint8_t		ra,
+		uint8_t		combined,
+		uint16_t	offset
+	} with { variant "" };
+
+	type record L1ctlParReq {
+		int8_t		ta,
+		uint8_t		tx_power,
+		OCT2		padding
+	} with { variant "" };
+
+	type record L1ctlH1 {
+		uint8_t		hsn,
+		uint8_t		maio,
+		uint8_t		n,
+		OCT1		padding,
+		bitstring	ma length(64)
+	} with { variant "" };
+
+	type record L1ctlDmEstReq {
+		GsmTsc		tsc,
+		uint8_t		h,
+		Arfcn		arfcn optional,
+		L1ctlH1		hopping optional,
+		L1ctlTchMode	tch_mode,
+		L1ctlAudioMode	audio_mode
+	} with { variant (arfcn) "PRESENCE(h = 0)"
+		 variant (hopping) "PRESENCE(h = 1)" };
+
+	type record L1ctlReset {
+		L1ctlResetType	reset_type,
+		OCT3		padding
+	} with { variant "" };
+
+
+	type record L1ctlTrafficReq {
+		octetstring	data length(TRAFFIC_DATA_LEN)
+	} with { variant "" };
+
+	type union L1ctlUlPayload {
+		L1ctlFbsbReq		fbsb_req,
+		L1ctlCcchModeReq	ccch_mode_req,
+		L1ctlTchModeReq		tch_mode_req,
+		L1ctlRachReq		rach_req,
+		L1ctlParReq		par_req,
+		L1ctlDmEstReq		dm_est_req,
+		L1ctlReset		reset_req,
+		//L1ctlNeighPmReq		neigh_pm_req,
+		L1ctlTrafficReq		traffic_req,
+		octetstring		other
+	} with { variant "" };
+
+	type record L1ctlUlMessage {
+		L1ctlHeader	header,
+		L1ctlUlInfo	ul_info optional,
+		L1ctlUlPayload	payload
+	} with { variant (ul_info) "PRESENCE(header.msg_type = L1CTL_RACH_REQ,
+					     header.msg_type = L1CTL_PARAM_REQ,
+					     header.msg_type = L1CTL_CRYPTO_REQ,
+					     header.msg_type = L1CTL_DATA_REQ,
+					     header.msg_type = L1CTL_DM_EST_REQ,
+					     header.msg_type = L1CTL_DM_FREQ_REQ,
+					     header.msg_type = L1CTL_DM_REL_REQ,
+					     header.msg_type = L1CTL_TRAFFIC_REQ)"
+		 variant (payload) "CROSSTAG(fbsb_req, header.msg_type = L1CTL_FBSB_REQ;
+					     ccch_mode_req, header.msg_type = L1CTL_CCCH_MODE_REQ;
+					     tch_mode_req, header.msg_type = L1CTL_TCH_MODE_REQ;
+					     rach_req, header.msg_type = L1CTL_RACH_REQ;
+					     par_req, header.msg_type = L1CTL_PARAM_REQ;
+					     dm_est_req, header.msg_type = L1CTL_DM_EST_REQ;
+					     reset_req, header.msg_type = L1CTL_RESET_REQ;
+					     traffic_req, header.msg_type = L1CTL_TRAFFIC_REQ;
+					     other, OTHERWISE;
+				)" };
+
+	external function enc_L1ctlUlMessage(in L1ctlUlMessage msg) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_L1ctlUlMessage(in octetstring stream) return L1ctlUlMessage
+		with { extension "prototype(convert) decode(RAW)" };
+
+} with { encode "RAW" };
diff --git a/lapd/LAPDm_Types.ttcn b/lapd/LAPDm_Types.ttcn
new file mode 100644
index 0000000..c52b96c
--- /dev/null
+++ b/lapd/LAPDm_Types.ttcn
@@ -0,0 +1,156 @@
+/* LAPDm definitiona according to 3GPP TS 44.006 */
+/* (C) 2017 bh Harald Welte <laforge@gnumonks.org> */
+module LAPDm_Types {
+
+	import from General_Types all;
+	import from Osmocom_Types all;
+
+	type uint3_t LapdmSapi;
+	type BIT2 LapdmSBits;
+	type BIT3 LapdmUBits;
+	type BIT2 LapdmU2Bits;
+
+	type record LapdmLengthIndicator {
+		uint6_t len,
+		boolean m,
+		uint1_t el
+	} with { variant "" };
+
+	/* TS 44.006 Figure 4 */
+	type record LapdmAddressField {
+		BIT1		spare,
+		uint2_t		lpd (0),
+		LapdmSapi	sapi,
+		boolean		c_r,
+		boolean		ea
+	} with { variant "" };
+
+	template LapdmAddressField tr_LapdmAddr(LapdmSapi sapi, boolean c_r) := {
+		spare := '0'B,
+		lpd := 0,
+		sapi := sapi,
+		c_r := c_r,
+		ea := true
+	};
+
+	type record LapdmCtrlI {
+		uint3_t n_r,
+		boolean	p,
+		uint3_t	n_s,
+		BIT1	spare ('0'B)
+	} with { variant "" };
+
+	type record LapdmCtrlS {
+		uint3_t		n_r,
+		boolean		p_f,
+		LapdmSBits	s,
+		BIT2	spare ('01'B)
+	} with { variant "" };
+
+	type record LapdmCtrlU {
+		LapdmUBits	u,
+		boolean		p_f,
+		LapdmU2Bits	u2,
+		BIT2	spare ('11'B)
+	} with { variant "" };
+
+	/* TS 44.006 Table 3 */
+	type union LapdmCtrl {
+		LapdmCtrlI	i,
+		LapdmCtrlS	s,
+		LapdmCtrlU	u
+	} with { variant "TAG(i, spare = '0'B;
+			      s, spare = '01'B;
+			      u, spare = '11'B)" };
+
+	template LapdmCtrl t_LapdmCtrlS := {
+		s := { n_r := ?, p_f := ?, s:= ?, spare := '01'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlU := {
+		u := { u := ?, p_f := ?, u2 := ?, spare := '11'B }
+	};
+
+	/* TS 44.006 Table 4 */
+	template LapdmCtrl t_LapdmCtrlI(template uint3_t nr, template uint3_t ns, template boolean p) := {
+		i := { n_r := nr, p := p, n_s := ns, spare := '0'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlRR(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+		s := { n_r := nr, p_f := pf, s := '00'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlRNR(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+		s := { n_r := nr, p_f := pf, s := '01'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlREJ(template uint3_t nr, template boolean pf) modifies t_LapdmCtrlS := {
+		s := { n_r := nr, p_f := pf, s := '10'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlSABM(template boolean p) modifies t_LapdmCtrlU := {
+		u := { u := '001'B, p_f := p, u2 := '11'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlDM(template boolean f) modifies t_LapdmCtrlU := {
+		u := { u := '000'B, p_f := f, u2 := '11'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlUI(template boolean p) modifies t_LapdmCtrlU := {
+		u := { u := '000'B, p_f := p, u2 := '00'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlDISC(template boolean p) modifies t_LapdmCtrlU := {
+		u := { u := '010'B, p_f := p, u2 := '00'B }
+	};
+
+	template LapdmCtrl t_LapdmCtrlUA(template boolean f) modifies t_LapdmCtrlU := {
+		u := { u := '011'B, p_f := f, u2 := '00'B }
+	};
+
+	/* Format A is used on DCCHs for frames where there is no information field */
+	type record LapdmFrameA {
+		LapdmAddressField	addr,
+		LapdmCtrl		ctrl,
+		LapdmLengthIndicator	len
+	} with { variant "" };
+
+	external function enc_LapdmFrameA(in LapdmFrameA si) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_LapdmFrameA(in octetstring stream) return LapdmFrameA
+		with { extension "prototype(convert) decode(RAW)" };
+
+	/* Formats B, Bter and B4 are used on DCCHs for frames containing an information field: 
+	/* - format Bter is used on request of higher layers if and only if short L2 header type 1 is
+	 *   supported and a UI command is to be transmitted on SAPI 0 */
+	/* - format B4 is used for UI frames transmitted by the network on SACCH; */
+	/* - format B is applied in all other cases. */
+	/* Format Bbis is used only on BCCH, PCH, NCH, and AGCH.
+
+	/* Format B */
+	type record LapdmFrameB {
+		LapdmAddressField	addr,
+		LapdmCtrl		ctrl,
+		LapdmLengthIndicator	len,
+		octetstring		payload
+	} with { variant "" };
+
+	external function enc_LapdmFrameB(in LapdmFrameB si) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_LapdmFrameB(in octetstring stream) return LapdmFrameB
+		with { extension "prototype(convert) decode(RAW)" };
+
+
+	/* Format B4 */
+	type record LapdmFrameB4 {
+		LapdmAddressField	addr,
+		LapdmCtrl		ctrl,
+		octetstring		payload
+	} with { variant "" };
+
+	external function enc_LapdmFrameB4(in LapdmFrameB4 si) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_LapdmFrameB4(in octetstring stream) return LapdmFrameB4
+		with { extension "prototype(convert) decode(RAW)" };
+
+} with { encode "RAW" };
diff --git a/lapd/gen_links.sh b/lapd/gen_links.sh
new file mode 100755
index 0000000..630b156
--- /dev/null
+++ b/lapd/gen_links.sh
@@ -0,0 +1,16 @@
+#!/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=../sysinfo
+FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn"
+gen_links $DIR $FILES
diff --git a/lapd/regen_makefile.sh b/lapd/regen_makefile.sh
new file mode 100755
index 0000000..a772686
--- /dev/null
+++ b/lapd/regen_makefile.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+FILES="*.ttcn"
+
+ttcn3_makefilegen -f L1CTL_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
diff --git a/sysinfo/Osmocom_Types.ttcn b/sysinfo/Osmocom_Types.ttcn
index 6fe1b57..0c26f02 100644
--- a/sysinfo/Osmocom_Types.ttcn
+++ b/sysinfo/Osmocom_Types.ttcn
@@ -6,6 +6,7 @@
 	type integer int8_t (-128..127) with { variant "8 bit" };
 	type integer int16_t (-32768..32767) with { variant "16 bit" };
 
+	type integer uint1_t (0..1) with { variant "unsigned 1 bit" };
 	type integer uint2_t (0..3) with { variant "unsigned 2 bit" };
 	type integer uint3_t (0..7) with { variant "unsigned 3 bit" };
 	type integer uint4_t (0..15) with { variant "unsigned 4 bit" };