Extend IPA_Emulation with support for CTRL
diff --git a/library/IPA_Emulation.ttcn b/library/IPA_Emulation.ttcn
index 61f357c..fc42dba 100644
--- a/library/IPA_Emulation.ttcn
+++ b/library/IPA_Emulation.ttcn
@@ -18,6 +18,8 @@
 
 import from MGCP_Types all;
 
+import from Osmocom_CTRL_Types all;
+
 modulepar {
 	/* Use Osmocom extended IPA mux header */
 	boolean mp_ipa_mgcp_uses_osmo_ext := true;
@@ -92,6 +94,11 @@
 	inout ASP_RSL_Unitdata, ASP_IPA_Event;
 } with { extension "internal" }
 
+/* Client port for CTRL inside IPA */
+type port IPA_CTRL_PT message {
+	inout CtrlMessage, ASP_IPA_Event;
+} with { extension "internal" }
+
 type component IPA_Emulation_CT {
 	/* down-facing port to IPA codec port */
 	port IPA_CODEC_PT IPA_PORT;
@@ -101,6 +108,8 @@
 	port IPA_MGCP_PT IPA_MGCP_PORT;
 	/* up-facing port for RSL */
 	port IPA_RSL_PT IPA_RSL_PORT;
+	/* up-facing port for CTRL */
+	port IPA_CTRL_PT IPA_CTRL_PORT;
 	/* up-facing port for other streams */
 	port IPA_SP_PT IPA_SP_PORT;
 
@@ -332,6 +341,11 @@
 	}
 }
 
+private function f_ctrl_to_user(octetstring msg) runs on IPA_Emulation_CT {
+	var charstring msg_ch := oct2char(msg);
+	IPA_CTRL_PORT.send(dec_CtrlMessage(msg_ch));
+}
+
 private function f_mgcp_to_ud(octetstring payload) runs on IPA_Emulation_CT return ASP_IPA_Unitdata {
 	if (mp_ipa_mgcp_uses_osmo_ext) {
 		return valueof(t_ASP_IPA_UD(IPAC_PROTO_MGCP_OLD, payload));
@@ -348,6 +362,7 @@
 	var ASP_Event asp_evt;
 	var MgcpCommand mgcp_cmd;
 	var MgcpResponse mgcp_rsp;
+	var CtrlMessage ctrl_msg;
 	var octetstring payload;
 	var ASP_RSL_Unitdata rsl;
 
@@ -376,6 +391,8 @@
 				select (ipa_rx.streamIdExt) {
 					case (IPAC_PROTO_EXT_MGCP) {
 						f_mgcp_to_user(ipa_rx.msg);
+					} case (IPAC_PROTO_EXT_CTRL) {
+						f_ctrl_to_user(ipa_rx.msg);
 					} case else {
 						IPA_SP_PORT.send(f_to_asp(ipa_rx));
 					}
@@ -422,6 +439,12 @@
 			IPA_PORT.send(f_from_asp(g_ipa_conn_id, ipa_ud));
 		}
 
+		[] IPA_CTRL_PORT.receive(CtrlMessage:?) -> value ctrl_msg {
+			payload := char2oct(enc_CtrlMessage(ctrl_msg));
+			ipa_ud := valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_CTRL));
+			IPA_PORT.send(f_from_asp(g_ipa_conn_id, ipa_ud));
+		}
+
 		/* Received RSL -> down into IPA */
 		[] IPA_RSL_PORT.receive(ASP_RSL_Unitdata:?) -> value rsl {
 			IPA_PORT.send(f_from_rsl(g_ipa_conn_id, rsl));
diff --git a/library/Osmocom_CTRL_Types.ttcn b/library/Osmocom_CTRL_Types.ttcn
new file mode 100644
index 0000000..db5012c
--- /dev/null
+++ b/library/Osmocom_CTRL_Types.ttcn
@@ -0,0 +1,69 @@
+module Osmocom_CTRL_Types {
+
+type charstring CtrlVerb ("GET", "SET") with {
+	/* see https://www.eclipse.org/forums/index.php/t/1088893/ on why this
+	 * match expression is needed here */
+	variant "TEXT_CODING(,convert=upper_case,'((GET)|(SET)|(TRAP))',case_insensitive)"
+};
+
+type charstring CtrlReplyToken ("REPLY") with {
+	variant "TEXT_CODING(,convert=upper_case,'(REPLY)',case_insensitive)"
+};
+
+type charstring CtrlId	(pattern "\d#(1,9)");
+type charstring CtrlVariable (pattern "[^, \{\}\[\]\(\)<>\|~\\\^`'\"\?=;/\+\*&%$\#!]*");
+type charstring CtrlValue (pattern "[^ ]");
+type charstring CtrlReason;
+
+
+type record CtrlCommand {
+	CtrlVerb	verb,
+	CtrlId		id,
+	CtrlVariable	variable,
+	CtrlValue	val optional	/* only for SET */
+} with {
+	variant "SEPARATOR(' ',)"
+};
+
+type record CtrlResponse {
+	CtrlVerb	verb,
+	CtrlReplyToken	repl,
+	CtrlId		id,
+	CtrlVariable	variable,
+	CtrlValue	val
+} with {
+	variant "TEXT_CODING(,,'* REPLY *',case_insensitive)"
+	variant "SEPARATOR(' ',)"
+};
+
+type record CtrlError {
+	CtrlId		id,
+	CtrlReason	reason
+} with {
+	variant "BEGIN('ERROR ',,case_insensitive)"
+	variant "SEPARATOR(' ',)"
+};
+
+type record CtrlTrap {
+	CtrlVariable	variable,
+	CtrlValue	val
+} with {
+	variant "BEGIN('TRAP ',,case_insensitive)"
+	variant "SEPARATOR(' ',)"
+};
+
+type union CtrlMessage {
+	CtrlCommand	cmd,
+	CtrlResponse	resp,
+	CtrlError	err,
+	CtrlTrap	trap
+};
+
+external function enc_CtrlMessage(in CtrlMessage id) return charstring
+	with { extension "prototype(convert) encode(TEXT)"};
+
+external function dec_CtrlMessage(in charstring id) return CtrlMessage
+	with { extension "prototype(convert) decode(TEXT)"};
+
+
+} with { encode "TEXT" }