gprs_gb: working UL TBF with segmentation

Change-Id: I0f93246f02e1bee2e1e9db62af5e1e3559c415e9
diff --git a/library/GSM_Types.ttcn b/library/GSM_Types.ttcn
index c7b7991..0289b08 100644
--- a/library/GSM_Types.ttcn
+++ b/library/GSM_Types.ttcn
@@ -26,6 +26,19 @@
 		CS1, CS2, CS3, CS4
 	};
 
+	function f_gprs_blocksize(GprsCodingScheme cs) return integer {
+		select (cs) {
+		case (CS1) { return 22 }
+		case (CS2) { return 32 }
+		case (CS3) { return 38 }
+		case (CS3) { return 52 }
+		case else {
+			setverdict(fail, "Invalid GPRS CS ", cs);
+			return -1;
+			}
+		}
+	}
+
 	/* 10.5.2.8 */
 	type enumerated ChannelNeeded {
 		CHAN_NEED_ANY	(0),
diff --git a/library/Osmocom_Types.ttcn b/library/Osmocom_Types.ttcn
index e36d2dc..d01fe37 100644
--- a/library/Osmocom_Types.ttcn
+++ b/library/Osmocom_Types.ttcn
@@ -126,5 +126,14 @@
 	T.start;
 }
 
+/* divide two integers and return rounded-up result */
+function f_div_round_up(integer dividend, integer divisor) return integer {
+	var integer x := dividend / divisor;
+	if (dividend rem divisor != 0) {
+		x := x+1;
+	}
+	return x;
+}
+
 
 } with { encode "RAW"; variant "FIELDORDER(msb)" }
diff --git a/library/RLCMAC_EncDec.cc b/library/RLCMAC_EncDec.cc
index ad52f91..b911c75 100644
--- a/library/RLCMAC_EncDec.cc
+++ b/library/RLCMAC_EncDec.cc
@@ -32,9 +32,9 @@
 		for (i = 0; i < in.blocks().size_of(); i++) {
 			/* fix the 'E' bit in case it is not clear */
 			if (i < in.blocks().size_of()-1)
-				in.blocks()[i].hdr().e() = false;
+				in.blocks()[i].hdr()().e() = false;
 			else
-				in.blocks()[i].hdr().e() = true;
+				in.blocks()[i].hdr()().e() = true;
 			in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
 		}
 	}
@@ -69,7 +69,7 @@
 			ret_val.blocks()[num_llc_blocks++] = lb;
 
 			/* if E == '1'B, we can proceed further */
-			if (lb.hdr().e() == true)
+			if (lb.hdr()().e() == true)
 				break;
 		}
 	}
@@ -87,7 +87,7 @@
 	} else {
 		if (ret_val.blocks().is_bound()) {
 			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
-				unsigned int length = ret_val.blocks()[i].hdr().length__ind();
+				unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
 				if (length > ttcn_buffer.get_read_len())
 					length = ttcn_buffer.get_read_len();
 				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
@@ -107,31 +107,57 @@
 	TTCN_Buffer ttcn_buffer;
 	int i;
 
-	/* Fix 'e' bit of initial header based on following blocks */
-	if (!in.blocks().is_bound() ||
-	    (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
-		in.mac__hdr().e() = true;
-	else
+	if (!in.blocks().is_bound()) {
+		/* we don't have nay blocks: Add length value (zero) */
+		in.mac__hdr().e() = false; /* E=0: extension octet follows */
+	} else if (in.blocks().size_of() == 1 && in.blocks()[0].hdr() == OMIT_VALUE) {
+		/* If there's only a single block, and that block has no HDR value defined, */
+		in.mac__hdr().e() = true; /* E=0: extension octet follows */
+	} else {
+		/* Length value */
 		in.mac__hdr().e() = false;
+	}
 
 	/* Fix other presence indications */
 	in.mac__hdr().tlli__ind() = in.tlli().is_bound() && in.tlli() != OMIT_VALUE;
 	in.mac__hdr().pfi__ind() = in.pfi().is_bound() && in.pfi() != OMIT_VALUE;
 
-	/* use automatic/generated decoder for header */
+	/* use automatic/generated encoder for header */
 	in.mac__hdr().encode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
 
-	/* Add LI octets, if any */
-	if (in.blocks().is_bound() &&
-	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
-		/* first write LI octets */
-		for (i = 0; i < in.blocks().size_of(); i++) {
-			/* fix the 'E' bit in case it is not clear */
-			if (i < in.blocks().size_of()-1)
-				in.blocks()[i].hdr().e() = false;
-			else
-				in.blocks()[i].hdr().e() = true;
-			in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+	if (in.mac__hdr().e() == false) {
+		/* Add LI octets, if any */
+		if (!in.blocks().is_bound()) {
+			ttcn_buffer.put_c(0x01); /* M=0, E=1 LEN=0 */
+		} else {
+			for (i = 0; i < in.blocks().size_of(); i++) {
+#if 0
+				/* check for penultimate block */
+				if (i == in.blocks().size_of()-2) {
+					/* if last block has no header, no more LI */
+					if (in.blocks()[i+1].hdr() == OMIT_VALUE) {
+						in.blocks()[i].hdr()().more() = true;
+					} else {
+						/* header present, we have to encode LI */
+						in.blocks()[i].hdr()().more() = false;
+						in.blocks()[i].hdr()().length__ind() =
+								in.blocks()[i+1].payload().lengthof();
+					}
+				} else if (i < in.blocks().size_of()-2) {
+					/* one of the first blocks, before the penultimate or last */
+					in.blocks()[i].hdr()().e() = false; /* LI present */
+					/* re-compute length */
+					in.blocks()[i].hdr()().length__ind() =
+								in.blocks()[i+1].payload().lengthof();
+				}
+				/* Encode LI octet if E=0 */
+				}
+#endif
+				if (in.blocks()[i].hdr() != OMIT_VALUE) {
+					in.blocks()[i].hdr()().encode(LlcBlockHdr_descr_, ttcn_buffer,
+									TTCN_EncDec::CT_RAW);
+				}
+			}
 		}
 	}
 
@@ -201,7 +227,7 @@
 			TTCN_Logger::end_event();
 
 			/* if E == '1'B, we can proceed further */
-			if (lb.hdr().e() == true)
+			if (lb.hdr()().e() == true)
 				break;
 		}
 	}
@@ -229,7 +255,7 @@
 	} else {
 		if (ret_val.blocks().is_bound()) {
 			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
-				unsigned int length = ret_val.blocks()[i].hdr().length__ind();
+				unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
 				if (length > ttcn_buffer.get_read_len())
 					length = ttcn_buffer.get_read_len();
 				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
diff --git a/library/RLCMAC_Types.ttcn b/library/RLCMAC_Types.ttcn
index 937aa7b..78861ff 100644
--- a/library/RLCMAC_Types.ttcn
+++ b/library/RLCMAC_Types.ttcn
@@ -108,7 +108,7 @@
 	type record LlcBlockHdr {
 		uint6_t		length_ind,
 		/* 1 = new LLC PDU starts */
-		BIT1		more,
+		boolean		more,
 		/* 0 = another extension octet after LLC PDU, 1 = no more extension octets */
 		boolean		e
 	} with {
@@ -116,7 +116,7 @@
 	};
 	type record LlcBlock {
 		/* Header is only present if LI field was present */
-		LlcBlockHdr	hdr,
+		LlcBlockHdr	hdr optional,
 		octetstring 	payload
 	} with { variant "" };
 	type record of LlcBlock LlcBlocks;
@@ -239,4 +239,119 @@
 		}
 	}
 
+	/* Template fro uplink Data block */
+	template RlcmacUlBlock t_RLCMAC_UL_DATA(template uint5_t tfi, template uint4_t cv, template uint7_t bsn,
+						template LlcBlocks blocks := {}, template boolean stall := false) := {
+		data := {
+			mac_hdr := {
+				payload_type := MAC_PT_RLC_DATA,
+				countdown := cv,
+				stall_ind := false,
+				retry := false,
+				spare := '0'B,
+				pfi_ind := false,
+				tfi := tfi,
+				tlli_ind := false,
+				bsn := bsn,
+				e := false
+			},
+			tlli := omit,
+			pfi := omit,
+			blocks := blocks
+		}
+	}
+	template RlcmacUlBlock t_RLCMAC_UL_DATA_TLLI(template uint5_t tfi, template uint4_t cv, template uint7_t bsn,
+						     template LlcBlocks blocks := {}, template boolean stall := false, template GprsTlli tlli) := {
+		data := {
+			mac_hdr := {
+				payload_type := MAC_PT_RLC_DATA,
+				countdown := cv,
+				stall_ind := false,
+				retry := false,
+				spare := '0'B,
+				pfi_ind := false,
+				tfi := tfi,
+				tlli_ind := true,
+				bsn := bsn,
+				e := false
+			},
+			tlli := tlli,
+			pfi := omit,
+			blocks := blocks
+		}
+	}
+
+	template DlMacHeader t_RLCMAC_DlMacH(template MacPayloadType pt, template MacRrbp rrbp, template
+uint3_t usf) := {
+		payload_type := pt,
+		rrbp := rrbp,
+		rrbp_valid := ispresent(rrbp),
+		usf := usf
+	}
+
+	/* Receive Template for Downlink ACK/NACK */
+	template RlcmacDlBlock tr_RLCMAC_ACK_NACK(template uint5_t ul_tfi, template GprsTlli tlli := ?) := {
+		ctrl := {
+			mac_hdr := {
+				payload_type := (MAC_PT_RLCMAC_NO_OPT, MAC_PT_RLCMAC_OPT),
+				rrbp:= ?,
+				rrbp_valid := true,
+				usf := ?
+			},
+			opt := *,
+			payload := {
+				msg_type := PACKET_UL_ACK_NACK,
+				u := {
+					ul_ack_nack := {
+						page_mode := ?,
+						msg_excape := ?,
+						uplink_tfi := ul_tfi,
+						is_egprs := '0'B,
+						gprs := {
+							ch_coding_cmd := ?,
+							ack_nack_desc := ?,
+							cont_res_tlli_present := ?,
+							cont_res_tlli := tlli,
+							pkt_ta_present := ?,
+							pkt_ta := *,
+							pwr_ctrl_present := ?,
+							pwr_ctrl := *
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/* Template for Uplink MAC Control Header */
+	template UlMacCtrlHeader t_RLCMAC_UlMacCtrlH(template MacPayloadType pt, template boolean retry := false) := {
+		payload_type := pt,
+		spare := '00000'B,
+		retry := retry
+	}
+
+	/* Template for Uplink Conntrol ACK */
+	template RlcmacUlBlock ts_RLCMAC_CTRL_ACK(GprsTlli tlli, CtrlAck ack := MS_RCVD_TWO_RLC_SAME_RTI_DIFF_RBSN) := {
+		ctrl := {
+			mac_hdr := t_RLCMAC_UlMacCtrlH(MAC_PT_RLCMAC_NO_OPT),
+			payload := {
+				msg_type := PACKET_CONTROL_ACK,
+				u := {
+					ctrl_ack := {
+						tlli := tlli,
+						ctrl_ack := ack
+					}
+				}
+			}
+		}
+	}
+
+	/* Template for a LlcBlock (part of a LLC frame inside RlcMac?lDataBlock */
+	template LlcBlock t_RLCMAC_LLCBLOCK(octetstring data, boolean more := false, boolean e := true) := {
+		/* let encoder figure out the header */
+		hdr := omit,
+		payload := data
+	}
+
+
 } with { encode "RAW"; variant "FIELDORDER(msb)" }