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 (#3).

gprs_gb: 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/Osmocom_Types.ttcn
M library/RLCMAC_EncDec.cc
M library/RLCMAC_Types.ttcn
7 files changed, 651 insertions(+), 150 deletions(-)


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

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 <lafo...@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/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: 3
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <lafo...@gnumonks.org>
Gerrit-Reviewer: Harald Welte <lafo...@gnumonks.org>
Gerrit-Reviewer: Jenkins Builder

Reply via email to