blob: 229aff044601c63956632067e9b16064bf319377 [file] [log] [blame]
Harald Welted4ba7ff2017-07-17 21:00:48 +02001/* Test Port that stacks on top of L1CTL test port and performs LAPDm encoding/decoding, so the user can send
2 * and receive LAPDm frames in decoded TTCN-3 data types. This is particularly useful for sending/receiving
3 * all kinds of hand-crafted LAPDm frames for testing of the remote LAPDm layer */
4module LAPDm_RAW_PT {
5 import from GSM_Types all;
Harald Welteffcad682017-07-30 22:51:04 +02006 import from GSM_RR_Types all;
Harald Welted4ba7ff2017-07-17 21:00:48 +02007 import from Osmocom_Types all;
8 import from L1CTL_Types all;
9 import from L1CTL_PortType all;
10 import from LAPDm_Types all;
Harald Welte4b6c7722017-08-01 00:07:12 +020011 import from RLCMAC_Types all;
Harald Welted4ba7ff2017-07-17 21:00:48 +020012
13 /* request to tune to a given ARFCN and start BCCH decoding */
14 type record BCCH_tune_req {
15 Arfcn arfcn,
16 boolean combined_ccch
17 }
18
19 /* ask for a dedicated channel to be established */
20 type record DCCH_establish_req {
21 uint8_t ra
22 }
23
24 type record DCCH_establish_res {
25 ChannelDescription chan_desc optional,
26 charstring err optional
27 }
28
Harald Welte4b6c7722017-08-01 00:07:12 +020029 type record TBF_establish_res {
30 charstring err optional
31 }
32
Harald Welted4ba7ff2017-07-17 21:00:48 +020033 type record DCCH_release_req {
34 }
35
36 /* PH-DATA.ind / PH-DATA.req */
37 type record LAPDm_ph_data {
38 boolean sacch,
39 GsmSapi sapi,
40 LapdmFrame lapdm
41 }
42
Harald Welte4b6c7722017-08-01 00:07:12 +020043 type record TBF_establish_req {
44 uint8_t ra
45 }
46
47 /* PH-DATA.ind / PH-DATA.req */
48 type record RLCMAC_ph_data_ind {
49 GprsCodingScheme cs,
50 RlcmacDlBlock block
51 }
52 type record RLCMAC_ph_data_req {
53 uint8_t tbf_id,
54 GprsCodingScheme cs,
55 RlcmacUlBlock block
56 }
57
Harald Welted4ba7ff2017-07-17 21:00:48 +020058 /* port from our (internal) point of view */
59 type port LAPDm_SP_PT message {
60 in BCCH_tune_req,
61 DCCH_establish_req,
62 DCCH_release_req,
Harald Welte4b6c7722017-08-01 00:07:12 +020063 TBF_establish_req,
64 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +020065 LAPDm_ph_data;
66 out DCCH_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +020067 TBF_establish_res,
68 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +020069 LAPDm_ph_data;
70 } with {extension "internal"};
71
72 /* port from user (external) point of view */
73 type port LAPDm_PT message {
74 in DCCH_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +020075 TBF_establish_res,
76 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +020077 LAPDm_ph_data;
78 out BCCH_tune_req,
79 DCCH_establish_req,
80 DCCH_release_req,
Harald Welte4b6c7722017-08-01 00:07:12 +020081 TBF_establish_req,
82 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +020083 LAPDm_ph_data;
84 } with {extension "internal"};
85
86 function LAPDmStart() runs on lapdm_CT {
87 f_init();
88 ScanEvents();
89 }
90
91 /* TS 44.004 Figure 5.1 */
92 type enumerated ph_state_enum {
93 PH_STATE_NULL,
94 PH_STATE_BCH,
95 PH_STATE_SEARCHING_BCH,
96 PH_STATE_TUNING_DCH,
Harald Welte4b6c7722017-08-01 00:07:12 +020097 PH_STATE_DCH,
98 PH_STATE_TBF
Harald Welted4ba7ff2017-07-17 21:00:48 +020099 }
100
101 type component lapdm_CT {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200102
103 /* L1CTL port towards the bottom */
104 port L1CTL_PT L1CTL;
105 /* Port towards L2 */
106 port LAPDm_SP_PT LAPDM_SP;
107
108 /* physical layer state */
109 var ph_state_enum ph_state := PH_STATE_NULL;
110
111 /* channel description of the currently active DCH */
112 var ChannelDescription chan_desc;
113 };
114
115 /* wrapper function to log state transitions */
116 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
117 log("PH-STATE ", ph_state, " -> ", new_state);
118 ph_state := new_state;
119 }
120
121 private function f_init() runs on lapdm_CT {
Harald Welteb26cfff2017-08-25 09:56:47 +0200122 f_connect_reset(L1CTL);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200123 set_ph_state(PH_STATE_NULL);
124 }
125
126 /* release the dedicated radio channel */
127 private function f_release_dcch() runs on lapdm_CT {
128 L1CTL.send(t_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
Harald Welte4b6c7722017-08-01 00:07:12 +0200129 set_ph_state(PH_STATE_BCH);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200130 }
131
132 /* tune to given ARFCN and start BCCH/CCCH decoding */
133 private function f_tune_bcch(Arfcn arfcn, boolean combined) runs on lapdm_CT {
134 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
135 if (combined) {
136 mode := CCCH_MODE_COMBINED;
137 }
138
139 if (ph_state == PH_STATE_DCH) {
140 /* release any previous DCH */
141 f_release_dcch();
Harald Welte4b6c7722017-08-01 00:07:12 +0200142 } else if (ph_state == PH_STATE_TBF) {
143 f_release_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200144 }
145
146 set_ph_state(PH_STATE_SEARCHING_BCH);
147
148 /* send FB/SB req to sync to cell */
149 f_L1CTL_FBSB(L1CTL, arfcn, mode);
150 set_ph_state(PH_STATE_BCH);
151 }
152
153 /* master function establishing a dedicated radio channel */
154 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
155 var ImmediateAssignment imm_ass;
156 var GsmFrameNumber rach_fn;
157
158 /* send RACH request and obtain FN at which it was sent */
159 rach_fn := f_L1CTL_RACH(L1CTL, ra);
160 //if (not rach_fn) { return; }
161
162 /* wait for receiving matching IMM ASS */
163 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
164 //if (not imm_ass) { return; }
165 set_ph_state(PH_STATE_TUNING_DCH);
166
167 /* store/save channel description */
168 chan_desc := imm_ass.chan_desc;
169
170 /* send DM_EST_REQ */
171 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
172 set_ph_state(PH_STATE_DCH);
173 }
174
Harald Welte4b6c7722017-08-01 00:07:12 +0200175 /* initialize a tfi_usf array with "not used" value 255 for all TN */
176 function f_TfiUsfArrInit() return TfiUsfArr {
177 var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
178 return tua;
179 }
180
181 /* set TFI/USF value for one given timeslot number (index) */
182 function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
183 a[idx] := tfi_usf;
184 }
185
186 /* Match an IMM.ASS for an Uplink TBF with a dynamic allocation */
187 template ImmediateAssignment t_IMM_ASS_TBF_UL_DYN(uint8_t ra, GsmFrameNumber fn) modifies t_IMM_ASS := {
188 ded_or_tbf := { spare := ?, tma := ?, downlink := false, tbf := true},
189 chan_desc := omit,
190 pkt_chan_desc := ?,
191 rest_octets := {
192 presence := '11'B,
193 ll := omit,
194 lh := omit,
195 hl := omit,
196 hh := {
197 presence := '00'B,
198 ul := {
199 presence := '1'B,
200 dynamic := {
201 tfi_assignment := ?,
202 polling := ?,
203 spare := '0'B,
204 usf := ?,
205 usf_granularity := ?,
206 p0_present := ?,
207 p0 := *,
208 pr_mode := *,
209 ch_coding_cmd := ?,
210 tlli_block_chan_coding:= ?,
211 alpha_present := ?,
212 alpha := *,
213 gamma := ?,
214 ta_index_present := ?,
215 ta_index := *,
216 tbf_starting_time_present := ?,
217 tbf_starting_time := *
218 },
219 single := omit
220 },
221 dl := omit
222 }
223 }
224 };
225
226 private function f_establish_tbf(uint8_t ra) runs on lapdm_CT {
227 var ImmediateAssignment imm_ass;
228 var GsmFrameNumber rach_fn;
229 var TfiUsfArr tua := f_TfiUsfArrInit();
230
231 /* send RACH request and obtain FN at which it was sent */
232 rach_fn := f_L1CTL_RACH(L1CTL, ra);
233
234 /* wait for receiving matching IMM ASS */
235 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
236
237 if (match(imm_ass, t_IMM_ASS_TBF_UL_DYN(ra, rach_fn))) {
238 set_ph_state(PH_STATE_TBF);
239
240 /* store/save channel description */
241 //chan_desc := imm_ass.chan_desc;
242
243 /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
244 f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, imm_ass.rest_octets.hh.ul.dynamic.usf);
245 f_L1CTL_TBF_CFG(L1CTL, true, tua);
246 } else {
247 /* FIXME: single block uplink allocation */
248 log("Failed to match ", t_IMM_ASS_TBF_UL_DYN(ra, rach_fn));
249 log("Non-dynamic UL TBF assignment not supported yet");
250 }
251 }
252
253 private function f_release_tbf() runs on lapdm_CT {
254 var TfiUsfArr tua := f_TfiUsfArrInit();
255 /* send "all timeslots unused" for both UL and DL */
256 f_L1CTL_TBF_CFG(L1CTL, true, tua);
257 f_L1CTL_TBF_CFG(L1CTL, false, tua);
258 /* L1 will then fall back to BCCH/CCCH */
259 set_ph_state(PH_STATE_BCH);
260 }
261
Harald Welted4ba7ff2017-07-17 21:00:48 +0200262 function ScanEvents() runs on lapdm_CT {
263 var L1ctlDlMessage dl;
264 var BCCH_tune_req bt;
265 var LAPDm_ph_data lpd;
Harald Welte4b6c7722017-08-01 00:07:12 +0200266 var RLCMAC_ph_data_ind rpdi;
267 var RLCMAC_ph_data_req rpdr;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200268 var DCCH_establish_req est_req;
269 var DCCH_establish_res est_res;
Harald Welte4b6c7722017-08-01 00:07:12 +0200270 var TBF_establish_req tbf_req;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200271
272 while (true) {
273 if (ph_state == PH_STATE_NULL) {
274 alt {
275 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
276 f_tune_bcch(bt.arfcn, bt.combined_ccch);
277 }
278
279 [] LAPDM_SP.receive {}
280 [] L1CTL.receive {}
281
282 }
283 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
284 alt {
285 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
286 f_tune_bcch(bt.arfcn, bt.combined_ccch);
287 }
288
289 /* forward CCCH SAPI from L1CTL to User */
290 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
291 lpd.sacch := false;
292 lpd.sapi := 0;
293 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
294 LAPDM_SP.send(lpd);
295 }
296
297 /* forward BCCH SAPI from L1CTL to User */
298 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
299 lpd.sacch := false;
300 lpd.sapi := 0;
301 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
302 LAPDM_SP.send(lpd);
303 }
304
305 /* Establish dedicated channel */
306 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
307 var DCCH_establish_res res;
308 f_establish_dcch(est_req.ra);
309 if (ph_state == PH_STATE_DCH) {
310 res := { chan_desc, omit };
311 } else {
312 res := { omit, "Unable to esetablish DCCH" };
313 }
314 LAPDM_SP.send(res);
315 }
316
Harald Welte4b6c7722017-08-01 00:07:12 +0200317 /* Establish TBF / packet transfer mode */
318 [] LAPDM_SP.receive(TBF_establish_req:?) -> value tbf_req {
319 var TBF_establish_res res;
320 f_establish_tbf(tbf_req.ra);
321 if (ph_state == PH_STATE_TBF) {
322 res := { err := omit };
323 } else {
324 res := { err := "Unable to establish TBF" };
325 }
326 LAPDM_SP.send(res);
327 }
328
Harald Welted4ba7ff2017-07-17 21:00:48 +0200329 [] LAPDM_SP.receive {}
330 [] L1CTL.receive {}
331
332 }
333
334 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
335 alt {
336
337 /* decode any received DATA frames for the dedicated channel and pass them up */
338 [] L1CTL.receive(t_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
339 if (dl.dl_info.link_id.c == SACCH) {
340 lpd.sacch := true;
341 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
342 } else {
343 lpd.sacch := false;
344 }
345 lpd.sapi := dl.dl_info.link_id.sapi;
346 lpd.lapdm.b := dec_LapdmFrameB(dl.payload.data_ind.payload);
347 LAPDM_SP.send(lpd);
348 }
349
350 /* encode any LAPDm record from user and pass it on to L1CTL */
351 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
352 var octetstring buf;
353 var RslLinkId link_id;
354 if (lpd.sacch) {
355 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
356 } else {
357 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
358 }
359 buf := enc_LapdmFrame(lpd.lapdm);
360 L1CTL.send(t_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
361 }
362
363 /* Release dedicated channel */
364 [] LAPDM_SP.receive(DCCH_release_req:?) {
365 /* go back to BCCH */
366 f_release_dcch();
367 }
368
369 [] LAPDM_SP.receive {}
370 [] L1CTL.receive {}
371
372
373 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200374 } else if (ph_state == PH_STATE_TBF) {
375 alt {
376
377 /* decode + forward any blocks from L1 to L23*/
378 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
379 rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
380 rpdi.cs := CS1; /* FIXME */
381 log("RPDI: ", rpdi);
382 LAPDM_SP.send(rpdi);
383 }
384
385 [] L1CTL.receive { }
386
387 /* encode + forward any blocks from L23 to L1 */
388 [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
389 var octetstring buf;
390
391 buf := enc_RlcmacUlBlock(rpdr.block);
392 L1CTL.send(t_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.tbf_id));
393 }
394
395 /* FIXME: release TBF mode */
396 [] LAPDM_SP.receive(DCCH_release_req:?) {
397 /* go back to BCCH */
398 f_release_tbf();
399 }
400
401 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200402 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200403
Harald Welted4ba7ff2017-07-17 21:00:48 +0200404 } /* while (1) */
405 }
406}