ipa: Pull everything together: L3->BSSAP->SCCP->IPA
diff --git a/ipa/IPA_Test.ttcn b/ipa/IPA_Test.ttcn
index 6c00dce..4e87fa6 100644
--- a/ipa/IPA_Test.ttcn
+++ b/ipa/IPA_Test.ttcn
@@ -1,42 +1,49 @@
 module IPA_Test {
 
-	import from IPL4asp_Types all;
+import from IPL4asp_Types all;
 
-	import from IPA_Emulation all;
+import from IPA_Emulation all;
 
-	import from SCCP_Types all;
-	import from SCCPasp_Types all;
-	import from SCCP_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;
+import from MobileL3_Types all;
+import from MobileL3_CommonIE_Types all;
+import from L3_Templates all;
 
-		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;
+import from BSSAP_Types all;
+import from BSSMAP_Templates all;
 
-		var ConnectionId g_ipa_conn_id := -1;
-	}
+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;
 
-	modulepar {
-		PortNumber mp_local_port := 0;
-		charstring mp_local_ip := "127.0.0.1";
-		PortNumber mp_remote_port := 3002;
-		charstring mp_remote_ip := "127.0.0.1";
+	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;
 
-		charstring mp_sccp_service_type := "mtp3_itu";
+	var ConnectionId g_ipa_conn_id := -1;
+}
 
-		integer mp_own_pc := 196;
-		integer mp_own_ssn := 254;
+modulepar {
+	PortNumber mp_local_port := 0;
+	charstring mp_local_ip := "127.0.0.1";
+	PortNumber mp_remote_port := 3002;
+	charstring mp_remote_ip := "127.0.0.1";
 
-		integer mp_peer_pc := 185;	/* 0.23.1 */
-		integer mp_peer_ssn := 254;
-	}
+	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;
+}
 
 /* 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) := {
@@ -53,6 +60,21 @@
 }
 
 
+function f_gen_cl3() return PDU_BSSAP {
+	var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV('901770123456789'H));
+	var PDU_ML3_MS_NW l3 := valueof(ts_CM_SERV_REQ('0001'B, mi));
+	var BSSMAP_IE_CellIdentifier cell_id := valueof(ts_CellID_LAC_CI(23, 42));
+	var PDU_BSSAP bssap := valueof(ts_BSSMAP_ComplL3(cell_id, enc_PDU_ML3_MS_NW(l3)));
+	return bssap;
+}
+
+function f_send_bssap_cc(PDU_BSSAP bssap) runs on test_CT {
+	var ASP_SCCP_N_CONNECT_req prim;
+	prim := valueof(t_ASP_N_CONNECT_req(g_sccp_addr_peer, g_sccp_addr_own, omit, omit,
+				    enc_PDU_BSSAP(bssap), 23, omit));
+	SCCP.send(prim);
+}
+
 function init_pars() runs on test_CT {
 	g_sio := '83'O;
 	g_sccp_pars := {
@@ -71,43 +93,47 @@
 	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;
+private function f_init() runs on test_CT {
+	var Result res;
 
-		if (g_initialized == true) {
-			return;
-		}
-		g_initialized := true;
-
-		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);
+	if (g_initialized == true) {
+		return;
 	}
+	g_initialized := true;
 
-	testcase TC_recv_dump() runs on test_CT {
-		f_init();
+	init_pars();
 
-		while (true) {
-			SCCP.receive;
-		}
+	/* 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 test_CT {
+	f_init();
+
+	var PDU_BSSAP bssap := f_gen_cl3();
+	f_send_bssap_cc(bssap);
+
+	while (true) {
+		SCCP.receive;
 	}
+}
 
-	control {
-		execute( TC_recv_dump() );
-	}
+control {
+	execute( TC_recv_dump() );
+}
+
 }
diff --git a/ipa/gen_links.sh b/ipa/gen_links.sh
index 11d2217..d9bc7a4 100755
--- a/ipa/gen_links.sh
+++ b/ipa/gen_links.sh
@@ -37,7 +37,15 @@
 gen_links $DIR $FILES
 ln -s SCCP_Mapping.ttcnpp SCCP_Mapping.ttcn
 
+DIR=$BASEDIR/titan.ProtocolModules.BSSMAP_v11.2.0/src
+FILES="BSSAP_Types.ttcn"
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.ProtocolModules.MobileL3_v13.4.0/src
+FILES="MobileL3_CC_Types.ttcn MobileL3_CommonIE_Types.ttcn MobileL3_GMM_SM_Types.ttcn MobileL3_MM_Types.ttcn MobileL3_RRM_Types.ttcn MobileL3_SMS_Types.ttcn MobileL3_SS_Types.ttcn MobileL3_Types.ttcn"
+gen_links $DIR $FILES
+
 
 DIR=../library
-FILES="General_Types.ttcn 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 L3_Templates.ttcn BSSMAP_Templates.ttcn"
 gen_links $DIR $FILES
diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn
new file mode 100644
index 0000000..aea1e01
--- /dev/null
+++ b/library/BSSMAP_Templates.ttcn
@@ -0,0 +1,170 @@
+module BSSMAP_Templates {
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from BSSAP_Types all;
+
+type integer BssmapCause;
+
+template PDU_BSSAP ts_BSSAP_BSSMAP := {
+	discriminator := '0'B,
+	spare := '0000000'B,
+	dlci := omit,
+	lengthIndicator := 0,	/* overwritten by codec */
+	pdu := ?
+}
+
+template PDU_BSSAP tr_BSSAP_BSSMAP := {
+	discriminator := '0'B,
+	spare := '0000000'B,
+	dlci := omit,
+	lengthIndicator := ?,
+	pdu := {
+		bssmap := ?
+	}
+}
+
+template (value) BSSMAP_IE_Cause ts_BSSMAP_IE_Cause(BssmapCause val) := {
+	elementIdentifier := '04'O,
+	lengthIndicator := 0,
+	causeValue := int2bit(val, 7),
+	extensionCauseValue := '0'B,
+	spare1 := omit
+}
+
+template (value) PDU_BSSAP ts_BSSMAP_Reset(BssmapCause cause) modifies ts_BSSAP_BSSMAP := {
+	pdu := {
+		bssmap := {
+			reset := {
+				messageType := '30'O,
+				cause := ts_BSSMAP_IE_Cause(cause),
+				a_InterfaceSelectorForReset := omit
+			}
+		}
+	}
+}
+
+template (value) PDU_BSSAP ts_BSSMAP_ResetAck modifies ts_BSSAP_BSSMAP := {
+	pdu := {
+		bssmap := {
+			resetAck := {
+				messageType := '31'O,
+				a_InterfaceSelectorForReset := omit
+			}
+		}
+	}
+}
+
+template PDU_BSSAP tr_BSSMAP_ResetAck modifies tr_BSSAP_BSSMAP := {
+	pdu := {
+		bssmap := {
+			resetAck := {
+				messageType := '31'O,
+				a_InterfaceSelectorForReset := *
+			}
+		}
+	}
+}
+
+template BSSMAP_IE_CellIdentifier ts_BSSMAP_IE_CellID := {
+	elementIdentifier := '05'O,
+	lengthIndicator := 0,
+	cellIdentifierDiscriminator := '0000'B,
+	spare1_4 := '0000'B,
+	cellIdentification := ?
+}
+
+type uint16_t BssmapLAC;
+type uint16_t BssmapCI;
+
+/*
+template BSSMAP_IE_CellIdentifier ts_CellId_CGI(mcc, mnc, lac, ci)
+modifies ts_BSSMAP_IE_CellID := {
+	cellIdentification := {
+		cI_LAC_CGI := {
+			mnc_mcc := FIXME,
+			lac := int2oct(lac, 2),
+			ci := int2oct(ci, 2)
+		}
+	}
+}
+*/
+
+template BSSMAP_IE_CellIdentifier ts_CellID_LAC_CI(BssmapLAC lac, BssmapCI ci)
+modifies ts_BSSMAP_IE_CellID := {
+	cellIdentification := {
+		cI_LAC_CI := {
+			lac := int2oct(lac, 2),
+			ci := int2oct(ci, 2)
+		}
+	}
+}
+
+template BSSMAP_IE_CellIdentifier ts_CellId_CI(BssmapCI ci)
+modifies ts_BSSMAP_IE_CellID := {
+	cellIdentification := {
+		cI_CI := int2oct(ci, 2)
+	}
+}
+
+template BSSMAP_IE_CellIdentifier ts_CellId_none
+modifies ts_BSSMAP_IE_CellID := {
+	cellIdentification := {
+		cI_noCell := ''O
+	}
+}
+
+
+template BSSMAP_IE_Layer3Information ts_BSSMAP_IE_L3Info(octetstring l3info) := {
+	elementIdentifier := '17'O,
+	lengthIndicator := 0,
+	layer3info := l3info
+}
+
+template PDU_BSSAP ts_BSSMAP_ComplL3(BSSMAP_IE_CellIdentifier cell_id, octetstring l3_info)
+modifies ts_BSSAP_BSSMAP := {
+	pdu := {
+		bssmap := {
+			completeLayer3Information := {
+				messageType := '57'O,
+				cellIdentifier := cell_id,
+				layer3Information := ts_BSSMAP_IE_L3Info(l3_info),
+				chosenChannel := omit,
+				lSAIdentifier := omit,
+				aPDU := omit,
+				codecList := omit,
+				redirectAttemptFlag := omit,
+				sendSequenceNumber := omit,
+				iMSI := omit
+			}
+		}
+	}
+}
+
+template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_IE_CellIdentifierList cid_list)
+modifies ts_BSSAP_BSSMAP := {
+	pdu := {
+		bssmap := {
+			handoverRequired := {
+				messageType := '11'O,
+				cause := ts_BSSMAP_IE_Cause(cause),
+				responseRequest := omit,
+				cellIdentifierList := cid_list,
+				circuitPoolList := omit,
+				currentChannelType1 := omit,
+				speechVersion := omit,
+				queueingIndicator := omit,
+				oldToNewBSSInfo := omit,
+				sourceToTargetRNCTransparentInfo := omit,
+				sourceToTargetRNCTransparentInfoCDMA := omit,
+				gERANClassmark := omit,
+				talkerPriority := omit,
+				speechCodec := omit,
+				cSG_Identifier := omit
+			}
+		}
+	}
+}
+
+
+}
diff --git a/library/IPA_CodecPort.ttcn b/library/IPA_CodecPort.ttcn
index 70d3ccb..ad94a92 100644
--- a/library/IPA_CodecPort.ttcn
+++ b/library/IPA_CodecPort.ttcn
@@ -16,6 +16,19 @@
 		octetstring	msg
 	}
 
+
+	/* 'stream' contains the octets received so far, we must return the total length */
+	function f_IPA_getMsgLen(in octetstring stream, inout ro_integer args) return integer {
+		var integer stream_len := lengthof(stream);
+		var integer len;
+		if (stream_len < 2) {
+			/* insufficient length to determine the length */
+			return -1;
+		}
+		len := 3 + oct2int(substr(stream, 0, 2));
+		return len;
+	}
+
 	private function IPL4_to_IPA_RecvFrom(in ASP_RecvFrom pin, out IPA_RecvFrom pout) {
 		var PDU_IPA ipa := dec_PDU_IPA(pin.msg);
 		pout.connId := pin.connId;
diff --git a/library/IPA_Emulation.ttcn b/library/IPA_Emulation.ttcn
index 31f5c27..4d21bb4 100644
--- a/library/IPA_Emulation.ttcn
+++ b/library/IPA_Emulation.ttcn
@@ -12,11 +12,22 @@
 }
 */
 
+type record ASP_IPA_Unitdata {
+	IpaStreamId	streamId,
+	octetstring	payload
+}
+
+type port IPA_SP_PT message {
+	inout ASP_IPA_Unitdata;
+} with { extension "internal" }
+
 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;
+	/* up-facing port for other streams */
+	port IPA_SP_PT IPA_SP_PORT;
 
 	var boolean g_initialized := false;
 	var ConnectionId g_ipa_conn_id := -1;
@@ -114,9 +125,26 @@
 	}
 }
 
+private function f_to_asp(IPA_RecvFrom ipa_rx) return ASP_IPA_Unitdata {
+	var ASP_IPA_Unitdata ret := {
+		streamId := ipa_rx.streamId,
+		payload := ipa_rx.msg
+	}
+	return ret;
+}
+
+private function f_from_asp(ConnectionId connId, ASP_IPA_Unitdata ipa_tx) return IPA_Send {
+	var IPA_Send ret := {
+		connId := connId,
+		streamId := ipa_tx.streamId,
+		msg := ipa_tx.payload
+	}
+	return ret;
+}
 
 function ScanEvents() runs on IPA_Emulation_CT {
 	var IPA_RecvFrom ipa_rx;
+	var ASP_IPA_Unitdata ipa_ud;
 	var ASP_MTP3_TRANSFERreq mtp_req;
 
 	f_connect("127.0.0.1", 5000, "127.0.0.1", 49999);
@@ -137,7 +165,7 @@
 				MTP3_SP_PORT.send(mtp);
 				}
 			case else {
-				log("Unknown/unsupported IPA Stream ID", ipa_rx);
+				IPA_SP_PORT.send(f_to_asp(ipa_rx));
 				}
 			}
 		}
@@ -151,6 +179,13 @@
 			}
 			IPA_PORT.send(ipa_tx);
 		}
+
+		/* Received MISC (RSL/OML/CTRL/MGCP) -> down into IPA */
+		[] IPA_SP_PORT.receive(ASP_IPA_Unitdata: ?) -> value ipa_ud {
+			IPA_PORT.send(f_from_asp(g_ipa_conn_id, ipa_ud));
+		}
+
+
 		}
 	}
 }
diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn
new file mode 100644
index 0000000..8f60b5c
--- /dev/null
+++ b/library/L3_Templates.ttcn
@@ -0,0 +1,112 @@
+module L3_Templates {
+
+import from General_Types all;
+import from MobileL3_Types all;
+import from MobileL3_CommonIE_Types all;
+import from MobileL3_MM_Types all;
+import from MobileL3_RRM_Types all;
+
+type enumerated CmServiceType {
+	CM_TYPE_MO_CALL		('0001'B),
+	CM_TYPE_EMERG_CALL	('0010'B),
+	CM_TYPE_MO_SMS		('0100'B),
+	CM_TYPE_SS_ACT		('1000'B)
+}
+
+
+/* send template fro Mobile Identity (TMSI) */
+template MobileIdentityLV ts_MI_TMSI_LV(OCT4 tmsi) := {
+	lengthIndicator := 0, /* overwritten */
+	mobileIdentityV := {
+		typeOfIdentity := '000'B,	/* overwritten */
+		oddEvenInd_identity := {
+			tmsi_ptmsi := {
+				oddevenIndicator := '0'B,
+				fillerDigit := '1111'B,
+				octets := tmsi
+			}
+		}
+	}
+}
+
+private function f_enc_IMSI_L3(hexstring digits) return IMSI_L3 {
+	var IMSI_L3 l3;
+	var integer len := lengthof(digits);
+	if (len rem 2 == 1) {	/* modulo remainder */
+		l3.oddevenIndicator := '0'B;
+		l3.fillerDigit := '1111'B;
+	} else {
+		l3.oddevenIndicator := '1'B;
+		l3.fillerDigit := omit;
+	}
+	l3.digits := digits;
+	return l3;
+}
+
+/* send template fro Mobile Identity (IMSI) */
+template (value) MobileIdentityLV ts_MI_IMSI_LV(hexstring imsi_digits) := {
+	lengthIndicator := 0, /* overwritten */
+	mobileIdentityV := {
+		typeOfIdentity := '000'B, /* overwritten */
+		oddEvenInd_identity := {
+			imsi := f_enc_IMSI_L3(imsi_digits)
+		}
+	}
+}
+
+/* Send template for Classmark 2 */
+template (value) MobileStationClassmark2_LV ts_CM2 := {
+	lengthIndicator := 0,
+	rf_PowerCapability := '000'B,
+	a5_1 := '0'B,
+	esind := '1'B,
+	revisionLevel := '10'B,
+	spare1_1 := '0'B,
+	mobileStationClassmark2_oct4 := omit,
+	mobileStationClassmark2_oct5 := omit
+};
+
+/* Send template for CM SERVICE REQUEST */
+template (value) PDU_ML3_MS_NW ts_CM_SERV_REQ(BIT4 serv_type, MobileIdentityLV mi_lv) := {
+	discriminator := '0000'B, /* overwritten */
+	tiOrSkip := {
+		skipIndicator := '0000'B
+	},
+	msgs := {
+		mm := {
+			cMServiceRequest := {
+				messageType := '000000'B, /* overwritten */
+				nsd := '00'B,
+				cm_ServiceType := serv_type,
+				cipheringKeySequenceNumber := { '000'B, '0'B },
+				mobileStationClassmark2 := ts_CM2,
+				mobileIdentity := mi_lv,
+				priorityLevel := omit,
+				additionalUpdateParameterTV := omit,
+				deviceProperties := omit
+			}
+		}
+	}
+}
+
+/* Send template for PAGING RESPONSE */
+template (value) PDU_ML3_MS_NW ts_PAG_RESP(MobileIdentityLV mi_lv) := {
+	discriminator := '0000'B, /* overwritten */
+	tiOrSkip := {
+		skipIndicator := '0000'B
+	},
+	msgs := {
+		rrm := {
+			pagingResponse := {
+				messageType := '00000000'B, /* overwritten */
+				cipheringKeySequenceNumber := { '000'B, '0'B },
+				spare1_4 := '0000'B,
+				mobileStationClassmark := ts_CM2,
+				mobileIdentity := mi_lv,
+				additionalUpdateParameters := omit
+			}
+		}
+	}
+}
+
+}