blob: 618cf117801075f84a39758e4efb62a7dc6f218a [file] [log] [blame]
Harald Welte714ded92017-12-08 14:00:22 +01001module RSL_Emulation {
2
Harald Welte35bb7162018-01-03 21:07:52 +01003/* RSL Emulation, runs on top of IPA_Emulation. It multiplexes/demultiplexes
4 * the individual connections (logical channels), so there can be separate TTCN-3 components
5 * handling each of the connections.
6 *
7 * The RSL_Emulation.main() function processes RSL messages from the IPA demultiplex
8 * stack via the IPA_RSL_PT, and dispatches them to the per-connection components.
9 *
10 * Outbound RSL connections are initiated by sending a RSLDC_ChanRqd primitive
11 * to the component running the RSL_Emulation.main() function.
12 *
Harald Weltead2647b2018-03-22 19:53:55 +010013 * in case we're emulating the BTS-side of RSL: We have no clue as to which particular logical
14 * channel the BSC may decide to activate at which point, so we have to blindly acknowledge all
15 * channel activations. Still, a test case might be interested in the exact assignment message to
16 * determine the type of activation, encryption, codec flags etc. at some later point, when it's
17 * clear for which transaction this activation happened. We keep this in the LastChanAct table.
18 *
19 * (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
Harald Welte35bb7162018-01-03 21:07:52 +010020 * All rights reserved.
21 *
22 * Released under the terms of GNU General Public License, Version 2 or
23 * (at your option) any later version.
Harald Welte34b5a952019-05-27 11:54:11 +020024 *
25 * SPDX-License-Identifier: GPL-2.0-or-later
Harald Welte35bb7162018-01-03 21:07:52 +010026 */
27
Pau Espin Pedrola07cfd92018-10-22 15:54:41 +020028import from Misc_Helpers all;
Harald Welte714ded92017-12-08 14:00:22 +010029import from General_Types all;
30import from Osmocom_Types all;
31import from GSM_Types all;
32import from GSM_RR_Types all;
33import from RSL_Types all;
34import from IPA_Types all;
35import from IPA_Emulation all;
36
37
Vadim Yanitskiy82af0b52020-06-09 20:21:43 +070038modulepar {
39 /* Work around switch for ttcn3-bts-test-latest, enables patching of IPA
40 * stream ID in the "BSC" mode. See I5927f59a49724170a63e87be604973f7c9d5d8be. */
41 boolean mp_rslem_patch_ipa_cid := false;
42};
43
Harald Welte714ded92017-12-08 14:00:22 +010044/* General "base class" component definition, of which specific implementations
45 * derive themselves by means of the "extends" feature */
46type component RSL_DchanHdlr {
47 /* port facing up towards dedicated channel handler */
48 port RSL_DCHAN_PT RSL;
Harald Weltef70df652018-01-29 22:00:23 +010049 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +010050 var RslChannelNr g_chan_nr;
Harald Welte421e4d42018-02-12 20:04:50 +010051 /* second BTS / DChan during hand-over */
52 port RSL_DCHAN_PT RSL1;
53 port RSLEM_PROC_PT RSL1_PROC;
Neels Hofmeyr91401012019-07-11 00:42:35 +020054 port RSL_DCHAN_PT RSL2;
55 port RSLEM_PROC_PT RSL2_PROC;
Harald Welte714ded92017-12-08 14:00:22 +010056};
57
58type record RSLDC_ChanRqd {
59 OCT1 ra,
Pau Espin Pedrol6451b042018-10-24 20:36:16 +020060 GsmFrameNumber fn optional
Harald Welte714ded92017-12-08 14:00:22 +010061};
62
Pau Espin Pedrol6451b042018-10-24 20:36:16 +020063template (value) RSLDC_ChanRqd ts_RSLDC_ChanRqd(OCT1 ra, GsmFrameNumber fn) := {
Harald Welte714ded92017-12-08 14:00:22 +010064 ra := ra,
65 fn := fn
66}
67
Pau Espin Pedrol6451b042018-10-24 20:36:16 +020068template (value) RSLDC_ChanRqd ts_RSLDC_ChanRqd_anyFN(OCT1 ra) := {
69 ra := ra,
70 fn := omit
71}
72
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +070073type enumerated RSLEm_EventType {
74 RSLEM_EV_TRX_UP,
75 RSLEM_EV_TRX_DOWN
76};
77
78type record RSLEm_Event {
79 RSLEm_EventType ev_type,
80 IpaStreamId sid
81};
82
83template (value) RSLEm_Event ts_RSLEm_EV(RSLEm_EventType ev_type,
84 IpaStreamId sid) := {
85 ev_type := ev_type,
86 sid := sid
87};
88template RSLEm_Event tr_RSLEm_EV(template RSLEm_EventType ev_type,
89 template IpaStreamId sid := ?) := {
90 ev_type := ev_type,
91 sid := sid
92};
93
Harald Welte714ded92017-12-08 14:00:22 +010094type port RSL_DCHAN_PT message {
95 inout RSLDC_ChanRqd, RSL_Message;
96} with { extension "internal" };
97
Harald Welte1c02fd12018-02-19 19:20:47 +010098type port RSL_CCHAN_PT message {
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +070099 inout ASP_RSL_Unitdata, RSLEm_Event;
Harald Welte1c02fd12018-02-19 19:20:47 +0100100} with { extension "internal" };
101
102
Harald Weltef70df652018-01-29 22:00:23 +0100103signature RSLEM_register(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Welte1909f462018-01-29 22:29:29 +0100104signature RSLEM_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Welte70b52c92018-02-12 20:47:31 +0100105signature RSLEM_suspend(boolean suspend);
Harald Weltead2647b2018-03-22 19:53:55 +0100106signature RSLEM_get_last_act(in uint8_t trx_nr, in RslChannelNr chan_nr, out RSL_Message chan_act);
Harald Weltef70df652018-01-29 22:00:23 +0100107
108type port RSLEM_PROC_PT procedure {
Harald Weltead2647b2018-03-22 19:53:55 +0100109 inout RSLEM_register, RSLEM_unregister, RSLEM_suspend, RSLEM_get_last_act;
Harald Weltef70df652018-01-29 22:00:23 +0100110} with { extension "internal" };
111
Harald Welte714ded92017-12-08 14:00:22 +0100112/***********************************************************************
113 * Client Component for a single dedicated channel
114 ***********************************************************************/
115
116private function f_rx_or_fail(template RSL_Message exp_rx) runs on RSL_DchanHdlr return RSL_Message
117{
118 var RSL_Message rx_rsl;
119 timer T := 10.0;
120
121 /* request a channel to be established */
122 T.start;
123 alt {
124 [] RSL.receive(exp_rx) -> value rx_rsl {
125 T.stop;
126 return rx_rsl;
127 }
128 [] RSL.receive {
129 setverdict(fail, "Unexpected RSL message on DCHAN");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200130 mtc.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100131 }
132 [] T.timeout {
133 setverdict(fail, "Timeout waiting for RSL on DCHAN");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200134 mtc.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100135 }
136 }
137 /* never reached */
138 return rx_rsl;
139}
140
141/* establish a dedicated channel using 'ra' */
142function f_chan_est(OCT1 ra, octetstring est_l3, template RslLinkId link_id, GsmFrameNumber fn := 23)
143runs on RSL_DchanHdlr {
144 var RSL_Message rx_rsl;
145 var GsmRrMessage rr;
146
147 /* request a channel to be established */
148 RSL.send(ts_RSLDC_ChanRqd(ra, fn));
149 /* expect immediate assignment */
150 rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN);
151 rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload);
152 g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
153 RSL.send(ts_RSL_EST_IND(g_chan_nr, valueof(link_id), est_l3));
154}
155
156function f_deact_chan(RSL_Cause cause) runs on RSL_DchanHdlr
157{
158 var RSL_Message rx_rsl;
159
160 RSL.send(ts_RSL_CONN_FAIL_IND(g_chan_nr, cause));
161 rx_rsl := f_rx_or_fail(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL));
162 /* FIXME RSL.send(ts_RSL_RF_CHAN_REL_ACK()) */
163}
164
165
166
167/***********************************************************************
168 * Main Component
169 ***********************************************************************/
170
171private type record ConnectionData {
172 /* component reference to the client component */
173 RSL_DchanHdlr comp_ref,
174 /* RSL (dedicated) Channel number we're handling */
175 uint8_t trx_nr optional,
176 IpaStreamId stream_id optional,
177 RslChannelNr chan_nr optional,
178 /* Random Reference */
179 OCT1 ra optional,
180 GsmFrameNumber ra_fn optional
181};
182
183private function f_cid_by_comp_ref(RSL_DchanHdlr comp_ref)
184runs on RSL_Emulation_CT return integer {
185 var integer i;
186 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
187 if (ispresent(ConnectionTable[i].comp_ref) and
188 ConnectionTable[i].comp_ref == comp_ref) {
189 return i;
190 }
191 }
Vadim Yanitskiyf79d9362020-06-03 04:06:18 +0700192 log("No Dchan handler for comp_ref=", comp_ref);
Harald Welte714ded92017-12-08 14:00:22 +0100193 return -1;
194}
195
196private function f_cid_by_chan_nr(uint8_t trx_nr, RslChannelNr chan_nr)
197runs on RSL_Emulation_CT return integer {
198 var integer i;
199 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
200 if (ispresent(ConnectionTable[i].chan_nr) and
201 ConnectionTable[i].chan_nr == chan_nr and ConnectionTable[i].trx_nr == trx_nr) {
202 return i;
203 }
204 }
Vadim Yanitskiyf79d9362020-06-03 04:06:18 +0700205 log("No Dchan handler for trx_nr=", trx_nr, " and chan_nr=", chan_nr);
Harald Welte714ded92017-12-08 14:00:22 +0100206 return -1;
207}
208
209private function f_cid_by_ra_fn(OCT1 ra, GsmFrameNumber fn)
210runs on RSL_Emulation_CT return integer {
211 var integer i;
212 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
213 if (ispresent(ConnectionTable[i].ra) and
214 ConnectionTable[i].ra == ra and ConnectionTable[i].ra_fn == fn) {
215 return i;
216 }
217 }
Vadim Yanitskiyf79d9362020-06-03 04:06:18 +0700218 log("No Dchan handler for ra=", ra, " and fn=", fn);
Harald Welte714ded92017-12-08 14:00:22 +0100219 return -1;
220}
221
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200222/* Matches by only RA if FN is ommited in one of the connections allocated */
223private function f_cid_by_ra_fn2(OCT1 ra, RSL_IE_FrameNumber fn)
224runs on RSL_Emulation_CT return integer {
225 var integer i;
226 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
227 if (ispresent(ConnectionTable[i].ra) and
228 ConnectionTable[i].ra == ra) {
229 if (not ispresent(ConnectionTable[i].ra_fn) or
230 fn == valueof(ts_RSL_IE_FrameNumber(ConnectionTable[i].ra_fn))) {
231 return i;
232 }
233 }
234 }
Vadim Yanitskiyf79d9362020-06-03 04:06:18 +0700235 log("No Dchan handler for ra=", ra, " and fn=", fn);
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200236 return -1;
237}
238
Harald Welte714ded92017-12-08 14:00:22 +0100239/* create an ew client with given RA and FN */
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200240private function f_cid_create(OCT1 ra, template (omit) GsmFrameNumber fn, RSL_DchanHdlr comp_ref)
Harald Welte930d0a72018-03-22 22:08:40 +0100241runs on RSL_Emulation_CT {
Harald Welte714ded92017-12-08 14:00:22 +0100242 var integer i;
243 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
Harald Weltef70df652018-01-29 22:00:23 +0100244 if (not ispresent(ConnectionTable[i].ra) and
245 not ispresent(ConnectionTable[i].trx_nr)) {
Harald Welte714ded92017-12-08 14:00:22 +0100246 ConnectionTable[i].ra := ra;
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200247 if (ispresent(fn)) {
248 ConnectionTable[i].ra_fn := valueof(fn);
249 } else {
250 ConnectionTable[i].ra_fn := omit;
251 }
Harald Welte714ded92017-12-08 14:00:22 +0100252 ConnectionTable[i].comp_ref := comp_ref;
Harald Welte930d0a72018-03-22 22:08:40 +0100253 return;
Harald Welte714ded92017-12-08 14:00:22 +0100254 }
255 }
Daniel Willmann8273fb92018-07-05 17:31:20 +0200256 testcase.stop("No free entry in conn table for ", ra, fn);
Harald Welte714ded92017-12-08 14:00:22 +0100257}
258
Vadim Yanitskiyefc94132020-05-24 04:26:45 +0700259/* create a new client with given RA and FN */
Harald Weltef70df652018-01-29 22:00:23 +0100260private function f_cid_create_cnr(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
Harald Welte930d0a72018-03-22 22:08:40 +0100261runs on RSL_Emulation_CT {
Harald Weltef70df652018-01-29 22:00:23 +0100262 var integer i;
263 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
264 if (not ispresent(ConnectionTable[i].ra) and
265 not ispresent(ConnectionTable[i].trx_nr)) {
266 ConnectionTable[i].stream_id := f_streamId_by_trx(trx_nr);
267 ConnectionTable[i].trx_nr := trx_nr;
268 ConnectionTable[i].chan_nr := chan_nr;
269 ConnectionTable[i].comp_ref := comp_ref;
Harald Welte930d0a72018-03-22 22:08:40 +0100270 return;
Harald Weltef70df652018-01-29 22:00:23 +0100271 }
272 }
Daniel Willmann8273fb92018-07-05 17:31:20 +0200273 testcase.stop("No free entry in conn table for ", trx_nr, chan_nr, comp_ref);
Harald Weltef70df652018-01-29 22:00:23 +0100274}
275
276
Vadim Yanitskiyefc94132020-05-24 04:26:45 +0700277/* delete client with given RA and FN */
Harald Weltef70df652018-01-29 22:00:23 +0100278private function f_cid_delete_cnr(IpaStreamId stream_id, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
279runs on RSL_Emulation_CT return integer {
280 var integer i;
281 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
282 if (ConnectionTable[i].comp_ref == null) {
283 continue;
284 }
285 if (ConnectionTable[i].stream_id == stream_id and
286 ConnectionTable[i].chan_nr == chan_nr and
287 ConnectionTable[i].comp_ref == comp_ref) {
288 f_cid_clear(i);
289 }
290 }
291 log("Unable to find entry to delete for ", stream_id, chan_nr, comp_ref);
292 return -1;
293}
294
295
Harald Welte714ded92017-12-08 14:00:22 +0100296private function f_cid_clear(integer cid)
297runs on RSL_Emulation_CT {
298 ConnectionTable[cid].ra := omit;
299 ConnectionTable[cid].ra_fn := omit;
Harald Welte714ded92017-12-08 14:00:22 +0100300 ConnectionTable[cid].trx_nr := omit;
301 ConnectionTable[cid].stream_id := omit;
302 ConnectionTable[cid].chan_nr := omit;
Harald Weltef70df652018-01-29 22:00:23 +0100303 ConnectionTable[cid].comp_ref := null;
Harald Welte714ded92017-12-08 14:00:22 +0100304}
305
Harald Weltead2647b2018-03-22 19:53:55 +0100306/* last activation for a given channel number */
307type record LastActData {
308 uint8_t trx_nr optional,
309 RslChannelNr chan_nr optional,
310 RSL_Message chan_act optional
311}
312
313private function f_store_last_act_data(uint8_t trx_nr, RslChannelNr chan_nr, RSL_Message chan_act)
314runs on RSL_Emulation_CT {
315 for (var integer i := 0; i < sizeof(LastActTable); i := i+1) {
316 if (not ispresent(LastActTable[i].chan_nr) or
317 (LastActTable[i].chan_nr == chan_nr and LastActTable[i].trx_nr == trx_nr)) {
318 LastActTable[i].trx_nr := trx_nr;
319 LastActTable[i].chan_nr := chan_nr;
320 LastActTable[i].chan_act := chan_act;
321 return;
322 }
323 }
Daniel Willmann8273fb92018-07-05 17:31:20 +0200324 testcase.stop("No space left in LastActTable to store chan_act for ", chan_nr);
Harald Weltead2647b2018-03-22 19:53:55 +0100325}
326
327private function f_lookup_last_act(uint8_t trx_nr, RslChannelNr chan_nr)
328runs on RSL_Emulation_CT return RSL_Message {
329 for (var integer i := 0; i < sizeof(LastActTable); i := i+1) {
330 if (ispresent(LastActTable[i].chan_nr) and LastActTable[i].chan_nr == chan_nr
331 and LastActTable[i].trx_nr == trx_nr) {
332 return LastActTable[i].chan_act;
333 }
334 }
Daniel Willmann8273fb92018-07-05 17:31:20 +0200335 testcase.stop("No LastActTable entry found for TRX ", trx_nr, " ", chan_nr);
Harald Weltead2647b2018-03-22 19:53:55 +0100336}
337
338private function f_last_act_table_init() runs on RSL_Emulation_CT {
339 for (var integer i := 0; i < sizeof(LastActTable); i := i+1) {
340 LastActTable[i] := { omit, omit, omit };
341 }
342}
343
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700344private function f_trx_conn_map_init()
345runs on RSL_Emulation_CT {
346 for (var integer i := 0; i < sizeof(TrxConnMap); i := i + 1) {
347 TrxConnMap[i] := -1;
348 }
349}
350
351private function f_trx_conn_map_register(integer conn_id, in IpaCcmIdResp id_resp)
352runs on RSL_Emulation_CT return IpaStreamId {
353 var template charstring unit_id_fmt := pattern "(\d+)/(\d+)/(\d+)";
354 var charstring unit_id;
355 var integer trx_nr;
356 var integer idx;
357
358 /* Check if we have room for a new connection */
359 if (TrxConnNum >= sizeof(TrxConnMap)) {
360 testcase.stop("We cannot handle more than ", sizeof(TrxConnMap), " transceivers");
361 }
362
363 /* Find IPAC_IDTAG_UNITID in the IPA IDENTITY RESPONSE */
364 idx := f_ipa_id_resp_find_ie(id_resp, IPAC_IDTAG_UNITID);
365 if (idx < 0) {
366 testcase.stop("IPA IDENTITY RESPONSE contains no unit-id");
367 }
368
369 /* Make sure that IPA unit-id is valid */
370 unit_id := oct2char(id_resp[idx].data);
371 if (not match(unit_id, unit_id_fmt)) {
372 testcase.stop("IPA unit-id has unknown/unexpected format");
373 }
374
375 /* Parse transceiver number (site/bts/trx).
376 * TODO: implement and use declaratice types. */
377 unit_id := regexp(unit_id, unit_id_fmt, 2);
378 trx_nr := str2int(unit_id);
379
380 if (trx_nr >= sizeof(TrxConnMap)) {
381 testcase.stop("Transceiver #", trx_nr, " does not fit");
382 } else if (TrxConnMap[trx_nr] != -1) {
383 testcase.stop("Transceiver #", trx_nr, " is already connected?!?");
384 }
385
386 /* Finally, store the connection ID */
387 log("Mapped TRX#", trx_nr, " to TCP/IP conn_id=", conn_id);
388 TrxConnMap[trx_nr] := conn_id;
389 TrxConnNum := TrxConnNum + 1;
390
391 return f_streamId_by_trx(trx_nr);
392}
393
394private function f_trx_conn_map_resolve(IpaStreamId id)
395runs on RSL_Emulation_CT return integer {
396 var integer trx_nr := f_trx_by_streamId(id);
397
398 if (TrxConnMap[trx_nr] == -1) {
399 testcase.stop("Transceiver #", trx_nr, " is not connected");
400 }
401
402 return TrxConnMap[trx_nr];
403}
404
Vadim Yanitskiy82af0b52020-06-09 20:21:43 +0700405/* Work around for a bug in osmo-bts when all transceivers use IPAC_PROTO_RSL_TRX0 */
406private function f_trx_conn_map_patch_ud(inout ASP_RSL_Unitdata ud)
407runs on RSL_Emulation_CT {
408 for (var integer i := 0; i < sizeof(TrxConnMap); i := i + 1) {
409 if (ud.conn_id == TrxConnMap[i]) {
410 ud.streamId := f_streamId_by_trx(i);
411 return; /* We're done */
412 }
413 }
414
415 testcase.stop("Failed to patch IPA stream ID in ASP RSL UD: ", ud);
416}
417
Harald Welte714ded92017-12-08 14:00:22 +0100418type component RSL_Emulation_CT {
419 /* port facing down towards IPA emulation */
420 port IPA_RSL_PT IPA_PT;
421 /* port facing up towards dedicated channel handler */
422 port RSL_DCHAN_PT CLIENT_PT;
Harald Weltef70df652018-01-29 22:00:23 +0100423 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +0100424
Harald Welte1c02fd12018-02-19 19:20:47 +0100425 /* port for Common Channel / TRX Management */
426 port RSL_CCHAN_PT CCHAN_PT;
427
Harald Welte714ded92017-12-08 14:00:22 +0100428 /* state of all concurrent connections / dedicated channels */
429 var ConnectionData ConnectionTable[64];
Harald Weltead2647b2018-03-22 19:53:55 +0100430
431 /* last RSL CHAN ACT for each chan_nr */
432 var LastActData LastActTable[64];
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700433
434 /* IPA stream ID -> TCP/IP connection ID mapping for transceivers */
435 var integer TrxConnNum := 0; /* number of connected transceivers */
436 var integer TrxConnMap[4]; /* up to 4 transceivers for now */
Harald Welte714ded92017-12-08 14:00:22 +0100437}
438
439
Harald Welte714ded92017-12-08 14:00:22 +0100440private function f_trx_by_streamId(IpaStreamId id) return integer {
441 return enum2int(id);
442}
443
Harald Weltef70df652018-01-29 22:00:23 +0100444private function f_streamId_by_trx(uint8_t trx_nr) return IpaStreamId {
445 select (trx_nr) {
446 case (0) { return IPAC_PROTO_RSL_TRX0; }
447 case (1) { return IPAC_PROTO_RSL_TRX1; }
448 case (2) { return IPAC_PROTO_RSL_TRX2; }
449 case (3) { return IPAC_PROTO_RSL_TRX3; }
450 }
Daniel Willmanna6ea2ef2018-07-24 09:55:52 +0200451 setverdict(fail, "Unknown stream ID ", trx_nr);
452 mtc.stop;
Harald Weltef70df652018-01-29 22:00:23 +0100453}
454
Harald Welte714ded92017-12-08 14:00:22 +0100455
Harald Welte1c02fd12018-02-19 19:20:47 +0100456function main(boolean bts_role := true) runs on RSL_Emulation_CT {
Harald Weltebb6aed32018-02-21 12:19:18 +0100457 var ASP_IPA_Event evt;
Harald Welte714ded92017-12-08 14:00:22 +0100458 var ASP_RSL_Unitdata rx_rsl;
459 var RSL_Message rx_rsl_msg;
460 var RSLDC_ChanRqd chan_rqd;
461 var RSL_DchanHdlr vc_conn;
Harald Weltef70df652018-01-29 22:00:23 +0100462 var RslChannelNr chan_nr;
463 var uint8_t trx_nr;
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700464 var integer conn_id;
Harald Welte714ded92017-12-08 14:00:22 +0100465 var integer cid;
466 var integer i;
Harald Welte70b52c92018-02-12 20:47:31 +0100467 /* special synchronization handling during hand-over */
468 var boolean dchan_suspended := false;
Harald Welte714ded92017-12-08 14:00:22 +0100469
Daniel Willmann17f970f2018-01-17 12:03:19 +0100470 f_conn_table_init();
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700471 f_trx_conn_map_init();
Harald Weltead2647b2018-03-22 19:53:55 +0100472 f_last_act_table_init();
Daniel Willmann17f970f2018-01-17 12:03:19 +0100473
Harald Welte714ded92017-12-08 14:00:22 +0100474 while (true) {
475 alt {
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700476 [bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_UP)) {
Harald Weltebb6aed32018-02-21 12:19:18 +0100477 }
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700478 [not bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_UP)) -> value evt {
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700479 log("A new IPA/RSL connection has been established (conn_id=",
480 evt.conn_id, "), waiting for IDENTITY RESPONSE...");
481 }
482 [not bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_RESP)) -> value evt {
483 log("Got IDENTITY RESPONSE (conn_id=", evt.conn_id, "): ", evt.id_resp);
484 /* Update [ IPA stream ID -> TCP/IP connection ID ] mapping */
485 var IpaStreamId sid := f_trx_conn_map_register(evt.conn_id, evt.id_resp);
486 /* Notify the upper layers about a new connection */
487 CCHAN_PT.send(ts_RSLEm_EV(RSLEM_EV_TRX_UP, sid));
Harald Welte624f9632017-12-16 19:26:04 +0100488 }
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700489 [bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) {
Pau Espin Pedrola07cfd92018-10-22 15:54:41 +0200490 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Lost IPA connection!");
491
492 }
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700493 [not bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) {
Pau Espin Pedrola07cfd92018-10-22 15:54:41 +0200494 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Lost IPA connection!");
495 }
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700496 [bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) {
Vadim Yanitskiy9b4e3562020-05-25 21:40:52 +0700497 IPA_PT.send(ts_ASP_RSL_UD(ts_RSL_PAGING_LOAD_IND(23)));
Harald Welte714ded92017-12-08 14:00:22 +0100498 }
Vadim Yanitskiya2afacc2020-05-18 21:16:19 +0700499 [not bts_role] IPA_PT.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) { }
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700500 [bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_IMM_ASSIGN, sid := ?)) -> value rx_rsl {
Harald Welte714ded92017-12-08 14:00:22 +0100501 var GsmRrMessage rr;
502 var OCT1 ra;
503 var GsmFrameNumber fn;
Harald Welte714ded92017-12-08 14:00:22 +0100504 rr := dec_GsmRrMessage(rx_rsl.rsl.ies[1].body.full_imm_ass_info.payload);
505 if (ischosen(rr.payload.imm_ass)) {
506 ra := bit2oct(rr.payload.imm_ass.req_ref.ra);
507 fn := 23; //FIXME(rr.payload.imm_ass);
508 /* lookup client based on RA+time, deliver to client */
509 cid := f_cid_by_ra_fn(ra, fn);
510 if (cid == -1) {
511 setverdict(fail, "IMM ASS for unknown DChan");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200512 mtc.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100513 }
514 /* update client with trx_nr */
515 ConnectionTable[cid].trx_nr := f_trx_by_streamId(rx_rsl.streamId);
516 ConnectionTable[cid].stream_id := rx_rsl.streamId;
517 /* update client with chan_nr */
518 ConnectionTable[cid].chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
519 /* TODO: add timer to time-out ConnectionTable entries which
520 * never get followed-up to */
521 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
522 } else if (ischosen(rr.payload.imm_ass_rej)) {
523 for (i := 0; i < sizeof(rr.payload.imm_ass_rej.payload); i := i + 1) {
524 ra := bit2oct(rr.payload.imm_ass_rej.payload[i].req_ref.ra);
525 fn := 23; //FIXME();
526 /* lookup client based on RA+time, deliver to client */
527 cid := f_cid_by_ra_fn(ra, fn);
528 if (cid != -1) {
529 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
530 /* delete ClientTable entry, as it failed */
531 f_cid_clear(cid);
532 }
533 }
534 }
535 }
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700536 [not bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(?), sid := ?)) -> value rx_rsl {
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200537 var RSL_IE_RequestRef req_ref;
538 req_ref := rx_rsl.rsl.ies[1].body.req_ref;
539 cid := f_cid_by_ra_fn2(req_ref.ra, req_ref.frame_nr);
540 if (cid != -1) {
541 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
542 f_cid_clear(cid);
543 } else {
544 CCHAN_PT.send(rx_rsl);
545 }
546 }
Harald Welte714ded92017-12-08 14:00:22 +0100547
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700548 [bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_CMD(?, ?), sid := ?)) -> value rx_rsl {
Harald Welte714ded92017-12-08 14:00:22 +0100549 /* broadcast to all clients? */
550 for (i := 0; i < sizeof(ConnectionTable); i := i + 1) {
Neels Hofmeyrd7eabd62020-06-08 22:30:54 +0200551 if (ispresent(ConnectionTable[i].comp_ref) and ConnectionTable[i].comp_ref != null) {
Harald Welte714ded92017-12-08 14:00:22 +0100552 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[i].comp_ref;
553 }
554 }
555 }
556
Harald Welte1c02fd12018-02-19 19:20:47 +0100557 /* Forward common channel management to the special port for it */
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700558 [] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeT(?), sid := ?)) -> value rx_rsl {
Vadim Yanitskiy82af0b52020-06-09 20:21:43 +0700559 if (not bts_role and mp_rslem_patch_ipa_cid) {
560 f_trx_conn_map_patch_ud(rx_rsl);
561 }
Harald Welte1c02fd12018-02-19 19:20:47 +0100562 CCHAN_PT.send(rx_rsl);
Harald Welte714ded92017-12-08 14:00:22 +0100563 }
564
Harald Welte1c02fd12018-02-19 19:20:47 +0100565 /* Forward common channel management to the special port for it */
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700566 [] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeC(?), sid := ?)) -> value rx_rsl {
Vadim Yanitskiy82af0b52020-06-09 20:21:43 +0700567 if (not bts_role and mp_rslem_patch_ipa_cid) {
568 f_trx_conn_map_patch_ud(rx_rsl);
569 }
Harald Welte1c02fd12018-02-19 19:20:47 +0100570 CCHAN_PT.send(rx_rsl);
Harald Welte714ded92017-12-08 14:00:22 +0100571 }
572
573 /* blindly acknowledge all channel activations */
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700574 [bts_role] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV), sid := ?)) -> value rx_rsl {
Harald Weltef70df652018-01-29 22:00:23 +0100575 chan_nr := rx_rsl.rsl.ies[0].body.chan_nr;
Harald Weltead2647b2018-03-22 19:53:55 +0100576 trx_nr := f_trx_by_streamId(rx_rsl.streamId);
577 f_store_last_act_data(trx_nr, chan_nr, rx_rsl.rsl);
Vadim Yanitskiy9b4e3562020-05-25 21:40:52 +0700578 IPA_PT.send(ts_ASP_RSL_UD(ts_RSL_CHAN_ACT_ACK(chan_nr, 23), rx_rsl.streamId));
Harald Welte714ded92017-12-08 14:00:22 +0100579 }
580
Vadim Yanitskiya60e5cf2020-05-26 01:46:31 +0700581 [not dchan_suspended] IPA_PT.receive(tr_ASP_RSL_UD(tr_RSL_MsgTypeDR(?), sid := ?)) -> value rx_rsl {
Harald Welte714ded92017-12-08 14:00:22 +0100582 /* dispatch to channel based on ChanId */
583 cid := f_cid_by_chan_nr(f_trx_by_streamId(rx_rsl.streamId),
584 rx_rsl.rsl.ies[0].body.chan_nr);
585 if (cid != -1) {
586 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
587 } else {
588 setverdict(fail, "RSL for unknown Dchan");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200589 mtc.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100590 }
591 }
592
Harald Welte70b52c92018-02-12 20:47:31 +0100593 [not dchan_suspended] IPA_PT.receive {
Harald Welte714ded92017-12-08 14:00:22 +0100594 setverdict(fail, "Received unknown primitive from IPA");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200595 mtc.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100596 }
597
Harald Welte1c02fd12018-02-19 19:20:47 +0100598 [bts_role] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
Harald Welte714ded92017-12-08 14:00:22 +0100599 /* Store the knowledge that this sender has requested a certain RQ+time */
600 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
Vadim Yanitskiy9b4e3562020-05-25 21:40:52 +0700601 IPA_PT.send(ts_ASP_RSL_UD(ts_RSL_CHAN_RQD(chan_rqd.ra, chan_rqd.fn)));
Harald Welte714ded92017-12-08 14:00:22 +0100602 }
603
Pau Espin Pedrol6451b042018-10-24 20:36:16 +0200604 [not bts_role] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
605 /* Store the knowledge that this sender has requested a certain RQ+time */
606 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
607 }
608
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700609 /* RSL message from a component that runs on RSL_DchanHdlr */
610 [bts_role] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
Harald Welte714ded92017-12-08 14:00:22 +0100611 cid := f_cid_by_comp_ref(vc_conn);
Vadim Yanitskiy9b4e3562020-05-25 21:40:52 +0700612 IPA_PT.send(ts_ASP_RSL_UD(rx_rsl_msg, ConnectionTable[cid].stream_id));
Harald Welte714ded92017-12-08 14:00:22 +0100613 }
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700614 [not bts_role] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
615 cid := f_cid_by_comp_ref(vc_conn);
616 conn_id := f_trx_conn_map_resolve(ConnectionTable[cid].stream_id);
617 IPA_PT.send(ts_ASP_RSL_UD(rx_rsl_msg, ConnectionTable[cid].stream_id, conn_id));
618 }
Harald Welte714ded92017-12-08 14:00:22 +0100619
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700620 /* RSL message from MTC */
621 [bts_role] CCHAN_PT.receive(tr_ASP_RSL_UD(?, sid := ?)) -> value rx_rsl {
Vadim Yanitskiy9b4e3562020-05-25 21:40:52 +0700622 IPA_PT.send(ts_ASP_RSL_UD(rx_rsl.rsl, rx_rsl.streamId));
Harald Welte34252c52018-02-24 04:51:50 +0100623 }
Vadim Yanitskiy6de2fcb2020-05-25 19:40:45 +0700624 [not bts_role] CCHAN_PT.receive(tr_ASP_RSL_UD(?, sid := ?)) -> value rx_rsl {
625 conn_id := f_trx_conn_map_resolve(rx_rsl.streamId);
626 IPA_PT.send(ts_ASP_RSL_UD(rx_rsl.rsl, rx_rsl.streamId, conn_id));
627 }
Harald Welte34252c52018-02-24 04:51:50 +0100628
Harald Weltef70df652018-01-29 22:00:23 +0100629 /* explicit registration, e.g. in (non-immediate) assignment case */
630 [] RSL_PROC.getcall(RSLEM_register:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
631 f_cid_create_cnr(trx_nr, chan_nr, vc_conn);
Harald Weltee32ad992018-05-31 22:17:46 +0200632 RSL_PROC.reply(RSLEM_register:{trx_nr, chan_nr, vc_conn}) to vc_conn;
Harald Weltef70df652018-01-29 22:00:23 +0100633 }
634
Harald Welte1909f462018-01-29 22:29:29 +0100635 [] RSL_PROC.getcall(RSLEM_unregister:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
636 cid := f_cid_by_chan_nr(trx_nr, chan_nr);
637 f_cid_clear(cid);
Harald Weltee32ad992018-05-31 22:17:46 +0200638 RSL_PROC.reply(RSLEM_unregister:{trx_nr, chan_nr, vc_conn}) to vc_conn;
Harald Welte1909f462018-01-29 22:29:29 +0100639 }
640
Harald Weltee32ad992018-05-31 22:17:46 +0200641 [] RSL_PROC.getcall(RSLEM_suspend:{true}) -> sender vc_conn {
Harald Welte70b52c92018-02-12 20:47:31 +0100642 log("Suspending DChan");
643 dchan_suspended := true;
Harald Weltee32ad992018-05-31 22:17:46 +0200644 RSL_PROC.reply(RSLEM_suspend:{true}) to vc_conn;
Harald Welte70b52c92018-02-12 20:47:31 +0100645 }
646
Harald Weltee32ad992018-05-31 22:17:46 +0200647 [] RSL_PROC.getcall(RSLEM_suspend:{false}) -> sender vc_conn {
Harald Welte70b52c92018-02-12 20:47:31 +0100648 log("Resuming DChan");
649 dchan_suspended := false;
Harald Weltee32ad992018-05-31 22:17:46 +0200650 RSL_PROC.reply(RSLEM_suspend:{false}) to vc_conn;
Harald Welte70b52c92018-02-12 20:47:31 +0100651 }
Harald Welte1909f462018-01-29 22:29:29 +0100652
Harald Weltee32ad992018-05-31 22:17:46 +0200653 [] RSL_PROC.getcall(RSLEM_get_last_act:{?,?,?}) -> param(trx_nr, chan_nr) sender vc_conn {
Harald Weltead2647b2018-03-22 19:53:55 +0100654 var RSL_Message last_chan_act := f_lookup_last_act(trx_nr, chan_nr);
Harald Weltee32ad992018-05-31 22:17:46 +0200655 RSL_PROC.reply(RSLEM_get_last_act:{trx_nr, chan_nr, last_chan_act}) to vc_conn;
Harald Weltead2647b2018-03-22 19:53:55 +0100656 }
Harald Welte714ded92017-12-08 14:00:22 +0100657 }
658 }
659}
660
Daniel Willmann17f970f2018-01-17 12:03:19 +0100661private function f_conn_table_init()
662runs on RSL_Emulation_CT {
663 var integer i;
664
665 /* Initialize the ConnectionTable */
666 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
667 f_cid_clear(i);
668 }
669}
Harald Welte714ded92017-12-08 14:00:22 +0100670
Harald Weltef70df652018-01-29 22:00:23 +0100671/* client/conn_hdlr side function to use procedure port to register stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100672function f_rslem_register(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
673runs on RSL_DchanHdlr {
674 PT.call(RSLEM_register:{trx_nr, chan_nr, self}) {
675 [] PT.getreply(RSLEM_register:{?,?,?}) {};
Harald Weltef70df652018-01-29 22:00:23 +0100676 }
677}
678
Harald Welte1909f462018-01-29 22:29:29 +0100679/* client/conn_hdlr side function to use procedure port to unregister stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100680function f_rslem_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
681runs on RSL_DchanHdlr {
682 PT.call(RSLEM_unregister:{trx_nr, chan_nr, self}) {
683 [] PT.getreply(RSLEM_unregister:{?,?,?}) {};
Harald Welte1909f462018-01-29 22:29:29 +0100684 }
685}
686
Harald Welte70b52c92018-02-12 20:47:31 +0100687/* resume handling of RSL DChan messages from IPA until f_rslem_resume() is called */
688function f_rslem_suspend(RSLEM_PROC_PT PT)
689runs on RSL_DchanHdlr {
690 PT.call(RSLEM_suspend:{true}) {
691 [] PT.getreply(RSLEM_suspend:{true}) {};
692 }
693}
694
695/* resume handling of RSL DChan messages after f_rslem_suspend() is called */
696function f_rslem_resume(RSLEM_PROC_PT PT)
697runs on RSL_DchanHdlr {
698 PT.call(RSLEM_suspend:{false}) {
699 [] PT.getreply(RSLEM_suspend:{false}) {};
700 }
701}
702
Harald Weltead2647b2018-03-22 19:53:55 +0100703/* obtain the last RSL_CHAN_ACT message for the given chan_nr */
704function f_rslem_get_last_act(RSLEM_PROC_PT PT, uint8_t trx_nr, RslChannelNr chan_nr)
705runs on RSL_DchanHdlr return RSL_Message {
706 var RSL_Message chan_act;
707 PT.call(RSLEM_get_last_act:{trx_nr, chan_nr, -}) {
708 [] PT.getreply(RSLEM_get_last_act:{trx_nr, chan_nr, ?}) -> param(chan_act) {};
709 }
710 return chan_act;
711}
712
713
Harald Welte70b52c92018-02-12 20:47:31 +0100714
Harald Welte1909f462018-01-29 22:29:29 +0100715
Harald Welte714ded92017-12-08 14:00:22 +0100716}