LAPDm_RAW_PT: Add support for TBF mode (GPRS)
diff --git a/library/LAPDm_RAW_PT.ttcn b/library/LAPDm_RAW_PT.ttcn
index 06225bb..06f5590 100644
--- a/library/LAPDm_RAW_PT.ttcn
+++ b/library/LAPDm_RAW_PT.ttcn
@@ -8,6 +8,7 @@
import from L1CTL_Types all;
import from L1CTL_PortType all;
import from LAPDm_Types all;
+ import from RLCMAC_Types all;
/* request to tune to a given ARFCN and start BCCH decoding */
type record BCCH_tune_req {
@@ -25,6 +26,10 @@
charstring err optional
}
+ type record TBF_establish_res {
+ charstring err optional
+ }
+
type record DCCH_release_req {
}
@@ -35,23 +40,46 @@
LapdmFrame lapdm
}
+ type record TBF_establish_req {
+ uint8_t ra
+ }
+
+ /* PH-DATA.ind / PH-DATA.req */
+ type record RLCMAC_ph_data_ind {
+ GprsCodingScheme cs,
+ RlcmacDlBlock block
+ }
+ type record RLCMAC_ph_data_req {
+ uint8_t tbf_id,
+ GprsCodingScheme cs,
+ RlcmacUlBlock block
+ }
+
/* port from our (internal) point of view */
type port LAPDm_SP_PT message {
in BCCH_tune_req,
DCCH_establish_req,
DCCH_release_req,
+ TBF_establish_req,
+ RLCMAC_ph_data_req,
LAPDm_ph_data;
out DCCH_establish_res,
+ TBF_establish_res,
+ RLCMAC_ph_data_ind,
LAPDm_ph_data;
} with {extension "internal"};
/* port from user (external) point of view */
type port LAPDm_PT message {
in DCCH_establish_res,
+ TBF_establish_res,
+ RLCMAC_ph_data_ind,
LAPDm_ph_data;
out BCCH_tune_req,
DCCH_establish_req,
DCCH_release_req,
+ TBF_establish_req,
+ RLCMAC_ph_data_req,
LAPDm_ph_data;
} with {extension "internal"};
@@ -66,7 +94,8 @@
PH_STATE_BCH,
PH_STATE_SEARCHING_BCH,
PH_STATE_TUNING_DCH,
- PH_STATE_DCH
+ PH_STATE_DCH,
+ PH_STATE_TBF
}
type component lapdm_CT {
@@ -98,7 +127,7 @@
/* release the dedicated radio channel */
private function f_release_dcch() runs on lapdm_CT {
L1CTL.send(t_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
- set_ph_state(PH_STATE_NULL);
+ set_ph_state(PH_STATE_BCH);
}
/* tune to given ARFCN and start BCCH/CCCH decoding */
@@ -111,6 +140,8 @@
if (ph_state == PH_STATE_DCH) {
/* release any previous DCH */
f_release_dcch();
+ } else if (ph_state == PH_STATE_TBF) {
+ f_release_tbf();
}
set_ph_state(PH_STATE_SEARCHING_BCH);
@@ -142,12 +173,102 @@
set_ph_state(PH_STATE_DCH);
}
+ /* initialize a tfi_usf array with "not used" value 255 for all TN */
+ function f_TfiUsfArrInit() return TfiUsfArr {
+ var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
+ return tua;
+ }
+
+ /* set TFI/USF value for one given timeslot number (index) */
+ function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
+ a[idx] := tfi_usf;
+ }
+
+ /* Match an IMM.ASS for an Uplink TBF with a dynamic allocation */
+ template ImmediateAssignment t_IMM_ASS_TBF_UL_DYN(uint8_t ra, GsmFrameNumber fn) modifies t_IMM_ASS := {
+ ded_or_tbf := { spare := ?, tma := ?, downlink := false, tbf := true},
+ chan_desc := omit,
+ pkt_chan_desc := ?,
+ rest_octets := {
+ presence := '11'B,
+ ll := omit,
+ lh := omit,
+ hl := omit,
+ hh := {
+ presence := '00'B,
+ ul := {
+ presence := '1'B,
+ dynamic := {
+ tfi_assignment := ?,
+ polling := ?,
+ spare := '0'B,
+ usf := ?,
+ usf_granularity := ?,
+ p0_present := ?,
+ p0 := *,
+ pr_mode := *,
+ ch_coding_cmd := ?,
+ tlli_block_chan_coding:= ?,
+ alpha_present := ?,
+ alpha := *,
+ gamma := ?,
+ ta_index_present := ?,
+ ta_index := *,
+ tbf_starting_time_present := ?,
+ tbf_starting_time := *
+ },
+ single := omit
+ },
+ dl := omit
+ }
+ }
+ };
+
+ private function f_establish_tbf(uint8_t ra) runs on lapdm_CT {
+ var ImmediateAssignment imm_ass;
+ var GsmFrameNumber rach_fn;
+ var TfiUsfArr tua := f_TfiUsfArrInit();
+
+ /* send RACH request and obtain FN at which it was sent */
+ rach_fn := f_L1CTL_RACH(L1CTL, ra);
+
+ /* wait for receiving matching IMM ASS */
+ imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
+
+ if (match(imm_ass, t_IMM_ASS_TBF_UL_DYN(ra, rach_fn))) {
+ set_ph_state(PH_STATE_TBF);
+
+ /* store/save channel description */
+ //chan_desc := imm_ass.chan_desc;
+
+ /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
+ f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, imm_ass.rest_octets.hh.ul.dynamic.usf);
+ f_L1CTL_TBF_CFG(L1CTL, true, tua);
+ } else {
+ /* FIXME: single block uplink allocation */
+ log("Failed to match ", t_IMM_ASS_TBF_UL_DYN(ra, rach_fn));
+ log("Non-dynamic UL TBF assignment not supported yet");
+ }
+ }
+
+ private function f_release_tbf() runs on lapdm_CT {
+ var TfiUsfArr tua := f_TfiUsfArrInit();
+ /* send "all timeslots unused" for both UL and DL */
+ f_L1CTL_TBF_CFG(L1CTL, true, tua);
+ f_L1CTL_TBF_CFG(L1CTL, false, tua);
+ /* L1 will then fall back to BCCH/CCCH */
+ set_ph_state(PH_STATE_BCH);
+ }
+
function ScanEvents() runs on lapdm_CT {
var L1ctlDlMessage dl;
var BCCH_tune_req bt;
var LAPDm_ph_data lpd;
+ var RLCMAC_ph_data_ind rpdi;
+ var RLCMAC_ph_data_req rpdr;
var DCCH_establish_req est_req;
var DCCH_establish_res est_res;
+ var TBF_establish_req tbf_req;
while (true) {
if (ph_state == PH_STATE_NULL) {
@@ -194,6 +315,18 @@
LAPDM_SP.send(res);
}
+ /* Establish TBF / packet transfer mode */
+ [] LAPDM_SP.receive(TBF_establish_req:?) -> value tbf_req {
+ var TBF_establish_res res;
+ f_establish_tbf(tbf_req.ra);
+ if (ph_state == PH_STATE_TBF) {
+ res := { err := omit };
+ } else {
+ res := { err := "Unable to establish TBF" };
+ }
+ LAPDM_SP.send(res);
+ }
+
[] LAPDM_SP.receive {}
[] L1CTL.receive {}
@@ -239,7 +372,36 @@
}
+ } else if (ph_state == PH_STATE_TBF) {
+ alt {
+
+ /* decode + forward any blocks from L1 to L23*/
+ [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
+ rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
+ rpdi.cs := CS1; /* FIXME */
+ log("RPDI: ", rpdi);
+ LAPDM_SP.send(rpdi);
+ }
+
+ [] L1CTL.receive { }
+
+ /* encode + forward any blocks from L23 to L1 */
+ [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
+ var octetstring buf;
+
+ buf := enc_RlcmacUlBlock(rpdr.block);
+ L1CTL.send(t_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.tbf_id));
+ }
+
+ /* FIXME: release TBF mode */
+ [] LAPDM_SP.receive(DCCH_release_req:?) {
+ /* go back to BCCH */
+ f_release_tbf();
+ }
+
+ }
}
+
} /* while (1) */
}
}