blob: 222c1aa011f80ee43efeca39699aff8829ec0164 [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 */
Harald Welte34b5a952019-05-27 11:54:11 +02004
5/* (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
6 * All rights reserved.
7 *
8 * Released under the terms of GNU General Public License, Version 2 or
9 * (at your option) any later version.
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
Harald Welted4ba7ff2017-07-17 21:00:48 +020014module LAPDm_RAW_PT {
15 import from GSM_Types all;
Harald Welteffcad682017-07-30 22:51:04 +020016 import from GSM_RR_Types all;
Harald Welted4ba7ff2017-07-17 21:00:48 +020017 import from Osmocom_Types all;
18 import from L1CTL_Types all;
19 import from L1CTL_PortType all;
20 import from LAPDm_Types all;
Harald Welte4b6c7722017-08-01 00:07:12 +020021 import from RLCMAC_Types all;
Harald Welted4ba7ff2017-07-17 21:00:48 +020022
23 /* request to tune to a given ARFCN and start BCCH decoding */
24 type record BCCH_tune_req {
25 Arfcn arfcn,
26 boolean combined_ccch
27 }
28
29 /* ask for a dedicated channel to be established */
30 type record DCCH_establish_req {
31 uint8_t ra
32 }
33
34 type record DCCH_establish_res {
35 ChannelDescription chan_desc optional,
36 charstring err optional
37 }
38
Harald Welte66f07542019-05-30 17:35:31 +020039 /* directly switch to a dedicated channel (without RACH/IMM.ASS */
40 type record DCCH_switch_req {
41 Arfcn arfcn,
42 RslChannelNr chan_nr,
43 GsmTsc tsc
44 }
45
46 type record DCCH_switch_res {
47 charstring err optional
48 }
49
Harald Welteb669ee02018-03-09 12:50:02 +010050 type record length(8) of uint8_t TfiList;
51 type record TbfPars {
52 GsmArfcn arfcn optional,
53 /* Temporary Flow Identifier for each TN */
54 TfiList tfi
55 }
56 type record length(8) of TbfPars TbfParsPerTs;
57
58 template TbfPars t_TbfParsInit := {
59 arfcn := omit,
60 tfi := { 255, 255, 255, 255, 255, 255, 255, 255 }
61 }
62
63 type record TBF_UL_establish_res {
64 TbfPars pars optional,
Harald Welte4b6c7722017-08-01 00:07:12 +020065 charstring err optional
66 }
67
Harald Welted4ba7ff2017-07-17 21:00:48 +020068 type record DCCH_release_req {
69 }
70
71 /* PH-DATA.ind / PH-DATA.req */
72 type record LAPDm_ph_data {
73 boolean sacch,
74 GsmSapi sapi,
75 LapdmFrame lapdm
76 }
77
Harald Welteb669ee02018-03-09 12:50:02 +010078 type integer TbfNr (0..7); /* maximum of 8 concurrent TBF per direction */
79 type record TBF_UL_establish_req {
80 TbfNr tbf_nr,
Harald Welte4b6c7722017-08-01 00:07:12 +020081 uint8_t ra
82 }
83
Harald Welteb669ee02018-03-09 12:50:02 +010084 type record TBF_DL_establish_req {
85 TbfNr tbf_nr,
86 TbfPars pars
87 }
88
Harald Welte4b6c7722017-08-01 00:07:12 +020089 /* PH-DATA.ind / PH-DATA.req */
90 type record RLCMAC_ph_data_ind {
91 GprsCodingScheme cs,
Harald Welte7024baa2018-03-02 23:37:51 +010092 uint8_t ts_nr,
93 GsmFrameNumber fn,
Harald Welte4b6c7722017-08-01 00:07:12 +020094 RlcmacDlBlock block
95 }
Harald Welte7024baa2018-03-02 23:37:51 +010096 type record RLCMAC_ph_data_req_dyn {
Harald Welte4b6c7722017-08-01 00:07:12 +020097 uint8_t tbf_id,
98 GprsCodingScheme cs,
99 RlcmacUlBlock block
100 }
Harald Welte7024baa2018-03-02 23:37:51 +0100101 type record RLCMAC_ph_data_req_abs {
102 uint8_t tbf_id,
103 GprsCodingScheme cs,
104 uint8_t ts_nr,
105 GsmFrameNumber fn,
106 Arfcn arfcn,
107 RlcmacUlBlock block
108 }
109 type union RLCMAC_ph_data_req {
110 RLCMAC_ph_data_req_dyn dyn,
111 RLCMAC_ph_data_req_abs abs
112 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200113
Harald Welted4ba7ff2017-07-17 21:00:48 +0200114 /* port from our (internal) point of view */
115 type port LAPDm_SP_PT message {
116 in BCCH_tune_req,
117 DCCH_establish_req,
Harald Welte66f07542019-05-30 17:35:31 +0200118 DCCH_switch_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200119 DCCH_release_req,
Harald Welteb669ee02018-03-09 12:50:02 +0100120 TBF_UL_establish_req,
121 TBF_DL_establish_req,
Harald Welte4b6c7722017-08-01 00:07:12 +0200122 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200123 LAPDm_ph_data;
124 out DCCH_establish_res,
Harald Welte66f07542019-05-30 17:35:31 +0200125 DCCH_switch_res,
Harald Welteb669ee02018-03-09 12:50:02 +0100126 TBF_UL_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +0200127 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200128 LAPDm_ph_data;
129 } with {extension "internal"};
130
131 /* port from user (external) point of view */
132 type port LAPDm_PT message {
133 in DCCH_establish_res,
Harald Welte66f07542019-05-30 17:35:31 +0200134 DCCH_switch_res,
Harald Welteb669ee02018-03-09 12:50:02 +0100135 TBF_UL_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +0200136 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200137 LAPDm_ph_data;
138 out BCCH_tune_req,
139 DCCH_establish_req,
Harald Welte66f07542019-05-30 17:35:31 +0200140 DCCH_switch_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200141 DCCH_release_req,
Harald Welteb669ee02018-03-09 12:50:02 +0100142 TBF_UL_establish_req,
143 TBF_DL_establish_req,
Harald Welte4b6c7722017-08-01 00:07:12 +0200144 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200145 LAPDm_ph_data;
146 } with {extension "internal"};
147
148 function LAPDmStart() runs on lapdm_CT {
149 f_init();
150 ScanEvents();
151 }
152
153 /* TS 44.004 Figure 5.1 */
154 type enumerated ph_state_enum {
155 PH_STATE_NULL,
156 PH_STATE_BCH,
157 PH_STATE_SEARCHING_BCH,
158 PH_STATE_TUNING_DCH,
Harald Welte4b6c7722017-08-01 00:07:12 +0200159 PH_STATE_DCH,
160 PH_STATE_TBF
Harald Welted4ba7ff2017-07-17 21:00:48 +0200161 }
162
163 type component lapdm_CT {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200164
165 /* L1CTL port towards the bottom */
166 port L1CTL_PT L1CTL;
167 /* Port towards L2 */
168 port LAPDm_SP_PT LAPDM_SP;
169
170 /* physical layer state */
171 var ph_state_enum ph_state := PH_STATE_NULL;
172
173 /* channel description of the currently active DCH */
174 var ChannelDescription chan_desc;
Harald Welteb669ee02018-03-09 12:50:02 +0100175
176 var TbfParsPerTs g_tbf_ul;
177 var TbfParsPerTs g_tbf_dl;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200178 };
179
180 /* wrapper function to log state transitions */
181 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
182 log("PH-STATE ", ph_state, " -> ", new_state);
183 ph_state := new_state;
184 }
185
186 private function f_init() runs on lapdm_CT {
Harald Welteb26cfff2017-08-25 09:56:47 +0200187 f_connect_reset(L1CTL);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200188 set_ph_state(PH_STATE_NULL);
189 }
190
191 /* release the dedicated radio channel */
192 private function f_release_dcch() runs on lapdm_CT {
Harald Weltef8df4cb2018-03-10 15:15:08 +0100193 L1CTL.send(ts_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
Harald Welte4b6c7722017-08-01 00:07:12 +0200194 set_ph_state(PH_STATE_BCH);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200195 }
196
197 /* tune to given ARFCN and start BCCH/CCCH decoding */
198 private function f_tune_bcch(Arfcn arfcn, boolean combined) runs on lapdm_CT {
199 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
200 if (combined) {
201 mode := CCCH_MODE_COMBINED;
202 }
203
204 if (ph_state == PH_STATE_DCH) {
205 /* release any previous DCH */
206 f_release_dcch();
Harald Welte4b6c7722017-08-01 00:07:12 +0200207 } else if (ph_state == PH_STATE_TBF) {
208 f_release_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200209 }
210
211 set_ph_state(PH_STATE_SEARCHING_BCH);
212
213 /* send FB/SB req to sync to cell */
214 f_L1CTL_FBSB(L1CTL, arfcn, mode);
215 set_ph_state(PH_STATE_BCH);
216 }
217
218 /* master function establishing a dedicated radio channel */
219 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
220 var ImmediateAssignment imm_ass;
221 var GsmFrameNumber rach_fn;
222
223 /* send RACH request and obtain FN at which it was sent */
224 rach_fn := f_L1CTL_RACH(L1CTL, ra);
225 //if (not rach_fn) { return; }
226
227 /* wait for receiving matching IMM ASS */
228 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
229 //if (not imm_ass) { return; }
230 set_ph_state(PH_STATE_TUNING_DCH);
231
232 /* store/save channel description */
233 chan_desc := imm_ass.chan_desc;
234
235 /* send DM_EST_REQ */
236 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
237 set_ph_state(PH_STATE_DCH);
238 }
239
Harald Welte66f07542019-05-30 17:35:31 +0200240 /* switching directly to a dedicated channel *without RACH/IMM-ASS */
241 private function f_switch_dcch(Arfcn arfcn, RslChannelNr chan_nr, GsmTsc tsc) runs on lapdm_CT {
242 set_ph_state(PH_STATE_TUNING_DCH);
243 /* store/save channel description */
244 chan_desc.chan_nr := chan_nr;
245 f_L1CTL_DM_EST_REQ(L1CTL,arfcn, chan_nr, tsc);
246 set_ph_state(PH_STATE_DCH);
247 }
248
Harald Welte4b6c7722017-08-01 00:07:12 +0200249 /* initialize a tfi_usf array with "not used" value 255 for all TN */
250 function f_TfiUsfArrInit() return TfiUsfArr {
251 var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
252 return tua;
253 }
254
255 /* set TFI/USF value for one given timeslot number (index) */
256 function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
257 a[idx] := tfi_usf;
258 }
259
260 /* Match an IMM.ASS for an Uplink TBF with a dynamic allocation */
261 template ImmediateAssignment t_IMM_ASS_TBF_UL_DYN(uint8_t ra, GsmFrameNumber fn) modifies t_IMM_ASS := {
262 ded_or_tbf := { spare := ?, tma := ?, downlink := false, tbf := true},
263 chan_desc := omit,
264 pkt_chan_desc := ?,
265 rest_octets := {
266 presence := '11'B,
267 ll := omit,
268 lh := omit,
269 hl := omit,
270 hh := {
271 presence := '00'B,
272 ul := {
273 presence := '1'B,
274 dynamic := {
275 tfi_assignment := ?,
276 polling := ?,
277 spare := '0'B,
278 usf := ?,
279 usf_granularity := ?,
280 p0_present := ?,
281 p0 := *,
282 pr_mode := *,
283 ch_coding_cmd := ?,
284 tlli_block_chan_coding:= ?,
285 alpha_present := ?,
286 alpha := *,
287 gamma := ?,
288 ta_index_present := ?,
289 ta_index := *,
290 tbf_starting_time_present := ?,
291 tbf_starting_time := *
292 },
293 single := omit
294 },
295 dl := omit
296 }
297 }
298 };
299
Harald Welte7024baa2018-03-02 23:37:51 +0100300 template (value) RLCMAC_ph_data_req ts_PH_DATA_ABS(uint8_t tbf_id, GprsCodingScheme cs,
301 uint8_t ts, uint32_t fn, Arfcn arfcn,
302 RlcmacUlBlock block) := {
303 abs := {
304 tbf_id := tbf_id,
305 cs := CS1, /* FIXME */
306 ts_nr := ts,
307 fn := fn,
308 arfcn := arfcn,
309 block := block
310 }
311 }
312
Harald Welte4b6c7722017-08-01 00:07:12 +0200313 private function f_establish_tbf(uint8_t ra) runs on lapdm_CT {
314 var ImmediateAssignment imm_ass;
315 var GsmFrameNumber rach_fn;
316 var TfiUsfArr tua := f_TfiUsfArrInit();
317
318 /* send RACH request and obtain FN at which it was sent */
319 rach_fn := f_L1CTL_RACH(L1CTL, ra);
320
321 /* wait for receiving matching IMM ASS */
322 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
323
324 if (match(imm_ass, t_IMM_ASS_TBF_UL_DYN(ra, rach_fn))) {
325 set_ph_state(PH_STATE_TBF);
326
327 /* store/save channel description */
328 //chan_desc := imm_ass.chan_desc;
329
330 /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
331 f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, imm_ass.rest_octets.hh.ul.dynamic.usf);
332 f_L1CTL_TBF_CFG(L1CTL, true, tua);
333 } else {
334 /* FIXME: single block uplink allocation */
335 log("Failed to match ", t_IMM_ASS_TBF_UL_DYN(ra, rach_fn));
336 log("Non-dynamic UL TBF assignment not supported yet");
337 }
338 }
339
340 private function f_release_tbf() runs on lapdm_CT {
341 var TfiUsfArr tua := f_TfiUsfArrInit();
342 /* send "all timeslots unused" for both UL and DL */
343 f_L1CTL_TBF_CFG(L1CTL, true, tua);
344 f_L1CTL_TBF_CFG(L1CTL, false, tua);
345 /* L1 will then fall back to BCCH/CCCH */
346 set_ph_state(PH_STATE_BCH);
347 }
348
Harald Welteb669ee02018-03-09 12:50:02 +0100349 /* Establish TBF / packet transfer mode */
350 private altstep as_tbf_ul_est() runs on lapdm_CT {
351 var TBF_UL_establish_req tbf_ul_req;
352 [] LAPDM_SP.receive(TBF_UL_establish_req:?) -> value tbf_ul_req {
353 var TbfNr tbf_nr := tbf_ul_req.tbf_nr;
354 var TBF_UL_establish_res res;
355 if (isvalue(g_tbf_ul[tbf_nr].arfcn)) {
356 setverdict(fail, "Cannot establish UL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200357 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100358 }
359 f_establish_tbf(tbf_ul_req.ra);
360 if (ph_state == PH_STATE_TBF) {
361 g_tbf_ul[tbf_nr] := valueof(t_TbfParsInit); /* FIXME: Actual TFI[s] */
362 log("Established UL TBF ", tbf_nr);
363 res := { pars := g_tbf_ul[tbf_nr], err := omit };
364 } else {
365 res := { pars := omit, err := "Unable to establish UL TBF" };
366 }
367 LAPDM_SP.send(res);
368 }
369 }
370
371 private altstep as_tbf_dl_est() runs on lapdm_CT {
372 var TBF_DL_establish_req tbf_dl_req;
373 [] LAPDM_SP.receive(TBF_DL_establish_req:?) -> value tbf_dl_req {
374 var TbfNr tbf_nr := tbf_dl_req.tbf_nr;
375 if (isvalue(g_tbf_dl[tbf_nr].arfcn)) {
376 setverdict(fail, "Cannot establish DL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200377 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100378 }
379 g_tbf_dl[tbf_nr] := tbf_dl_req.pars;
380 f_L1CTL_TBF_CFG(L1CTL, false, tbf_dl_req.pars.tfi);
381 set_ph_state(PH_STATE_TBF);
382 log("Established DL TBF ", tbf_nr, ": ", tbf_dl_req.pars);
383 }
384 }
385
386 private function f_init_tbf() runs on lapdm_CT {
387 var integer i;
388 for (i := 0; i < 8; i := i+1) {
389 g_tbf_ul[i] := valueof(t_TbfParsInit);
390 g_tbf_dl[i] := valueof(t_TbfParsInit);
391 }
392 }
393
Harald Welted4ba7ff2017-07-17 21:00:48 +0200394 function ScanEvents() runs on lapdm_CT {
395 var L1ctlDlMessage dl;
396 var BCCH_tune_req bt;
397 var LAPDm_ph_data lpd;
Harald Welte4b6c7722017-08-01 00:07:12 +0200398 var RLCMAC_ph_data_ind rpdi;
399 var RLCMAC_ph_data_req rpdr;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200400 var DCCH_establish_req est_req;
Harald Welte66f07542019-05-30 17:35:31 +0200401 var DCCH_switch_req sw_req;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200402 var DCCH_establish_res est_res;
Harald Welteb669ee02018-03-09 12:50:02 +0100403
404 f_init_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200405
406 while (true) {
407 if (ph_state == PH_STATE_NULL) {
408 alt {
409 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
410 f_tune_bcch(bt.arfcn, bt.combined_ccch);
411 }
412
413 [] LAPDM_SP.receive {}
414 [] L1CTL.receive {}
415
416 }
417 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
418 alt {
419 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
420 f_tune_bcch(bt.arfcn, bt.combined_ccch);
421 }
422
423 /* forward CCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100424 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200425 lpd.sacch := false;
426 lpd.sapi := 0;
427 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
428 LAPDM_SP.send(lpd);
429 }
430
431 /* forward BCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100432 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200433 lpd.sacch := false;
434 lpd.sapi := 0;
435 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
436 LAPDM_SP.send(lpd);
437 }
438
439 /* Establish dedicated channel */
440 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
441 var DCCH_establish_res res;
442 f_establish_dcch(est_req.ra);
443 if (ph_state == PH_STATE_DCH) {
444 res := { chan_desc, omit };
445 } else {
446 res := { omit, "Unable to esetablish DCCH" };
447 }
448 LAPDM_SP.send(res);
449 }
Harald Welte66f07542019-05-30 17:35:31 +0200450 [] LAPDM_SP.receive(DCCH_switch_req:?) -> value sw_req {
451 var DCCH_switch_res res;
452 f_switch_dcch(sw_req.arfcn, sw_req.chan_nr, sw_req.tsc);
453 if (ph_state == PH_STATE_DCH) {
454 res := { omit };
455 } else {
456 res := { "Unable to switch to DCCH" };
457 }
458 LAPDM_SP.send(res);
459 }
460
Harald Welted4ba7ff2017-07-17 21:00:48 +0200461
Harald Welteb669ee02018-03-09 12:50:02 +0100462 [] as_tbf_ul_est();
463 [] as_tbf_dl_est();
Harald Welte4b6c7722017-08-01 00:07:12 +0200464
Harald Welted4ba7ff2017-07-17 21:00:48 +0200465 [] LAPDM_SP.receive {}
466 [] L1CTL.receive {}
467
468 }
469
470 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
471 alt {
472
473 /* decode any received DATA frames for the dedicated channel and pass them up */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100474 [] L1CTL.receive(tr_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200475 if (dl.dl_info.link_id.c == SACCH) {
476 lpd.sacch := true;
477 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
478 } else {
479 lpd.sacch := false;
480 }
481 lpd.sapi := dl.dl_info.link_id.sapi;
Harald Welted879bd92018-03-12 15:01:23 +0100482 lpd.lapdm.ab := dec_LapdmFrameAB(dl.payload.data_ind.payload);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200483 LAPDM_SP.send(lpd);
484 }
485
486 /* encode any LAPDm record from user and pass it on to L1CTL */
487 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
488 var octetstring buf;
489 var RslLinkId link_id;
490 if (lpd.sacch) {
491 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200492 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 21, '2B'O);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200493 } else {
494 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200495 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 23, '2B'O);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200496 }
Harald Weltef8df4cb2018-03-10 15:15:08 +0100497 L1CTL.send(ts_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200498 }
499
500 /* Release dedicated channel */
501 [] LAPDM_SP.receive(DCCH_release_req:?) {
502 /* go back to BCCH */
503 f_release_dcch();
504 }
505
506 [] LAPDM_SP.receive {}
507 [] L1CTL.receive {}
508
509
510 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200511 } else if (ph_state == PH_STATE_TBF) {
512 alt {
513
514 /* decode + forward any blocks from L1 to L23*/
Harald Weltef8df4cb2018-03-10 15:15:08 +0100515 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
Harald Welte4b6c7722017-08-01 00:07:12 +0200516 rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
Harald Welteb669ee02018-03-09 12:50:02 +0100517 /* FIXME: Filter based on g_tbf_dl */
Harald Welte7024baa2018-03-02 23:37:51 +0100518 rpdi.fn := dl.dl_info.frame_nr;
519 rpdi.ts_nr := dl.dl_info.chan_nr.tn;
Harald Welte4b6c7722017-08-01 00:07:12 +0200520 rpdi.cs := CS1; /* FIXME */
521 log("RPDI: ", rpdi);
522 LAPDM_SP.send(rpdi);
523 }
524
525 [] L1CTL.receive { }
526
527 /* encode + forward any blocks from L23 to L1 */
528 [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
529 var octetstring buf;
Harald Welte7024baa2018-03-02 23:37:51 +0100530 if (ischosen(rpdr.dyn)) {
531 buf := enc_RlcmacUlBlock(rpdr.dyn.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100532 L1CTL.send(ts_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.dyn.tbf_id));
Harald Welte7024baa2018-03-02 23:37:51 +0100533 } else {
534 buf := enc_RlcmacUlBlock(rpdr.abs.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100535 L1CTL.send(ts_L1CTL_DATA_ABS_REQ(buf, rpdr.abs.arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100536 rpdr.abs.ts_nr, rpdr.abs.fn,
537 L1CTL_CS1, rpdr.abs.tbf_id));
538 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200539 }
540
Harald Welteb669ee02018-03-09 12:50:02 +0100541 [] as_tbf_ul_est();
542 [] as_tbf_dl_est();
543
Harald Welte4b6c7722017-08-01 00:07:12 +0200544 /* FIXME: release TBF mode */
545 [] LAPDM_SP.receive(DCCH_release_req:?) {
546 /* go back to BCCH */
547 f_release_tbf();
Harald Welteb669ee02018-03-09 12:50:02 +0100548 f_init_tbf();
Harald Welte4b6c7722017-08-01 00:07:12 +0200549 }
550
551 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200552 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200553
Harald Welted4ba7ff2017-07-17 21:00:48 +0200554 } /* while (1) */
555 }
556}