gprs_gb: NS + BSSGP operational with OsmoPCU, receiving LLC-PDUs
diff --git a/gprs_gb/BSSGP_Emulation.ttcn b/gprs_gb/BSSGP_Emulation.ttcn
index 25b96cd..5937a27 100644
--- a/gprs_gb/BSSGP_Emulation.ttcn
+++ b/gprs_gb/BSSGP_Emulation.ttcn
@@ -11,7 +11,7 @@
 		BvcState	state
 	}
 
-	template BssgpStatusIndication t_BssgpStsInd(Nsei nsei, BssgpBvci bvci, BvcState state) := {
+	template BssgpStatusIndication t_BssgpStsInd(template Nsei nsei, template BssgpBvci bvci, template BvcState state) := {
 		nsei := nsei,
 		bvci := bvci,
 		state := state
@@ -56,20 +56,21 @@
 		/* NS-User SAP towards the user */
 		port BSSGP_SP_PT BSSGP_SP;
 
+		var boolean g_sgsn_role := true;
 		var BvcState g_ptp_bvc_state := BVC_S_BLOCKED;
 		timer g_T1 := 15.0;
 		timer g_T2 := 60.0;
 	}
 
 	modulepar {
-		Nsvci mp_nsei := 2342;
-		Nsvci mp_bvci := 2342;
+		Nsvci mp_nsei := 96;
+		Nsvci mp_bvci := 196;
 		BssgpCellId mp_cellid := { ra_id := { lai := { mcc_mnc := '234F04'H, lac := 200}, rac := 0 }, cell_id := 20960 };
 	};
 
-	function f_BnsUdReq(template BssgpPdu pdu) return NsUnitdataRequest {
+	function f_BnsUdReq(template BssgpPdu pdu, BssgpBvci bvci := mp_bvci) return NsUnitdataRequest {
 		var NsUnitdataRequest udr := {
-			bvci := mp_bvci,
+			bvci := bvci,
 			nsei := mp_nsei,
 			/* for some weird reason we get "Dynamic test case error: Text encoder: Encoding an
 			 * unbound integer value." when trying to send the reocrd rather than the octetstring */
@@ -81,9 +82,9 @@
 		return udr;
 	}
 
-	function f_BnsUdInd(template BssgpPdu pdu) return template NsUnitdataIndication {
+	function f_BnsUdInd(template BssgpPdu pdu, template BssgpBvci bvci := mp_bvci) return template NsUnitdataIndication {
 		var template NsUnitdataIndication udi := {
-			bvci := mp_bvci,
+			bvci := bvci,
 			nsei := mp_nsei,
 			sdu := *,
 			bssgp := pdu
@@ -102,57 +103,71 @@
 		log("PDU: ", pdu);
 		log("ENC: ", enc_BssgpPdu(pdu));
 
-		BSCP.send(f_BnsUdReq(pdu));
+		/* BVC-RESET is always sent via the SIGNALLING BVCI, see Table 5.4.1 */
+		BSCP.send(f_BnsUdReq(pdu, 0));
 		g_T2.start;
 		//f_change_state(BVC_S_WAIT_RESET);
 	}
 
 	private function f_sendUnblock() runs on BSSGP_CT {
-		BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK(mp_bvci)));
+		BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK(mp_bvci), 0));
 		g_T1.start;
 	}
 
 	private function f_sendBlock(BssgpCause cause) runs on BSSGP_CT {
-		BSCP.send(f_BnsUdReq(t_BVC_BLOCK(mp_bvci, cause)));
+		BSCP.send(f_BnsUdReq(t_BVC_BLOCK(mp_bvci, cause), 0));
 		g_T1.start;
 	}
 
+	private function f_sendStatus(BssgpCause cause, BssgpPdu pdu) runs on BSSGP_CT {
+		/* FIXME: Make sure correct Signaling or PTP BVCI is used! */
+		BSCP.send(f_BnsUdReq(t_BSSGP_STATUS({ t_BSSGP_IE_Cause(cause), t_BSSGP_IE_Bvci(mp_bvci), t_BSSGP_IE_PDU(pdu)})));
+	}
+
 	altstep as_allstate() runs on BSSGP_CT {
 		var NsUnitdataIndication udi;
 		var NsStatusIndication nsi;
 		var ASP_Event evt;
 
 		/* Respond to BLOCK for wrong NSVCI */
-		[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(?, ?))) -> value udi {
+		[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(?, ?), 0)) -> value udi {
 			log("Rx BVC-BLOCK for unknown BVCI");
-			/* FIXME */
+			f_sendStatus(BSSGP_CAUSE_BVCI_UNKNOWN, udi.bssgp);
 		}
 
-		/* Respond to RESET with correct NSEI/NSVCI */
-		[] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, mp_bvci, mp_cellid))) -> value udi {
+		/* Respond to RESET with correct BVCI/CellID */
+		[] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, mp_bvci, mp_cellid), 0)) -> value udi {
+			log("Rx BVC-RESET for Our BVCI=", mp_bvci);
+			BSCP.send(f_BnsUdReq(t_BVC_RESET_ACK(mp_bvci, mp_cellid), 0));
 			f_change_state(BVC_S_UNBLOCKED);
-			BSCP.send(f_BnsUdReq(t_BVC_RESET_ACK(mp_bvci, mp_cellid)));
+		}
+
+		/* Respond to RESET for signalling BVCI 0 */
+		[] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, 0, mp_cellid), 0)) -> value udi {
+			log("Rx BVC-RESET for Signaling BVCI=0");
+			BSCP.send(f_BnsUdReq(t_BVC_RESET_ACK(0, mp_cellid), 0));
 		}
 
 		/* Respond to RESET with wrong NSEI/NSVCI */
-		[] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, ?, ?))) -> value udi {
+		[] BSCP.receive(f_BnsUdInd(t_BVC_RESET(?, ?, ?), 0)) -> value udi {
 			log("Rx BVC-RESET for unknown BVCI");
-			/* FIXME */
+			f_sendStatus(BSSGP_CAUSE_BVCI_UNKNOWN, udi.bssgp);
 		}
 
 		/* default case of handling unknown PDUs */
-		[] BSCP.receive(f_BnsUdInd(?)) -> value udi {
+		[] BSCP.receive(f_BnsUdInd(?, ?)) -> value udi {
 			log("Rx Unexpected BSSGP PDU ", udi.bssgp," in state ", g_ptp_bvc_state);
-			BSCP.send(f_BnsUdReq(t_BSSGP_STATUS({
-						t_BSSGP_IE_Cause(BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE),
-						t_BSSGP_IE_Bvci(mp_bvci), t_BSSGP_IE_PDU(udi.bssgp)})));
+			f_sendStatus(BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, udi.bssgp);
 		}
 		/* Forwarding of ASP_Event and NsStatusIndication to user */
 		[] BSCP.receive(ASP_Event:?) -> value evt { BSSGP_SP.send(evt); }
 		[] BSCP.receive(NsStatusIndication:?) -> value nsi { 
 			/* if we just became NS-unblocked, send a BCC-RESET */
 			if (nsi.old_state != NSE_S_ALIVE_UNBLOCKED and nsi.new_state == NSE_S_ALIVE_UNBLOCKED) {
-				f_sendReset();
+				if (g_sgsn_role == false) {
+					f_sendReset();
+				}
+				/* Idea: We coudl send BVC-UNBLOCK here like some SGSN do */
 			}
 			BSSGP_SP.send(nsi);
 		}
@@ -163,6 +178,9 @@
 		var BssgpPdu bs_pdu;
 		var default d;
 
+
+		log("matching against ", t_BVC_RESET(?, mp_bvci, mp_cellid));
+
 		d := activate(as_allstate());
 
 		while (true) {
@@ -179,26 +197,35 @@
 		} else if (g_ptp_bvc_state == BVC_S_UNBLOCKED) {
 			alt {
 				/* bogus unblock, just respond with ACK */
-				[] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK(mp_bvci))) -> value udi {
-					BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK_ACK(mp_bvci)));
+				[] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK(mp_bvci), 0)) -> value udi {
+					BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK_ACK(mp_bvci), 0));
 				}
 				/* Respond to BLOCK with BLOCK-ACK + change state */
-				[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(mp_bvci, ?))) -> value udi {
-					BSCP.send(f_BnsUdReq(t_BVC_BLOCK_ACK(mp_bvci)));
+				[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(mp_bvci, ?), 0)) -> value udi {
+					BSCP.send(f_BnsUdReq(t_BVC_BLOCK_ACK(mp_bvci), 0));
 					g_T1.stop;
 					f_change_state(BVC_S_BLOCKED);
 				}
 				[] g_T1.timeout {
 					f_sendBlock(BSSGP_CAUSE_OM_INTERVENTION);
 				}
-				[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK_ACK(mp_bvci))) -> value udi {
+				[] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK_ACK(mp_bvci), 0)) -> value udi {
 					g_T1.stop;
 					f_change_state(BVC_S_BLOCKED);
 				}
-				[] BSCP.receive(f_BnsUdInd(t_BVC_RESET_ACK(mp_bvci, mp_cellid))) -> value udi {
+				[] BSCP.receive(f_BnsUdInd(t_BVC_RESET_ACK(mp_bvci, mp_cellid), 0)) -> value udi {
 					g_T2.stop;
 					f_change_state(BVC_S_UNBLOCKED);
 				}
+
+				/* simply acknowledge all Flow Control Messages */
+				[g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_BVC)) {
+					BSCP.send(f_BnsUdReq(t_BVC_FC_BVC_ACK));
+				}
+				[g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_MS)) {
+					BSCP.send(f_BnsUdReq(t_BVC_FC_MS_ACK));
+				}
+
 				/* BSSGP-UNITDATA PDUs from network to NS-UNITDATA.ind to user */
 				[] BSCP.receive(f_BnsUdInd(tr_BSSGP_type(DL_UNITDATA))) -> value udi {
 					BSSGP_SP.send(udi.bssgp);
diff --git a/gprs_gb/BSSGP_Types.ttcn b/gprs_gb/BSSGP_Types.ttcn
index 5e4a0bc..aef111a 100644
--- a/gprs_gb/BSSGP_Types.ttcn
+++ b/gprs_gb/BSSGP_Types.ttcn
@@ -278,6 +278,12 @@
 		variant (len) "LENGTHTO(u)"
 	};
 
+	external function enc_BssgpTLV(in BssgpTLV pdu) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_BssgpTLV(in octetstring stream) return BssgpTLV
+		with { extension "prototype(convert) decode(RAW)" };
+
+
 	type record of BssgpTLV BssgpTLVs;
 
 	/* 10.2.1 */
@@ -344,6 +350,13 @@
 		u := { bvci := bvci }
 	}
 
+	template BssgpTLV t_BSSGP_IE_CellId(template BssgpCellId cid) := {
+		iei := CELL_ID,
+		len := 8,
+		u := { cell_id := cid }
+	}
+
+
 	template BssgpTLV t_BssgpIE(template BssgpIEI iei, template BssgpIeUnion u) := {
 		iei := iei,
 		u := u
@@ -351,7 +364,6 @@
 
 	template BssgpTLV t_BSSGP_IE_PDU(BssgpPdu pdu) :=
 		t_BssgpIE(PDU_IN_ERROR, { other := f_BSSGP_compact_len(enc_BssgpPdu(pdu)) });
-	template BssgpTLV t_BSSGP_IE_CellId(template BssgpCellId cid) := t_BssgpIE(CELL_ID, { cell_id := cid });
 
 	template BssgpPdu t_BVC_RESET(template BssgpCause cause, template BssgpBvci bvci, template BssgpCellId cell_id) := 
 		t_BSSGP_other(BVC_RESET, { t_BSSGP_IE_Bvci(bvci), t_BSSGP_IE_Cause(cause), t_BSSGP_IE_CellId(cell_id) });
@@ -365,11 +377,16 @@
 		t_BSSGP_other(BVC_BLOCK, { t_BSSGP_IE_Bvci(bvci), t_BSSGP_IE_Cause(cause) });
 	template BssgpPdu t_BVC_BLOCK_ACK(template BssgpBvci bvci) := t_BSSGP_other(BVC_BLOCK_ACK, { t_BSSGP_IE_Bvci(bvci) });
 
-	template BssgpPdu tr_BSSGP_type(template BssgpPduType pdu_type) := {
+
+	template BssgpPdu tr_BSSGP_type(template BssgpPduType pdu_type, template BssgpPduUnion u := ?) := {
 		pdu_type := pdu_type,
-		u := ?
+		u := u
 	}
 
+	template BssgpPdu t_BVC_FC_BVC := t_BSSGP_other(FLOW_CONTROL_BVC, ?);
+	template BssgpPdu t_BVC_FC_BVC_ACK := t_BSSGP_other(FLOW_CONTROL_BVC_ACK, {});
+	template BssgpPdu t_BVC_FC_MS := t_BSSGP_other(FLOW_CONTROL_MS, ?);
+	template BssgpPdu t_BVC_FC_MS_ACK := t_BSSGP_other(FLOW_CONTROL_MS_ACK, {});
 
 	template BssgpPdu t_BSSGP_STATUS(template BssgpTLVs tlvs) := t_BSSGP_other(STATUS, tlvs);
 	
diff --git a/gprs_gb/Test.cfg b/gprs_gb/Test.cfg
new file mode 100644
index 0000000..252704f
--- /dev/null
+++ b/gprs_gb/Test.cfg
@@ -0,0 +1,27 @@
+[LOGGING]
+
+[MODULE_PARAMETERS]
+mp_local_ip := "192.168.100.239"
+mp_local_udp_port := 23000
+mp_remote_ip := "192.168.100.196"
+mp_remote_udp_port := 21000
+mp_nsei := 96
+
+[TESTPORT_PARAMETERS]
+#*.BSCVTY.CTRL_MODE := "client"
+#*.BSCVTY.CTRL_HOSTNAME := "127.0.0.1"
+#*.BSCVTY.CTRL_PORTNUM := "4242"
+#*.BSCVTY.CTRL_LOGIN_SKIPPED := "yes"
+#*.BSCVTY.CTRL_DETECT_SERVER_DISCONNECTED := "yes"
+#*.BSCVTY.CTRL_READMODE := "buffered"
+#*.BSCVTY.CTRL_CLIENT_CLEANUP_LINEFEED := "yes"
+#*.BSCVTY.PROMPT1 := "OpenBSC> "
+#*.BSCVTY.PROMPT2 := "OpenBSC# "
+#*.BSCVTY.REGEX_PROMPT1 := "^OpenBSC.*$"
+
+*.*.udpReuseAddress := "yes";
+
+[MAIN_CONTROLLER]
+
+[EXECUTE]
+Test.TC_nsem
diff --git a/gprs_gb/Test.ttcn b/gprs_gb/Test.ttcn
index dec5cda..6e772dc 100644
--- a/gprs_gb/Test.ttcn
+++ b/gprs_gb/Test.ttcn
@@ -133,8 +133,28 @@
 	}
 
 	testcase TC_nsem() runs on dummy_CT {
+		timer T:= 60.0;
+
 		f_init();
-		while (true) { }
+		T.start
+		alt {
+			[] BSSGP.receive(t_BssgpStsInd(?, ?, BVC_S_UNBLOCKED)) { }
+			[] BSSGP.receive { repeat; }
+			[] T.timeout { setverdict(fail); }
+		}
+		T.stop
+		log("BSSGP successfully initialized");
+
+		while (true) {
+			var BssgpPdu pdu;
+			alt {
+				[] BSSGP.receive(BssgpPdu:?) -> value pdu {
+					log("BSSGP Rx: ", pdu);
+				}
+				[] BSSGP.receive(t_BssgpStsInd(?, ?, BVC_S_UNBLOCKED)) { repeat; }
+				[] BSSGP.receive { repeat; }
+			}
+		}
 	}
 
 	control {