blob: f45b4257c0e2b7dcc3659f2edfcc7e804f4d9e64 [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 Weltef70df652018-01-29 22:00:23 +010057
58type port RSLEM_PROC_PT procedure {
Harald Welte1909f462018-01-29 22:29:29 +010059 inout RSLEM_register, RSLEM_unregister;
Harald Weltef70df652018-01-29 22:00:23 +010060} with { extension "internal" };
61
Harald Welte714ded92017-12-08 14:00:22 +010062/***********************************************************************
63 * Client Component for a single dedicated channel
64 ***********************************************************************/
65
66private function f_rx_or_fail(template RSL_Message exp_rx) runs on RSL_DchanHdlr return RSL_Message
67{
68 var RSL_Message rx_rsl;
69 timer T := 10.0;
70
71 /* request a channel to be established */
72 T.start;
73 alt {
74 [] RSL.receive(exp_rx) -> value rx_rsl {
75 T.stop;
76 return rx_rsl;
77 }
78 [] RSL.receive {
79 setverdict(fail, "Unexpected RSL message on DCHAN");
80 self.stop;
81 }
82 [] T.timeout {
83 setverdict(fail, "Timeout waiting for RSL on DCHAN");
84 self.stop;
85 }
86 }
87 /* never reached */
88 return rx_rsl;
89}
90
91/* establish a dedicated channel using 'ra' */
92function f_chan_est(OCT1 ra, octetstring est_l3, template RslLinkId link_id, GsmFrameNumber fn := 23)
93runs on RSL_DchanHdlr {
94 var RSL_Message rx_rsl;
95 var GsmRrMessage rr;
96
97 /* request a channel to be established */
98 RSL.send(ts_RSLDC_ChanRqd(ra, fn));
99 /* expect immediate assignment */
100 rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN);
101 rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload);
102 g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
103 RSL.send(ts_RSL_EST_IND(g_chan_nr, valueof(link_id), est_l3));
104}
105
106function f_deact_chan(RSL_Cause cause) runs on RSL_DchanHdlr
107{
108 var RSL_Message rx_rsl;
109
110 RSL.send(ts_RSL_CONN_FAIL_IND(g_chan_nr, cause));
111 rx_rsl := f_rx_or_fail(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL));
112 /* FIXME RSL.send(ts_RSL_RF_CHAN_REL_ACK()) */
113}
114
115
116
117/***********************************************************************
118 * Main Component
119 ***********************************************************************/
120
121private type record ConnectionData {
122 /* component reference to the client component */
123 RSL_DchanHdlr comp_ref,
124 /* RSL (dedicated) Channel number we're handling */
125 uint8_t trx_nr optional,
126 IpaStreamId stream_id optional,
127 RslChannelNr chan_nr optional,
128 /* Random Reference */
129 OCT1 ra optional,
130 GsmFrameNumber ra_fn optional
131};
132
133private function f_cid_by_comp_ref(RSL_DchanHdlr comp_ref)
134runs on RSL_Emulation_CT return integer {
135 var integer i;
136 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
137 if (ispresent(ConnectionTable[i].comp_ref) and
138 ConnectionTable[i].comp_ref == comp_ref) {
139 return i;
140 }
141 }
142 log("No Dchan handler for ", comp_ref);
143 return -1;
144}
145
146private function f_cid_by_chan_nr(uint8_t trx_nr, RslChannelNr chan_nr)
147runs on RSL_Emulation_CT return integer {
148 var integer i;
149 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
150 if (ispresent(ConnectionTable[i].chan_nr) and
151 ConnectionTable[i].chan_nr == chan_nr and ConnectionTable[i].trx_nr == trx_nr) {
152 return i;
153 }
154 }
155 log("No Dchan handler for ", trx_nr, chan_nr);
156 return -1;
157}
158
159private function f_cid_by_ra_fn(OCT1 ra, GsmFrameNumber fn)
160runs on RSL_Emulation_CT return integer {
161 var integer i;
162 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
163 if (ispresent(ConnectionTable[i].ra) and
164 ConnectionTable[i].ra == ra and ConnectionTable[i].ra_fn == fn) {
165 return i;
166 }
167 }
168 log("No Dchan handler for ", ra, fn);
169 return -1;
170}
171
172/* create an ew client with given RA and FN */
173private function f_cid_create(OCT1 ra, GsmFrameNumber fn, RSL_DchanHdlr comp_ref)
174runs on RSL_Emulation_CT return integer {
175 var integer i;
176 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
Harald Weltef70df652018-01-29 22:00:23 +0100177 if (not ispresent(ConnectionTable[i].ra) and
178 not ispresent(ConnectionTable[i].trx_nr)) {
Harald Welte714ded92017-12-08 14:00:22 +0100179 ConnectionTable[i].ra := ra;
180 ConnectionTable[i].ra_fn := fn;
181 ConnectionTable[i].comp_ref := comp_ref;
182 return i;
183 }
184 }
185 log("No free entry in conn table for ", ra, fn);
186 return -1;
187}
188
Harald Weltef70df652018-01-29 22:00:23 +0100189/* create an ew client with given RA and FN */
190private function f_cid_create_cnr(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
191runs on RSL_Emulation_CT return integer {
192 var integer i;
193 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
194 if (not ispresent(ConnectionTable[i].ra) and
195 not ispresent(ConnectionTable[i].trx_nr)) {
196 ConnectionTable[i].stream_id := f_streamId_by_trx(trx_nr);
197 ConnectionTable[i].trx_nr := trx_nr;
198 ConnectionTable[i].chan_nr := chan_nr;
199 ConnectionTable[i].comp_ref := comp_ref;
200 return i;
201 }
202 }
203 log("No free entry in conn table for ", trx_nr, chan_nr, comp_ref);
204 return -1;
205}
206
207
208/* create an ew client with given RA and FN */
209private function f_cid_delete_cnr(IpaStreamId stream_id, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
210runs on RSL_Emulation_CT return integer {
211 var integer i;
212 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
213 if (ConnectionTable[i].comp_ref == null) {
214 continue;
215 }
216 if (ConnectionTable[i].stream_id == stream_id and
217 ConnectionTable[i].chan_nr == chan_nr and
218 ConnectionTable[i].comp_ref == comp_ref) {
219 f_cid_clear(i);
220 }
221 }
222 log("Unable to find entry to delete for ", stream_id, chan_nr, comp_ref);
223 return -1;
224}
225
226
Harald Welte714ded92017-12-08 14:00:22 +0100227private function f_cid_clear(integer cid)
228runs on RSL_Emulation_CT {
229 ConnectionTable[cid].ra := omit;
230 ConnectionTable[cid].ra_fn := omit;
Harald Welte714ded92017-12-08 14:00:22 +0100231 ConnectionTable[cid].trx_nr := omit;
232 ConnectionTable[cid].stream_id := omit;
233 ConnectionTable[cid].chan_nr := omit;
Harald Weltef70df652018-01-29 22:00:23 +0100234 ConnectionTable[cid].comp_ref := null;
Harald Welte714ded92017-12-08 14:00:22 +0100235}
236
237type component RSL_Emulation_CT {
238 /* port facing down towards IPA emulation */
239 port IPA_RSL_PT IPA_PT;
240 /* port facing up towards dedicated channel handler */
241 port RSL_DCHAN_PT CLIENT_PT;
Harald Weltef70df652018-01-29 22:00:23 +0100242 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +0100243
244 /* state of all concurrent connections / dedicated channels */
245 var ConnectionData ConnectionTable[64];
246}
247
248
249/* template for an ASP_RSL_Unitdata as we receive it from the IPA_Emulateion component */
250private template ASP_RSL_Unitdata tr_RSL(template RSL_Message rsl, template IpaStreamId sid := ?) := {
251 streamId := sid,
252 rsl := rsl
253}
254
Harald Welte714ded92017-12-08 14:00:22 +0100255private function f_trx_by_streamId(IpaStreamId id) return integer {
256 return enum2int(id);
257}
258
Harald Weltef70df652018-01-29 22:00:23 +0100259private function f_streamId_by_trx(uint8_t trx_nr) return IpaStreamId {
260 select (trx_nr) {
261 case (0) { return IPAC_PROTO_RSL_TRX0; }
262 case (1) { return IPAC_PROTO_RSL_TRX1; }
263 case (2) { return IPAC_PROTO_RSL_TRX2; }
264 case (3) { return IPAC_PROTO_RSL_TRX3; }
265 }
266 self.stop;
267}
268
Harald Welte714ded92017-12-08 14:00:22 +0100269
270function main() runs on RSL_Emulation_CT {
271 var ASP_RSL_Unitdata rx_rsl;
272 var RSL_Message rx_rsl_msg;
273 var RSLDC_ChanRqd chan_rqd;
274 var RSL_DchanHdlr vc_conn;
Harald Weltef70df652018-01-29 22:00:23 +0100275 var RslChannelNr chan_nr;
276 var uint8_t trx_nr;
Harald Welte714ded92017-12-08 14:00:22 +0100277 var integer cid;
278 var integer i;
279
Daniel Willmann17f970f2018-01-17 12:03:19 +0100280 f_conn_table_init();
281
Harald Welte714ded92017-12-08 14:00:22 +0100282 while (true) {
283 alt {
Harald Welte624f9632017-12-16 19:26:04 +0100284 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) {
285 }
Harald Welte714ded92017-12-08 14:00:22 +0100286 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) {
Harald Welte7ae019e2017-12-09 00:54:15 +0100287 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,ts_RSL_PAGING_LOAD_IND(23)));
Harald Welte714ded92017-12-08 14:00:22 +0100288 }
289 [] IPA_PT.receive(tr_RSL(tr_RSL_IMM_ASSIGN)) -> value rx_rsl {
290 var GsmRrMessage rr;
291 var OCT1 ra;
292 var GsmFrameNumber fn;
293 log("IMM ASS INFO ", rx_rsl.rsl.ies[1].body);
294 rr := dec_GsmRrMessage(rx_rsl.rsl.ies[1].body.full_imm_ass_info.payload);
295 if (ischosen(rr.payload.imm_ass)) {
296 ra := bit2oct(rr.payload.imm_ass.req_ref.ra);
297 fn := 23; //FIXME(rr.payload.imm_ass);
298 /* lookup client based on RA+time, deliver to client */
299 cid := f_cid_by_ra_fn(ra, fn);
300 if (cid == -1) {
301 setverdict(fail, "IMM ASS for unknown DChan");
Harald Weltef70df652018-01-29 22:00:23 +0100302 self.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100303 }
304 /* update client with trx_nr */
305 ConnectionTable[cid].trx_nr := f_trx_by_streamId(rx_rsl.streamId);
306 ConnectionTable[cid].stream_id := rx_rsl.streamId;
307 /* update client with chan_nr */
308 ConnectionTable[cid].chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
309 /* TODO: add timer to time-out ConnectionTable entries which
310 * never get followed-up to */
311 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
312 } else if (ischosen(rr.payload.imm_ass_rej)) {
313 for (i := 0; i < sizeof(rr.payload.imm_ass_rej.payload); i := i + 1) {
314 ra := bit2oct(rr.payload.imm_ass_rej.payload[i].req_ref.ra);
315 fn := 23; //FIXME();
316 /* lookup client based on RA+time, deliver to client */
317 cid := f_cid_by_ra_fn(ra, fn);
318 if (cid != -1) {
319 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
320 /* delete ClientTable entry, as it failed */
321 f_cid_clear(cid);
322 }
323 }
324 }
325 }
326
327 [] IPA_PT.receive(tr_RSL(tr_RSL_PAGING_CMD(?, ?))) -> value rx_rsl {
328 log("PAGING IDENTITY ", rx_rsl.rsl.ies[2].body.other);
329 /* broadcast to all clients? */
330 for (i := 0; i < sizeof(ConnectionTable); i := i + 1) {
331 if (ispresent(ConnectionTable[i].comp_ref)) {
332 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[i].comp_ref;
333 }
334 }
335 }
336
337 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeT(?))) -> value rx_rsl {
338 log("Ingnoring TRX Mgmt ", rx_rsl.rsl);
339 }
340
341 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeC(?))) -> value rx_rsl {
342 log("Ignoring Common Channel Mgmt ", rx_rsl.rsl);
343 }
344
345 /* blindly acknowledge all channel activations */
346 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) -> value rx_rsl {
Harald Weltef70df652018-01-29 22:00:23 +0100347 chan_nr := rx_rsl.rsl.ies[0].body.chan_nr;
Harald Welte7ae019e2017-12-09 00:54:15 +0100348 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 +0100349 }
350
351 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeDR(?))) -> value rx_rsl {
352 /* dispatch to channel based on ChanId */
353 cid := f_cid_by_chan_nr(f_trx_by_streamId(rx_rsl.streamId),
354 rx_rsl.rsl.ies[0].body.chan_nr);
355 if (cid != -1) {
356 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
357 } else {
358 setverdict(fail, "RSL for unknown Dchan");
359 }
360 }
361
362 [] IPA_PT.receive {
363 setverdict(fail, "Received unknown primitive from IPA");
364 self.stop;
365 }
366
367 [] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
368 /* Store the knowledge that this sender has requested a certain RQ+time */
369 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100370 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,
Harald Welte714ded92017-12-08 14:00:22 +0100371 ts_RSL_CHAN_RQD(chan_rqd.ra, chan_rqd.fn)));
372 }
373
374 [] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
375 /* forward to BSC */
376 cid := f_cid_by_comp_ref(vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100377 IPA_PT.send(ts_ASP_RSL_UD(ConnectionTable[cid].stream_id, rx_rsl_msg));
Harald Welte714ded92017-12-08 14:00:22 +0100378 }
379
Harald Weltef70df652018-01-29 22:00:23 +0100380 /* explicit registration, e.g. in (non-immediate) assignment case */
381 [] RSL_PROC.getcall(RSLEM_register:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
382 f_cid_create_cnr(trx_nr, chan_nr, vc_conn);
383 RSL_PROC.reply(RSLEM_register:{trx_nr, chan_nr, vc_conn});
384 }
385
Harald Welte1909f462018-01-29 22:29:29 +0100386 [] RSL_PROC.getcall(RSLEM_unregister:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
387 cid := f_cid_by_chan_nr(trx_nr, chan_nr);
388 f_cid_clear(cid);
389 RSL_PROC.reply(RSLEM_unregister:{trx_nr, chan_nr, vc_conn});
390 }
391
392
Harald Welte714ded92017-12-08 14:00:22 +0100393 }
394 }
395}
396
Daniel Willmann17f970f2018-01-17 12:03:19 +0100397private function f_conn_table_init()
398runs on RSL_Emulation_CT {
399 var integer i;
400
401 /* Initialize the ConnectionTable */
402 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
403 f_cid_clear(i);
404 }
405}
Harald Welte714ded92017-12-08 14:00:22 +0100406
Harald Weltef70df652018-01-29 22:00:23 +0100407/* client/conn_hdlr side function to use procedure port to register stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100408function f_rslem_register(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
409runs on RSL_DchanHdlr {
410 PT.call(RSLEM_register:{trx_nr, chan_nr, self}) {
411 [] PT.getreply(RSLEM_register:{?,?,?}) {};
Harald Weltef70df652018-01-29 22:00:23 +0100412 }
413}
414
Harald Welte1909f462018-01-29 22:29:29 +0100415/* client/conn_hdlr side function to use procedure port to unregister stream_id/chan_nr */
Harald Welte421e4d42018-02-12 20:04:50 +0100416function f_rslem_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSLEM_PROC_PT PT := RSL_PROC)
417runs on RSL_DchanHdlr {
418 PT.call(RSLEM_unregister:{trx_nr, chan_nr, self}) {
419 [] PT.getreply(RSLEM_unregister:{?,?,?}) {};
Harald Welte1909f462018-01-29 22:29:29 +0100420 }
421}
422
423
Harald Welte714ded92017-12-08 14:00:22 +0100424}