Expand IPA emulaitpn code + example to include CCM and SCCP binding
diff --git a/ipa/IPA_Test.ttcn b/ipa/IPA_Test.ttcn
index 925f86f..6c00dce 100644
--- a/ipa/IPA_Test.ttcn
+++ b/ipa/IPA_Test.ttcn
@@ -1,12 +1,24 @@
 module IPA_Test {
 
-	import from IPA_CodecPort all;
-	import from IPA_CodecPort_CtrlFunct all;
 	import from IPL4asp_Types all;
 
-	type component ipa_CT {
-		port IPA_CODEC_PT IPA;
+	import from IPA_Emulation all;
+
+	import from SCCP_Types all;
+	import from SCCPasp_Types all;
+	import from SCCP_Emulation all;
+
+	type component test_CT {
+		/* component references */
+		var IPA_Emulation_CT vc_IPA;
+		var SCCP_CT vc_SCCP;
+		/* test port to SCCP emulation */
+		port SCCPasp_PT SCCP;
+
 		var boolean g_initialized := false;
+		var octetstring g_sio;
+		var MSC_SCCP_MTP3_parameters g_sccp_pars;
+		var SCCP_PAR_Address g_sccp_addr_own, g_sccp_addr_peer;
 
 		var ConnectionId g_ipa_conn_id := -1;
 	}
@@ -16,27 +28,82 @@
 		charstring mp_local_ip := "127.0.0.1";
 		PortNumber mp_remote_port := 3002;
 		charstring mp_remote_ip := "127.0.0.1";
+
+		charstring mp_sccp_service_type := "mtp3_itu";
+
+		integer mp_own_pc := 196;
+		integer mp_own_ssn := 254;
+
+		integer mp_peer_pc := 185;	/* 0.23.1 */
+		integer mp_peer_ssn := 254;
 	}
 
-	private function f_init() runs on ipa_CT {
+/* construct a SCCP_PAR_Address with just PC + SSN and no GT */
+template (value) SCCP_PAR_Address ts_SccpAddr_PC_SSN(integer pc, integer ssn) := {
+	addressIndicator := {
+		pointCodeIndic := '1'B,
+		ssnIndicator := '1'B,
+		globalTitleIndic := '0000'B,
+		routingIndicator := '1'B
+	},
+	signPointCode := SCCP_SPC_int2bit(pc, mp_sccp_service_type, '83'O),
+	//signPointCode := SCCP_SPC_int2bit(pc, mp_sccp_service_type, g_sio),
+	subsystemNumber := ssn,
+	globalTitle := omit
+}
+
+
+function init_pars() runs on test_CT {
+	g_sio := '83'O;
+	g_sccp_pars := {
+		sio := {
+			ni := substr(oct2bit(g_sio),0,2),
+			prio := substr(oct2bit(g_sio),2,2),
+			si := substr(oct2bit(g_sio),4,4)
+		},
+		opc := mp_own_pc,
+		dpc := mp_peer_pc,
+		sls := 0,
+		sccp_serviceType := mp_sccp_service_type,
+		ssn := mp_own_ssn
+	};
+	g_sccp_addr_own := valueof(ts_SccpAddr_PC_SSN(mp_own_pc, mp_own_ssn));
+	g_sccp_addr_peer := valueof(ts_SccpAddr_PC_SSN(mp_peer_pc, mp_peer_ssn));
+}
+
+	private function f_init() runs on test_CT {
 		var Result res;
 
 		if (g_initialized == true) {
 			return;
 		}
 		g_initialized := true;
-		map(self:IPA, system:IPA_CODEC_PT);
 
-		res := IPA_CodecPort_CtrlFunct.f_IPL4_connect(IPA, mp_remote_ip, mp_remote_port,
-							mp_local_ip, mp_local_port, 0, { tcp:={} });
-		g_ipa_conn_id := res.connId;
+		init_pars();
+
+		/* create components */
+		vc_IPA := IPA_Emulation_CT.create;
+		vc_SCCP := SCCP_CT.create;
+
+		map(vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
+
+		/* connect MTP3 service provider (IPA) to lower side of SCCP */
+		connect(vc_IPA:MTP3_SP_PORT, vc_SCCP:MTP3_SCCP_PORT);
+
+		/* connect us to upper side of SCCP */
+		connect(self:SCCP, vc_SCCP:SCCP_SP_PORT);
+
+		vc_IPA.start(IPA_Emulation.ScanEvents());
+		vc_SCCP.start(SCCPStart(g_sccp_pars));
+
+		//IPA_Emulation.f_connect(mp_remote_ip, mp_remote_port, mp_local_ip, mp_local_port);
 	}
 
-	testcase TC_recv_dump() runs on ipa_CT {
+	testcase TC_recv_dump() runs on test_CT {
 		f_init();
 
 		while (true) {
-			IPA.receive;
+			SCCP.receive;
 		}
 	}
 
diff --git a/ipa/gen_links.sh b/ipa/gen_links.sh
index 775ae71..11d2217 100755
--- a/ipa/gen_links.sh
+++ b/ipa/gen_links.sh
@@ -27,6 +27,17 @@
 FILES="IPL4asp_Functions.ttcn  IPL4asp_PT.cc  IPL4asp_PT.hh IPL4asp_PortType.ttcn  IPL4asp_Types.ttcn  IPL4asp_discovery.cc IPL4asp_protocol_L234.hh"
 gen_links $DIR $FILES
 
+# required by SCCP Emulation
+DIR=../MTP3asp_CNL113337/src
+FILES="MTP3asp_PortType.ttcn  MTP3asp_Types.ttcn"
+gen_links $DIR $FILES
+
+DIR=../SCCP_CNL113341/src
+FILES="SCCP_Emulation.ttcn  SCCP_EncDec.cc  SCCP_Mapping.ttcnpp  SCCP_Types.ttcn  SCCPasp_Types.ttcn"
+gen_links $DIR $FILES
+ln -s SCCP_Mapping.ttcnpp SCCP_Mapping.ttcn
+
+
 DIR=../library
-FILES="Osmocom_Types.ttcn IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc"
+FILES="General_Types.ttcn Osmocom_Types.ttcn IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc"
 gen_links $DIR $FILES
diff --git a/ipa/regen_makefile.sh b/ipa/regen_makefile.sh
index a5d05de..1acda32 100755
--- a/ipa/regen_makefile.sh
+++ b/ipa/regen_makefile.sh
@@ -2,7 +2,7 @@
 
 MAIN=IPA_Test.ttcn
 
-FILES="*.ttcn IPA_CodecPort_CtrlFunctDef.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc"
+FILES="*.ttcn SCCP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc"
 
 ttcn3_makefilegen -l -f $MAIN $FILES
 sed -i -e 's/# TTCN3_DIR = /TTCN3_DIR = \/usr/' Makefile
diff --git a/library/IPA_Emulation.ttcn b/library/IPA_Emulation.ttcn
new file mode 100644
index 0000000..31f5c27
--- /dev/null
+++ b/library/IPA_Emulation.ttcn
@@ -0,0 +1,158 @@
+module IPA_Emulation {
+
+import from IPA_Types all;
+import from IPA_CodecPort all;
+import from IPA_CodecPort_CtrlFunct all;
+import from IPL4asp_Types all;
+import from MTP3asp_Types all;
+import from MTP3asp_PortType all;
+
+/*
+modulepar {
+}
+*/
+
+type component IPA_Emulation_CT {
+	/* down-facing port to IPA codec port */
+	port IPA_CODEC_PT IPA_PORT;
+	/* up-facing port to SCCP */
+	port MTP3asp_SP_PT MTP3_SP_PORT;
+
+	var boolean g_initialized := false;
+	var ConnectionId g_ipa_conn_id := -1;
+}
+
+function f_connect(charstring remote_host, PortNumber remote_port,
+		   charstring local_host, PortNumber local_port) runs on IPA_Emulation_CT {
+	var Result res;
+	res := IPA_CodecPort_CtrlFunct.f_IPL4_connect(IPA_PORT, remote_host, remote_port,
+						local_host, local_port, 0, { tcp:={} });
+	g_ipa_conn_id := res.connId;
+}
+
+template ASP_MTP3_TRANSFERind ts_MTP3_XFER_ind(integer opc, octetstring data) := {
+	sio := { '10'B, '00'B, '0011'B },
+	opc := opc,
+	dpc := 0,
+	sls := 0,
+	data := data
+}
+
+
+private template IpaCcmRespPart t_IdRespPart(IpaCcmIdTag tag, charstring payload) := {
+	len := 0,	/* overwritten by codec */
+	tag := tag,
+	data := payload
+}
+
+/* build IPA CCM ID RESP response from IPA CCM GET */
+private function f_ccm_make_id_resp(PDU_IPA_CCM get) return PDU_IPA_CCM {
+	var integer i;
+	var PDU_IPA_CCM resp := {
+		msg_type := IPAC_MSGT_ID_RESP,
+		u := {
+			resp := {}
+		}
+	}
+
+	for (i := 0; i < sizeof(get.u.get); i := i + 1) {
+		var IpaCcmIdTag tag := get.u.get[i].tag;
+		var charstring foo;
+		select (tag) {
+			case (IPAC_IDTAG_UNIT) {
+				foo := "0/1/2";
+			}
+			case (IPAC_IDTAG_UNITNAME) {
+				foo := "mahlzeit";
+			}
+			case else {
+				foo := "foo";
+			}
+		}
+		resp.u.resp[sizeof(resp.u.resp)] := valueof(t_IdRespPart(tag, foo));
+	}
+
+	return resp;
+}
+
+/* transmit IPA CCM message */
+private function f_ccm_tx(PDU_IPA_CCM ccm) runs on IPA_Emulation_CT {
+	var IPA_Send ipa_tx := {
+		connId := g_ipa_conn_id,
+		streamId := IPAC_PROTO_CCM,
+		msg := enc_PDU_IPA_CCM(ccm)
+	}
+	log("CCM Tx:", ccm);
+	IPA_PORT.send(ipa_tx);
+}
+
+template PDU_IPA_CCM ts_IPA_PONG := {
+	msg_type := IPAC_MSGT_PONG,
+	u := omit
+}
+
+template PDU_IPA_CCM ts_IPA_ACK := {
+	msg_type := IPAC_MSGT_ID_ACK,
+	u := omit
+}
+
+/* receive IPA CCM message */
+private function f_ccm_rx(PDU_IPA_CCM ccm) runs on IPA_Emulation_CT {
+	select (ccm.msg_type) {
+		case (IPAC_MSGT_PING) {
+			f_ccm_tx(valueof(ts_IPA_PONG));
+		}
+		case (IPAC_MSGT_ID_ACK) {
+			f_ccm_tx(valueof(ts_IPA_ACK));
+		}
+		case (IPAC_MSGT_ID_GET) {
+			f_ccm_tx(f_ccm_make_id_resp(ccm));
+		}
+		case else {
+			log("Unknown/unsupported IPA CCM message type", ccm);
+		}
+	}
+}
+
+
+function ScanEvents() runs on IPA_Emulation_CT {
+	var IPA_RecvFrom ipa_rx;
+	var ASP_MTP3_TRANSFERreq mtp_req;
+
+	f_connect("127.0.0.1", 5000, "127.0.0.1", 49999);
+
+	while (true) {
+		alt {
+		/* Received IPA -> up into SCCP stack */
+		[] IPA_PORT.receive(IPA_RecvFrom: ?) -> value ipa_rx {
+			select (ipa_rx.streamId) {
+			case (IPAC_PROTO_CCM) {
+				var PDU_IPA_CCM ccm := dec_PDU_IPA_CCM(ipa_rx.msg);
+				log("CCM Rx:", ccm);
+				f_ccm_rx(ccm);
+				}
+			case (IPAC_PROTO_SCCP) {
+				var ASP_MTP3_TRANSFERind mtp;
+				mtp := valueof(ts_MTP3_XFER_ind(0, ipa_rx.msg));
+				MTP3_SP_PORT.send(mtp);
+				}
+			case else {
+				log("Unknown/unsupported IPA Stream ID", ipa_rx);
+				}
+			}
+		}
+
+		/* Received SCCP -> down into IPA */
+		[] MTP3_SP_PORT.receive(ASP_MTP3_TRANSFERreq: ?) -> value mtp_req {
+			var IPA_Send ipa_tx := {
+				connId := g_ipa_conn_id,
+				streamId := IPAC_PROTO_SCCP,
+				msg := mtp_req.data
+			}
+			IPA_PORT.send(ipa_tx);
+		}
+		}
+	}
+}
+
+}
diff --git a/library/IPA_Types.ttcn b/library/IPA_Types.ttcn
index ceb31ce..1636f44 100644
--- a/library/IPA_Types.ttcn
+++ b/library/IPA_Types.ttcn
@@ -4,7 +4,24 @@
 
 import from Osmocom_Types all;
 
-type uint8_t IpaStreamId;
+type enumerated IpaStreamId {
+	IPAC_PROTO_RSL		(0),
+	IPAC_PROTO_OSMO		(238),
+	IPAC_PROTO_MGCP_OLD	(252),
+	IPAC_PROTO_SCCP		(253),
+	IPAC_PROTO_CCM		(254),
+	IPAC_PROTO_OML		(255)
+} with { variant "FIELDLENGTH(8)" }
+
+type enumerated IpaExtStreamId {
+	IPAC_PROTO_EXT_CTRL	('00'H),
+	IPAC_PROTO_EXT_MGCP	('01'H),
+	IPAC_PROTO_EXT_LAC	('02'H),
+	IPAC_PROTO_EXT_SMSC	('03'H),
+	IPAC_PROTO_EXT_ORC	('04'H),
+	IPAC_PROTO_EXT_GSUP	('05'H),
+	IPAC_PROTO_EXT_OAP	('06'H)
+} with { variant "FIELDLENGTH(8)" }
 
 external function enc_PDU_IPA(in PDU_IPA pdu) return octetstring
 with { extension "prototype(convert)"
@@ -22,10 +39,67 @@
 	IpaStreamId	streamId,
 	octetstring	payloadData
 } with {
-	variant (lengthInd) "LENGTHTO(lengthInd,streamId,payloadData)";
+	variant (lengthInd) "LENGTHTO(payloadData)";
 	variant (lengthInd) "FIELDLENGTH(16)";
 	variant (lengthInd) "BYTEORDER(last)";
-	encode "RAW";
 }
 
+
+type enumerated IpaCcmMsgtype {
+	IPAC_MSGT_PING		('00'H),
+	IPAC_MSGT_PONG		('01'H),
+	IPAC_MSGT_ID_GET	('04'H),
+	IPAC_MSGT_ID_RESP	('05'H),
+	IPAC_MSGT_ID_ACK	('06'H)
+} with { variant "FIELDLENGTH(8)" }
+
+type enumerated IpaCcmIdTag {
+	IPAC_IDTAG_SERNR	('00'H),
+	IPAC_IDTAG_UNITNAME	('01'H),
+	IPAC_IDTAG_LOCATION1	('02'H),
+	IPAC_IDTAG_LOCATION2	('03'H),
+	IPAC_IDTAG_EQUIPVERS	('04'H),
+	IPAC_IDTAG_SWVERSION	('05'H),
+	IPAC_IDTAG_IPADDR	('06'H),
+	IPAC_IDTAG_MACADDR	('07'H),
+	IPAC_IDTAG_UNIT		('08'H)
+} with { variant "FIELDLENGTH(8)" }
+
+
+type record IpaCcmGetPart {
+	uint8_t		one,
+	IpaCcmIdTag	tag
 }
+type set of IpaCcmGetPart IpaCcmIdGet;
+
+type record IpaCcmRespPart {
+	uint16_t	len,
+	IpaCcmIdTag	tag,
+	charstring	data
+} with { variant (len) "LENGTHTO(data,tag)" }
+type set of IpaCcmRespPart IpaCcmIdResp;
+
+type union IpaCcmUnion {
+	IpaCcmIdGet	get,
+	IpaCcmIdResp	resp,
+	octetstring	other
+}
+
+type record PDU_IPA_CCM {
+	IpaCcmMsgtype	msg_type,
+	IpaCcmUnion	u optional
+} with { variant "" };
+
+
+external function enc_PDU_IPA_CCM(in PDU_IPA_CCM pdu) return octetstring
+with { extension "prototype(convert)"
+       extension "encode(RAW)"
+     }
+
+external function dec_PDU_IPA_CCM(in octetstring stream) return PDU_IPA_CCM
+with { extension "prototype(convert)"
+       extension "decode(RAW)"
+     }
+
+
+} with { encode "RAW" }