blob: 7c5a1681a40a180a37ad828159a76990c433d68f [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;
34 var RslChannelNr g_chan_nr;
35};
36
37type record RSLDC_ChanRqd {
38 OCT1 ra,
39 GsmFrameNumber fn
40};
41
42template RSLDC_ChanRqd ts_RSLDC_ChanRqd(OCT1 ra, GsmFrameNumber fn) := {
43 ra := ra,
44 fn := fn
45}
46
47type port RSL_DCHAN_PT message {
48 inout RSLDC_ChanRqd, RSL_Message;
49} with { extension "internal" };
50
51/***********************************************************************
52 * Client Component for a single dedicated channel
53 ***********************************************************************/
54
55private function f_rx_or_fail(template RSL_Message exp_rx) runs on RSL_DchanHdlr return RSL_Message
56{
57 var RSL_Message rx_rsl;
58 timer T := 10.0;
59
60 /* request a channel to be established */
61 T.start;
62 alt {
63 [] RSL.receive(exp_rx) -> value rx_rsl {
64 T.stop;
65 return rx_rsl;
66 }
67 [] RSL.receive {
68 setverdict(fail, "Unexpected RSL message on DCHAN");
69 self.stop;
70 }
71 [] T.timeout {
72 setverdict(fail, "Timeout waiting for RSL on DCHAN");
73 self.stop;
74 }
75 }
76 /* never reached */
77 return rx_rsl;
78}
79
80/* establish a dedicated channel using 'ra' */
81function f_chan_est(OCT1 ra, octetstring est_l3, template RslLinkId link_id, GsmFrameNumber fn := 23)
82runs on RSL_DchanHdlr {
83 var RSL_Message rx_rsl;
84 var GsmRrMessage rr;
85
86 /* request a channel to be established */
87 RSL.send(ts_RSLDC_ChanRqd(ra, fn));
88 /* expect immediate assignment */
89 rx_rsl := f_rx_or_fail(tr_RSL_IMM_ASSIGN);
90 rr := dec_GsmRrMessage(rx_rsl.ies[1].body.full_imm_ass_info.payload);
91 g_chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
92 RSL.send(ts_RSL_EST_IND(g_chan_nr, valueof(link_id), est_l3));
93}
94
95function f_deact_chan(RSL_Cause cause) runs on RSL_DchanHdlr
96{
97 var RSL_Message rx_rsl;
98
99 RSL.send(ts_RSL_CONN_FAIL_IND(g_chan_nr, cause));
100 rx_rsl := f_rx_or_fail(tr_RSL_MsgTypeD(RSL_MT_RF_CHAN_REL));
101 /* FIXME RSL.send(ts_RSL_RF_CHAN_REL_ACK()) */
102}
103
104
105
106/***********************************************************************
107 * Main Component
108 ***********************************************************************/
109
110private type record ConnectionData {
111 /* component reference to the client component */
112 RSL_DchanHdlr comp_ref,
113 /* RSL (dedicated) Channel number we're handling */
114 uint8_t trx_nr optional,
115 IpaStreamId stream_id optional,
116 RslChannelNr chan_nr optional,
117 /* Random Reference */
118 OCT1 ra optional,
119 GsmFrameNumber ra_fn optional
120};
121
122private function f_cid_by_comp_ref(RSL_DchanHdlr comp_ref)
123runs on RSL_Emulation_CT return integer {
124 var integer i;
125 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
126 if (ispresent(ConnectionTable[i].comp_ref) and
127 ConnectionTable[i].comp_ref == comp_ref) {
128 return i;
129 }
130 }
131 log("No Dchan handler for ", comp_ref);
132 return -1;
133}
134
135private function f_cid_by_chan_nr(uint8_t trx_nr, RslChannelNr chan_nr)
136runs on RSL_Emulation_CT return integer {
137 var integer i;
138 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
139 if (ispresent(ConnectionTable[i].chan_nr) and
140 ConnectionTable[i].chan_nr == chan_nr and ConnectionTable[i].trx_nr == trx_nr) {
141 return i;
142 }
143 }
144 log("No Dchan handler for ", trx_nr, chan_nr);
145 return -1;
146}
147
148private function f_cid_by_ra_fn(OCT1 ra, GsmFrameNumber fn)
149runs on RSL_Emulation_CT return integer {
150 var integer i;
151 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
152 if (ispresent(ConnectionTable[i].ra) and
153 ConnectionTable[i].ra == ra and ConnectionTable[i].ra_fn == fn) {
154 return i;
155 }
156 }
157 log("No Dchan handler for ", ra, fn);
158 return -1;
159}
160
161/* create an ew client with given RA and FN */
162private function f_cid_create(OCT1 ra, GsmFrameNumber fn, RSL_DchanHdlr comp_ref)
163runs on RSL_Emulation_CT return integer {
164 var integer i;
165 for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
166 if (not ispresent(ConnectionTable[i].ra)) {
167 ConnectionTable[i].ra := ra;
168 ConnectionTable[i].ra_fn := fn;
169 ConnectionTable[i].comp_ref := comp_ref;
170 return i;
171 }
172 }
173 log("No free entry in conn table for ", ra, fn);
174 return -1;
175}
176
177private function f_cid_clear(integer cid)
178runs on RSL_Emulation_CT {
179 ConnectionTable[cid].ra := omit;
180 ConnectionTable[cid].ra_fn := omit;
181 ConnectionTable[cid].ra_fn := omit;
182 ConnectionTable[cid].trx_nr := omit;
183 ConnectionTable[cid].stream_id := omit;
184 ConnectionTable[cid].chan_nr := omit;
185}
186
187type component RSL_Emulation_CT {
188 /* port facing down towards IPA emulation */
189 port IPA_RSL_PT IPA_PT;
190 /* port facing up towards dedicated channel handler */
191 port RSL_DCHAN_PT CLIENT_PT;
192
193 /* state of all concurrent connections / dedicated channels */
194 var ConnectionData ConnectionTable[64];
195}
196
197
198/* template for an ASP_RSL_Unitdata as we receive it from the IPA_Emulateion component */
199private template ASP_RSL_Unitdata tr_RSL(template RSL_Message rsl, template IpaStreamId sid := ?) := {
200 streamId := sid,
201 rsl := rsl
202}
203
Harald Welte714ded92017-12-08 14:00:22 +0100204private function f_trx_by_streamId(IpaStreamId id) return integer {
205 return enum2int(id);
206}
207
208
209function main() runs on RSL_Emulation_CT {
210 var ASP_RSL_Unitdata rx_rsl;
211 var RSL_Message rx_rsl_msg;
212 var RSLDC_ChanRqd chan_rqd;
213 var RSL_DchanHdlr vc_conn;
214 var integer cid;
215 var integer i;
216
217 while (true) {
218 alt {
Harald Welte624f9632017-12-16 19:26:04 +0100219 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) {
220 }
Harald Welte714ded92017-12-08 14:00:22 +0100221 [] IPA_PT.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) {
Harald Welte7ae019e2017-12-09 00:54:15 +0100222 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,ts_RSL_PAGING_LOAD_IND(23)));
Harald Welte714ded92017-12-08 14:00:22 +0100223 }
224 [] IPA_PT.receive(tr_RSL(tr_RSL_IMM_ASSIGN)) -> value rx_rsl {
225 var GsmRrMessage rr;
226 var OCT1 ra;
227 var GsmFrameNumber fn;
228 log("IMM ASS INFO ", rx_rsl.rsl.ies[1].body);
229 rr := dec_GsmRrMessage(rx_rsl.rsl.ies[1].body.full_imm_ass_info.payload);
230 if (ischosen(rr.payload.imm_ass)) {
231 ra := bit2oct(rr.payload.imm_ass.req_ref.ra);
232 fn := 23; //FIXME(rr.payload.imm_ass);
233 /* lookup client based on RA+time, deliver to client */
234 cid := f_cid_by_ra_fn(ra, fn);
235 if (cid == -1) {
236 setverdict(fail, "IMM ASS for unknown DChan");
237 }
238 /* update client with trx_nr */
239 ConnectionTable[cid].trx_nr := f_trx_by_streamId(rx_rsl.streamId);
240 ConnectionTable[cid].stream_id := rx_rsl.streamId;
241 /* update client with chan_nr */
242 ConnectionTable[cid].chan_nr := rr.payload.imm_ass.chan_desc.chan_nr;
243 /* TODO: add timer to time-out ConnectionTable entries which
244 * never get followed-up to */
245 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
246 } else if (ischosen(rr.payload.imm_ass_rej)) {
247 for (i := 0; i < sizeof(rr.payload.imm_ass_rej.payload); i := i + 1) {
248 ra := bit2oct(rr.payload.imm_ass_rej.payload[i].req_ref.ra);
249 fn := 23; //FIXME();
250 /* lookup client based on RA+time, deliver to client */
251 cid := f_cid_by_ra_fn(ra, fn);
252 if (cid != -1) {
253 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
254 /* delete ClientTable entry, as it failed */
255 f_cid_clear(cid);
256 }
257 }
258 }
259 }
260
261 [] IPA_PT.receive(tr_RSL(tr_RSL_PAGING_CMD(?, ?))) -> value rx_rsl {
262 log("PAGING IDENTITY ", rx_rsl.rsl.ies[2].body.other);
263 /* broadcast to all clients? */
264 for (i := 0; i < sizeof(ConnectionTable); i := i + 1) {
265 if (ispresent(ConnectionTable[i].comp_ref)) {
266 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[i].comp_ref;
267 }
268 }
269 }
270
271 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeT(?))) -> value rx_rsl {
272 log("Ingnoring TRX Mgmt ", rx_rsl.rsl);
273 }
274
275 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeC(?))) -> value rx_rsl {
276 log("Ignoring Common Channel Mgmt ", rx_rsl.rsl);
277 }
278
279 /* blindly acknowledge all channel activations */
280 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeD(RSL_MT_CHAN_ACTIV))) -> value rx_rsl {
281 var RslChannelNr chan_nr := rx_rsl.rsl.ies[0].body.chan_nr;
Harald Welte7ae019e2017-12-09 00:54:15 +0100282 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 +0100283 }
284
285 [] IPA_PT.receive(tr_RSL(tr_RSL_MsgTypeDR(?))) -> value rx_rsl {
286 /* dispatch to channel based on ChanId */
287 cid := f_cid_by_chan_nr(f_trx_by_streamId(rx_rsl.streamId),
288 rx_rsl.rsl.ies[0].body.chan_nr);
289 if (cid != -1) {
290 CLIENT_PT.send(rx_rsl.rsl) to ConnectionTable[cid].comp_ref;
291 } else {
292 setverdict(fail, "RSL for unknown Dchan");
293 }
294 }
295
296 [] IPA_PT.receive {
297 setverdict(fail, "Received unknown primitive from IPA");
298 self.stop;
299 }
300
301 [] CLIENT_PT.receive(RSLDC_ChanRqd:?) -> value chan_rqd sender vc_conn {
302 /* Store the knowledge that this sender has requested a certain RQ+time */
303 f_cid_create(chan_rqd.ra, chan_rqd.fn, vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100304 IPA_PT.send(ts_ASP_RSL_UD(IPAC_PROTO_RSL_TRX0,
Harald Welte714ded92017-12-08 14:00:22 +0100305 ts_RSL_CHAN_RQD(chan_rqd.ra, chan_rqd.fn)));
306 }
307
308 [] CLIENT_PT.receive(tr_RSL_MsgType(?)) -> value rx_rsl_msg sender vc_conn {
309 /* forward to BSC */
310 cid := f_cid_by_comp_ref(vc_conn);
Harald Welte7ae019e2017-12-09 00:54:15 +0100311 IPA_PT.send(ts_ASP_RSL_UD(ConnectionTable[cid].stream_id, rx_rsl_msg));
Harald Welte714ded92017-12-08 14:00:22 +0100312 }
313
314 }
315 }
316}
317
318
319}