blob: 34e17becb51bfe25fa282ce0835a449d9f762daf [file] [log] [blame]
Harald Welte505cf9b2018-09-15 17:47:23 +03001module BTS_Tests_SMSCB {
2
3/* Integration Tests for OsmoBTS
4 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
5 * All rights reserved.
6 *
7 * Released under the terms of GNU General Public License, Version 2 or
8 * (at your option) any later version.
9 *
10 * This test suite tests the SMSCB (Cell Broadcast) related functionality of
11 * OsmoBTS by attaching to the A-bis RSL and Um interface and emulating both
12 * BSC and MS.
13 */
14
15import from Misc_Helpers all;
16import from General_Types all;
17import from Osmocom_Types all;
18import from GSM_Types all;
19import from L1CTL_PortType all;
20import from L1CTL_Types all;
21import from LAPDm_Types all;
22
23import from RSL_Types all;
24
25import from Osmocom_VTY_Functions all;
26
27import from BTS_Tests all;
28
29/***********************************************************************
30 * Cell Broadcast related tests
31 ***********************************************************************/
32
33type record CbchTestPars {
34 boolean use_sdcch4,
35 CbchTestMsgs msgs
36};
37
38type record CbchTestMsg {
39 /* config / input data */
40 RSL_CbCommand rsl_cb_cmd,
41 uint2_t last_block, /* 0..3 */
42 octetstring payload,
43 /* computed / result data */
44 CbchBlocks blocks optional
45};
46type record of CbchTestMsg CbchTestMsgs;
47
48/* a single 22byte block within a CbchTestMsg */
49type record CbchBlock {
50 uint4_t seq_nr, /* as per TS 04.12 */
51 boolean is_last,
52 OCT22 payload,
53 boolean seen_once
54};
55type record of CbchBlock CbchBlocks;
56
57/* compute the expected blocks for given test parameters */
58private function f_cbch_compute_exp_blocks(inout CbchTestPars pars) {
59 var integer i;
60
61 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
62 pars.msgs[i].blocks := f_comp_blocks(pars.msgs[i]);
63 }
64}
65private function f_comp_blocks(in CbchTestMsg msg) return CbchBlocks {
66 var CbchBlocks blocks := {};
67 var integer i;
68
69 for (i := 0; i <= msg.last_block; i := i+1) {
70 var CbchBlock block := {
71 seq_nr := i,
72 is_last := false,
73 payload := substr(msg.payload, 22*i, 22),
74 seen_once := false
75 };
76 if (msg.rsl_cb_cmd == RSL_CB_CMD_SCHEDULE and i == 0) {
77 block.seq_nr := 8;
78 }
79 if (i == msg.last_block) {
80 block.is_last := true;
81 }
82 blocks := blocks & {block};
83 }
84
85 return blocks;
86};
87
88/* TS 48.058 Section 9.3.41 */
89private function f_cbch_block_nr2rsl(uint2_t nr) return uint2_t {
90 select (nr) {
91 case (0) { return 1; }
92 case (1) { return 2; }
93 case (2) { return 3; }
94 case (3) { return 0; }
95 }
96 setverdict(fail, "Invalid block number");
97 mtc.stop;
98}
99
Harald Welte88e5dff2019-05-20 15:14:46 +0200100private function f_cbch_fn2tb(uint32_t fn) return integer
101{
102 return (fn/51) mod 8; /* TS 05.02 Section 6.5.4 */
103}
104
Harald Welte505cf9b2018-09-15 17:47:23 +0300105/* Verify the CBCH TB scheduling rules of TS 05.02 Section 6.5.4 */
106private function f_cbch_fn_verify(uint32_t fn, CBCH_Block cb)
107{
Harald Welte88e5dff2019-05-20 15:14:46 +0200108 var integer tb := f_cbch_fn2tb(fn);
Harald Welte505cf9b2018-09-15 17:47:23 +0300109 if (cb.block_type.seq_nr == 15 /* null */) {
110 /* always permitted */
111 return;
112 } else if (cb.block_type.seq_nr == 8 /* schedule */) {
113 if (tb != 0) {
114 setverdict(fail, "Schedule block at TB=", tb);
115 }
116 } else if (cb.block_type.seq_nr < 4) {
117 if (cb.block_type.seq_nr != tb and cb.block_type.seq_nr+4 != tb) {
118 setverdict(fail, "Normal block at wrong TB=", tb, ": ", cb);
119 }
120 }
121}
122
Harald Welte88e5dff2019-05-20 15:14:46 +0200123private function f_smscb_setup(inout CbchTestPars pars) runs on test_CT {
124 var integer i;
Harald Welte505cf9b2018-09-15 17:47:23 +0300125
126 f_cbch_compute_exp_blocks(pars);
127
128 f_init_vty_bsc();
129 /* ensure that a CBCH is present in channel combination */
130 if (pars.use_sdcch4) {
131 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
132 "phys_chan_config CCCH+SDCCH4+CBCH");
133 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
134 "phys_chan_config SDCCH8");
135 } else {
136 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
137 "phys_chan_config CCCH+SDCCH4");
138 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
139 "phys_chan_config SDCCH8+CBCH");
140 }
141 f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
142 f_sleep(2.0);
143 f_init(testcasename());
144
145 f_init_l1ctl();
146 f_l1_tune(L1CTL);
147 /* FIXME: switch to dedicated mode for SDCCH/8 */
148
149 /* send SMSCB[s] via RSL */
150 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
151 var CbchTestMsg msg := pars.msgs[i];
152 var uint2_t rsl_last_block := f_cbch_block_nr2rsl(msg.last_block);
153 var RSL_IE_CbCommandType cmd_type :=
154 valueof(ts_RSL_IE_CbCmdType(msg.rsl_cb_cmd, rsl_last_block));
155 RSL_CCHAN.send(ts_RSL_UD(ts_RSL_SMSCB_CMD(cmd_type, msg.payload)));
156 }
Harald Welte88e5dff2019-05-20 15:14:46 +0200157}
158
159private function f_smscb_cleanup() runs on test_CT {
160 /* reset timeslot 0 channel combination to default */
161 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
162 "phys_chan_config CCCH+SDCCH4");
163 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
164 "phys_chan_config SDCCH8");
165}
166
167/* shared function doing the heavy lifting for most CBCH tests */
168private function f_TC_smscb(CbchTestPars pars) runs on test_CT {
169 var L1ctlDlMessage dl;
170 var integer i, j;
171 timer T := 5.0 * int2float(lengthof(pars.msgs));
172
173 f_smscb_setup(pars);
174
Harald Welte505cf9b2018-09-15 17:47:23 +0300175 T.start;
176 /* Expect this to show up exactly once on the basic CBCH (four blocks) */
177 alt {
178 /* FIXME: Channel Nr for SDCCH/8 */
179 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_CBCH(0))) -> value dl {
180 log("CBCH: ", dl);
181 var CBCH_Block cb := dec_CBCH_Block(dl.payload.data_ind.payload);
182 /* detect the proper CBCH messages; check frame number */
183 f_cbch_fn_verify(dl.dl_info.frame_nr, cb);
184 if (not match(cb, tr_CBCH_Block)) {
185 setverdict(fail, "Illegal CBCH Block received: ", cb);
186 } else {
187 var boolean matched := false;
188 /* ignore NULL messages */
189 if (match(cb, tr_CBCH_Block(15, ?, ?))) { repeat; }
190 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
191 for (j := 0; j < lengthof(pars.msgs[i].blocks); j := j+1) {
192 var CbchBlock b := pars.msgs[i].blocks[j];
193 if (match(cb, tr_CBCH_Block(b.seq_nr, b.is_last, b.payload))) {
194 if (not pars.msgs[i].blocks[j].seen_once) {
195 pars.msgs[i].blocks[j].seen_once := true;
196 setverdict(pass);
197 } else {
198 setverdict(fail, "Received SMSCB twice! ", cb);
199 }
200 matched := true;
201 continue;
202 }
203 }
204 }
205 if (not matched) {
206 setverdict(fail, "Received unexpected CBCH block: ", cb);
207 }
208 repeat;
209 }
210 }
211 [] L1CTL.receive { repeat; }
212 [] T.timeout {
213 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
214 for (j := 0; j < lengthof(pars.msgs[i].blocks); j := j+1) {
215 var CbchBlock b := pars.msgs[i].blocks[j];
216 if (not b.seen_once) {
217 setverdict(fail, "Timeout waiting for CBCH");
218 }
219 }
220 }
221 }
222 }
223
Harald Welte88e5dff2019-05-20 15:14:46 +0200224 f_smscb_cleanup();
225 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
226}
227
228private function f_TC_smscb_default_only(CbchTestPars pars) runs on test_CT {
229 var L1ctlDlMessage dl;
230 timer T := 5.0;
231
232 f_smscb_setup(pars);
233
234 /* ensure whatever initial NULL messages have all been drained */
235 f_sleep(5.0);
236 L1CTL.clear;
237
238 T.start;
239 alt {
240 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_CBCH(0))) -> value dl {
241 var integer tb := f_cbch_fn2tb(dl.dl_info.frame_nr);
242 log("CBCH: ", dl);
243 var CBCH_Block cb := dec_CBCH_Block(dl.payload.data_ind.payload);
244 /* detect the proper CBCH messages; check frame number */
245 f_cbch_fn_verify(dl.dl_info.frame_nr, cb);
246 if (tb >= 4) {
247 /* skip extended CBCH for now */
248 repeat;
249 }
250 if (not match(cb, tr_CBCH_Block)) {
251 setverdict(fail, "Illegal CBCH Block received: ", cb);
252 } else {
253 var uint4_t rx_seq_nr := cb.block_type.seq_nr;
254 var template CBCH_Block tr;
255 if (rx_seq_nr < lengthof(pars.msgs[0].blocks)) {
256 var CbchBlock b := pars.msgs[0].blocks[rx_seq_nr];
257 tr := tr_CBCH_Block(b.seq_nr, b.is_last, b.payload);
258 } else {
259 tr := tr_CBCH_Block(15, ?, ?);
260 }
261 if (match(cb, tr)) {
262 setverdict(pass); /* FIXME: check that all blocks are received? */
263 repeat;
264 } else {
265 setverdict(fail, "Unexpected CBCH block ", cb, ", expected ", tr);
266 }
267 }
268 }
269 [] L1CTL.receive { repeat; }
270 [] T.timeout {}
271 }
272
273 f_smscb_cleanup();
274 /* don't shut down; some tests still want to continue */
Harald Welte505cf9b2018-09-15 17:47:23 +0300275}
276
277private const CbchTestMsgs msgs_1m_1b_norm := {
278 { RSL_CB_CMD_NORMAL, 0, '001000320f1141660c344dd3cba09a0c000000000000'O, omit }
279}
280
281private const CbchTestMsgs msgs_1m_2b_norm := {
282 { RSL_CB_CMD_NORMAL, 1, '001000320f1141660c344dd3cba09a0c000000000000'O &
283 '000102030405060708090a0b0c0d0e0f101213141516'O,
284 omit }
285}
286
287private const CbchTestMsgs msgs_1m_3b_norm := {
288 { RSL_CB_CMD_NORMAL, 2, '001000320f1141660c344dd3cba09a0c000000000000'O &
289 '000102030405060708090a0b0c0d0e0f101213141516'O &
290 '101112131415161718191a1b1c1d1e1f202223242526'O,
291 omit }
292}
293
294private const CbchTestMsgs msgs_1m_4b_norm := {
295 { RSL_CB_CMD_NORMAL, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
296 '000102030405060708090a0b0c0d0e0f101213141516'O &
297 '101112131415161718191a1b1c1d1e1f202223242526'O &
298 '202122232425262728292a2b2c2d2e2f303233343536'O,
299 omit }
300}
301
302private const CbchTestMsgs msgs_1m_4b_sched := {
303 { RSL_CB_CMD_SCHEDULE, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
304 '000102030405060708090a0b0c0d0e0f101213141516'O &
305 '101112131415161718191a1b1c1d1e1f202223242526'O &
306 '202122232425262728292a2b2c2d2e2f303233343536'O,
307 omit }
308}
309
Harald Weltee0026c32019-05-20 00:27:30 +0200310private const CbchTestMsgs msgs_3m_4b_norm := {
311 { RSL_CB_CMD_NORMAL, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
312 '000102030405060708090a0b0c0d0e0f101213141516'O &
313 '101112131415161718191a1b1c1d1e1f202223242526'O &
314 '201122232425262728292a2b2c2d2e2f303233343536'O,
315 omit },
316 { RSL_CB_CMD_NORMAL, 3, '002000320f1141660c344dd3cba09a0c000000000000'O &
317 '002102030405060708090a0b0c0d0e0f101213141516'O &
318 '102112131415161718191a1b1c1d1e1f202223242526'O &
319 '202122232425262728292a2b2c2d2e2f303233343536'O,
320 omit },
321 { RSL_CB_CMD_NORMAL, 3, '003000320f1141660c344dd3cba09a0c000000000000'O &
322 '003102030405060708090a0b0c0d0e0f101213141516'O &
323 '103112131415161718191a1b1c1d1e1f202223242526'O &
324 '203122232425262728292a2b2c2d2e2f303233343536'O,
325 omit }
326}
327
Harald Welte88e5dff2019-05-20 15:14:46 +0200328private const CbchTestMsgs msgs_1m_3b_default := {
329 { RSL_CB_CMD_DEFAULT, 2, '001000320f1141660c344dd3cba09a0c000000000000'O &
330 '000102030405060708090a0b0c0d0e0f101213141516'O &
331 '101112131415161718191a1b1c1d1e1f202223242526'O,
332 omit }
333}
334
Harald Welte505cf9b2018-09-15 17:47:23 +0300335/* transmit single-block SMSCB COMMAND */
336testcase TC_sms_cb_cmd_sdcch4_1block() runs on test_CT {
337 var CbchTestPars pars := {
338 use_sdcch4 := true,
339 msgs := msgs_1m_1b_norm
340 };
341 f_TC_smscb(pars);
342}
343testcase TC_sms_cb_cmd_sdcch8_1block() runs on test_CT {
344 var CbchTestPars pars := {
345 use_sdcch4 := false,
346 msgs := msgs_1m_1b_norm
347 };
348 f_TC_smscb(pars);
349}
350
351/* transmit dual-block SMSCB COMMAND */
352testcase TC_sms_cb_cmd_sdcch4_2block() runs on test_CT {
353 var CbchTestPars pars := {
354 use_sdcch4 := true,
355 msgs := msgs_1m_2b_norm
356 };
357 f_TC_smscb(pars);
358}
359testcase TC_sms_cb_cmd_sdcch8_2block() runs on test_CT {
360 var CbchTestPars pars := {
361 use_sdcch4 := false,
362 msgs := msgs_1m_2b_norm
363 };
364 f_TC_smscb(pars);
365}
366
367/* transmit triple-block SMSCB COMMAND */
368testcase TC_sms_cb_cmd_sdcch4_3block() runs on test_CT {
369 var CbchTestPars pars := {
370 use_sdcch4 := true,
371 msgs := msgs_1m_3b_norm
372 };
373 f_TC_smscb(pars);
374}
375testcase TC_sms_cb_cmd_sdcch8_3block() runs on test_CT {
376 var CbchTestPars pars := {
377 use_sdcch4 := false,
378 msgs := msgs_1m_3b_norm
379 };
380 f_TC_smscb(pars);
381}
382
383/* transmit quad-block SMSCB COMMAND */
384testcase TC_sms_cb_cmd_sdcch4_4block() runs on test_CT {
385 var CbchTestPars pars := {
386 use_sdcch4 := true,
387 msgs := msgs_1m_4b_norm
388 };
389 f_TC_smscb(pars);
390}
391testcase TC_sms_cb_cmd_sdcch8_4block() runs on test_CT {
392 var CbchTestPars pars := {
393 use_sdcch4 := false,
394 msgs := msgs_1m_4b_norm
395 };
396 f_TC_smscb(pars);
397}
398
Harald Weltee0026c32019-05-20 00:27:30 +0200399/* transmit multiple commands of each 4 blocks */
400testcase TC_sms_cb_cmd_sdcch4_multi() runs on test_CT {
401 var CbchTestPars pars := {
402 use_sdcch4 := true,
403 msgs := msgs_3m_4b_norm
404 };
405 f_TC_smscb(pars);
406}
407testcase TC_sms_cb_cmd_sdcch8_multi() runs on test_CT {
408 var CbchTestPars pars := {
409 use_sdcch4 := false,
410 msgs := msgs_3m_4b_norm
411 };
412 f_TC_smscb(pars);
413}
414
Harald Welte505cf9b2018-09-15 17:47:23 +0300415/* transmit SMSCB COMMAND with SCHEDULE payload */
416testcase TC_sms_cb_cmd_sdcch4_schedule() runs on test_CT {
417 var CbchTestPars pars := {
418 use_sdcch4 := true,
419 msgs := msgs_1m_4b_sched
420 };
421 f_TC_smscb(pars);
422}
423testcase TC_sms_cb_cmd_sdcch8_schedule() runs on test_CT {
424 var CbchTestPars pars := {
425 use_sdcch4 := false,
426 msgs := msgs_1m_4b_sched
427 };
428 f_TC_smscb(pars);
429}
430
Harald Welte88e5dff2019-05-20 15:14:46 +0200431/* set a DEFAULT message; verify it gets transmitted all the time */
432testcase TC_sms_cb_cmd_sdcch4_default_only() runs on test_CT {
433 var CbchTestPars pars := {
434 use_sdcch4 := true,
435 msgs := msgs_1m_3b_default
436 };
437 f_TC_smscb_default_only(pars);
438 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
439}
440testcase TC_sms_cb_cmd_sdcch8_default_only() runs on test_CT {
441 var CbchTestPars pars := {
442 use_sdcch4 := true,
443 msgs := msgs_1m_3b_default
444 };
445 f_TC_smscb_default_only(pars);
446 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
447}
448
449
Harald Welte505cf9b2018-09-15 17:47:23 +0300450/* SMSCB TODO:
451 * multiple SMS BC CMD at the same time: Ensure all of them are sent exactly once
452 * extended CBCH vs. normal CBCH
453 *
454 */
455
456control {
457 execute( TC_sms_cb_cmd_sdcch4_1block() );
458 execute( TC_sms_cb_cmd_sdcch4_2block() );
459 execute( TC_sms_cb_cmd_sdcch4_3block() );
460 execute( TC_sms_cb_cmd_sdcch4_4block() );
Harald Weltee0026c32019-05-20 00:27:30 +0200461 execute( TC_sms_cb_cmd_sdcch4_multi() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300462 execute( TC_sms_cb_cmd_sdcch4_schedule() );
Harald Welte88e5dff2019-05-20 15:14:46 +0200463 execute( TC_sms_cb_cmd_sdcch4_default_only() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300464 if (false) { /* FIXME: SDCCH/8 support broken, needs trxcon + L1CTL work */
465 execute( TC_sms_cb_cmd_sdcch8_1block() );
466 execute( TC_sms_cb_cmd_sdcch8_2block() );
467 execute( TC_sms_cb_cmd_sdcch8_3block() );
468 execute( TC_sms_cb_cmd_sdcch8_4block() );
Harald Weltee0026c32019-05-20 00:27:30 +0200469 execute( TC_sms_cb_cmd_sdcch8_multi() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300470 execute( TC_sms_cb_cmd_sdcch8_schedule() );
Harald Welte88e5dff2019-05-20 15:14:46 +0200471 execute( TC_sms_cb_cmd_sdcch8_default_only() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300472 }
473}
474
475
476}