blob: d20718f61f738132db1533290f387a74ab28e815 [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;
36};
37
38type record RSLDC_ChanRqd {
39 OCT1 ra,
40 GsmFrameNumber fn
41};
42
43template RSLDC_ChanRqd ts_RSLDC_ChanRqd(OCT1 ra, GsmFrameNumber fn) := {
44 ra := ra,
45 fn := fn
46}
47
48type port RSL_DCHAN_PT message {
49 inout RSLDC_ChanRqd, RSL_Message;
50} with { extension "internal" };
51
Harald Weltef70df652018-01-29 22:00:23 +010052signature RSLEM_register(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Welte1909f462018-01-29 22:29:29 +010053signature RSLEM_unregister(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr hdlr);
Harald Weltef70df652018-01-29 22:00:23 +010054
55type port RSLEM_PROC_PT procedure {
Harald Welte1909f462018-01-29 22:29:29 +010056 inout RSLEM_register, RSLEM_unregister;
Harald Weltef70df652018-01-29 22:00:23 +010057} with { extension "internal" };
58
Harald Welte714ded92017-12-08 14:00:22 +010059/***********************************************************************
60 * Client Component for a single dedicated channel
61 ***********************************************************************/
62
63private function f_rx_or_fail(template RSL_Message exp_rx) runs on RSL_DchanHdlr return RSL_Message
64{
65 var RSL_Message rx_rsl;
66 timer T := 10.0;
67
68 /* request a channel to be established */
69 T.start;
70 alt {
71 [] RSL.receive(exp_rx) -> value rx_rsl {
72 T.stop;
73 return rx_rsl;
74 }
75 [] RSL.receive {
76 setverdict(fail, "Unexpected RSL message on DCHAN");
77 self.stop;
78 }
79 [] T.timeout {
80 setverdict(fail, "Timeout waiting for RSL on DCHAN");
81 self.stop;
82 }
83 }
84 /* never reached */
85 return rx_rsl;
86}
87
88/* establish a dedicated channel using 'ra' */
89function f_chan_est(OCT1 ra, octetstring est_l3, template RslLinkId link_id, GsmFrameNumber fn := 23)
90runs on RSL_DchanHdlr {
91 var RSL_Message rx_rsl;
92 var GsmRrMessage rr;
93
94 /* request a channel to be established */
95 RSL.send(ts_RSLDC_ChanRqd(ra, fn));
96 /* expect immediate assignment */
97 rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN);
98 rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload);
99 g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
100 RSL.send(ts_RSL_EST_IND(g_chan_nr, valueof(link_id), est_l3));
101}
102
103function f_deact_chan(RSL_Cause cause) runs on RSL_DchanHdlr
104{
105 var RSL_Message rx_rsl;
106
107 RSL.send(ts_RSL_CONN_FAIL_IND(g_chan_nr, cause));
108 rx_rsl := f_rx_or_fail(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL));
109 /* FIXME RSL.send(ts_RSL_RF_CHAN_REL_ACK()) */
110}
111
112
113
114/***********************************************************************
115 * Main Component
116 ***********************************************************************/
117
118private type record ConnectionData {
119 /* component reference to the client component */
120 RSL_DchanHdlr comp_ref,
121 /* RSL (dedicated) Channel number we're handling */
122 uint8_t trx_nr optional,
123 IpaStreamId stream_id optional,
124 RslChannelNr chan_nr optional,
125 /* Random Reference */
126 OCT1 ra optional,
127 GsmFrameNumber ra_fn optional
128};
129
130private function f_cid_by_comp_ref(RSL_DchanHdlr comp_ref)
131runs on RSL_Emulation_CT return integer {
132 var integer i;
133 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
134 if (ispresent(ConnectionTable[i].comp_ref) and
135 ConnectionTable[i].comp_ref == comp_ref) {
136 return i;
137 }
138 }
139 log("No Dchan handler for ", comp_ref);
140 return -1;
141}
142
143private function f_cid_by_chan_nr(uint8_t trx_nr, RslChannelNr chan_nr)
144runs on RSL_Emulation_CT return integer {
145 var integer i;
146 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
147 if (ispresent(ConnectionTable[i].chan_nr) and
148 ConnectionTable[i].chan_nr == chan_nr and ConnectionTable[i].trx_nr == trx_nr) {
149 return i;
150 }
151 }
152 log("No Dchan handler for ", trx_nr, chan_nr);
153 return -1;
154}
155
156private function f_cid_by_ra_fn(OCT1 ra, GsmFrameNumber fn)
157runs on RSL_Emulation_CT return integer {
158 var integer i;
159 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
160 if (ispresent(ConnectionTable[i].ra) and
161 ConnectionTable[i].ra == ra and ConnectionTable[i].ra_fn == fn) {
162 return i;
163 }
164 }
165 log("No Dchan handler for ", ra, fn);
166 return -1;
167}
168
169/* create an ew client with given RA and FN */
170private function f_cid_create(OCT1 ra, GsmFrameNumber fn, RSL_DchanHdlr comp_ref)
171runs on RSL_Emulation_CT return integer {
172 var integer i;
173 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
Harald Weltef70df652018-01-29 22:00:23 +0100174 if (not ispresent(ConnectionTable[i].ra) and
175 not ispresent(ConnectionTable[i].trx_nr)) {
Harald Welte714ded92017-12-08 14:00:22 +0100176 ConnectionTable[i].ra := ra;
177 ConnectionTable[i].ra_fn := fn;
178 ConnectionTable[i].comp_ref := comp_ref;
179 return i;
180 }
181 }
182 log("No free entry in conn table for ", ra, fn);
183 return -1;
184}
185
Harald Weltef70df652018-01-29 22:00:23 +0100186/* create an ew client with given RA and FN */
187private function f_cid_create_cnr(uint8_t trx_nr, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
188runs on RSL_Emulation_CT return integer {
189 var integer i;
190 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
191 if (not ispresent(ConnectionTable[i].ra) and
192 not ispresent(ConnectionTable[i].trx_nr)) {
193 ConnectionTable[i].stream_id := f_streamId_by_trx(trx_nr);
194 ConnectionTable[i].trx_nr := trx_nr;
195 ConnectionTable[i].chan_nr := chan_nr;
196 ConnectionTable[i].comp_ref := comp_ref;
197 return i;
198 }
199 }
200 log("No free entry in conn table for ", trx_nr, chan_nr, comp_ref);
201 return -1;
202}
203
204
205/* create an ew client with given RA and FN */
206private function f_cid_delete_cnr(IpaStreamId stream_id, RslChannelNr chan_nr, RSL_DchanHdlr comp_ref)
207runs on RSL_Emulation_CT return integer {
208 var integer i;
209 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
210 if (ConnectionTable[i].comp_ref == null) {
211 continue;
212 }
213 if (ConnectionTable[i].stream_id == stream_id and
214 ConnectionTable[i].chan_nr == chan_nr and
215 ConnectionTable[i].comp_ref == comp_ref) {
216 f_cid_clear(i);
217 }
218 }
219 log("Unable to find entry to delete for ", stream_id, chan_nr, comp_ref);
220 return -1;
221}
222
223
Harald Welte714ded92017-12-08 14:00:22 +0100224private function f_cid_clear(integer cid)
225runs on RSL_Emulation_CT {
226 ConnectionTable[cid].ra := omit;
227 ConnectionTable[cid].ra_fn := omit;
Harald Welte714ded92017-12-08 14:00:22 +0100228 ConnectionTable[cid].trx_nr := omit;
229 ConnectionTable[cid].stream_id := omit;
230 ConnectionTable[cid].chan_nr := omit;
Harald Weltef70df652018-01-29 22:00:23 +0100231 ConnectionTable[cid].comp_ref := null;
Harald Welte714ded92017-12-08 14:00:22 +0100232}
233
234type component RSL_Emulation_CT {
235 /* port facing down towards IPA emulation */
236 port IPA_RSL_PT IPA_PT;
237 /* port facing up towards dedicated channel handler */
238 port RSL_DCHAN_PT CLIENT_PT;
Harald Weltef70df652018-01-29 22:00:23 +0100239 port RSLEM_PROC_PT RSL_PROC;
Harald Welte714ded92017-12-08 14:00:22 +0100240
241 /* state of all concurrent connections / dedicated channels */
242 var ConnectionData ConnectionTable[64];
243}
244
245
246/* template for an ASP_RSL_Unitdata as we receive it from the IPA_Emulateion component */
247private template ASP_RSL_Unitdata tr_RSL(template RSL_Message rsl, template IpaStreamId sid := ?) := {
248 streamId := sid,
249 rsl := rsl
250}
251
Harald Welte714ded92017-12-08 14:00:22 +0100252private function f_trx_by_streamId(IpaStreamId id) return integer {
253 return enum2int(id);
254}
255
Harald Weltef70df652018-01-29 22:00:23 +0100256private function f_streamId_by_trx(uint8_t trx_nr) return IpaStreamId {
257 select (trx_nr) {
258 case (0) { return IPAC_PROTO_RSL_TRX0; }
259 case (1) { return IPAC_PROTO_RSL_TRX1; }
260 case (2) { return IPAC_PROTO_RSL_TRX2; }
261 case (3) { return IPAC_PROTO_RSL_TRX3; }
262 }
263 self.stop;
264}
265
Harald Welte714ded92017-12-08 14:00:22 +0100266
267function main() runs on RSL_Emulation_CT {
268 var ASP_RSL_Unitdata rx_rsl;
269 var RSL_Message rx_rsl_msg;
270 var RSLDC_ChanRqd chan_rqd;
271 var RSL_DchanHdlr vc_conn;
Harald Weltef70df652018-01-29 22:00:23 +0100272 var RslChannelNr chan_nr;
273 var uint8_t trx_nr;
Harald Welte714ded92017-12-08 14:00:22 +0100274 var integer cid;
275 var integer i;
276
Daniel Willmann17f970f2018-01-17 12:03:19 +0100277 f_conn_table_init();
278
Harald Welte714ded92017-12-08 14:00:22 +0100279 while (true) {
280 alt {
Harald Welte624f9632017-12-16 19:26:04 +0100281 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) {
282 }
Harald Welte714ded92017-12-08 14:00:22 +0100283 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) {
Harald Welte7ae019e2017-12-09 00:54:15 +0100284 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,ts_RSL_PAGING_LOAD_IND(23)));
Harald Welte714ded92017-12-08 14:00:22 +0100285 }
286 [] IPA_PT.receive(tr_RSL(tr_RSL_IMM_ASSIGN)) -> value rx_rsl {
287 var GsmRrMessage rr;
288 var OCT1 ra;
289 var GsmFrameNumber fn;
290 log("IMM ASS INFO ", rx_rsl.rsl.ies[1].body);
291 rr := dec_GsmRrMessage(rx_rsl.rsl.ies[1].body.full_imm_ass_info.payload);
292 if (ischosen(rr.payload.imm_ass)) {
293 ra := bit2oct(rr.payload.imm_ass.req_ref.ra);
294 fn := 23; //FIXME(rr.payload.imm_ass);
295 /* lookup client based on RA+time, deliver to client */
296 cid := f_cid_by_ra_fn(ra, fn);
297 if (cid == -1) {
298 setverdict(fail, "IMM ASS for unknown DChan");
Harald Weltef70df652018-01-29 22:00:23 +0100299 self.stop;
Harald Welte714ded92017-12-08 14:00:22 +0100300 }
301 /* update client with trx_nr */
302 ConnectionTable[cid].trx_nr := f_trx_by_streamId(rx_rsl.streamId);
303 ConnectionTable[cid].stream_id := rx_rsl.streamId;
304 /* update client with chan_nr */
305 ConnectionTable[cid].chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
306 /* TODO: add timer to time-out ConnectionTable entries which
307 * never get followed-up to */
308 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
309 } else if (ischosen(rr.payload.imm_ass_rej)) {
310 for (i := 0; i < sizeof(rr.payload.imm_ass_rej.payload); i := i + 1) {
311 ra := bit2oct(rr.payload.imm_ass_rej.payload[i].req_ref.ra);
312 fn := 23; //FIXME();
313 /* lookup client based on RA+time, deliver to client */
314 cid := f_cid_by_ra_fn(ra, fn);
315 if (cid != -1) {
316 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
317 /* delete ClientTable entry, as it failed */
318 f_cid_clear(cid);
319 }
320 }
321 }
322 }
323
324 [] IPA_PT.receive(tr_RSL(tr_RSL_PAGING_CMD(?, ?))) -> value rx_rsl {
325 log("PAGING IDENTITY ", rx_rsl.rsl.ies[2].body.other);
326 /* broadcast to all clients? */
327 for (i := 0; i < sizeof(ConnectionTable); i := i + 1) {
328 if (ispresent(ConnectionTable[i].comp_ref)) {
329 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[i].comp_ref;
330 }
331 }
332 }
333
334 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeT(?))) -> value rx_rsl {
335 log("Ingnoring TRX Mgmt ", rx_rsl.rsl);
336 }
337
338 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeC(?))) -> value rx_rsl {
339 log("Ignoring Common Channel Mgmt ", rx_rsl.rsl);
340 }
341
342 /* blindly acknowledge all channel activations */
343 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) -> value rx_rsl {
Harald Weltef70df652018-01-29 22:00:23 +0100344 chan_nr := rx_rsl.rsl.ies[0].body.chan_nr;
Harald Welte7ae019e2017-12-09 00:54:15 +0100345 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 +0100346 }
347
348 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeDR(?))) -> value rx_rsl {
349 /* dispatch to channel based on ChanId */
350 cid := f_cid_by_chan_nr(f_trx_by_streamId(rx_rsl.streamId),
351 rx_rsl.rsl.ies[0].body.chan_nr);
352 if (cid != -1) {
353 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
354 } else {
355 setverdict(fail, "RSL for unknown Dchan");
356 }
357 }
358
359 [] IPA_PT.receive {
360 setverdict(fail, "Received unknown primitive from IPA");
361 self.stop;
362 }
363
364 [] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
365 /* Store the knowledge that this sender has requested a certain RQ+time */
366 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100367 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,
Harald Welte714ded92017-12-08 14:00:22 +0100368 ts_RSL_CHAN_RQD(chan_rqd.ra, chan_rqd.fn)));
369 }
370
371 [] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
372 /* forward to BSC */
373 cid := f_cid_by_comp_ref(vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100374 IPA_PT.send(ts_ASP_RSL_UD(ConnectionTable[cid].stream_id, rx_rsl_msg));
Harald Welte714ded92017-12-08 14:00:22 +0100375 }
376
Harald Weltef70df652018-01-29 22:00:23 +0100377 /* explicit registration, e.g. in (non-immediate) assignment case */
378 [] RSL_PROC.getcall(RSLEM_register:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
379 f_cid_create_cnr(trx_nr, chan_nr, vc_conn);
380 RSL_PROC.reply(RSLEM_register:{trx_nr, chan_nr, vc_conn});
381 }
382
Harald Welte1909f462018-01-29 22:29:29 +0100383 [] RSL_PROC.getcall(RSLEM_unregister:{?,?,?}) -> param(trx_nr, chan_nr, vc_conn) {
384 cid := f_cid_by_chan_nr(trx_nr, chan_nr);
385 f_cid_clear(cid);
386 RSL_PROC.reply(RSLEM_unregister:{trx_nr, chan_nr, vc_conn});
387 }
388
389
Harald Welte714ded92017-12-08 14:00:22 +0100390 }
391 }
392}
393
Daniel Willmann17f970f2018-01-17 12:03:19 +0100394private function f_conn_table_init()
395runs on RSL_Emulation_CT {
396 var integer i;
397
398 /* Initialize the ConnectionTable */
399 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
400 f_cid_clear(i);
401 }
402}
Harald Welte714ded92017-12-08 14:00:22 +0100403
Harald Weltef70df652018-01-29 22:00:23 +0100404/* client/conn_hdlr side function to use procedure port to register stream_id/chan_nr */
405function f_rslem_register(uint8_t trx_nr, RslChannelNr chan_nr) runs on RSL_DchanHdlr {
406 RSL_PROC.call(RSLEM_register:{trx_nr, chan_nr, self}) {
407 [] RSL_PROC.getreply(RSLEM_register:{?,?,?}) {};
408 }
409}
410
Harald Welte1909f462018-01-29 22:29:29 +0100411/* client/conn_hdlr side function to use procedure port to unregister stream_id/chan_nr */
412function f_rslem_unregister(uint8_t trx_nr, RslChannelNr chan_nr) runs on RSL_DchanHdlr {
413 RSL_PROC.call(RSLEM_unregister:{trx_nr, chan_nr, self}) {
414 [] RSL_PROC.getreply(RSLEM_unregister:{?,?,?}) {};
415 }
416}
417
418
Harald Welte714ded92017-12-08 14:00:22 +0100419}