blob: c991877360b2549fc70771c9c584d6a95f5aa98d [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 Weltedf4d0f02019-05-20 16:04:11 +0200123private function f_rsl_smscb_default_null() runs on test_CT
124{
125 var RSL_IE_CbCommandType cmd_type :=
126 valueof(ts_RSL_IE_CbCmdType(RSL_CB_CMD_DEFAULT, 1, true));
127 RSL_CCHAN.send(ts_RSL_UD(ts_RSL_SMSCB_CMD(cmd_type, ''O)));
128}
129
Harald Welte88e5dff2019-05-20 15:14:46 +0200130private function f_smscb_setup(inout CbchTestPars pars) runs on test_CT {
131 var integer i;
Harald Welte505cf9b2018-09-15 17:47:23 +0300132
133 f_cbch_compute_exp_blocks(pars);
134
135 f_init_vty_bsc();
136 /* ensure that a CBCH is present in channel combination */
137 if (pars.use_sdcch4) {
138 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
139 "phys_chan_config CCCH+SDCCH4+CBCH");
140 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
141 "phys_chan_config SDCCH8");
142 } else {
143 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
144 "phys_chan_config CCCH+SDCCH4");
145 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
146 "phys_chan_config SDCCH8+CBCH");
147 }
148 f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
149 f_sleep(2.0);
150 f_init(testcasename());
151
152 f_init_l1ctl();
153 f_l1_tune(L1CTL);
154 /* FIXME: switch to dedicated mode for SDCCH/8 */
155
156 /* send SMSCB[s] via RSL */
157 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
158 var CbchTestMsg msg := pars.msgs[i];
159 var uint2_t rsl_last_block := f_cbch_block_nr2rsl(msg.last_block);
160 var RSL_IE_CbCommandType cmd_type :=
161 valueof(ts_RSL_IE_CbCmdType(msg.rsl_cb_cmd, rsl_last_block));
162 RSL_CCHAN.send(ts_RSL_UD(ts_RSL_SMSCB_CMD(cmd_type, msg.payload)));
163 }
Harald Welte88e5dff2019-05-20 15:14:46 +0200164}
165
166private function f_smscb_cleanup() runs on test_CT {
167 /* reset timeslot 0 channel combination to default */
168 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 0"},
169 "phys_chan_config CCCH+SDCCH4");
170 f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 6"},
171 "phys_chan_config SDCCH8");
172}
173
174/* shared function doing the heavy lifting for most CBCH tests */
175private function f_TC_smscb(CbchTestPars pars) runs on test_CT {
176 var L1ctlDlMessage dl;
177 var integer i, j;
178 timer T := 5.0 * int2float(lengthof(pars.msgs));
179
180 f_smscb_setup(pars);
181
Harald Welte505cf9b2018-09-15 17:47:23 +0300182 T.start;
183 /* Expect this to show up exactly once on the basic CBCH (four blocks) */
184 alt {
185 /* FIXME: Channel Nr for SDCCH/8 */
186 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_CBCH(0))) -> value dl {
187 log("CBCH: ", dl);
188 var CBCH_Block cb := dec_CBCH_Block(dl.payload.data_ind.payload);
189 /* detect the proper CBCH messages; check frame number */
190 f_cbch_fn_verify(dl.dl_info.frame_nr, cb);
191 if (not match(cb, tr_CBCH_Block)) {
192 setverdict(fail, "Illegal CBCH Block received: ", cb);
193 } else {
194 var boolean matched := false;
195 /* ignore NULL messages */
196 if (match(cb, tr_CBCH_Block(15, ?, ?))) { repeat; }
197 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
198 for (j := 0; j < lengthof(pars.msgs[i].blocks); j := j+1) {
199 var CbchBlock b := pars.msgs[i].blocks[j];
200 if (match(cb, tr_CBCH_Block(b.seq_nr, b.is_last, b.payload))) {
201 if (not pars.msgs[i].blocks[j].seen_once) {
202 pars.msgs[i].blocks[j].seen_once := true;
203 setverdict(pass);
204 } else {
205 setverdict(fail, "Received SMSCB twice! ", cb);
206 }
207 matched := true;
208 continue;
209 }
210 }
211 }
212 if (not matched) {
213 setverdict(fail, "Received unexpected CBCH block: ", cb);
214 }
215 repeat;
216 }
217 }
218 [] L1CTL.receive { repeat; }
219 [] T.timeout {
220 for (i := 0; i < lengthof(pars.msgs); i := i+1) {
221 for (j := 0; j < lengthof(pars.msgs[i].blocks); j := j+1) {
222 var CbchBlock b := pars.msgs[i].blocks[j];
223 if (not b.seen_once) {
224 setverdict(fail, "Timeout waiting for CBCH");
225 }
226 }
227 }
228 }
229 }
230
Harald Welte88e5dff2019-05-20 15:14:46 +0200231 f_smscb_cleanup();
232 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
233}
234
235private function f_TC_smscb_default_only(CbchTestPars pars) runs on test_CT {
236 var L1ctlDlMessage dl;
237 timer T := 5.0;
238
239 f_smscb_setup(pars);
240
241 /* ensure whatever initial NULL messages have all been drained */
242 f_sleep(5.0);
243 L1CTL.clear;
244
245 T.start;
246 alt {
247 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_CBCH(0))) -> value dl {
248 var integer tb := f_cbch_fn2tb(dl.dl_info.frame_nr);
249 log("CBCH: ", dl);
250 var CBCH_Block cb := dec_CBCH_Block(dl.payload.data_ind.payload);
251 /* detect the proper CBCH messages; check frame number */
252 f_cbch_fn_verify(dl.dl_info.frame_nr, cb);
253 if (tb >= 4) {
254 /* skip extended CBCH for now */
255 repeat;
256 }
257 if (not match(cb, tr_CBCH_Block)) {
258 setverdict(fail, "Illegal CBCH Block received: ", cb);
259 } else {
260 var uint4_t rx_seq_nr := cb.block_type.seq_nr;
261 var template CBCH_Block tr;
262 if (rx_seq_nr < lengthof(pars.msgs[0].blocks)) {
263 var CbchBlock b := pars.msgs[0].blocks[rx_seq_nr];
264 tr := tr_CBCH_Block(b.seq_nr, b.is_last, b.payload);
265 } else {
266 tr := tr_CBCH_Block(15, ?, ?);
267 }
268 if (match(cb, tr)) {
269 setverdict(pass); /* FIXME: check that all blocks are received? */
270 repeat;
271 } else {
272 setverdict(fail, "Unexpected CBCH block ", cb, ", expected ", tr);
273 }
274 }
275 }
276 [] L1CTL.receive { repeat; }
277 [] T.timeout {}
278 }
279
280 f_smscb_cleanup();
281 /* don't shut down; some tests still want to continue */
Harald Welte505cf9b2018-09-15 17:47:23 +0300282}
283
284private const CbchTestMsgs msgs_1m_1b_norm := {
285 { RSL_CB_CMD_NORMAL, 0, '001000320f1141660c344dd3cba09a0c000000000000'O, omit }
286}
287
288private const CbchTestMsgs msgs_1m_2b_norm := {
289 { RSL_CB_CMD_NORMAL, 1, '001000320f1141660c344dd3cba09a0c000000000000'O &
290 '000102030405060708090a0b0c0d0e0f101213141516'O,
291 omit }
292}
293
294private const CbchTestMsgs msgs_1m_3b_norm := {
295 { RSL_CB_CMD_NORMAL, 2, '001000320f1141660c344dd3cba09a0c000000000000'O &
296 '000102030405060708090a0b0c0d0e0f101213141516'O &
297 '101112131415161718191a1b1c1d1e1f202223242526'O,
298 omit }
299}
300
301private const CbchTestMsgs msgs_1m_4b_norm := {
302 { RSL_CB_CMD_NORMAL, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
303 '000102030405060708090a0b0c0d0e0f101213141516'O &
304 '101112131415161718191a1b1c1d1e1f202223242526'O &
305 '202122232425262728292a2b2c2d2e2f303233343536'O,
306 omit }
307}
308
309private const CbchTestMsgs msgs_1m_4b_sched := {
310 { RSL_CB_CMD_SCHEDULE, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
311 '000102030405060708090a0b0c0d0e0f101213141516'O &
312 '101112131415161718191a1b1c1d1e1f202223242526'O &
313 '202122232425262728292a2b2c2d2e2f303233343536'O,
314 omit }
315}
316
Harald Weltee0026c32019-05-20 00:27:30 +0200317private const CbchTestMsgs msgs_3m_4b_norm := {
318 { RSL_CB_CMD_NORMAL, 3, '001000320f1141660c344dd3cba09a0c000000000000'O &
319 '000102030405060708090a0b0c0d0e0f101213141516'O &
320 '101112131415161718191a1b1c1d1e1f202223242526'O &
321 '201122232425262728292a2b2c2d2e2f303233343536'O,
322 omit },
323 { RSL_CB_CMD_NORMAL, 3, '002000320f1141660c344dd3cba09a0c000000000000'O &
324 '002102030405060708090a0b0c0d0e0f101213141516'O &
325 '102112131415161718191a1b1c1d1e1f202223242526'O &
326 '202122232425262728292a2b2c2d2e2f303233343536'O,
327 omit },
328 { RSL_CB_CMD_NORMAL, 3, '003000320f1141660c344dd3cba09a0c000000000000'O &
329 '003102030405060708090a0b0c0d0e0f101213141516'O &
330 '103112131415161718191a1b1c1d1e1f202223242526'O &
331 '203122232425262728292a2b2c2d2e2f303233343536'O,
332 omit }
333}
334
Harald Welte88e5dff2019-05-20 15:14:46 +0200335private const CbchTestMsgs msgs_1m_3b_default := {
336 { RSL_CB_CMD_DEFAULT, 2, '001000320f1141660c344dd3cba09a0c000000000000'O &
337 '000102030405060708090a0b0c0d0e0f101213141516'O &
338 '101112131415161718191a1b1c1d1e1f202223242526'O,
339 omit }
340}
341
Harald Welte505cf9b2018-09-15 17:47:23 +0300342/* transmit single-block SMSCB COMMAND */
343testcase TC_sms_cb_cmd_sdcch4_1block() runs on test_CT {
344 var CbchTestPars pars := {
345 use_sdcch4 := true,
346 msgs := msgs_1m_1b_norm
347 };
348 f_TC_smscb(pars);
349}
350testcase TC_sms_cb_cmd_sdcch8_1block() runs on test_CT {
351 var CbchTestPars pars := {
352 use_sdcch4 := false,
353 msgs := msgs_1m_1b_norm
354 };
355 f_TC_smscb(pars);
356}
357
358/* transmit dual-block SMSCB COMMAND */
359testcase TC_sms_cb_cmd_sdcch4_2block() runs on test_CT {
360 var CbchTestPars pars := {
361 use_sdcch4 := true,
362 msgs := msgs_1m_2b_norm
363 };
364 f_TC_smscb(pars);
365}
366testcase TC_sms_cb_cmd_sdcch8_2block() runs on test_CT {
367 var CbchTestPars pars := {
368 use_sdcch4 := false,
369 msgs := msgs_1m_2b_norm
370 };
371 f_TC_smscb(pars);
372}
373
374/* transmit triple-block SMSCB COMMAND */
375testcase TC_sms_cb_cmd_sdcch4_3block() runs on test_CT {
376 var CbchTestPars pars := {
377 use_sdcch4 := true,
378 msgs := msgs_1m_3b_norm
379 };
380 f_TC_smscb(pars);
381}
382testcase TC_sms_cb_cmd_sdcch8_3block() runs on test_CT {
383 var CbchTestPars pars := {
384 use_sdcch4 := false,
385 msgs := msgs_1m_3b_norm
386 };
387 f_TC_smscb(pars);
388}
389
390/* transmit quad-block SMSCB COMMAND */
391testcase TC_sms_cb_cmd_sdcch4_4block() runs on test_CT {
392 var CbchTestPars pars := {
393 use_sdcch4 := true,
394 msgs := msgs_1m_4b_norm
395 };
396 f_TC_smscb(pars);
397}
398testcase TC_sms_cb_cmd_sdcch8_4block() runs on test_CT {
399 var CbchTestPars pars := {
400 use_sdcch4 := false,
401 msgs := msgs_1m_4b_norm
402 };
403 f_TC_smscb(pars);
404}
405
Harald Weltee0026c32019-05-20 00:27:30 +0200406/* transmit multiple commands of each 4 blocks */
407testcase TC_sms_cb_cmd_sdcch4_multi() runs on test_CT {
408 var CbchTestPars pars := {
409 use_sdcch4 := true,
410 msgs := msgs_3m_4b_norm
411 };
412 f_TC_smscb(pars);
413}
414testcase TC_sms_cb_cmd_sdcch8_multi() runs on test_CT {
415 var CbchTestPars pars := {
416 use_sdcch4 := false,
417 msgs := msgs_3m_4b_norm
418 };
419 f_TC_smscb(pars);
420}
421
Harald Welte505cf9b2018-09-15 17:47:23 +0300422/* transmit SMSCB COMMAND with SCHEDULE payload */
423testcase TC_sms_cb_cmd_sdcch4_schedule() runs on test_CT {
424 var CbchTestPars pars := {
425 use_sdcch4 := true,
426 msgs := msgs_1m_4b_sched
427 };
428 f_TC_smscb(pars);
429}
430testcase TC_sms_cb_cmd_sdcch8_schedule() runs on test_CT {
431 var CbchTestPars pars := {
432 use_sdcch4 := false,
433 msgs := msgs_1m_4b_sched
434 };
435 f_TC_smscb(pars);
436}
437
Harald Welte88e5dff2019-05-20 15:14:46 +0200438/* set a DEFAULT message; verify it gets transmitted all the time */
439testcase TC_sms_cb_cmd_sdcch4_default_only() runs on test_CT {
440 var CbchTestPars pars := {
441 use_sdcch4 := true,
442 msgs := msgs_1m_3b_default
443 };
444 f_TC_smscb_default_only(pars);
445 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
446}
447testcase TC_sms_cb_cmd_sdcch8_default_only() runs on test_CT {
448 var CbchTestPars pars := {
449 use_sdcch4 := true,
450 msgs := msgs_1m_3b_default
451 };
452 f_TC_smscb_default_only(pars);
453 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
454}
455
Harald Weltedf4d0f02019-05-20 16:04:11 +0200456/* first set a DEFAULT message, then disable it again */
457testcase TC_sms_cb_cmd_sdcch4_default_then_null() runs on test_CT {
458 var CbchTestPars pars := {
459 use_sdcch4 := true,
460 msgs := msgs_1m_3b_default
461 };
462 var L1ctlDlMessage dl;
463 timer T := 5.0;
464
465 f_TC_smscb_default_only(pars);
466
467 /* disable DEFAULT message; switch back to NULL */
468 f_rsl_smscb_default_null();
469
470 /* ensure whatever initial non-NULL messages have all been drained */
471 f_sleep(5.0);
472 L1CTL.clear;
473
474 T.start;
475 alt {
476 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_CBCH(0))) -> value dl {
477 log("CBCH: ", dl);
478 var CBCH_Block cb := dec_CBCH_Block(dl.payload.data_ind.payload);
479 /* detect the proper CBCH messages; check frame number */
480 f_cbch_fn_verify(dl.dl_info.frame_nr, cb);
481 if (not match(cb, tr_CBCH_Block)) {
482 setverdict(fail, "Illegal CBCH Block received: ", cb);
483 } else {
484 if (not match(cb, tr_CBCH_Block(15, ?, ?))) {
485 setverdict(fail, "Unexpected non-NULL CBCH block received");
486 }
487 repeat;
488 }
489 }
490 [] L1CTL.receive { repeat; }
491 [] T.timeout {
492 setverdict(pass);
493 }
494 }
495
496 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
497}
498
499
Harald Welte88e5dff2019-05-20 15:14:46 +0200500
Harald Welte505cf9b2018-09-15 17:47:23 +0300501/* SMSCB TODO:
502 * multiple SMS BC CMD at the same time: Ensure all of them are sent exactly once
503 * extended CBCH vs. normal CBCH
504 *
505 */
506
507control {
508 execute( TC_sms_cb_cmd_sdcch4_1block() );
509 execute( TC_sms_cb_cmd_sdcch4_2block() );
510 execute( TC_sms_cb_cmd_sdcch4_3block() );
511 execute( TC_sms_cb_cmd_sdcch4_4block() );
Harald Weltee0026c32019-05-20 00:27:30 +0200512 execute( TC_sms_cb_cmd_sdcch4_multi() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300513 execute( TC_sms_cb_cmd_sdcch4_schedule() );
Harald Welte88e5dff2019-05-20 15:14:46 +0200514 execute( TC_sms_cb_cmd_sdcch4_default_only() );
Harald Weltedf4d0f02019-05-20 16:04:11 +0200515 execute( TC_sms_cb_cmd_sdcch4_default_then_null() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300516 if (false) { /* FIXME: SDCCH/8 support broken, needs trxcon + L1CTL work */
517 execute( TC_sms_cb_cmd_sdcch8_1block() );
518 execute( TC_sms_cb_cmd_sdcch8_2block() );
519 execute( TC_sms_cb_cmd_sdcch8_3block() );
520 execute( TC_sms_cb_cmd_sdcch8_4block() );
Harald Weltee0026c32019-05-20 00:27:30 +0200521 execute( TC_sms_cb_cmd_sdcch8_multi() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300522 execute( TC_sms_cb_cmd_sdcch8_schedule() );
Harald Welte88e5dff2019-05-20 15:14:46 +0200523 execute( TC_sms_cb_cmd_sdcch8_default_only() );
Harald Welte505cf9b2018-09-15 17:47:23 +0300524 }
525}
526
527
528}