blob: f2b0efbcb3af6faa817acb35827bdeb47547e4cf [file] [log] [blame]
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +02001module GPRS_Components {
2/*
3 * Osmocom PCU test suite in TTCN-3, components for GPRS handlng
4 * (C) 2018-2019 Harald Welte <laforge@gnumonks.org>
5 * (C) 2019 Vadim Yanitskiy <axilirator@gmail.com>
6 * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
7 * All rights reserved.
8 *
9 * Released under the terms of GNU General Public License, Version 2 or
10 * (at your option) any later version.
11 *
12 * SPDX-License-Identifier: GPL-2.0-or-later
13 */
14
15import from General_Types all;
16import from Osmocom_Types all;
17import from GSM_Types all;
18import from GSM_RR_Types all;
19
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +020020import from RLCMAC_CSN1_Types all;
21import from RLCMAC_CSN1_Templates all;
22import from RLCMAC_Types all;
23import from RLCMAC_Templates all;
24
25import from MobileL3_CommonIE_Types all;
26import from L3_Templates all;
27
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +020028import from PCUIF_Types all;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +020029import from PCUIF_Components all;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +020030import from Native_Functions all;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +020031
Pau Espin Pedrol2aead382020-10-29 20:46:47 +010032modulepar {
33 /* ARFCN of 1st TRX. Subsequent TRX are allocated incrementing ARFCNs. Nth TRX => base_arfcn + N-1 */
34 GsmArfcn mp_base_arfcn := 871;
35};
36
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +070037type record TsTrxBtsNum {
38 uint3_t ts_nr,
39 uint3_t trx_nr,
40 uint8_t bts_nr,
41 uint8_t blk_nr
42};
43
44template (value) TsTrxBtsNum ts_TsTrxBtsNum(uint3_t ts_nr := 7,
45 uint3_t trx_nr := 0,
46 uint8_t bts_nr := 0,
47 uint8_t blk_nr := 0) := {
48 ts_nr := ts_nr,
49 trx_nr := trx_nr,
50 bts_nr := bts_nr,
51 blk_nr := blk_nr
52};
53template TsTrxBtsNum tr_TsTrxBtsNum(template uint3_t ts_nr := ?,
54 template uint3_t trx_nr := ?,
55 template uint8_t bts_nr := ?,
56 template uint8_t blk_nr := ?) := {
57 ts_nr := ts_nr,
58 trx_nr := trx_nr,
59 bts_nr := bts_nr,
60 blk_nr := blk_nr
61};
62
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +020063type union PacketDlAssignChan {
64 PacketDlAssign ccch,
65 PacketDlAssignment pacch
66};
67
68type record DlTbf {
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +010069 GsmRrMessage rr_imm_ass optional,
70 PacketDlAssignChan ass optional,
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +020071 uint5_t tfi,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +010072 GsmArfcn arfcn optional,
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +010073 BIT8 ts_mask,
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +020074 AckNackDescription acknack_desc
75};
76
77type union PacketUlAssignChan {
78 PacketUlAssign ccch,
79 EgprsUlAss ccch_egprs,
80 PacketUlAssignment pacch
81};
82
83type record UlTbf {
Pau Espin Pedroleb980252020-05-18 18:28:17 +020084 GsmRrMessage rr_imm_ass optional,
85 PacketUlAssignChan ass optional,
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +020086 uint5_t tfi,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +010087 GsmArfcn arfcn optional,
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +010088 BIT8 ts_mask,
Pau Espin Pedroleb980252020-05-18 18:28:17 +020089 uint3_t usf[8],
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +020090 boolean is_egprs,
91 uint14_t bsn,
92 CodingScheme tx_cs_mcs
93};
94
95type record GprsMS {
96 hexstring imsi,
97 GprsTlli tlli,
98 uint16_t ra,
99 uint8_t ra_is_11bit,
100 PCUIF_BurstType burst_type,
101 TimingAdvance ta,
102 int16_t lqual_cb,
103 UlTbf ul_tbf optional, /* TODO: Only 1 UL tbf supported for now */
104 DlTbf dl_tbf optional /* TODO: Only 1 DL tbf supported for now */
105};
Vadim Yanitskiycff3e1b2020-10-31 21:12:12 +0700106type record of GprsMS GprsMSList;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200107
108template AckNackDescription t_AckNackDescription_init := {
109 final_ack := '0'B,
110 starting_seq_nr := 0,
111 receive_block_bitmap := '0000000000000000000000000000000000000000000000000000000000000000'B
112}
113
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200114const uint3_t USF_UNUSED := 7; /* used to indicate PRACH */
115
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200116template (value) GprsMS t_GprsMS_def := {
117 imsi := f_gen_imsi(42),
Pau Espin Pedrolbfd69f62020-10-22 18:06:07 +0200118 tlli := f_gen_tlli(),
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200119 ra := bit2int(chan_req_def),
120 ra_is_11bit := 0,
121 burst_type := BURST_TYPE_0,
122 ta := 0,
123 lqual_cb := 0,
124 ul_tbf := omit,
125 dl_tbf := omit
126};
127
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100128template (value) DlTbf t_DlTbf_def := {
129 rr_imm_ass := omit,
130 ass := omit,
131 tfi := 0,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100132 arfcn := omit,
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100133 ts_mask := '00000000'B,
134 acknack_desc := t_AckNackDescription_init
135};
136
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200137template (value) UlTbf t_UlTbf_def := {
138 rr_imm_ass := omit,
139 ass := omit,
140 tfi := 0,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100141 arfcn := omit,
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100142 ts_mask := '00000000'B,
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200143 usf := { USF_UNUSED, USF_UNUSED, USF_UNUSED, USF_UNUSED, USF_UNUSED, USF_UNUSED, USF_UNUSED, USF_UNUSED },
144 is_egprs := false,
145 bsn := 0,
146 tx_cs_mcs := CS_1
147};
148
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200149type component MS_BTS_IFACE_CT {
150 /* Virtual BTS component */
151 var RAW_PCU_BTS_CT vc_BTS;
152 /* Connection to the BTS component (one for now) */
153 port RAW_PCU_MSG_PT BTS;
154
Vadim Yanitskiyfa6b8822020-10-31 21:10:29 +0700155 /* Mobile station(s) involved in a testing scenario */
Vadim Yanitskiycff3e1b2020-10-31 21:12:12 +0700156 var GprsMSList g_ms := { };
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200157
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200158 /* Value at which Countdown Procedure starts. Announced by network (GPRS Cell Options as per TS 04.60 Chapter 12.24) */
159 var uint4_t g_bs_cv_max := 4;
160}
161
Vadim Yanitskiy79425802020-10-31 21:23:08 +0700162/* Generate a list of GprsMS (unique IMSI/TLLI, cyclic RA) of the given size */
Vadim Yanitskiyfa6b8822020-10-31 21:10:29 +0700163function f_init_gprs_ms(integer num_ms := 1, template (value) GprsMS t_ms := t_GprsMS_def)
164runs on MS_BTS_IFACE_CT {
Pau Espin Pedrolbfd69f62020-10-22 18:06:07 +0200165 for (var integer i := 0; i < num_ms; i := i + 1 ) {
Vadim Yanitskiyfa6b8822020-10-31 21:10:29 +0700166 var GprsMS ms := valueof(t_ms);
167
Vadim Yanitskiye12b2ac2020-10-31 21:21:47 +0700168 /* We assume that num_ms is not a large number */
169 ms.imsi := f_gen_imsi(i + 1);
170 ms.tlli := int2oct(i + 1, 4);
171
Vadim Yanitskiy79425802020-10-31 21:23:08 +0700172 /* Ensure different RA for siblings */
173 if (ms.ra == bit2int(chan_req_def)) {
174 /* 01111 { 0xx | x0x | xx0 } */
175 f_ms_use_ra(ms, ms.ra + (i mod 7));
176 } else if (ms.ra == bit2int(chan_req_sb)) {
177 /* 01110xxx */
178 f_ms_use_ra(ms, ms.ra + (i mod 8));
179 }
180
Vadim Yanitskiyfa6b8822020-10-31 21:10:29 +0700181 /* Append to the global list */
182 g_ms := g_ms & { ms };
Pau Espin Pedrolbfd69f62020-10-22 18:06:07 +0200183 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200184}
185
186
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200187function f_shutdown(charstring file, integer line,
188 boolean final := false)
189runs on MS_BTS_IFACE_CT {
190 /* Determine if the test case was aborted in the middle */
191 if (not final) {
192 log("Test case ", testcasename(), " aborted at ", file, ":", line);
193 } else {
194 /* Guard verdict to avoid 'none' */
195 setverdict(pass);
196 }
197
198 /* Properly shutdown virtual BTS and its clock generator */
199 BTS.send(ts_RAW_PCU_CMD(GENERAL_CMD_SHUTDOWN));
200 vc_BTS.done; /* wait untill it's done */
201
202 /* Shutdown the others and MTC */
203 all component.stop;
204 mtc.stop;
205}
206
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100207function f_arfcn2trxnr(GsmArfcn arfcn) runs on MS_BTS_IFACE_CT return uint3_t {
208 if (arfcn < mp_base_arfcn) {
209 setverdict(fail, "Unable to find TRX NR for arfcn ", arfcn);
210 f_shutdown(__BFILE__, __LINE__);
211 }
212 return arfcn - mp_base_arfcn;
213}
214
215function f_trxnr2arfcn(uint3_t trx_nr) return GsmArfcn {
216 return mp_base_arfcn + trx_nr;
217}
218
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200219function f_ultbf_new_from_rr_imm_ass(in GsmRrMessage rr_imm_ass)
220runs on MS_BTS_IFACE_CT return UlTbf {
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200221 var UlTbf ul_tbf := valueof(t_UlTbf_def);
222 var uint3_t tn_allocated := rr_imm_ass.payload.imm_ass.pkt_chan_desc.tn;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200223
224 ul_tbf.rr_imm_ass := rr_imm_ass;
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100225 if (rr_imm_ass.payload.imm_ass.pkt_chan_desc.presence == '0'B and
226 rr_imm_ass.payload.imm_ass.pkt_chan_desc.zero.hopping == '0'B) {
227 ul_tbf.arfcn := rr_imm_ass.payload.imm_ass.pkt_chan_desc.zero.arfcn;
228 }
229 ul_tbf.ts_mask[tn_allocated] := '1'B;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200230
231 /* Make sure we received an UL TBF Assignment */
232 if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := false, rest := tr_IaRestOctets_ULAss(?)))) {
233 ul_tbf.ass.ccch := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.ul;
234 log("Rx Uplink TBF GPRS assignment: ", ul_tbf.ass.ccch);
235 ul_tbf.is_egprs := false;
236 if (match(ul_tbf.ass.ccch, tr_PacketUlDynAssign)) {
237 ul_tbf.tfi := ul_tbf.ass.ccch.dynamic.tfi_assignment;
Pau Espin Pedrol90401112020-05-20 17:36:27 +0200238 ul_tbf.tx_cs_mcs := f_rlcmac_block_ChCodingCommand2cs_mcs(ul_tbf.ass.ccch.dynamic.ch_coding_cmd);
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200239 ul_tbf.usf[tn_allocated] := ul_tbf.ass.ccch.dynamic.usf;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200240 } else if (match(ul_tbf.ass.ccch, tr_PacketUlSglAssign)) {
241 /* Nothing to do here yet */
242 }
243 } else if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := false, rest := tr_IaRestOctets_EGPRSULAss(?)))) {
244 ul_tbf.ass.ccch_egprs := rr_imm_ass.payload.imm_ass.rest_octets.lh.egprs_ul;
245 log("Rx Uplink TBF EGPRS assignment: ", ul_tbf.ass.ccch_egprs);
246 ul_tbf.is_egprs := true;
247 if (match(ul_tbf.ass.ccch_egprs, tr_EgprsUlAssDynamic)) {
248 ul_tbf.tfi := ul_tbf.ass.ccch_egprs.dynamic.tfi_assignment;
Pau Espin Pedrol90401112020-05-20 17:36:27 +0200249 ul_tbf.tx_cs_mcs := f_rlcmac_block_EgprsChCodingCommand2cs_mcs(ul_tbf.ass.ccch_egprs.dynamic.egprs_ch_coding_cmd);
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200250 ul_tbf.usf[tn_allocated] := ul_tbf.ass.ccch_egprs.dynamic.usf;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200251 } else if (match(ul_tbf.ass.ccch_egprs, tr_EgprsUlAssMultiblock)) {
252 /* Nothing to do here yet */
253 }
254 } else {
255 setverdict(fail, "Failed to match UL TBF Assignment: ", rr_imm_ass);
256 f_shutdown(__BFILE__, __LINE__);
257 }
258
259 setverdict(pass);
260 return ul_tbf;
261}
262
263function f_ultbf_new_from_ass_pacch(RlcmacDlBlock dl_block)
264runs on MS_BTS_IFACE_CT return UlTbf {
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200265 var UlTbf ul_tbf := valueof(t_UlTbf_def);
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100266 var boolean freq_par_present := false;
267 var FrequencyParameters freq_par;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200268
269 ul_tbf.ass.pacch := dl_block.ctrl.payload.u.ul_assignment;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200270 ul_tbf.tx_cs_mcs := f_rlcmac_dl_block_get_assigned_ul_cs_mcs(dl_block);
271 ul_tbf.tfi := f_rlcmac_dl_block_get_tfi(dl_block);
272 /* TODO: handle GlobalTfiOrTlli tfi_or_tlli from pkt_ul_ass */
273
274 /* TODO: support single block allocation */
275 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(tr_DynamicAllocation(?))))) {
276 ul_tbf.tfi := dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ul_tfi_assignment;
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100277 freq_par_present := dl_block.ctrl.payload.u.ul_assignment.gprs.freq_par_present == '1'B;
278 if (freq_par_present) {
279 freq_par := dl_block.ctrl.payload.u.ul_assignment.gprs.freq_par;
280 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200281 ul_tbf.is_egprs := false;
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200282 for (var integer i := 0; i < 8; i := i + 1) {
283 if (dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ts_allocation.ts[i].presence == '1'B) {
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100284 ul_tbf.ts_mask[i] := '1'B;
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200285 ul_tbf.usf[i] := dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ts_allocation.ts[i].usf_tn;
286 }
287 }
288 } else if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(tr_DynamicAllocation(?))))) {
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200289 ul_tbf.tfi := dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ul_tfi_assignment;
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100290 freq_par_present := dl_block.ctrl.payload.u.ul_assignment.egprs.freq_par_present == '1'B;
291 if (freq_par_present) {
292 freq_par := dl_block.ctrl.payload.u.ul_assignment.egprs.freq_par;
293 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200294 ul_tbf.is_egprs := true;
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200295 for (var integer i := 0; i < 8; i := i + 1) {
296 if (dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ts_allocation.ts[i].presence == '1'B) {
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100297 ul_tbf.ts_mask[i] := '1'B;
Pau Espin Pedroleb980252020-05-18 18:28:17 +0200298 ul_tbf.usf[i] := dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ts_allocation.ts[i].usf_tn;
299 }
300 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200301 }
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100302
303 /* FIXME: freq_par and arfcn are optional. in that case we need to
304 * infer/reuse from current dl_tbf or ul_tbf */
305 if (freq_par_present and freq_par.presence == '00'B) {
306 ul_tbf.arfcn := freq_par.arfcn;
307 }
308
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200309 return ul_tbf;
310}
311
312function f_dltbf_new_from_rr_imm_ass(in GsmRrMessage rr_imm_ass, template PacketDlAssign dl_ass := tr_PacketDlAssign(?))
313runs on MS_BTS_IFACE_CT return DlTbf {
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100314 var DlTbf dl_tbf := valueof(t_DlTbf_def);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200315
316 dl_tbf.rr_imm_ass := rr_imm_ass;
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100317 if (rr_imm_ass.payload.imm_ass.pkt_chan_desc.presence == '0'B and
318 rr_imm_ass.payload.imm_ass.pkt_chan_desc.zero.hopping == '0'B) {
319 dl_tbf.arfcn := rr_imm_ass.payload.imm_ass.pkt_chan_desc.zero.arfcn;
320 }
321 dl_tbf.ts_mask[rr_imm_ass.payload.imm_ass.pkt_chan_desc.tn] := '1'B;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200322
323 /* Make sure we received a DL TBF Assignment */
324 if (match(rr_imm_ass, tr_IMM_TBF_ASS(dl := true, rest := tr_IaRestOctets_DLAss(dl_ass)))) {
325 dl_tbf.ass.ccch := rr_imm_ass.payload.imm_ass.rest_octets.hh.pa.uldl.ass.dl;
326 log("Rx Downlink TBF assignment: ", dl_tbf.ass);
327 } else {
328 setverdict(fail, "Failed to match DL TBF Assignment: ", rr_imm_ass);
329 f_shutdown(__BFILE__, __LINE__);
330 }
331
332 /* TODO: match TLLI */
333 if (dl_tbf.ass.ccch.group1_present == '1'B) {
334 dl_tbf.tfi := dl_tbf.ass.ccch.group1.tfi_assignment;
335 } else {
336 setverdict(fail, "Immediate Assignment contains no DL TFI");
337 f_shutdown(__BFILE__, __LINE__);
338 }
339
340 setverdict(pass);
341 return dl_tbf;
342}
343
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200344function f_dltbf_new_from_ass_pacch(RlcmacDlBlock dl_block)
345runs on MS_BTS_IFACE_CT return DlTbf {
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100346 var DlTbf dl_tbf := valueof(t_DlTbf_def);
347 var boolean freq_par_present := false;
348 var FrequencyParameters freq_par;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200349
350 dl_tbf.ass.pacch := dl_block.ctrl.payload.u.dl_assignment;
351 dl_tbf.tfi := f_rlcmac_dl_block_get_tfi(dl_block);
352 /* TODO: handle GlobalTfiOrTlli tfi_or_tlli from pkt_dl_ass */
Pau Espin Pedrolb20b7e52020-10-28 21:28:45 +0100353
354 if (match(dl_block, tr_RLCMAC_DL_PACKET_ASS(?))) {
355 dl_tbf.tfi := dl_block.ctrl.payload.u.dl_assignment.dl_tfi_assignment;
356 freq_par_present := dl_block.ctrl.payload.u.dl_assignment.freq_par_present == '1'B;
357 if (freq_par_present) {
358 freq_par := dl_block.ctrl.payload.u.dl_assignment.freq_par;
359 }
360 dl_tbf.ts_mask := dl_block.ctrl.payload.u.dl_assignment.timeslot_alloc; /* TODO: is this the correct order ? */
361 /* TODO: check egprs in dl_assignment.rel_additions (PktDlAssR99Additions) */
362 }
363
364 /* FIXME: freq_par and arfcn are optional. in that case we need to
365 * infer/reuse from current dl_tbf or ul_tbf */
366 if (freq_par_present and freq_par.presence == '00'B) {
367 dl_tbf.arfcn := freq_par.arfcn;
368 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200369 return dl_tbf;
370}
371
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100372function f_ms_tx_TsTrxBtsNum(inout GprsMS ms)
373runs on MS_BTS_IFACE_CT return TsTrxBtsNum {
374 var uint3_t ts_nr := f_ultbf_next_ts(ms.ul_tbf);
375
376 var uint3_t trx_nr;
377 if (ispresent(ms.ul_tbf.arfcn)) {
378 trx_nr := f_arfcn2trxnr(ms.ul_tbf.arfcn);
379 } else {
380 /* FIXME: implement search by hsn+maio+ma when freq hopping is enabled */
381 setverdict(fail, "Asked for trx_nr but arfcn not available in ms.ul_tbf!");
382 f_shutdown(__BFILE__, __LINE__);
383 }
384 return valueof(ts_TsTrxBtsNum(ts_nr, trx_nr));
385}
386
387function f_dltbf_num_slots(DlTbf dl_tbf)
388runs on MS_BTS_IFACE_CT return integer {
389 var integer n := 0;
390 for (var integer i := 0; i < lengthof(dl_tbf.ts_mask); i := i + 1) {
391 if (dl_tbf.ts_mask[i] == '1'B) {
392 n := n + 1;
393 }
394 }
395 return n;
396}
397
Pau Espin Pedrol05b6c172020-06-26 17:28:23 +0200398function f_ultbf_inc_bsn(inout UlTbf ul_tbf)
399runs on MS_BTS_IFACE_CT {
400 ul_tbf.bsn := ul_tbf.bsn + 1;
401 ul_tbf.bsn := ul_tbf.bsn mod 128; /* FIXME: EGPRS SNS: 2048 */
402}
403
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100404function f_ultbf_next_ts(UlTbf ul_tbf)
405runs on MS_BTS_IFACE_CT return uint3_t {
406 /* FIXME: in the future we probably want to store last used internally
407 /* and continue from there */
408 for (var integer i := 0; i < lengthof(ul_tbf.ts_mask); i := i + 1) {
409 if (ul_tbf.ts_mask[i] == '1'B) {
410 return i;
411 }
412 }
413 setverdict(fail, "No TS available for tx!");
414 f_shutdown(__BFILE__, __LINE__);
415 return 0;
416}
417
418function f_ultbf_num_slots(UlTbf ul_tbf)
419runs on MS_BTS_IFACE_CT return integer {
420 var integer n := 0;
421 for (var integer i := 0; i < lengthof(ul_tbf.ts_mask); i := i + 1) {
422 if (ul_tbf.ts_mask[i] == '1'B) {
423 n := n + 1;
424 }
425 }
426 return n;
427}
428
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200429function f_ms_use_ra(inout GprsMS ms, uint16_t ra, uint8_t ra_is_11bit := 0)
430runs on MS_BTS_IFACE_CT {
431 ms.ra_is_11bit := ra_is_11bit;
432 ms.ra := ra;
433 if (ra_is_11bit == 0) {
434 ms.burst_type := BURST_TYPE_0;
435 } else {
436 ms.burst_type := BURST_TYPE_1;
437 }
438}
439
Vadim Yanitskiy7431e9e2020-07-21 01:59:17 +0700440function f_ms_rx_pkt_ass_pacch(inout GprsMS ms, out uint32_t poll_fn,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700441 template RlcmacDlBlock t_pkt_ass := ?,
442 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200443runs on MS_BTS_IFACE_CT return RlcmacDlBlock {
444 var RlcmacDlBlock dl_block;
445 var uint32_t dl_fn;
446
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700447 f_rx_rlcmac_dl_block(dl_block, dl_fn, nr := nr);
Vadim Yanitskiy7431e9e2020-07-21 01:59:17 +0700448 if (not match(dl_block, t_pkt_ass)) {
449 setverdict(fail, "Failed to match Packet Assignment:", t_pkt_ass);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200450 f_shutdown(__BFILE__, __LINE__);
451 }
452
453 poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp);
454
455 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS)) {
456 ms.ul_tbf := f_ultbf_new_from_ass_pacch(dl_block);
457 if (ms.ul_tbf.ass.pacch.identity.tlli.tlli != ms.tlli) {
458 setverdict(fail, "Wrong TLLI ", ms.ul_tbf.ass.pacch.identity.tlli, " received vs exp ", ms.tlli);
459 f_shutdown(__BFILE__, __LINE__);
460 }
461 } else if (match(dl_block, tr_RLCMAC_DL_PACKET_ASS)) {
462 ms.dl_tbf := f_dltbf_new_from_ass_pacch(dl_block);
Pau Espin Pedrol25e0f292020-10-29 12:36:40 +0100463 if (ischosen(ms.dl_tbf.ass.pacch.tfi_or_tlli.tlli) and
464 ms.dl_tbf.ass.pacch.tfi_or_tlli.tlli.tlli != ms.tlli) {
465 setverdict(fail, "Wrong TLLI ", ms.dl_tbf.ass.pacch.tfi_or_tlli.tlli.tlli, " received vs exp ", ms.tlli);
466 f_shutdown(__BFILE__, __LINE__);
467 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200468 } else {
469 setverdict(fail, "Should not happen:", dl_block);
470 f_shutdown(__BFILE__, __LINE__);
471 }
472
473 return dl_block;
474}
475
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700476function f_ms_establish_ul_tbf(inout GprsMS ms, template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200477runs on MS_BTS_IFACE_CT {
478 var GsmRrMessage rr_imm_ass;
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200479
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700480 rr_imm_ass := f_pcuif_tx_rach_rx_imm_ass(ms.ra, ms.ra_is_11bit, ms.burst_type, ms.ta, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200481 ms.ul_tbf := f_ultbf_new_from_rr_imm_ass(rr_imm_ass);
482}
483
Vadim Yanitskiy84d1dd52020-05-28 21:09:22 +0700484function f_ms_exp_dl_tbf_ass_ccch(inout GprsMS ms, template PCUIF_Sapi sapi := PCU_IF_SAPI_AGCH,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700485 template GsmRrMessage t_imm_ass := tr_IMM_TBF_ASS(true, ?, ?),
486 template (present) TsTrxBtsNum nr := tr_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200487runs on MS_BTS_IFACE_CT {
488 var GsmRrMessage rr_imm_ass;
489
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700490 rr_imm_ass := f_pcuif_rx_imm_ass(sapi, t_imm_ass, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200491 ms.dl_tbf := f_dltbf_new_from_rr_imm_ass(rr_imm_ass, tr_PacketDlAssign(ms.tlli));
492}
493
494/* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700495function f_ms_tx_data_ind(inout GprsMS ms, octetstring data, uint32_t fn := 0,
496 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200497runs on MS_BTS_IFACE_CT {
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700498 f_pcuif_tx_data_ind(data, ms.lqual_cb, fn, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200499}
500
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700501function f_ms_tx_ul_block(inout GprsMS ms, template (value) RlcmacUlBlock ul_data,
502 uint32_t fn := 0, template (omit) CodingScheme force_cs_mcs := omit,
503 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200504runs on MS_BTS_IFACE_CT return integer {
505 var octetstring data;
506 var integer padding_len;
Pau Espin Pedrol245bfcb2020-06-30 20:17:07 +0200507 var CodingScheme cs_mcs;
508 var uint32_t cs_mcs_len;
509
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200510 /* Encode the payload of DATA.ind */
511 data := enc_RlcmacUlBlock(valueof(ul_data));
Pau Espin Pedrol245bfcb2020-06-30 20:17:07 +0200512
513 if (ispresent(force_cs_mcs)) {
514 cs_mcs := valueof(force_cs_mcs);
515 } else if (ischosen(ul_data.ctrl)) {
516 cs_mcs := CS_1; /* CTRL is always CS1 */
517 } else {
518 /* Add padding to encode payload to minimum required CS/MCS: */
519 cs_mcs := f_rlcmac_block_len_required_cs_mcs(lengthof(data), ischosen(ul_data.data_egprs));
520 }
521
522 cs_mcs_len := f_rlcmac_cs_mcs2block_len(cs_mcs);
523 padding_len := cs_mcs_len - lengthof(data);
524 if (padding_len < 0) {
525 setverdict(fail, "Unable to encode UL block of size ", lengthof(data), " with ", cs_mcs);
526 f_shutdown(__BFILE__, __LINE__);
527 }
528 data := f_pad_oct(data, cs_mcs_len, '00'O);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200529
530 /* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700531 f_ms_tx_data_ind(ms, data, fn, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200532 return padding_len;
533}
534
535/* FIXME: Only supports sending CS-1 so far */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700536function f_ms_tx_ul_data_block(inout GprsMS ms, octetstring payload,
537 uint4_t cv := 15, boolean with_tlli := false, uint32_t fn := 0,
538 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200539runs on MS_BTS_IFACE_CT return integer {
540 var template (value) RlcmacUlBlock ul_data;
541 ul_data := t_RLCMAC_UL_DATA(tfi := ms.ul_tbf.tfi,
542 cv := cv,
543 bsn := ms.ul_tbf.bsn,
544 blocks := {t_RLCMAC_LLCBLOCK(payload)});
545 if (with_tlli) {
546 ul_data.data.mac_hdr.tlli_ind := true;
547 ul_data.data.tlli := ms.tlli;
548 }
Pau Espin Pedrol05b6c172020-06-26 17:28:23 +0200549 f_ultbf_inc_bsn(ms.ul_tbf);
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700550 return f_ms_tx_ul_block(ms, ul_data, fn, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200551}
552
553/* Send random payload for last "num_blocks" blocks in Ul TBF (ending with CV=0). */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700554function f_ms_tx_ul_data_block_multi(inout GprsMS ms, integer num_blocks := 1, boolean with_tlli := false,
555 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200556runs on MS_BTS_IFACE_CT return octetstring {
557 var octetstring total_payload := ''O;
558
559 for (var integer i := 0; i < num_blocks; i := i + 1) {
560 var integer padding_len;
561 var octetstring payload := f_rnd_octstring(10);
562 /* Prepare a new UL block (CV, random payload) */
563 var integer cv := num_blocks - i - 1;
564 if (cv > g_bs_cv_max) {
565 cv := 15;
566 }
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700567 padding_len := f_ms_tx_ul_data_block(ms, payload, cv := cv, with_tlli := with_tlli, nr := nr);
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200568 total_payload := total_payload & payload & f_pad_oct(''O, padding_len, '00'O);
569 }
570 return total_payload;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200571}
572
573function f_rlcmac_dl_block_get_tfi(RlcmacDlBlock dl_block)
574runs on MS_BTS_IFACE_CT return uint5_t {
575 if (ischosen(dl_block.data)) {
576 return dl_block.data.mac_hdr.hdr_ext.tfi;
577 } else if (ischosen(dl_block.data_egprs)) {
578 return dl_block.data_egprs.mac_hdr.tfi;
579 } else { /* Ctrl block */
580 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(tr_DynamicAllocation(?))))) {
581 return dl_block.ctrl.payload.u.ul_assignment.gprs.dyn_block_alloc.ul_tfi_assignment;
582 }
583 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(tr_DynamicAllocation(?))))) {
584 return dl_block.ctrl.payload.u.ul_assignment.egprs.dyn_block_alloc.ul_tfi_assignment;
585 }
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200586 if (match(dl_block, tr_RLCMAC_DL_PACKET_ASS(?))) {
587 return dl_block.ctrl.payload.u.dl_assignment.dl_tfi_assignment;
588 }
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200589 }
590 setverdict(fail, "DlBlock doesn't contain a TFI:", dl_block);
591 f_shutdown(__BFILE__, __LINE__);
592 return 0; /* make compiler happy */
593}
594
595/* Get the Chan coding command from a dl block containing PACCH UL Assignment */
596function f_rlcmac_dl_block_get_assigned_ul_cs_mcs(RlcmacDlBlock dl_block)
597runs on MS_BTS_IFACE_CT return CodingScheme {
598 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_GPRS(?, tr_PktUlAssGprsDynamic(?)))) {
599 return f_rlcmac_block_ChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.gprs.ch_coding_cmd);
600 }
601 if (match(dl_block, tr_RLCMAC_UL_PACKET_ASS_EGPRS(?, tr_PktUlAssEgprsDynamic(?)))) {
602 return f_rlcmac_block_EgprsChCodingCommand2cs_mcs(dl_block.ctrl.payload.u.ul_assignment.egprs.chan_coding_cmd);
603 }
604 setverdict(fail, "DlBlock doesn't contain CS_MCS information:", dl_block);
605 f_shutdown(__BFILE__, __LINE__);
606 return CS_1; /* make compiler happy */
607}
608
609/* TS 44.060 sec 12.3 Ack/Nack Description */
610function f_acknackdesc_ack_block(inout AckNackDescription desc, RlcmacDlBlock dl_block, BIT1 final_ack := '0'B)
611{
612 var uint7_t bsn;
613 var integer i;
614 var integer inc;
615
616 if (ischosen(dl_block.data)) {
617 bsn := dl_block.data.mac_hdr.hdr_ext.bsn;
618 } else {
619 bsn := dl_block.data_egprs.mac_hdr.bsn1;
620 }
621
622 inc := bsn - desc.starting_seq_nr + 1;
623 /* Filling hole? */
624 if (bsn < desc.starting_seq_nr) {
625 desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - (desc.starting_seq_nr - bsn)] := int2bit(1, 1);
626 return;
627 }
628
629 /* SSN is increased, and so RBB values need to be moved */
630 for (i := 0; i < lengthof(desc.receive_block_bitmap) - inc; i := i+1) {
631 desc.receive_block_bitmap[i] := desc.receive_block_bitmap[i + inc];
632 }
633 for (i := lengthof(desc.receive_block_bitmap) - inc; i < lengthof(desc.receive_block_bitmap) - 1; i := i+1) {
634 desc.receive_block_bitmap[i] := int2bit(0, 1);
635 }
636 /* Now we can set current bit and update SSN */
637 desc.starting_seq_nr := bsn + 1;
638 desc.receive_block_bitmap[lengthof(desc.receive_block_bitmap) - 1] := int2bit(1, 1);
639
640 /* Finally update the final_ack bit as requested: */
641 desc.final_ack := final_ack;
642}
643
644/* This function can be used to send DATA.cnf in response to the IUT originated DATA.req.
645 * NOTE: it's the responsibility of caller to make sure that pcu_msg contains u.data_req. */
646function f_pcuif_tx_data_cnf(in PCUIF_Message pcu_msg)
647runs on MS_BTS_IFACE_CT {
648 var PCUIF_Message pcu_msg_cnf := {
649 msg_type := PCU_IF_MSG_DATA_CNF,
650 bts_nr := pcu_msg.bts_nr,
651 spare := pcu_msg.spare,
652 u := { data_cnf := pcu_msg.u.data_req }
653 };
654
655 /* PCU wants DATA.cnf containing basically everything that was in DATA.req,
656 * but PCU_IF_SAPI_PCH is a special case - paging group shall be excluded. */
657 if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) {
658 pcu_msg_cnf.u.data_cnf.data := substr(pcu_msg.u.data_req.data, 3,
659 pcu_msg.u.data_req.len - 3);
660 }
661
662 BTS.send(pcu_msg_cnf);
663}
664
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200665////////////////////////
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700666// Low level APIs
Pau Espin Pedrol4f7b8fd2020-05-18 18:28:17 +0200667////////////////////////
668
Vadim Yanitskiy7466c332020-05-28 20:41:19 +0700669function f_pcuif_rx_imm_ass(template PCUIF_Sapi sapi := PCU_IF_SAPI_AGCH,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700670 template GsmRrMessage t_imm_ass := ?,
671 template (present) TsTrxBtsNum nr := tr_TsTrxBtsNum)
Vadim Yanitskiy7466c332020-05-28 20:41:19 +0700672runs on MS_BTS_IFACE_CT return GsmRrMessage {
673 var GsmRrMessage rr_imm_ass;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200674 var PCUIF_Message pcu_msg;
675 var octetstring data;
676 timer T;
677
678 T.start(2.0);
679 alt {
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700680 [] BTS.receive(tr_PCUIF_DATA_REQ(nr.bts_nr, nr.trx_nr, ts_nr := 0,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200681 sapi := sapi, data := ?)) -> value pcu_msg {
682 /* On PCH the payload is prefixed with paging group (3 octets): skip it.
683 * TODO: add an additional template parameter, so we can match it. */
684 if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) {
685 data := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3);
686 } else {
687 data := pcu_msg.u.data_req.data;
688 }
689
690 rr_imm_ass := dec_GsmRrMessage(data);
691 if (not match(rr_imm_ass, t_imm_ass)) {
692 /* Not for us? Wait for more. */
693 repeat;
694 }
695
696 log("Rx Immediate Assignment: ", rr_imm_ass);
Pau Espin Pedrol34c940b2020-05-28 19:56:11 +0700697
698 /* Send DATA.cnf back to the IUT (only needed for PCH) */
699 if (pcu_msg.u.data_req.sapi == PCU_IF_SAPI_PCH) {
700 f_pcuif_tx_data_cnf(pcu_msg);
701 }
702
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200703 setverdict(pass);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200704 }
705 [] BTS.receive { repeat; }
706 [] T.timeout {
707 setverdict(fail, "Timeout waiting for Immediate Assignment");
Pau Espin Pedrolfddc20b2020-05-28 19:50:29 +0700708 f_shutdown(__BFILE__, __LINE__);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200709 }
710 }
711
Vadim Yanitskiy7466c332020-05-28 20:41:19 +0700712 return rr_imm_ass;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200713}
714
715/* One phase packet access (see 3GPP TS 44.018, table 9.1.8.1) */
Vadim Yanitskiy1cea9fd2020-10-31 21:34:05 +0700716const BIT8 chan_req_def := '01111000'B; /* 01111 { 0xx | x0x | xx0 } */
717/* Single block (two phase or RR signalling) packet access */
718const BIT8 chan_req_sb := '01110000'B; /* 01110xxx */
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200719
720/* Establish an Uplink TBF by sending RACH.ind towards the PCU */
Vadim Yanitskiy28d18e12020-05-29 15:25:59 +0700721function f_pcuif_tx_rach_rx_imm_ass(uint16_t ra := bit2int(chan_req_def),
722 uint8_t is_11bit := 0,
723 PCUIF_BurstType burst_type := BURST_TYPE_0,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700724 TimingAdvance ta := 0,
725 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Vadim Yanitskiy7466c332020-05-28 20:41:19 +0700726runs on MS_BTS_IFACE_CT return GsmRrMessage {
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200727 var uint32_t fn;
728
729 /* FIXME: ask the BTS component to give us the current TDMA fn */
730 fn := 1337 + ta;
731
732 /* Send RACH.ind */
733 log("Sending RACH.ind on fn=", fn, " with RA=", ra, ", TA=", ta);
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700734 BTS.send(ts_PCUIF_RACH_IND(nr.bts_nr, nr.trx_nr, ts_nr := 0,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200735 ra := ra, is_11bit := is_11bit,
736 burst_type := burst_type,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100737 fn := fn, arfcn := f_trxnr2arfcn(valueof(nr.trx_nr)),
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200738 qta := ta * 4));
739
740 /* 3GPP TS 44.018, table 9.1.8.1, note 2b: Request Reference shall be set to 127
741 * when Immediate Assignment is triggered by EGPRS Packet Channel Request. Here
742 * we assume that 11 bit RA always contains EGPRS Packet Channel Request. */
743 if (is_11bit != 0) { ra := 127; }
744
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700745 /* Expect Immediate (TBF) Assignment on the same TS/TRX/BTS */
746 return f_pcuif_rx_imm_ass(PCU_IF_SAPI_AGCH, tr_IMM_TBF_ASS(false, ra, fn), nr);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200747}
748
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200749/* Enqueue DATA.ind (both TDMA frame and block numbers to be patched) */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700750function f_pcuif_tx_data_ind(octetstring data, int16_t lqual_cb := 0, uint32_t fn := 0,
751 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200752runs on MS_BTS_IFACE_CT {
753 var template RAW_PCU_EventParam ev_param := {tdma_fn := ? };
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700754 BTS.send(ts_PCUIF_DATA_IND(nr.bts_nr, nr.trx_nr, nr.ts_nr, nr.blk_nr,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200755 sapi := PCU_IF_SAPI_PDTCH, data := data,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100756 fn := fn, arfcn := f_trxnr2arfcn(valueof(nr.trx_nr)),
757 lqual_cb := lqual_cb));
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200758 if (fn != 0) {
759 ev_param := {tdma_fn := fn };
760 }
761 BTS.receive(tr_RAW_PCU_EV(TDMA_EV_PDTCH_BLOCK_SENT, ev_param));
762}
763
764/* Enqueue RTS.req, expect DATA.req with UL ACK from the PCU */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700765function f_pcuif_rx_data_req(out PCUIF_Message pcu_msg,
766 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200767runs on MS_BTS_IFACE_CT {
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700768 BTS.send(ts_PCUIF_RTS_REQ(nr.bts_nr, nr.trx_nr, nr.ts_nr,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200769 sapi := PCU_IF_SAPI_PDTCH, fn := 0,
Pau Espin Pedrol2aead382020-10-29 20:46:47 +0100770 arfcn := f_trxnr2arfcn(valueof(nr.trx_nr)), block_nr := nr.blk_nr));
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700771 BTS.receive(tr_PCUIF_DATA_REQ(nr.bts_nr, nr.trx_nr, nr.ts_nr,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200772 sapi := PCU_IF_SAPI_PDTCH)) -> value pcu_msg;
773}
774
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200775/* Expect a Paging Request Type 1 from PCU on PCUIF on specified sapi. */
776function f_pcuif_rx_pch_pag_req1(template MobileIdentityV mi1 := ?,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700777 template integer pag_group := ?,
778 template (present) TsTrxBtsNum nr := tr_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200779runs on MS_BTS_IFACE_CT return GsmRrMessage {
780 var GsmRrMessage rr_pag_req1;
781 var PCUIF_Message pcu_msg;
782 var octetstring imsi_suff_octstr;
783 var integer pag_group_rx;
784 var octetstring macblock;
785
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700786 BTS.receive(tr_PCUIF_DATA_REQ(nr.bts_nr, nr.trx_nr, nr.ts_nr,
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200787 sapi := PCU_IF_SAPI_PCH)) -> value pcu_msg;
788
789 /* First 3 bytes contain IMSI suffix to calculate paging group: */
790 imsi_suff_octstr := substr(pcu_msg.u.data_req.data, 0, 3);
791 pag_group_rx := str2int(oct2char(imsi_suff_octstr[0])) * 100 +
792 str2int(oct2char(imsi_suff_octstr[1])) * 10 +
793 str2int(oct2char(imsi_suff_octstr[2]));
794
795 /* Make sure we've got RR Paging Request Type 1 for a given MI */
796 macblock := substr(pcu_msg.u.data_req.data, 3, pcu_msg.u.data_req.len - 3);
797 rr_pag_req1 := dec_GsmRrMessage(macblock);
798 if (not match(rr_pag_req1, tr_PAG_REQ1(tr_MI_LV(mi1)))) {
799 setverdict(fail, "Failed to match Paging Request Type 1: ", rr_pag_req1);
800 f_shutdown(__BFILE__, __LINE__);
801 }
802
803 /* Make sure that received paging froup matches the expected one */
804 if (not match(pag_group_rx, pag_group)) {
805 setverdict(fail, "Paging group", pag_group_rx, " does not match expected ", pag_group);
806 f_shutdown(__BFILE__, __LINE__);
807 }
808
809 f_pcuif_tx_data_cnf(pcu_msg);
810 return rr_pag_req1;
811}
812
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700813function f_rx_rlcmac_dl_block(out RlcmacDlBlock dl_block, out uint32_t dl_fn,
814 template (present) CodingScheme exp_cs_mcs := ?,
815 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200816runs on MS_BTS_IFACE_CT {
817 var PCUIF_Message pcu_msg;
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700818 f_pcuif_rx_data_req(pcu_msg, nr := nr);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200819 dl_block := dec_RlcmacDlBlock(pcu_msg.u.data_req.data);
820 dl_fn := pcu_msg.u.data_req.fn;
821
822 var integer len := lengthof(pcu_msg.u.data_req.data);
823 var CodingScheme cs_mcs := f_rlcmac_block_len2cs_mcs(len)
824 if (not match(f_rlcmac_block_len2cs_mcs(len), exp_cs_mcs)) {
825 setverdict(fail, "Failed to match Coding Scheme exp ", exp_cs_mcs, " vs ", cs_mcs, " (", len, ")");
826 f_shutdown(__BFILE__, __LINE__);
827 }
828}
829
Pau Espin Pedrolcdbe9032020-07-07 20:20:42 +0200830function f_rx_rlcmac_dl_block_exp_ack_nack(out RlcmacDlBlock dl_block, out uint32_t poll_fn,
831 template RlcmacDlBlock acknack_tmpl := (tr_RLCMAC_UL_ACK_NACK_GPRS(ul_tfi := ?),
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700832 tr_RLCMAC_UL_ACK_NACK_EGPRS(ul_tfi := ?)),
833 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200834runs on MS_BTS_IFACE_CT {
835 var uint32_t dl_fn;
836
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700837 f_rx_rlcmac_dl_block(dl_block, dl_fn, nr := nr);
Pau Espin Pedrolcdbe9032020-07-07 20:20:42 +0200838 if (match(dl_block, acknack_tmpl)) {
Pau Espin Pedrol692222c2020-05-17 00:25:37 +0200839 poll_fn := f_rrbp_ack_fn(dl_fn, dl_block.ctrl.mac_hdr.rrbp);
840 return;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200841 }
Pau Espin Pedrol692222c2020-05-17 00:25:37 +0200842 setverdict(fail, "Failed to match Packet Uplink ACK / NACK:", dl_block);
843 f_shutdown(__BFILE__, __LINE__);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200844}
845
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700846function f_rx_rlcmac_dl_block_exp_dummy(out RlcmacDlBlock dl_block,
847 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200848runs on MS_BTS_IFACE_CT {
849 var uint32_t dl_fn;
850
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700851 f_rx_rlcmac_dl_block(dl_block, dl_fn, nr := nr);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200852 if (not match(dl_block, tr_RLCMAC_DUMMY_CTRL())) {
853 setverdict(fail, "Failed to match Packet DUMMY DL");
854 f_shutdown(__BFILE__, __LINE__);
855 }
856}
857
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700858function f_rx_rlcmac_dl_block_exp_pkt_pag_req(out RlcmacDlBlock dl_block,
859 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200860runs on MS_BTS_IFACE_CT {
861 var uint32_t dl_fn;
862
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700863 f_rx_rlcmac_dl_block(dl_block, dl_fn, nr := nr);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200864 if (not match(dl_block, tr_RLCMAC_PACKET_PAG_REQ())) {
865 setverdict(fail, "Failed to match Packet Paging Request: ", dl_block, " vs ", tr_RLCMAC_PACKET_PAG_REQ());
866 f_shutdown(__BFILE__, __LINE__);
867 }
868}
869
870/* This function does what could probably be done with templates */
871function f_rlcmac_dl_block_verify_data_gprs(in RlcmacDlDataBlock data_block,
872 template (present) octetstring data := ?,
873 template (present) uint7_t exp_bsn := ?,
874 template (present) CodingScheme exp_cs := ?)
875runs on MS_BTS_IFACE_CT {
876 if (not match(data_block.mac_hdr.hdr_ext.bsn, exp_bsn)) {
877 setverdict(fail, "DL block BSN doesn't match: ",
878 data_block.mac_hdr.hdr_ext.bsn, " vs exp ", exp_bsn);
879 }
880
881 if (lengthof(data_block.blocks) < 1) {
882 setverdict(fail, "DL block has no LLC payload: ", data_block);
883 f_shutdown(__BFILE__, __LINE__);
884 }
885
886 if (not match(data_block.blocks[0].payload, data)) {
887 setverdict(fail, "Failed to match content of LLC payload in DL Block: ",
888 data_block.blocks[0].payload, " vs ", data);
889 f_shutdown(__BFILE__, __LINE__);
890 }
891
892 /* Check next data blocks contain dummy frames */
893 if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) {
894 setverdict(fail, "Second data payload is not a dummy frame: ",
895 data_block.blocks[1].payload);
896 f_shutdown(__BFILE__, __LINE__);
897 }
898
899 /* TODO: check exp_cs */
900}
901
902/* This function does what could probably be done with templates */
903function f_rlcmac_dl_block_verify_data_egprs(in RlcmacDlEgprsDataBlock data_block,
904 template (present) octetstring data := ?,
905 template (present) uint14_t exp_bsn := ?,
906 template (present) CodingScheme exp_cs := ?)
907runs on MS_BTS_IFACE_CT {
908 if (not match(data_block.mac_hdr.bsn1, exp_bsn)) {
909 setverdict(fail, "DL block BSN doesn't match: ",
910 data_block.mac_hdr.bsn1, " vs exp ", exp_bsn);
911 }
912
913 if (lengthof(data_block.blocks) < 1) {
914 setverdict(fail, "DL block has no LLC payload: ", data_block);
915 f_shutdown(__BFILE__, __LINE__);
916 }
917
918 if (not match(data_block.blocks[0].payload, data)) {
919 setverdict(fail, "Failed to match content of LLC payload in DL Block: ",
920 data_block.blocks[0].payload, " vs ", data);
921 f_shutdown(__BFILE__, __LINE__);
922 }
923
924 /* Check next data blocks contain dummy frames */
925 if (lengthof(data_block.blocks) > 1 and substr(data_block.blocks[1].payload, 0, 3) != '43C001'O) {
926 setverdict(fail, "Second data payload is not a dummy frame: ",
927 data_block.blocks[1].payload);
928 f_shutdown(__BFILE__, __LINE__);
929 }
930
931 /* TODO: Check exp_cs. In the case of EGPRS, first check mac_hdr.header_type and then decode CPS = exp_cs based on mac_hdr.header_type.
932 See wireshark's egprs_Header_type1_coding_puncturing_scheme_to_mcs. */
933}
934
935/* High level (task specific) helper for receiving and matching GPRS/EGPRS data blocks */
936function f_rx_rlcmac_dl_block_exp_data(out RlcmacDlBlock dl_block, out uint32_t dl_fn,
937 template (present) octetstring data := ?,
938 template (present) uint7_t exp_bsn := ?,
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700939 template (present) CodingScheme exp_cs := ?,
940 template (value) TsTrxBtsNum nr := ts_TsTrxBtsNum)
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200941runs on MS_BTS_IFACE_CT {
942 /* FIXME: ideally we should use an alt statement with timeout here, rather than
943 * having +100500 layers of abstraction. This would facilitate developing the
944 * multi-TBF/-TRX/-BTS tests, where you cannot expect that the first received
945 * block is exactly what you need. */
Vadim Yanitskiy4b7473d2020-09-21 04:15:39 +0700946 f_rx_rlcmac_dl_block(dl_block, dl_fn, nr := nr);
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200947
948 /* Make sure it's either GPRS or EGPRS data block */
949 if (not match(dl_block, tr_RLCMAC_DATA)) {
950 setverdict(fail, "Failed to match DL DATA: ", dl_block, " vs ", tr_RLCMAC_DATA);
951 f_shutdown(__BFILE__, __LINE__);
952 }
953
954 if (ischosen(dl_block.data_egprs)) {
955 f_rlcmac_dl_block_verify_data_egprs(dl_block.data_egprs, data, exp_bsn, exp_cs);
956 } else if (ischosen(dl_block.data)) {
957 f_rlcmac_dl_block_verify_data_gprs(dl_block.data, data, exp_bsn, exp_cs);
958 } else {
959 /* Should not happen, but the caller may theoretically give us a template for CTRL */
960 setverdict(fail, "DL block is neither GPRS nor EGPRS data block: ", dl_block);
961 f_shutdown(__BFILE__, __LINE__);
962 }
963}
964
965function f_dl_block_ack_fn(in RlcmacDlBlock dl_block, uint32_t dl_fn)
966runs on MS_BTS_IFACE_CT return uint32_t {
967 var boolean rrbp_valid;
968 var MacRrbp rrbp;
969
970 /* The argument must be either a GPRS or EGPRS data block */
971 if (ischosen(dl_block.data_egprs)) {
972 rrbp_valid := true; /* always valid */
973 rrbp := dl_block.data_egprs.mac_hdr.rrbp;
974 } else if (ischosen(dl_block.data)) {
975 rrbp_valid := dl_block.data.mac_hdr.mac_hdr.rrbp_valid;
976 rrbp := dl_block.data.mac_hdr.mac_hdr.rrbp;
977 } else {
978 rrbp_valid := dl_block.ctrl.mac_hdr.rrbp_valid;
979 rrbp := dl_block.ctrl.mac_hdr.rrbp;
980 }
981
982 /* Make sure that the given block really needs to be ACKnowledged */
983 if (not rrbp_valid) {
984 setverdict(fail, "DL block shall not be ACKnowledged, field RRBP is not valid");
985 f_shutdown(__BFILE__, __LINE__);
986 }
987
988 return f_rrbp_ack_fn(dl_fn, rrbp);
989}
990
Vadim Yanitskiy3e5f0742020-11-01 03:30:18 +0700991/* Return true if a given Packet Paging Request contains the given IMSI, false otherwise */
992function f_pkt_paging_match_imsi(in PacketPagingReq req, template hexstring imsi,
993 boolean cs_domain := true, boolean ps_domain := true)
994runs on MS_BTS_IFACE_CT return boolean {
Vadim Yanitskiye94164d2020-10-31 05:46:03 +0700995 if (not ispresent(req.repeated_pageinfo)) {
996 setverdict(fail, "Packet Paging Request without MIs?!?");
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +0200997 f_shutdown(__BFILE__, __LINE__);
998 }
Vadim Yanitskiye94164d2020-10-31 05:46:03 +0700999
1000 for (var integer i := 0; i < lengthof(req.repeated_pageinfo); i := i + 1) {
1001 var PageInfo info := req.repeated_pageinfo[i].item;
Vadim Yanitskiy3e5f0742020-11-01 03:30:18 +07001002 var MobileIdentityLV_Paging mi_lv;
Vadim Yanitskiye94164d2020-10-31 05:46:03 +07001003
Vadim Yanitskiy3e5f0742020-11-01 03:30:18 +07001004 if (ischosen(info.cs)) { /* CS domain */
1005 if (not ispresent(info.cs.mobile_identity))
1006 { continue; }
1007 if (not cs_domain)
1008 { continue; }
1009 mi_lv := info.cs.mobile_identity;
1010 } else { /* PS domain */
1011 if (not ispresent(info.ps.mobile_identity))
1012 { continue; }
1013 if (not ps_domain)
1014 { continue; }
1015 mi_lv := info.ps.mobile_identity;
1016 }
1017
1018 /* Make sure MI contains IMSI before referencing it */
1019 if (match(mi_lv.mobile_id, decmatch tr_MI_IMSI(imsi))) {
1020 return true;
Vadim Yanitskiye94164d2020-10-31 05:46:03 +07001021 }
1022 }
1023
Vadim Yanitskiy3e5f0742020-11-01 03:30:18 +07001024 return false;
1025}
1026
1027/* Return true if a given Packet Paging Request contains the given P-TMSI, false otherwise */
1028function f_pkt_paging_match_tmsi(in PacketPagingReq req, template GsmTmsi tmsi,
1029 boolean cs_domain := true, boolean ps_domain := true)
1030runs on MS_BTS_IFACE_CT return boolean {
1031 if (not ispresent(req.repeated_pageinfo)) {
1032 setverdict(fail, "Packet Paging Request without MIs?!?");
1033 f_shutdown(__BFILE__, __LINE__);
1034 }
1035
1036 for (var integer i := 0; i < lengthof(req.repeated_pageinfo); i := i + 1) {
1037 var PageInfo info := req.repeated_pageinfo[i].item;
1038
1039 if (cs_domain and ischosen(info.cs)) {
1040 if (not ispresent(info.cs.tmsi))
1041 { continue; }
1042 if (match(info.cs.tmsi, tmsi))
1043 { return true; }
1044 } else if (ps_domain) {
1045 if (not ispresent(info.ps.ptmsi))
1046 { continue; }
1047 if (match(info.ps.ptmsi, tmsi))
1048 { return true; }
1049 }
1050 }
1051
1052 return false;
Pau Espin Pedrolaedc5112020-05-16 17:30:42 +02001053}
1054
1055}