blob: 06f5590d680451ab28a7faa552f657120d4e3b85 [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 {
102 var charstring l1ctl_sock_path := "/tmp/osmocom_l2";
103
104 /* L1CTL port towards the bottom */
105 port L1CTL_PT L1CTL;
106 /* Port towards L2 */
107 port LAPDm_SP_PT LAPDM_SP;
108
109 /* physical layer state */
110 var ph_state_enum ph_state := PH_STATE_NULL;
111
112 /* channel description of the currently active DCH */
113 var ChannelDescription chan_desc;
114 };
115
116 /* wrapper function to log state transitions */
117 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
118 log("PH-STATE ", ph_state, " -> ", new_state);
119 ph_state := new_state;
120 }
121
122 private function f_init() runs on lapdm_CT {
Harald Welted1209a62017-07-29 12:55:06 +0200123 f_connect_reset(L1CTL, l1ctl_sock_path);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200124 set_ph_state(PH_STATE_NULL);
125 }
126
127 /* release the dedicated radio channel */
128 private function f_release_dcch() runs on lapdm_CT {
129 L1CTL.send(t_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
Harald Welte4b6c7722017-08-01 00:07:12 +0200130 set_ph_state(PH_STATE_BCH);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200131 }
132
133 /* tune to given ARFCN and start BCCH/CCCH decoding */
134 private function f_tune_bcch(Arfcn arfcn, boolean combined) runs on lapdm_CT {
135 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
136 if (combined) {
137 mode := CCCH_MODE_COMBINED;
138 }
139
140 if (ph_state == PH_STATE_DCH) {
141 /* release any previous DCH */
142 f_release_dcch();
Harald Welte4b6c7722017-08-01 00:07:12 +0200143 } else if (ph_state == PH_STATE_TBF) {
144 f_release_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200145 }
146
147 set_ph_state(PH_STATE_SEARCHING_BCH);
148
149 /* send FB/SB req to sync to cell */
150 f_L1CTL_FBSB(L1CTL, arfcn, mode);
151 set_ph_state(PH_STATE_BCH);
152 }
153
154 /* master function establishing a dedicated radio channel */
155 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
156 var ImmediateAssignment imm_ass;
157 var GsmFrameNumber rach_fn;
158
159 /* send RACH request and obtain FN at which it was sent */
160 rach_fn := f_L1CTL_RACH(L1CTL, ra);
161 //if (not rach_fn) { return; }
162
163 /* wait for receiving matching IMM ASS */
164 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
165 //if (not imm_ass) { return; }
166 set_ph_state(PH_STATE_TUNING_DCH);
167
168 /* store/save channel description */
169 chan_desc := imm_ass.chan_desc;
170
171 /* send DM_EST_REQ */
172 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
173 set_ph_state(PH_STATE_DCH);
174 }
175
Harald Welte4b6c7722017-08-01 00:07:12 +0200176 /* initialize a tfi_usf array with "not used" value 255 for all TN */
177 function f_TfiUsfArrInit() return TfiUsfArr {
178 var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
179 return tua;
180 }
181
182 /* set TFI/USF value for one given timeslot number (index) */
183 function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
184 a[idx] := tfi_usf;
185 }
186
187 /* Match an IMM.ASS for an Uplink TBF with a dynamic allocation */
188 template ImmediateAssignment t_IMM_ASS_TBF_UL_DYN(uint8_t ra, GsmFrameNumber fn) modifies t_IMM_ASS := {
189 ded_or_tbf := { spare := ?, tma := ?, downlink := false, tbf := true},
190 chan_desc := omit,
191 pkt_chan_desc := ?,
192 rest_octets := {
193 presence := '11'B,
194 ll := omit,
195 lh := omit,
196 hl := omit,
197 hh := {
198 presence := '00'B,
199 ul := {
200 presence := '1'B,
201 dynamic := {
202 tfi_assignment := ?,
203 polling := ?,
204 spare := '0'B,
205 usf := ?,
206 usf_granularity := ?,
207 p0_present := ?,
208 p0 := *,
209 pr_mode := *,
210 ch_coding_cmd := ?,
211 tlli_block_chan_coding:= ?,
212 alpha_present := ?,
213 alpha := *,
214 gamma := ?,
215 ta_index_present := ?,
216 ta_index := *,
217 tbf_starting_time_present := ?,
218 tbf_starting_time := *
219 },
220 single := omit
221 },
222 dl := omit
223 }
224 }
225 };
226
227 private function f_establish_tbf(uint8_t ra) runs on lapdm_CT {
228 var ImmediateAssignment imm_ass;
229 var GsmFrameNumber rach_fn;
230 var TfiUsfArr tua := f_TfiUsfArrInit();
231
232 /* send RACH request and obtain FN at which it was sent */
233 rach_fn := f_L1CTL_RACH(L1CTL, ra);
234
235 /* wait for receiving matching IMM ASS */
236 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
237
238 if (match(imm_ass, t_IMM_ASS_TBF_UL_DYN(ra, rach_fn))) {
239 set_ph_state(PH_STATE_TBF);
240
241 /* store/save channel description */
242 //chan_desc := imm_ass.chan_desc;
243
244 /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
245 f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, imm_ass.rest_octets.hh.ul.dynamic.usf);
246 f_L1CTL_TBF_CFG(L1CTL, true, tua);
247 } else {
248 /* FIXME: single block uplink allocation */
249 log("Failed to match ", t_IMM_ASS_TBF_UL_DYN(ra, rach_fn));
250 log("Non-dynamic UL TBF assignment not supported yet");
251 }
252 }
253
254 private function f_release_tbf() runs on lapdm_CT {
255 var TfiUsfArr tua := f_TfiUsfArrInit();
256 /* send "all timeslots unused" for both UL and DL */
257 f_L1CTL_TBF_CFG(L1CTL, true, tua);
258 f_L1CTL_TBF_CFG(L1CTL, false, tua);
259 /* L1 will then fall back to BCCH/CCCH */
260 set_ph_state(PH_STATE_BCH);
261 }
262
Harald Welted4ba7ff2017-07-17 21:00:48 +0200263 function ScanEvents() runs on lapdm_CT {
264 var L1ctlDlMessage dl;
265 var BCCH_tune_req bt;
266 var LAPDm_ph_data lpd;
Harald Welte4b6c7722017-08-01 00:07:12 +0200267 var RLCMAC_ph_data_ind rpdi;
268 var RLCMAC_ph_data_req rpdr;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200269 var DCCH_establish_req est_req;
270 var DCCH_establish_res est_res;
Harald Welte4b6c7722017-08-01 00:07:12 +0200271 var TBF_establish_req tbf_req;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200272
273 while (true) {
274 if (ph_state == PH_STATE_NULL) {
275 alt {
276 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
277 f_tune_bcch(bt.arfcn, bt.combined_ccch);
278 }
279
280 [] LAPDM_SP.receive {}
281 [] L1CTL.receive {}
282
283 }
284 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
285 alt {
286 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
287 f_tune_bcch(bt.arfcn, bt.combined_ccch);
288 }
289
290 /* forward CCCH SAPI from L1CTL to User */
291 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
292 lpd.sacch := false;
293 lpd.sapi := 0;
294 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
295 LAPDM_SP.send(lpd);
296 }
297
298 /* forward BCCH SAPI from L1CTL to User */
299 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
300 lpd.sacch := false;
301 lpd.sapi := 0;
302 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
303 LAPDM_SP.send(lpd);
304 }
305
306 /* Establish dedicated channel */
307 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
308 var DCCH_establish_res res;
309 f_establish_dcch(est_req.ra);
310 if (ph_state == PH_STATE_DCH) {
311 res := { chan_desc, omit };
312 } else {
313 res := { omit, "Unable to esetablish DCCH" };
314 }
315 LAPDM_SP.send(res);
316 }
317
Harald Welte4b6c7722017-08-01 00:07:12 +0200318 /* Establish TBF / packet transfer mode */
319 [] LAPDM_SP.receive(TBF_establish_req:?) -> value tbf_req {
320 var TBF_establish_res res;
321 f_establish_tbf(tbf_req.ra);
322 if (ph_state == PH_STATE_TBF) {
323 res := { err := omit };
324 } else {
325 res := { err := "Unable to establish TBF" };
326 }
327 LAPDM_SP.send(res);
328 }
329
Harald Welted4ba7ff2017-07-17 21:00:48 +0200330 [] LAPDM_SP.receive {}
331 [] L1CTL.receive {}
332
333 }
334
335 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
336 alt {
337
338 /* decode any received DATA frames for the dedicated channel and pass them up */
339 [] L1CTL.receive(t_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
340 if (dl.dl_info.link_id.c == SACCH) {
341 lpd.sacch := true;
342 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
343 } else {
344 lpd.sacch := false;
345 }
346 lpd.sapi := dl.dl_info.link_id.sapi;
347 lpd.lapdm.b := dec_LapdmFrameB(dl.payload.data_ind.payload);
348 LAPDM_SP.send(lpd);
349 }
350
351 /* encode any LAPDm record from user and pass it on to L1CTL */
352 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
353 var octetstring buf;
354 var RslLinkId link_id;
355 if (lpd.sacch) {
356 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
357 } else {
358 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
359 }
360 buf := enc_LapdmFrame(lpd.lapdm);
361 L1CTL.send(t_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
362 }
363
364 /* Release dedicated channel */
365 [] LAPDM_SP.receive(DCCH_release_req:?) {
366 /* go back to BCCH */
367 f_release_dcch();
368 }
369
370 [] LAPDM_SP.receive {}
371 [] L1CTL.receive {}
372
373
374 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200375 } else if (ph_state == PH_STATE_TBF) {
376 alt {
377
378 /* decode + forward any blocks from L1 to L23*/
379 [] L1CTL.receive(t_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
380 rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
381 rpdi.cs := CS1; /* FIXME */
382 log("RPDI: ", rpdi);
383 LAPDM_SP.send(rpdi);
384 }
385
386 [] L1CTL.receive { }
387
388 /* encode + forward any blocks from L23 to L1 */
389 [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
390 var octetstring buf;
391
392 buf := enc_RlcmacUlBlock(rpdr.block);
393 L1CTL.send(t_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.tbf_id));
394 }
395
396 /* FIXME: release TBF mode */
397 [] LAPDM_SP.receive(DCCH_release_req:?) {
398 /* go back to BCCH */
399 f_release_tbf();
400 }
401
402 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200403 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200404
Harald Welted4ba7ff2017-07-17 21:00:48 +0200405 } /* while (1) */
406 }
407}