[PATCH] osmo-ttcn3-hacks[master]: WIP: working UL TBF with segmentation

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Thu Mar 8 10:58:10 UTC 2018


Hello Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/7153

to look at the new patch set (#2).

WIP: working UL TBF with segmentation

Change-Id: I0f93246f02e1bee2e1e9db62af5e1e3559c415e9
---
A gprs_gb/GPRS_Context.ttcn
A gprs_gb/GPRS_TBF.ttcn
M gprs_gb/Test.ttcn
M library/GSM_Types.ttcn
M library/LAPDm_RAW_PT.ttcn
M library/Osmocom_Types.ttcn
M library/RLCMAC_EncDec.cc
M library/RLCMAC_Types.ttcn
8 files changed, 652 insertions(+), 150 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/53/7153/2

diff --git a/gprs_gb/GPRS_Context.ttcn b/gprs_gb/GPRS_Context.ttcn
new file mode 100644
index 0000000..af2b2e9
--- /dev/null
+++ b/gprs_gb/GPRS_Context.ttcn
@@ -0,0 +1,12 @@
+module GPRS_Context {
+
+import from Osmocom_Types all;
+import from GSM_Types all;
+
+	type record MmContext {
+		hexstring	imsi optional,
+		GprsTlli	tlli,
+		uint9_t		n_u
+	};
+
+}
diff --git a/gprs_gb/GPRS_TBF.ttcn b/gprs_gb/GPRS_TBF.ttcn
new file mode 100644
index 0000000..a35c780
--- /dev/null
+++ b/gprs_gb/GPRS_TBF.ttcn
@@ -0,0 +1,365 @@
+module GPRS_TBF {
+
+/* GPRS TBF Routines, intended as minimal MS-Side GPRS implementation
+ *
+ * (C) 2018 by Harald Welte <laforge at gnumonks.org>
+ * All rights reserved.
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ */
+
+
+import from GSM_Types all;
+import from Osmocom_Types all;
+import from General_Types all;
+import from RLCMAC_Types all;
+import from RLCMAC_CSN1_Types all;
+import from LLC_Types all;
+import from GPRS_Context all;
+
+/* input parameters into TBF (mostly mode/cs + LLC PDUs */
+
+type record UlTbfPars {
+	/* Acknowledged mode (true) or unacknowledged (false) */
+	boolean			ack_mode,
+	/* Coding Scheme for transmission, determines block size */
+	GprsCodingScheme	initial_cs,
+	/* list of abstract/decoded LLC PDUs */
+	record of PDU_LLC	llc_pdus optional,
+	/* possibly computed: list of encoded LLC PDUs */
+	record of octetstring 	llc_pdus_enc
+}
+
+type record RlcEndpointTx {
+	/* send state variable V(S) (9.1.1): 0 .. SNS-1 */
+	integer			v_s,
+	/* acknowledge state variable V(A) (9.1.2): BSN value of oldest RLC data block that has not
+	 * been positively acknowledged by peer. */
+	integer			v_a,
+	/* acknowledge state array V(B) (9.1.3) */
+	bitstring		v_b
+}
+private function f_RlcEndpointTx_init(inout RlcEndpointTx ep) {
+	ep.v_s := 0;
+	ep.v_a := 0;
+	ep.v_b := int2bit(0, 128); /* FIXME: EGPRS 2048 bits length */
+}
+
+type record RlcEndpointRx {
+	/* receive state variable V(R) (9.1.5): BSN one higher than highest BSN yet received (mod SNS) */
+	integer			v_r,
+	/* receive window state variable V(Q) (9.1.6): Lowest BSN not yet received (mod SNS) */
+	integer			v_q,
+	/* receive state array V(N) (9.1.7) */
+	bitstring		v_n
+}
+
+private function f_RlcEndpointRx_init(inout RlcEndpointRx ep) {
+	ep.v_r := 0;
+	ep.v_q := 0;
+	ep.v_n := int2bit(0, 128); /* FIXME: EGPRS 2048 bits length */
+}
+
+
+type record UlTbfState {
+	/* "const" input state with TBF Data */
+	UlTbfPars		tbf,
+	uint8_t			num_ts,
+
+	RlcEndpointTx		et,
+
+	integer			tfi,
+	/* total length of all encoded LLC PDUs */
+	integer			llc_pdu_totlen,
+	/* index of current/next PDU in llc_pdus_enc */
+	integer			cur_pdu,
+	/* byte offset into current/next PDU; next byte to transmit */
+	integer			cur_index,
+	/* block sequence number of next block within TBF */
+	integer			bsn_p,
+	/* total number of bytes remaining in TBF */
+	integer			total_bytes_remain,
+
+	/* list of already-sent RLC/MAC Blocks */
+	record of RlcmacUlBlock	rlc_sent
+}
+
+function f_UlTbfState_init(inout UlTbfState us, in UlTbfPars utpars) {
+	var RlcmacUlBlock blk;
+
+	us.tbf := utpars;
+	us.num_ts := 1;
+
+	f_RlcEndpointTx_init(us.et);
+
+	us.tfi := 0;	/* FIXME */
+	us.cur_pdu := 0;
+	us.cur_index := 0;
+	us.bsn_p := 0;
+	us.total_bytes_remain := 0;
+	us.rlc_sent := {};
+
+	/* encode all LLC PDUs from their abstract type to octetstring */
+	us.cur_pdu := 0;	/* currently processed PDU */
+	us.cur_index := 0;	/* next to-be transmitted index */
+	if (ispresent(us.tbf.llc_pdus)) {
+		us.tbf.llc_pdus_enc := {};
+		us.llc_pdu_totlen := 0;
+		for (var integer i := 0; i < sizeof(us.tbf.llc_pdus); i := i+1) {
+			var octetstring cur_enc := enc_PDU_LLC(us.tbf.llc_pdus[i]);
+			us.tbf.llc_pdus_enc := us.tbf.llc_pdus_enc & { cur_enc };
+			us.llc_pdu_totlen := us.llc_pdu_totlen + lengthof(cur_enc);
+		}
+		us.total_bytes_remain := us.llc_pdu_totlen;
+	}
+}
+
+private function f_UlTbf_ack_one_block(inout UlTbfState us, integer n) {
+	/* compute index into v_b */
+	var integer idx := n - us.et.v_a;
+	if (idx < 0 or idx > lengthof(us.et.v_b)) {
+		setverdict(fail, "UlTbf: Cannot ACK ", n, " while V(A) is ", us.et.v_a);
+		self.stop;
+	}
+	/* set the bit in the acknowledge state array */
+	us.et.v_b[idx] := '1'B;
+}
+
+/* check if V(B) contains '1' at lower end: if yes, advance V(A) and shift V(B) */
+private function f_UlTbf_check_advance_v_a(inout UlTbfState us) {
+	log("FIXME: Implement this");
+}
+
+/* copy 'len' number of bytes from current pending LLC PDU */
+private function f_copy_from_llc(inout UlTbfState us, integer len) return octetstring
+{
+	var integer pdu_len := lengthof(us.tbf.llc_pdus_enc[us.cur_pdu])
+	var octetstring ret;
+
+	ret := substr(us.tbf.llc_pdus_enc[us.cur_pdu], us.cur_index, len);
+
+	us.cur_index := us.cur_index + len;
+	us.total_bytes_remain := us.total_bytes_remain - len;
+
+	log("copy_from_llc: ", ret, " us: ", us);
+
+	/* if we completed this PDU, move on to the next, resetting the index */
+	if (us.cur_index >= pdu_len) {
+		us.cur_pdu := us.cur_pdu +1;
+		us.cur_index := 0;
+		log("copy_from_llc (incrementing pdu)");
+	}
+
+	return ret;
+}
+
+/* generate one LlcBlock (maximum size 'max_len') */
+private function f_ul_tbf_pull_one(out LlcBlock ret, inout LlcBlockHdr prev_hdr,
+				   inout UlTbfState us, integer max_len,
+				   boolean is_final) return boolean
+{
+	/* if we've already pulled all data from all LLC PDUs: nothing to do */
+	if (us.cur_pdu >= sizeof(us.tbf.llc_pdus_enc)) {
+		log("pull_one: No more data", us);
+		return false;
+	}
+
+	var integer pdu_len := lengthof(us.tbf.llc_pdus_enc[us.cur_pdu])
+	var integer cur_llc_remain_len := pdu_len - us.cur_index;
+	var integer len;
+	var LlcBlock lb;
+
+	if (us.cur_index == 0) {
+		/* start of a new LLC PDU */
+		prev_hdr.more := true;
+	}
+
+	if (cur_llc_remain_len > max_len) {
+		log("Chunk with length ", cur_llc_remain_len, " larger than space (",
+			max_len, ") left in block");
+		len := max_len;
+		lb := {
+			hdr := omit,
+			payload := f_copy_from_llc(us, len)
+		}
+	} else if (cur_llc_remain_len == max_len and is_final) {
+		/* final RLC block of TBF and LLC remainder fits exactly */
+		len := max_len;
+		lb := {
+			hdr := omit,
+			payload := f_copy_from_llc(us, len)
+		}
+		prev_hdr.e := true;
+	} else if (cur_llc_remain_len == max_len) {
+		/* LLC remaineder would fit exactly -> "singular case" with L=0 */
+		len := max_len - 1;
+		lb := {
+			hdr := {
+				length_ind := 0,
+				more := false,
+				e := true
+			},
+			payload := f_copy_from_llc(us, len)
+		}
+		prev_hdr.e := false;
+	} else {
+		/* normal case: cur_llc_remain_len < max_len */
+		len := cur_llc_remain_len;
+		lb := {
+			hdr := {
+				length_ind := len,
+				more := false,
+				e := true
+			},
+			payload := f_copy_from_llc(us, len)
+		}
+		prev_hdr.e := false;
+	}
+	ret := lb;
+	return true;
+}
+
+/* return encoded size of one given LlcBlock */
+private function f_LlcBlock_enc_len(LlcBlock blk) return integer {
+	if (isvalue(blk.hdr)) {
+		return 1 + lengthof(blk.payload);
+	} else {
+		return lengthof(blk.payload);
+	}
+}
+
+/* return encoded size of given record of LlcBlock */
+private function f_LlcBlocks_enc_len(LlcBlocks blks) return integer {
+	var integer sum := 0;
+	for (var integer i := 0; i < sizeof(blks); i := i+1) {
+		sum := sum + f_LlcBlock_enc_len(blks[i]);
+	}
+	return sum;
+}
+
+private function f_ul_tbf_pull_multi(out LlcBlocks ret, inout UlTbfState us, integer max_len,
+				     boolean is_final) return boolean
+{
+	var integer space_left;
+	var boolean rc;
+	ret := {};
+
+	/* loop as long as we have pending LLC data and space left in the current block... */
+	for (space_left := max_len; space_left > 0; space_left := max_len - f_LlcBlocks_enc_len(ret)) {
+		var LlcBlock lb;
+		var integer ret_size := sizeof(ret);
+		if (ret_size > 0) {
+			rc := f_ul_tbf_pull_one(lb, ret[ret_size-1].hdr, us, space_left, is_final);
+		} else {
+			var LlcBlockHdr dummy_hdr;
+			rc := f_ul_tbf_pull_one(lb, dummy_hdr, us, space_left, is_final);
+		}
+		if (rc == false) {
+			/* we couldn't fill the full RLC block, insufficient LLC Data */
+			if (sizeof(ret) == 0) {
+				/* we couldn't obtain any LlcBlocks at all */
+				return false;
+			} else {
+				/* we have some LlcBlocks from earlier iterations */
+				return true;
+			}
+		}
+		ret := ret & {lb};
+	}
+	return true;
+}
+
+/* Determine 'K' value for given CS (TS 44.060 9.3.1.1) */
+private function f_k_for_cs(GprsCodingScheme cs) return integer {
+	/* FIXME: EGPRS */
+	return 1;
+}
+
+/* TS 44.060 9.3.1.1 */
+private function f_calc_cv(integer nbc, integer bsn_p, integer nts, GprsCodingScheme cs,
+			   integer bs_cv_max) return integer {
+	var integer dividend := nbc - bsn_p - 1;
+	var integer k := f_k_for_cs(cs);
+	var integer divisor := nts * k;
+	var integer x := f_div_round_up(dividend, divisor);
+	if (x <= bs_cv_max) {
+		return x;
+	} else {
+		return 15;
+	}
+}
+
+private function f_rlcmac_hdr_size(boolean tlli_needed) return integer {
+	var integer ret := 2;
+	if (tlli_needed) {
+		ret := ret + 4;
+	}
+	return ret;
+}
+
+/* determine countdown value based on remaining length pending */
+private function f_ul_tbf_get_cv(inout UlTbfState us, GprsCodingScheme cs, boolean tlli_needed)
+return integer {
+	var integer hdr_size := f_rlcmac_hdr_size(tlli_needed); /* FIXME: PFI, ... */
+	var integer blk_len_net := f_gprs_blocksize(cs) - hdr_size;
+	var integer num_remain := f_div_round_up(us.total_bytes_remain, blk_len_net);
+	var integer cv := f_calc_cv(num_remain + sizeof(us.rlc_sent), us.bsn_p, us.num_ts, us.tbf.initial_cs, 14 /* FIXME */);
+	log("CV=", cv, ", num_rmain=", num_remain, " from ", us);
+	return cv;
+}
+
+
+function f_ul_tbf_get_next_block(out RlcmacUlBlock blk, inout UlTbfState us, inout MmContext mmctx,
+				 boolean tlli_needed := false) return boolean {
+	var integer hdr_size := f_rlcmac_hdr_size(tlli_needed); /* FIXME: TLLI, PFI, ... */
+	var integer len_remain := f_gprs_blocksize(us.tbf.initial_cs) - hdr_size;
+	var octetstring payload;
+	var integer cv;
+	var LlcBlocks llc_blocks;
+
+	cv := f_ul_tbf_get_cv(us, us.tbf.initial_cs, tlli_needed);
+	/* potentially get multiple payload chunks of multiple LLC PDUs */
+	if (f_ul_tbf_pull_multi(llc_blocks, us, len_remain, false) == false) {
+		return false;
+	}
+
+	/* include TLLI when needed */
+	if (tlli_needed) {
+		blk := valueof(t_RLCMAC_UL_DATA_TLLI(us.tfi, cv, us.et.v_s,
+						llc_blocks, false, mmctx.tlli));
+	} else {
+		blk := valueof(t_RLCMAC_UL_DATA(us.tfi, cv, us.et.v_s, llc_blocks, false));
+	}
+
+	/* Increment Block Sequence Number */
+	us.bsn_p := us.bsn_p + 1;
+	us.et.v_s := us.bsn_p mod 128; /* FIXME: EGPRS SNS: 2048 */
+
+	/* append to list of sent blocks */
+	us.rlc_sent := us.rlc_sent & { blk };
+
+	return true;
+}
+
+function f_ul_tbf_process_acknack(inout UlTbfState us, RlcmacDlCtrlBlock db) {
+	if (ispresent(db.payload.u.ul_ack_nack.gprs)) {
+		var UlAckNackGprs uan_gprs := db.payload.u.ul_ack_nack.gprs;
+		if (ispresent(uan_gprs.cont_res_tlli)) {
+			/* FIXME: check for contention resolution */
+		}
+		var integer i;
+		/* iterate over received bitmap, ACK each block in our TBF state */
+		for (i := lengthof(uan_gprs.ack_nack_desc.receive_block_bitmap)-1; i >= 0; i := i-1) {
+			if (uan_gprs.ack_nack_desc.receive_block_bitmap[i] == '1'B) {
+				var integer seq := uan_gprs.ack_nack_desc.starting_seq_nr + i;
+				f_UlTbf_ack_one_block(us, seq);
+			}
+		}
+		f_UlTbf_check_advance_v_a(us);
+	}
+}
+
+
+
+}
diff --git a/gprs_gb/Test.ttcn b/gprs_gb/Test.ttcn
index 8741e86..f94fe09 100644
--- a/gprs_gb/Test.ttcn
+++ b/gprs_gb/Test.ttcn
@@ -14,6 +14,8 @@
 	import from RLCMAC_Types all;
 	import from RLCMAC_CSN1_Types all;
 	import from LAPDm_RAW_PT all;
+	import from GPRS_Context all;
+	import from GPRS_TBF all;
 
 	modulepar {
 		BssgpConfig mp_gb_cfg := {
@@ -31,13 +33,6 @@
 			sgsn_role := true
 		};
 	}
-
-	type record MmContext {
-		hexstring	imsi optional,
-		GprsTlli	tlli,
-		uint9_t		n_u
-	};
-
 
 	type component dummy_CT extends BSSGP_Client_CT {
 		var lapdm_CT lapdm_component;
@@ -230,123 +225,6 @@
 		T.stop;
 	}
 
-	/* 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, BIT1 more := '0'B, boolean e := true) := {
-		hdr := {
-			length_ind := lengthof(data),
-			more := more,
-			e := e
-		},
-		payload := data
-	}
-
 	/* compute a random TLLI; FIXME: what about TLLI prefix / local/foreign/...? */
 	function f_random_tlli() return GprsTlli {
 		var GprsTlli tlli := f_rnd_octstring(4);
@@ -373,6 +251,60 @@
 		return (fn + add) mod 2715648;
 	}
 
+
+function f_bssgp_wait_ul_ud(template BssgpDecoded bd_exp) runs on dummy_CT {
+	timer T := 5.0;
+	T.start;
+	alt {
+	[] BSSGP.receive(bd_exp) {
+		log("found matching BSSGP UL-UNITDATA PDU");
+		}
+	[] T.timeout {
+		setverdict(fail, "Timeout waiting for ", bd_exp);
+		}
+	}
+}
+
+
+function f_ul_tbf(inout UlTbfState us) runs on dummy_CT {
+	var RLCMAC_ph_data_ind dl;
+
+	/* Establish an UL-TBF */
+	f_establish_ul_tbf();
+
+	while (true) {
+		var RlcmacUlBlock blk;
+		if (f_ul_tbf_get_next_block(blk, us, g_mmctx, true) == false) {
+			break;
+		}
+
+		/* Send the block to L1 for transmission */
+		log("L1=", blk);
+		L1.send(RLCMAC_ph_data_req:{dyn:={tbf_id := 0, cs := us.tbf.initial_cs, block := blk}});
+	}
+
+	alt {
+	[] L1.receive(RLCMAC_ph_data_ind:{cs:=?, ts_nr:=?, fn:=?, block:=tr_RLCMAC_ACK_NACK(0, g_mmctx.tlli)}) -> value dl {
+		log("found matching ACK/NACK");
+		/* send CTRL ACK in uplink */
+		var GsmFrameNumber ul_fn := f_rrbp_fn(dl.fn, dl.block.ctrl.mac_hdr.rrbp);
+		var RlcmacUlCtrlMsg ctrl_ack := valueof(ts_RlcMacUlCtrl_PKT_CTRL_ACK(g_mmctx.tlli));
+		var RlcmacUlBlock ul_block := valueof(ts_RLC_UL_CTRL_ACK(ctrl_ack));
+		L1.send(ts_PH_DATA_ABS(0, CS1, dl.ts_nr, ul_fn, {false, 871}, ul_block));
+		/* wait for the final ACK */
+		if (dl.block.ctrl.payload.u.ul_ack_nack.gprs.ack_nack_desc.final_ack == '0'B) {
+			repeat;
+		}
+		}
+	[] L1.receive { repeat; }
+	}
+
+	for (var integer i := 0; i < sizeof(us.tbf.llc_pdus_enc); i := i+1) {
+		f_bssgp_wait_ul_ud(tr_BD_BSSGP(tr_BSSGP_UL_UD(g_mmctx.tlli, ?, us.tbf.llc_pdus_enc[i])));
+	}
+	setverdict(pass);
+}
+
 	/* Send a single Uplink Block via Um; Verify reception on BSSGP; Expect UL_ACK on Um */
 	function f_single_ul_block(GprsCodingScheme cs) runs on dummy_CT {
 		var octetstring payload := '01020304'O;
@@ -389,7 +321,7 @@
 		/* ensure that this LLC-PDU arrives from the right TLLI at the (simulated) SGSN */
 		BSSGP.receive(tr_BD_BSSGP(tr_BSSGP_UL_UD(g_mmctx.tlli, ?, llc_enc)));
 
-		/* ensure the MS eceives an UL_ACK_NACK */
+		/* ensure the MS receives an UL_ACK_NACK */
 		alt {
 		[] L1.receive(RLCMAC_ph_data_ind:{cs:=?, ts_nr:=?, fn:=?, block:=tr_RLCMAC_ACK_NACK(0, g_mmctx.tlli)}) -> value dl {
 			log("found matching ACK/NACK");
@@ -573,6 +505,35 @@
 		log(dec_GsmRrMessage(c_ia_tbf));
 	}
 
+	function f_seq_octstr(integer len) return octetstring {
+		var octetstring payload := ''O;
+		for (var integer i := 0; i < len; i := i+1 ) {
+			payload := payload & int2oct(i mod 256, 1);
+		}
+		return payload;
+	}
+
+	testcase TC_ul_tbf() runs on dummy_CT {
+		g_mmctx.imsi := '262420123456789'H;
+		g_mmctx.tlli := f_random_tlli();
+		f_init();
+		f_bssgp_client_register(g_mmctx.imsi, g_mmctx.tlli, mp_gb_cfg.cell_id);
+		f_bssgp_establish();
+
+		var octetstring payload := f_seq_octstr(256);
+		var UlTbfPars ul_tbf_pars := {
+			ack_mode := true,
+			initial_cs := CS1,
+			llc_pdus := { 
+				valueof(ts_LLC_UI(payload, c_LLC_SAPI_LLGMM, '0'B, g_mmctx.n_u)),
+				valueof(ts_LLC_UI(payload, c_LLC_SAPI_LLGMM, '0'B, g_mmctx.n_u+1))
+			}
+		};
+		var UlTbfState ul_tbf_state;
+		f_UlTbfState_init(ul_tbf_state, ul_tbf_pars);
+		f_ul_tbf(ul_tbf_state);
+	}
+
 	control {
 		execute(TC_selftest_bssgp());
 		execute(TC_selftest_ns());
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/LAPDm_RAW_PT.ttcn b/library/LAPDm_RAW_PT.ttcn
index ab99538..30dbb3b 100644
--- a/library/LAPDm_RAW_PT.ttcn
+++ b/library/LAPDm_RAW_PT.ttcn
@@ -27,6 +27,7 @@
 	}
 
 	type record TBF_establish_res {
+		//uint8_t tfi,
 		charstring err optional
 	}
 
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)" }

-- 
To view, visit https://gerrit.osmocom.org/7153
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: I0f93246f02e1bee2e1e9db62af5e1e3559c415e9
Gerrit-PatchSet: 2
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list