blob: 946da67633f009f88b79826657d6627e5d392164 [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 {
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070025 GsmBandArfcn arfcn,
Harald Welted4ba7ff2017-07-17 21:00:48 +020026 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 {
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +070041 ChannelDescription chan_desc,
Vadim Yanitskiyca813922020-09-12 19:08:31 +070042 L1ctlMA ma optional
Harald Welte66f07542019-05-30 17:35:31 +020043 }
44
45 type record DCCH_switch_res {
46 charstring err optional
47 }
48
Harald Welteb669ee02018-03-09 12:50:02 +010049 type record length(8) of uint8_t TfiList;
50 type record TbfPars {
51 GsmArfcn arfcn optional,
52 /* Temporary Flow Identifier for each TN */
53 TfiList tfi
54 }
55 type record length(8) of TbfPars TbfParsPerTs;
56
57 template TbfPars t_TbfParsInit := {
58 arfcn := omit,
59 tfi := { 255, 255, 255, 255, 255, 255, 255, 255 }
60 }
61
62 type record TBF_UL_establish_res {
63 TbfPars pars optional,
Harald Welte4b6c7722017-08-01 00:07:12 +020064 charstring err optional
65 }
66
Harald Welted4ba7ff2017-07-17 21:00:48 +020067 type record DCCH_release_req {
68 }
69
70 /* PH-DATA.ind / PH-DATA.req */
71 type record LAPDm_ph_data {
72 boolean sacch,
73 GsmSapi sapi,
74 LapdmFrame lapdm
75 }
76
Harald Welteb669ee02018-03-09 12:50:02 +010077 type integer TbfNr (0..7); /* maximum of 8 concurrent TBF per direction */
78 type record TBF_UL_establish_req {
79 TbfNr tbf_nr,
Harald Welte4b6c7722017-08-01 00:07:12 +020080 uint8_t ra
81 }
82
Harald Welteb669ee02018-03-09 12:50:02 +010083 type record TBF_DL_establish_req {
84 TbfNr tbf_nr,
85 TbfPars pars
86 }
87
Harald Welte4b6c7722017-08-01 00:07:12 +020088 /* PH-DATA.ind / PH-DATA.req */
89 type record RLCMAC_ph_data_ind {
90 GprsCodingScheme cs,
Harald Welte7024baa2018-03-02 23:37:51 +010091 uint8_t ts_nr,
92 GsmFrameNumber fn,
Harald Welte4b6c7722017-08-01 00:07:12 +020093 RlcmacDlBlock block
94 }
Harald Welte7024baa2018-03-02 23:37:51 +010095 type record RLCMAC_ph_data_req_dyn {
Harald Welte4b6c7722017-08-01 00:07:12 +020096 uint8_t tbf_id,
97 GprsCodingScheme cs,
98 RlcmacUlBlock block
99 }
Harald Welte7024baa2018-03-02 23:37:51 +0100100 type record RLCMAC_ph_data_req_abs {
101 uint8_t tbf_id,
102 GprsCodingScheme cs,
103 uint8_t ts_nr,
104 GsmFrameNumber fn,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700105 GsmBandArfcn arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100106 RlcmacUlBlock block
107 }
108 type union RLCMAC_ph_data_req {
109 RLCMAC_ph_data_req_dyn dyn,
110 RLCMAC_ph_data_req_abs abs
111 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200112
Harald Welted4ba7ff2017-07-17 21:00:48 +0200113 /* port from our (internal) point of view */
114 type port LAPDm_SP_PT message {
115 in BCCH_tune_req,
116 DCCH_establish_req,
Harald Welte66f07542019-05-30 17:35:31 +0200117 DCCH_switch_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200118 DCCH_release_req,
Harald Welteb669ee02018-03-09 12:50:02 +0100119 TBF_UL_establish_req,
120 TBF_DL_establish_req,
Harald Welte4b6c7722017-08-01 00:07:12 +0200121 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200122 LAPDm_ph_data;
123 out DCCH_establish_res,
Harald Welte66f07542019-05-30 17:35:31 +0200124 DCCH_switch_res,
Harald Welteb669ee02018-03-09 12:50:02 +0100125 TBF_UL_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +0200126 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200127 LAPDm_ph_data;
128 } with {extension "internal"};
129
130 /* port from user (external) point of view */
131 type port LAPDm_PT message {
132 in DCCH_establish_res,
Harald Welte66f07542019-05-30 17:35:31 +0200133 DCCH_switch_res,
Harald Welteb669ee02018-03-09 12:50:02 +0100134 TBF_UL_establish_res,
Harald Welte4b6c7722017-08-01 00:07:12 +0200135 RLCMAC_ph_data_ind,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200136 LAPDm_ph_data;
137 out BCCH_tune_req,
138 DCCH_establish_req,
Harald Welte66f07542019-05-30 17:35:31 +0200139 DCCH_switch_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200140 DCCH_release_req,
Harald Welteb669ee02018-03-09 12:50:02 +0100141 TBF_UL_establish_req,
142 TBF_DL_establish_req,
Harald Welte4b6c7722017-08-01 00:07:12 +0200143 RLCMAC_ph_data_req,
Harald Welted4ba7ff2017-07-17 21:00:48 +0200144 LAPDm_ph_data;
145 } with {extension "internal"};
146
147 function LAPDmStart() runs on lapdm_CT {
148 f_init();
149 ScanEvents();
150 }
151
152 /* TS 44.004 Figure 5.1 */
153 type enumerated ph_state_enum {
154 PH_STATE_NULL,
155 PH_STATE_BCH,
156 PH_STATE_SEARCHING_BCH,
157 PH_STATE_TUNING_DCH,
Harald Welte4b6c7722017-08-01 00:07:12 +0200158 PH_STATE_DCH,
159 PH_STATE_TBF
Harald Welted4ba7ff2017-07-17 21:00:48 +0200160 }
161
162 type component lapdm_CT {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200163
164 /* L1CTL port towards the bottom */
165 port L1CTL_PT L1CTL;
166 /* Port towards L2 */
167 port LAPDm_SP_PT LAPDM_SP;
168
169 /* physical layer state */
170 var ph_state_enum ph_state := PH_STATE_NULL;
171
172 /* channel description of the currently active DCH */
173 var ChannelDescription chan_desc;
Harald Welteb669ee02018-03-09 12:50:02 +0100174
Harald Welte1365a4e2019-06-01 23:04:43 +0200175 /* last SACCH downlink L1 header we received */
176 var uint5_t ms_power_lvl := 0;
177 var uint8_t timing_adv := 0;
178
Harald Welteb669ee02018-03-09 12:50:02 +0100179 var TbfParsPerTs g_tbf_ul;
180 var TbfParsPerTs g_tbf_dl;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200181 };
182
183 /* wrapper function to log state transitions */
184 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
185 log("PH-STATE ", ph_state, " -> ", new_state);
186 ph_state := new_state;
187 }
188
189 private function f_init() runs on lapdm_CT {
Harald Welteb26cfff2017-08-25 09:56:47 +0200190 f_connect_reset(L1CTL);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200191 set_ph_state(PH_STATE_NULL);
192 }
193
194 /* release the dedicated radio channel */
195 private function f_release_dcch() runs on lapdm_CT {
Harald Weltef8df4cb2018-03-10 15:15:08 +0100196 L1CTL.send(ts_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
Harald Welte4b6c7722017-08-01 00:07:12 +0200197 set_ph_state(PH_STATE_BCH);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200198 }
199
200 /* tune to given ARFCN and start BCCH/CCCH decoding */
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700201 private function f_tune_bcch(GsmBandArfcn arfcn, boolean combined)
202 runs on lapdm_CT {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200203 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
204 if (combined) {
205 mode := CCCH_MODE_COMBINED;
206 }
207
208 if (ph_state == PH_STATE_DCH) {
209 /* release any previous DCH */
210 f_release_dcch();
Harald Welte4b6c7722017-08-01 00:07:12 +0200211 } else if (ph_state == PH_STATE_TBF) {
212 f_release_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200213 }
214
215 set_ph_state(PH_STATE_SEARCHING_BCH);
216
217 /* send FB/SB req to sync to cell */
218 f_L1CTL_FBSB(L1CTL, arfcn, mode);
219 set_ph_state(PH_STATE_BCH);
220 }
221
222 /* master function establishing a dedicated radio channel */
223 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
224 var ImmediateAssignment imm_ass;
225 var GsmFrameNumber rach_fn;
226
227 /* send RACH request and obtain FN at which it was sent */
228 rach_fn := f_L1CTL_RACH(L1CTL, ra);
229 //if (not rach_fn) { return; }
230
231 /* wait for receiving matching IMM ASS */
232 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
233 //if (not imm_ass) { return; }
234 set_ph_state(PH_STATE_TUNING_DCH);
235
236 /* store/save channel description */
237 chan_desc := imm_ass.chan_desc;
238
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700239 /* send DM_EST_REQ, TODO: Mobile Allocation */
Harald Welted4ba7ff2017-07-17 21:00:48 +0200240 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
241 set_ph_state(PH_STATE_DCH);
242 }
243
Harald Welte66f07542019-05-30 17:35:31 +0200244 /* switching directly to a dedicated channel *without RACH/IMM-ASS */
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700245 private function f_switch_dcch(in DCCH_switch_req sw_req) runs on lapdm_CT {
Harald Welte66f07542019-05-30 17:35:31 +0200246 set_ph_state(PH_STATE_TUNING_DCH);
247 /* store/save channel description */
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700248 chan_desc := sw_req.chan_desc;
249
250 /* tune the L1 to the indicated channel */
251 if (chan_desc.h) {
252 L1CTL.send(ts_L1CTL_DM_EST_REQ_H1(chan_desc.chan_nr,
253 chan_desc.tsc,
254 chan_desc.maio_hsn.hsn,
255 chan_desc.maio_hsn.maio,
256 sw_req.ma));
257 } else {
258 L1CTL.send(ts_L1CTL_DM_EST_REQ_H0(chan_desc.chan_nr,
259 chan_desc.tsc,
260 chan_desc.arfcn));
261 }
262
Harald Welte66f07542019-05-30 17:35:31 +0200263 set_ph_state(PH_STATE_DCH);
264 }
265
Harald Welte4b6c7722017-08-01 00:07:12 +0200266 /* initialize a tfi_usf array with "not used" value 255 for all TN */
267 function f_TfiUsfArrInit() return TfiUsfArr {
268 var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
269 return tua;
270 }
271
272 /* set TFI/USF value for one given timeslot number (index) */
273 function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
274 a[idx] := tfi_usf;
275 }
276
Harald Welte7024baa2018-03-02 23:37:51 +0100277 template (value) RLCMAC_ph_data_req ts_PH_DATA_ABS(uint8_t tbf_id, GprsCodingScheme cs,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700278 uint8_t ts, uint32_t fn,
279 GsmBandArfcn arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100280 RlcmacUlBlock block) := {
281 abs := {
282 tbf_id := tbf_id,
283 cs := CS1, /* FIXME */
284 ts_nr := ts,
285 fn := fn,
286 arfcn := arfcn,
287 block := block
288 }
289 }
290
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200291 private function f_establish_tbf(uint8_t ra) runs on lapdm_CT return boolean {
292 var template GsmRrMessage imm_ass_rr;
Harald Welte4b6c7722017-08-01 00:07:12 +0200293 var ImmediateAssignment imm_ass;
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200294 var PacketUlAssign pkt_ul_ass;
Harald Welte4b6c7722017-08-01 00:07:12 +0200295 var GsmFrameNumber rach_fn;
296 var TfiUsfArr tua := f_TfiUsfArrInit();
297
298 /* send RACH request and obtain FN at which it was sent */
299 rach_fn := f_L1CTL_RACH(L1CTL, ra);
300
301 /* wait for receiving matching IMM ASS */
302 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
303
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200304 /* make sure we got *Packet* (Uplink) Immediate Assignment */
305 imm_ass_rr := tr_IMM_TBF_ASS(dl := false, ra := ra, fn := rach_fn,
306 rest := tr_IaRestOctets_ULAss(?));
307 if (not match(imm_ass, imm_ass_rr.payload.imm_ass)) {
308 log("Failed to match Packet Immediate Assignment");
309 return false;
310 }
311
312 /* decapsulate PacketUlAssign for further matching */
313 pkt_ul_ass := imm_ass.rest_octets.hh.pa.uldl.ass.ul;
314
315 /* Dynamic Block Allocation */
316 if (match(pkt_ul_ass, tr_PacketUlDynAssign)) {
Harald Welte4b6c7722017-08-01 00:07:12 +0200317 set_ph_state(PH_STATE_TBF);
318
319 /* store/save channel description */
320 //chan_desc := imm_ass.chan_desc;
321
322 /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200323 f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, pkt_ul_ass.dynamic.usf);
Harald Welte4b6c7722017-08-01 00:07:12 +0200324 f_L1CTL_TBF_CFG(L1CTL, true, tua);
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200325 return true;
326 /* FIXME: Single Block Allocation */
327 } else if (match(pkt_ul_ass, tr_PacketUlSglAssign)) {
Harald Welte4b6c7722017-08-01 00:07:12 +0200328 log("Non-dynamic UL TBF assignment not supported yet");
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200329 return false;
330 } else {
331 log("Failed to match Uplink Block Allocation: ", pkt_ul_ass);
332 return false;
Harald Welte4b6c7722017-08-01 00:07:12 +0200333 }
334 }
335
336 private function f_release_tbf() runs on lapdm_CT {
337 var TfiUsfArr tua := f_TfiUsfArrInit();
338 /* send "all timeslots unused" for both UL and DL */
339 f_L1CTL_TBF_CFG(L1CTL, true, tua);
340 f_L1CTL_TBF_CFG(L1CTL, false, tua);
341 /* L1 will then fall back to BCCH/CCCH */
342 set_ph_state(PH_STATE_BCH);
343 }
344
Harald Welteb669ee02018-03-09 12:50:02 +0100345 /* Establish TBF / packet transfer mode */
346 private altstep as_tbf_ul_est() runs on lapdm_CT {
347 var TBF_UL_establish_req tbf_ul_req;
348 [] LAPDM_SP.receive(TBF_UL_establish_req:?) -> value tbf_ul_req {
349 var TbfNr tbf_nr := tbf_ul_req.tbf_nr;
350 var TBF_UL_establish_res res;
351 if (isvalue(g_tbf_ul[tbf_nr].arfcn)) {
352 setverdict(fail, "Cannot establish UL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200353 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100354 }
355 f_establish_tbf(tbf_ul_req.ra);
356 if (ph_state == PH_STATE_TBF) {
357 g_tbf_ul[tbf_nr] := valueof(t_TbfParsInit); /* FIXME: Actual TFI[s] */
358 log("Established UL TBF ", tbf_nr);
359 res := { pars := g_tbf_ul[tbf_nr], err := omit };
360 } else {
361 res := { pars := omit, err := "Unable to establish UL TBF" };
362 }
363 LAPDM_SP.send(res);
364 }
365 }
366
367 private altstep as_tbf_dl_est() runs on lapdm_CT {
368 var TBF_DL_establish_req tbf_dl_req;
369 [] LAPDM_SP.receive(TBF_DL_establish_req:?) -> value tbf_dl_req {
370 var TbfNr tbf_nr := tbf_dl_req.tbf_nr;
371 if (isvalue(g_tbf_dl[tbf_nr].arfcn)) {
372 setverdict(fail, "Cannot establish DL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200373 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100374 }
375 g_tbf_dl[tbf_nr] := tbf_dl_req.pars;
376 f_L1CTL_TBF_CFG(L1CTL, false, tbf_dl_req.pars.tfi);
377 set_ph_state(PH_STATE_TBF);
378 log("Established DL TBF ", tbf_nr, ": ", tbf_dl_req.pars);
379 }
380 }
381
382 private function f_init_tbf() runs on lapdm_CT {
383 var integer i;
384 for (i := 0; i < 8; i := i+1) {
385 g_tbf_ul[i] := valueof(t_TbfParsInit);
386 g_tbf_dl[i] := valueof(t_TbfParsInit);
387 }
388 }
389
Harald Welted4ba7ff2017-07-17 21:00:48 +0200390 function ScanEvents() runs on lapdm_CT {
391 var L1ctlDlMessage dl;
392 var BCCH_tune_req bt;
393 var LAPDm_ph_data lpd;
Harald Welte4b6c7722017-08-01 00:07:12 +0200394 var RLCMAC_ph_data_ind rpdi;
395 var RLCMAC_ph_data_req rpdr;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200396 var DCCH_establish_req est_req;
Harald Welte66f07542019-05-30 17:35:31 +0200397 var DCCH_switch_req sw_req;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200398 var DCCH_establish_res est_res;
Harald Welteb669ee02018-03-09 12:50:02 +0100399
400 f_init_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200401
402 while (true) {
403 if (ph_state == PH_STATE_NULL) {
404 alt {
405 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
406 f_tune_bcch(bt.arfcn, bt.combined_ccch);
407 }
408
409 [] LAPDM_SP.receive {}
410 [] L1CTL.receive {}
411
412 }
413 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
414 alt {
415 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
416 f_tune_bcch(bt.arfcn, bt.combined_ccch);
417 }
418
419 /* forward CCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100420 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200421 lpd.sacch := false;
422 lpd.sapi := 0;
423 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
424 LAPDM_SP.send(lpd);
425 }
426
427 /* forward BCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100428 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200429 lpd.sacch := false;
430 lpd.sapi := 0;
431 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
432 LAPDM_SP.send(lpd);
433 }
434
435 /* Establish dedicated channel */
436 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
437 var DCCH_establish_res res;
438 f_establish_dcch(est_req.ra);
439 if (ph_state == PH_STATE_DCH) {
440 res := { chan_desc, omit };
441 } else {
442 res := { omit, "Unable to esetablish DCCH" };
443 }
444 LAPDM_SP.send(res);
445 }
Harald Welte66f07542019-05-30 17:35:31 +0200446 [] LAPDM_SP.receive(DCCH_switch_req:?) -> value sw_req {
447 var DCCH_switch_res res;
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700448 f_switch_dcch(sw_req);
Harald Welte66f07542019-05-30 17:35:31 +0200449 if (ph_state == PH_STATE_DCH) {
450 res := { omit };
451 } else {
452 res := { "Unable to switch to DCCH" };
453 }
454 LAPDM_SP.send(res);
455 }
456
Harald Welted4ba7ff2017-07-17 21:00:48 +0200457
Harald Welteb669ee02018-03-09 12:50:02 +0100458 [] as_tbf_ul_est();
459 [] as_tbf_dl_est();
Harald Welte4b6c7722017-08-01 00:07:12 +0200460
Harald Welted4ba7ff2017-07-17 21:00:48 +0200461 [] LAPDM_SP.receive {}
462 [] L1CTL.receive {}
463
464 }
465
466 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
467 alt {
468
469 /* decode any received DATA frames for the dedicated channel and pass them up */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100470 [] L1CTL.receive(tr_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
Harald Welte1365a4e2019-06-01 23:04:43 +0200471 var octetstring l2;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200472 if (dl.dl_info.link_id.c == SACCH) {
473 lpd.sacch := true;
Harald Welte1365a4e2019-06-01 23:04:43 +0200474 var octetstring l1 := substr(dl.payload.data_ind.payload, 0, 2);
475 l2 := substr(dl.payload.data_ind.payload, 2,
476 lengthof(dl.payload.data_ind.payload)-2);
477 ms_power_lvl := oct2int(l1[0] and4b '1F'O);
478 timing_adv := oct2int(l1[1]);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200479 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
480 } else {
481 lpd.sacch := false;
Harald Welte1365a4e2019-06-01 23:04:43 +0200482 l2 := dl.payload.data_ind.payload;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200483 }
484 lpd.sapi := dl.dl_info.link_id.sapi;
Harald Welte1365a4e2019-06-01 23:04:43 +0200485 lpd.lapdm.ab := dec_LapdmFrameAB(l2);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200486 LAPDM_SP.send(lpd);
487 }
488
489 /* encode any LAPDm record from user and pass it on to L1CTL */
490 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
491 var octetstring buf;
492 var RslLinkId link_id;
493 if (lpd.sacch) {
494 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200495 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 21, '2B'O);
Harald Welte1365a4e2019-06-01 23:04:43 +0200496 var SacchL1Header l1h := valueof(ts_SacchL1Header(ms_power_lvl,
Vadim Yanitskiy68c4cff2022-07-30 23:45:02 +0700497 timing_adv));
Harald Welte1365a4e2019-06-01 23:04:43 +0200498 L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(chan_desc.chan_nr, link_id,
499 l1h, buf));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200500 } else {
501 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200502 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 23, '2B'O);
Harald Welte1365a4e2019-06-01 23:04:43 +0200503 L1CTL.send(ts_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200504 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200505 }
506
507 /* Release dedicated channel */
508 [] LAPDM_SP.receive(DCCH_release_req:?) {
509 /* go back to BCCH */
510 f_release_dcch();
511 }
512
513 [] LAPDM_SP.receive {}
514 [] L1CTL.receive {}
515
516
517 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200518 } else if (ph_state == PH_STATE_TBF) {
519 alt {
520
521 /* decode + forward any blocks from L1 to L23*/
Harald Weltef8df4cb2018-03-10 15:15:08 +0100522 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
Harald Welte4b6c7722017-08-01 00:07:12 +0200523 rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
Harald Welteb669ee02018-03-09 12:50:02 +0100524 /* FIXME: Filter based on g_tbf_dl */
Harald Welte7024baa2018-03-02 23:37:51 +0100525 rpdi.fn := dl.dl_info.frame_nr;
526 rpdi.ts_nr := dl.dl_info.chan_nr.tn;
Harald Welte4b6c7722017-08-01 00:07:12 +0200527 rpdi.cs := CS1; /* FIXME */
528 log("RPDI: ", rpdi);
529 LAPDM_SP.send(rpdi);
530 }
531
532 [] L1CTL.receive { }
533
534 /* encode + forward any blocks from L23 to L1 */
535 [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
536 var octetstring buf;
Harald Welte7024baa2018-03-02 23:37:51 +0100537 if (ischosen(rpdr.dyn)) {
538 buf := enc_RlcmacUlBlock(rpdr.dyn.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100539 L1CTL.send(ts_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.dyn.tbf_id));
Harald Welte7024baa2018-03-02 23:37:51 +0100540 } else {
541 buf := enc_RlcmacUlBlock(rpdr.abs.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100542 L1CTL.send(ts_L1CTL_DATA_ABS_REQ(buf, rpdr.abs.arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100543 rpdr.abs.ts_nr, rpdr.abs.fn,
544 L1CTL_CS1, rpdr.abs.tbf_id));
545 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200546 }
547
Harald Welteb669ee02018-03-09 12:50:02 +0100548 [] as_tbf_ul_est();
549 [] as_tbf_dl_est();
550
Harald Welte4b6c7722017-08-01 00:07:12 +0200551 /* FIXME: release TBF mode */
552 [] LAPDM_SP.receive(DCCH_release_req:?) {
553 /* go back to BCCH */
554 f_release_tbf();
Harald Welteb669ee02018-03-09 12:50:02 +0100555 f_init_tbf();
Harald Welte4b6c7722017-08-01 00:07:12 +0200556 }
557
558 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200559 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200560
Harald Welted4ba7ff2017-07-17 21:00:48 +0200561 } /* while (1) */
562 }
563}