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