blob: dab1785f5d8d35b53b5d1731ae7f52ae79275ab4 [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 *
13 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
14 * All rights reserved.
15 *
16 * Released under the terms of GNU General Public License, Version 2 or
17 * (at your option) any later version.
18 */
19
Harald Welte714ded92017-12-08 14:00:22 +010020import from General_Types all;
21import from Osmocom_Types all;
22import from GSM_Types all;
23import from GSM_RR_Types all;
24import from RSL_Types all;
25import from IPA_Types all;
26import from IPA_Emulation all;
27
28
29/* General "base class" component definition, of which specific implementations
30 * derive themselves by means of the "extends" feature */
31type component RSL_DchanHdlr {
32 /* port facing up towards dedicated channel handler */
33 port RSL_DCHAN_PT RSL;
Harald Weltef70df652018-01-29 22:00:23 +010034 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +010035 var RslChannelNr g_chan_nr;
Harald Welte421e4d42018-02-12 20:04:50 +010036 /* second BTS / DChan during hand-over */
37 port RSL_DCHAN_PT RSL1;
38 port RSLEM_PROC_PT RSL1_PROC;
Harald Welte714ded92017-12-08 14:00:22 +010039};
40
41type record RSLDC_ChanRqd {
42 OCT1 ra,
43 GsmFrameNumber fn
44};
45
46template RSLDC_ChanRqd ts_RSLDC_ChanRqd(OCT1 ra, GsmFrameNumber fn) := {
47 ra := ra,
48 fn := fn
49}
50
51type port RSL_DCHAN_PT message {
52 inout RSLDC_ChanRqd, RSL_Message;
53} with { extension "internal" };
54
Harald Weltef70df652018-01-29 22:00:23 +010055signature RSLEM_register(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Welte1909f462018-01-29 22:29:29 +010056signature RSLEM_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Welte70b52c92018-02-12 20:47:31 +010057signature RSLEM_suspend(boolean suspend);
Harald Weltef70df652018-01-29 22:00:23 +010058
59type port RSLEM_PROC_PT procedure {
Harald Welte70b52c92018-02-12 20:47:31 +010060 inout RSLEM_register, RSLEM_unregister, RSLEM_suspend;
Harald Weltef70df652018-01-29 22:00:23 +010061} with { extension "internal" };
62
Harald Welte714ded92017-12-08 14:00:22 +010063/***********************************************************************
64 * Client Component for a single dedicated channel
65 ***********************************************************************/
66
67private function f_rx_or_fail(template RSL_Message exp_rx) runs on RSL_DchanHdlr return RSL_Message
68{
69 var RSL_Message rx_rsl;
70 timer T := 10.0;
71
72 /* request a channel to be established */
73 T.start;
74 alt {
75 [] RSL.receive(exp_rx) -> value rx_rsl {
76 T.stop;
77 return rx_rsl;
78 }
79 [] RSL.receive {
80 setverdict(fail, "Unexpected RSL message on DCHAN");
81 self.stop;
82 }
83 [] T.timeout {
84 setverdict(fail, "Timeout waiting for RSL on DCHAN");
85 self.stop;
86 }
87 }
88 /* never reached */
89 return rx_rsl;
90}
91
92/* establish a dedicated channel using 'ra' */
93function f_chan_est(OCT1 ra, octetstring est_l3, template RslLinkId link_id, GsmFrameNumber fn := 23)
94runs on RSL_DchanHdlr {
95 var RSL_Message rx_rsl;
96 var GsmRrMessage rr;
97
98 /* request a channel to be established */
99 RSL.send(ts_RSLDC_ChanRqd(ra, fn));
100 /* expect immediate assignment */
101 rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN);
102 rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload);
103 g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
104 RSL.send(ts_RSL_EST_IND(g_chan_nr, valueof(link_id), est_l3));
105}
106
107function f_deact_chan(RSL_Cause cause) runs on RSL_DchanHdlr
108{
109 var RSL_Message rx_rsl;
110
111 RSL.send(ts_RSL_CONN_FAIL_IND(g_chan_nr, cause));
112 rx_rsl := f_rx_or_fail(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL));
113 /* FIXME RSL.send(ts_RSL_RF_CHAN_REL_ACK()) */
114}
115
116
117
118/***********************************************************************
119 * Main Component
120 ***********************************************************************/
121
122private type record ConnectionData {
123 /* component reference to the client component */
124 RSL_DchanHdlr comp_ref,
125 /* RSL (dedicated) Channel number we're handling */
126 uint8_t trx_nr optional,
127 IpaStreamId stream_id optional,
128 RslChannelNr chan_nr optional,
129 /* Random Reference */
130 OCT1 ra optional,
131 GsmFrameNumber ra_fn optional
132};
133
134private function f_cid_by_comp_ref(RSL_DchanHdlr comp_ref)
135runs on RSL_Emulation_CT return integer {
136 var integer i;
137 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
138 if (ispresent(ConnectionTable[i].comp_ref) and
139 ConnectionTable[i].comp_ref == comp_ref) {
140 return i;
141 }
142 }
143 log("No Dchan handler for ", comp_ref);
144 return -1;
145}
146
147private function f_cid_by_chan_nr(uint8_t trx_nr, RslChannelNr chan_nr)
148runs on RSL_Emulation_CT return integer {
149 var integer i;
150 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
151 if (ispresent(ConnectionTable[i].chan_nr) and
152 ConnectionTable[i].chan_nr == chan_nr and ConnectionTable[i].trx_nr == trx_nr) {
153 return i;
154 }
155 }
156 log("No Dchan handler for ", trx_nr, chan_nr);
157 return -1;
158}
159
160private function f_cid_by_ra_fn(OCT1 ra, GsmFrameNumber fn)
161runs on RSL_Emulation_CT return integer {
162 var integer i;
163 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
164 if (ispresent(ConnectionTable[i].ra) and
165 ConnectionTable[i].ra == ra and ConnectionTable[i].ra_fn == fn) {
166 return i;
167 }
168 }
169 log("No Dchan handler for ", ra, fn);
170 return -1;
171}
172
173/* create an ew client with given RA and FN */
174private function f_cid_create(OCT1 ra, GsmFrameNumber fn, RSL_DchanHdlr comp_ref)
175runs on RSL_Emulation_CT return integer {
176 var integer i;
177 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
Harald Weltef70df652018-01-29 22:00:23 +0100178 if (not ispresent(ConnectionTable[i].ra) and
179 not ispresent(ConnectionTable[i].trx_nr)) {
Harald Welte714ded92017-12-08 14:00:22 +0100180 ConnectionTable[i].ra := ra;
181 ConnectionTable[i].ra_fn := fn;
182 ConnectionTable[i].comp_ref := comp_ref;
183 return i;
184 }
185 }
186 log("No free entry in conn table for ", ra, fn);
187 return -1;
188}
189
Harald Weltef70df652018-01-29 22:00:23 +0100190/* create an ew client with given RA and FN */
191private function f_cid_create_cnr(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
192runs on RSL_Emulation_CT return integer {
193 var integer i;
194 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
195 if (not ispresent(ConnectionTable[i].ra) and
196 not ispresent(ConnectionTable[i].trx_nr)) {
197 ConnectionTable[i].stream_id := f_streamId_by_trx(trx_nr);
198 ConnectionTable[i].trx_nr := trx_nr;
199 ConnectionTable[i].chan_nr := chan_nr;
200 ConnectionTable[i].comp_ref := comp_ref;
201 return i;
202 }
203 }
204 log("No free entry in conn table for ", trx_nr, chan_nr, comp_ref);
205 return -1;
206}
207
208
209/* create an ew client with given RA and FN */
210private function f_cid_delete_cnr(IpaStreamId stream_id, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
211runs on RSL_Emulation_CT return integer {
212 var integer i;
213 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
214 if (ConnectionTable[i].comp_ref == null) {
215 continue;
216 }
217 if (ConnectionTable[i].stream_id == stream_id and
218 ConnectionTable[i].chan_nr == chan_nr and
219 ConnectionTable[i].comp_ref == comp_ref) {
220 f_cid_clear(i);
221 }
222 }
223 log("Unable to find entry to delete for ", stream_id, chan_nr, comp_ref);
224 return -1;
225}
226
227
Harald Welte714ded92017-12-08 14:00:22 +0100228private function f_cid_clear(integer cid)
229runs on RSL_Emulation_CT {
230 ConnectionTable[cid].ra := omit;
231 ConnectionTable[cid].ra_fn := omit;
Harald Welte714ded92017-12-08 14:00:22 +0100232 ConnectionTable[cid].trx_nr := omit;
233 ConnectionTable[cid].stream_id := omit;
234 ConnectionTable[cid].chan_nr := omit;
Harald Weltef70df652018-01-29 22:00:23 +0100235 ConnectionTable[cid].comp_ref := null;
Harald Welte714ded92017-12-08 14:00:22 +0100236}
237
238type component RSL_Emulation_CT {
239 /* port facing down towards IPA emulation */
240 port IPA_RSL_PT IPA_PT;
241 /* port facing up towards dedicated channel handler */
242 port RSL_DCHAN_PT CLIENT_PT;
Harald Weltef70df652018-01-29 22:00:23 +0100243 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +0100244
245 /* state of all concurrent connections / dedicated channels */
246 var ConnectionData ConnectionTable[64];
247}
248
249
250/* template for an ASP_RSL_Unitdata as we receive it from the IPA_Emulateion component */
251private template ASP_RSL_Unitdata tr_RSL(template RSL_Message rsl, template IpaStreamId sid := ?) := {
252 streamId := sid,
253 rsl := rsl
254}
255
Harald Welte714ded92017-12-08 14:00:22 +0100256private function f_trx_by_streamId(IpaStreamId id) return integer {
257 return enum2int(id);
258}
259
Harald Weltef70df652018-01-29 22:00:23 +0100260private function f_streamId_by_trx(uint8_t trx_nr) return IpaStreamId {
261 select (trx_nr) {
262 case (0) { return IPAC_PROTO_RSL_TRX0; }
263 case (1) { return IPAC_PROTO_RSL_TRX1; }
264 case (2) { return IPAC_PROTO_RSL_TRX2; }
265 case (3) { return IPAC_PROTO_RSL_TRX3; }
266 }
267 self.stop;
268}
269
Harald Welte714ded92017-12-08 14:00:22 +0100270
271function main() runs on RSL_Emulation_CT {
272 var ASP_RSL_Unitdata rx_rsl;
273 var RSL_Message rx_rsl_msg;
274 var RSLDC_ChanRqd chan_rqd;
275 var RSL_DchanHdlr vc_conn;
Harald Weltef70df652018-01-29 22:00:23 +0100276 var RslChannelNr chan_nr;
277 var uint8_t trx_nr;
Harald Welte714ded92017-12-08 14:00:22 +0100278 var integer cid;
279 var integer i;
Harald Welte70b52c92018-02-12 20:47:31 +0100280 /* special synchronization handling during hand-over */
281 var boolean dchan_suspended := false;
Harald Welte714ded92017-12-08 14:00:22 +0100282
Daniel Willmann17f970f2018-01-17 12:03:19 +0100283 f_conn_table_init();
284
Harald Welte714ded92017-12-08 14:00:22 +0100285 while (true) {
286 alt {
Harald Welte624f9632017-12-16 19:26:04 +0100287 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) {
288 }
Harald Welte714ded92017-12-08 14:00:22 +0100289 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) {
Harald Welte7ae019e2017-12-09 00:54:15 +0100290 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,ts_RSL_PAGING_LOAD_IND(23)));
Harald Welte714ded92017-12-08 14:00:22 +0100291 }
292 [] IPA_PT.receive(tr_RSL(tr_RSL_IMM_ASSIGN)) -> value rx_rsl {
293 var GsmRrMessage rr;
294 var OCT1 ra;
295 var GsmFrameNumber fn;
296 log("IMM ASS INFO ", rx_rsl.rsl.ies[1].body);
297 rr := dec_GsmRrMessage(rx_rsl.rsl.ies[1].body.full_imm_ass_info.payload);
298 if (ischosen(rr.payload.imm_ass)) {
299 ra := bit2oct(rr.payload.imm_ass.req_ref.ra);
300 fn := 23; //FIXME(rr.payload.imm_ass);
301 /* lookup client based on RA+time, deliver to client */
302 cid := f_cid_by_ra_fn(ra, fn);
303 if (cid == -1) {
304 setverdict(fail, "IMM ASS for unknown DChan");
Harald Weltef70df652018-01-29 22:00:23 +0100305 self.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100306 }
307 /* update client with trx_nr */
308 ConnectionTable[cid].trx_nr := f_trx_by_streamId(rx_rsl.streamId);
309 ConnectionTable[cid].stream_id := rx_rsl.streamId;
310 /* update client with chan_nr */
311 ConnectionTable[cid].chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
312 /* TODO: add timer to time-out ConnectionTable entries which
313 * never get followed-up to */
314 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
315 } else if (ischosen(rr.payload.imm_ass_rej)) {
316 for (i := 0; i < sizeof(rr.payload.imm_ass_rej.payload); i := i + 1) {
317 ra := bit2oct(rr.payload.imm_ass_rej.payload[i].req_ref.ra);
318 fn := 23; //FIXME();
319 /* lookup client based on RA+time, deliver to client */
320 cid := f_cid_by_ra_fn(ra, fn);
321 if (cid != -1) {
322 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
323 /* delete ClientTable entry, as it failed */
324 f_cid_clear(cid);
325 }
326 }
327 }
328 }
329
330 [] IPA_PT.receive(tr_RSL(tr_RSL_PAGING_CMD(?, ?))) -> value rx_rsl {
331 log("PAGING IDENTITY ", rx_rsl.rsl.ies[2].body.other);
332 /* broadcast to all clients? */
333 for (i := 0; i < sizeof(ConnectionTable); i := i + 1) {
334 if (ispresent(ConnectionTable[i].comp_ref)) {
335 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[i].comp_ref;
336 }
337 }
338 }
339
340 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeT(?))) -> value rx_rsl {
341 log("Ingnoring TRX Mgmt ", rx_rsl.rsl);
342 }
343
344 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeC(?))) -> value rx_rsl {
345 log("Ignoring Common Channel Mgmt ", rx_rsl.rsl);
346 }
347
348 /* blindly acknowledge all channel activations */
349 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) -> value rx_rsl {
Harald Weltef70df652018-01-29 22:00:23 +0100350 chan_nr := rx_rsl.rsl.ies[0].body.chan_nr;
Harald Welte7ae019e2017-12-09 00:54:15 +0100351 IPA_PT.send(ts_ASP_RSL_UD(rx_rsl.streamId, ts_RSL_CHAN_ACT_ACK(chan_nr, 23)));
Harald Welte714ded92017-12-08 14:00:22 +0100352 }
353
Harald Welte70b52c92018-02-12 20:47:31 +0100354 [not dchan_suspended] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeDR(?))) -> value rx_rsl {
Harald Welte714ded92017-12-08 14:00:22 +0100355 /* dispatch to channel based on ChanId */
356 cid := f_cid_by_chan_nr(f_trx_by_streamId(rx_rsl.streamId),
357 rx_rsl.rsl.ies[0].body.chan_nr);
358 if (cid != -1) {
359 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
360 } else {
361 setverdict(fail, "RSL for unknown Dchan");
362 }
363 }
364
Harald Welte70b52c92018-02-12 20:47:31 +0100365 [not dchan_suspended] IPA_PT.receive {
Harald Welte714ded92017-12-08 14:00:22 +0100366 setverdict(fail, "Received unknown primitive from IPA");
367 self.stop;
368 }
369
370 [] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
371 /* Store the knowledge that this sender has requested a certain RQ+time */
372 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100373 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,
Harald Welte714ded92017-12-08 14:00:22 +0100374 ts_RSL_CHAN_RQD(chan_rqd.ra, chan_rqd.fn)));
375 }
376
377 [] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
378 /* forward to BSC */
379 cid := f_cid_by_comp_ref(vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100380 IPA_PT.send(ts_ASP_RSL_UD(ConnectionTable[cid].stream_id, rx_rsl_msg));
Harald Welte714ded92017-12-08 14:00:22 +0100381 }
382
Harald Weltef70df652018-01-29 22:00:23 +0100383 /* explicit registration, e.g. in (non-immediate) assignment case */
384 [] RSL_PROC.getcall(RSLEM_register:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
385 f_cid_create_cnr(trx_nr, chan_nr, vc_conn);
386 RSL_PROC.reply(RSLEM_register:{trx_nr, chan_nr, vc_conn});
387 }
388
Harald Welte1909f462018-01-29 22:29:29 +0100389 [] RSL_PROC.getcall(RSLEM_unregister:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
390 cid := f_cid_by_chan_nr(trx_nr, chan_nr);
391 f_cid_clear(cid);
392 RSL_PROC.reply(RSLEM_unregister:{trx_nr, chan_nr, vc_conn});
393 }
394
Harald Welte70b52c92018-02-12 20:47:31 +0100395 [] RSL_PROC.getcall(RSLEM_suspend:{true}) {
396 log("Suspending DChan");
397 dchan_suspended := true;
398 RSL_PROC.reply(RSLEM_suspend:{true});
399 }
400
401 [] RSL_PROC.getcall(RSLEM_suspend:{false}) {
402 log("Resuming DChan");
403 dchan_suspended := false;
404 RSL_PROC.reply(RSLEM_suspend:{false});
405 }
Harald Welte1909f462018-01-29 22:29:29 +0100406
Harald Welte714ded92017-12-08 14:00:22 +0100407 }
408 }
409}
410
Daniel Willmann17f970f2018-01-17 12:03:19 +0100411private function f_conn_table_init()
412runs on RSL_Emulation_CT {
413 var integer i;
414
415 /* Initialize the ConnectionTable */
416 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
417 f_cid_clear(i);
418 }
419}
Harald Welte714ded92017-12-08 14:00:22 +0100420
Harald Weltef70df652018-01-29 22:00:23 +0100421/* client/conn_hdlr side function to use procedure port to register stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100422function f_rslem_register(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
423runs on RSL_DchanHdlr {
424 PT.call(RSLEM_register:{trx_nr, chan_nr, self}) {
425 [] PT.getreply(RSLEM_register:{?,?,?}) {};
Harald Weltef70df652018-01-29 22:00:23 +0100426 }
427}
428
Harald Welte1909f462018-01-29 22:29:29 +0100429/* client/conn_hdlr side function to use procedure port to unregister stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100430function f_rslem_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
431runs on RSL_DchanHdlr {
432 PT.call(RSLEM_unregister:{trx_nr, chan_nr, self}) {
433 [] PT.getreply(RSLEM_unregister:{?,?,?}) {};
Harald Welte1909f462018-01-29 22:29:29 +0100434 }
435}
436
Harald Welte70b52c92018-02-12 20:47:31 +0100437/* resume handling of RSL DChan messages from IPA until f_rslem_resume() is called */
438function f_rslem_suspend(RSLEM_PROC_PT PT)
439runs on RSL_DchanHdlr {
440 PT.call(RSLEM_suspend:{true}) {
441 [] PT.getreply(RSLEM_suspend:{true}) {};
442 }
443}
444
445/* resume handling of RSL DChan messages after f_rslem_suspend() is called */
446function f_rslem_resume(RSLEM_PROC_PT PT)
447runs on RSL_DchanHdlr {
448 PT.call(RSLEM_suspend:{false}) {
449 [] PT.getreply(RSLEM_suspend:{false}) {};
450 }
451}
452
453
Harald Welte1909f462018-01-29 22:29:29 +0100454
Harald Welte714ded92017-12-08 14:00:22 +0100455}