blob: 9630df0ecea9541dbcdcccb81a01a52545afa1b4 [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;
Vadim Yanitskiy150d6d12022-10-20 19:10:04 +070017 import from GSM_RestOctets all;
Harald Welted4ba7ff2017-07-17 21:00:48 +020018 import from Osmocom_Types all;
19 import from L1CTL_Types all;
20 import from L1CTL_PortType all;
21 import from LAPDm_Types all;
Harald Welte4b6c7722017-08-01 00:07:12 +020022 import from RLCMAC_Types all;
Harald Welted4ba7ff2017-07-17 21:00:48 +020023
24 /* request to tune to a given ARFCN and start BCCH decoding */
25 type record BCCH_tune_req {
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070026 GsmBandArfcn arfcn,
Harald Welted4ba7ff2017-07-17 21:00:48 +020027 boolean combined_ccch
28 }
29
30 /* ask for a dedicated channel to be established */
31 type record DCCH_establish_req {
32 uint8_t ra
33 }
34
35 type record DCCH_establish_res {
36 ChannelDescription chan_desc optional,
37 charstring err optional
38 }
39
Harald Welte66f07542019-05-30 17:35:31 +020040 /* directly switch to a dedicated channel (without RACH/IMM.ASS */
41 type record DCCH_switch_req {
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +070042 ChannelDescription chan_desc,
Vadim Yanitskiyca813922020-09-12 19:08:31 +070043 L1ctlMA ma optional
Harald Welte66f07542019-05-30 17:35:31 +020044 }
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,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700106 GsmBandArfcn arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100107 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
Harald Welte1365a4e2019-06-01 23:04:43 +0200176 /* last SACCH downlink L1 header we received */
177 var uint5_t ms_power_lvl := 0;
178 var uint8_t timing_adv := 0;
179
Harald Welteb669ee02018-03-09 12:50:02 +0100180 var TbfParsPerTs g_tbf_ul;
181 var TbfParsPerTs g_tbf_dl;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200182 };
183
184 /* wrapper function to log state transitions */
185 private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT {
186 log("PH-STATE ", ph_state, " -> ", new_state);
187 ph_state := new_state;
188 }
189
190 private function f_init() runs on lapdm_CT {
Harald Welteb26cfff2017-08-25 09:56:47 +0200191 f_connect_reset(L1CTL);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200192 set_ph_state(PH_STATE_NULL);
193 }
194
195 /* release the dedicated radio channel */
196 private function f_release_dcch() runs on lapdm_CT {
Harald Weltef8df4cb2018-03-10 15:15:08 +0100197 L1CTL.send(ts_L1CTL_DM_REL_REQ(chan_desc.chan_nr));
Harald Welte4b6c7722017-08-01 00:07:12 +0200198 set_ph_state(PH_STATE_BCH);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200199 }
200
201 /* tune to given ARFCN and start BCCH/CCCH decoding */
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700202 private function f_tune_bcch(GsmBandArfcn arfcn, boolean combined)
203 runs on lapdm_CT {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200204 var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED;
205 if (combined) {
206 mode := CCCH_MODE_COMBINED;
207 }
208
209 if (ph_state == PH_STATE_DCH) {
210 /* release any previous DCH */
211 f_release_dcch();
Harald Welte4b6c7722017-08-01 00:07:12 +0200212 } else if (ph_state == PH_STATE_TBF) {
213 f_release_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200214 }
215
216 set_ph_state(PH_STATE_SEARCHING_BCH);
217
218 /* send FB/SB req to sync to cell */
219 f_L1CTL_FBSB(L1CTL, arfcn, mode);
220 set_ph_state(PH_STATE_BCH);
221 }
222
223 /* master function establishing a dedicated radio channel */
224 private function f_establish_dcch(uint8_t ra) runs on lapdm_CT {
225 var ImmediateAssignment imm_ass;
226 var GsmFrameNumber rach_fn;
227
228 /* send RACH request and obtain FN at which it was sent */
229 rach_fn := f_L1CTL_RACH(L1CTL, ra);
230 //if (not rach_fn) { return; }
231
232 /* wait for receiving matching IMM ASS */
233 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn)
234 //if (not imm_ass) { return; }
235 set_ph_state(PH_STATE_TUNING_DCH);
236
237 /* store/save channel description */
238 chan_desc := imm_ass.chan_desc;
239
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700240 /* send DM_EST_REQ, TODO: Mobile Allocation */
Harald Welted4ba7ff2017-07-17 21:00:48 +0200241 f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass);
242 set_ph_state(PH_STATE_DCH);
243 }
244
Harald Welte66f07542019-05-30 17:35:31 +0200245 /* switching directly to a dedicated channel *without RACH/IMM-ASS */
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700246 private function f_switch_dcch(in DCCH_switch_req sw_req) runs on lapdm_CT {
Harald Welte66f07542019-05-30 17:35:31 +0200247 set_ph_state(PH_STATE_TUNING_DCH);
248 /* store/save channel description */
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700249 chan_desc := sw_req.chan_desc;
250
251 /* tune the L1 to the indicated channel */
252 if (chan_desc.h) {
253 L1CTL.send(ts_L1CTL_DM_EST_REQ_H1(chan_desc.chan_nr,
254 chan_desc.tsc,
255 chan_desc.maio_hsn.hsn,
256 chan_desc.maio_hsn.maio,
257 sw_req.ma));
258 } else {
259 L1CTL.send(ts_L1CTL_DM_EST_REQ_H0(chan_desc.chan_nr,
260 chan_desc.tsc,
261 chan_desc.arfcn));
262 }
263
Harald Welte66f07542019-05-30 17:35:31 +0200264 set_ph_state(PH_STATE_DCH);
265 }
266
Harald Welte4b6c7722017-08-01 00:07:12 +0200267 /* initialize a tfi_usf array with "not used" value 255 for all TN */
268 function f_TfiUsfArrInit() return TfiUsfArr {
269 var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 };
270 return tua;
271 }
272
273 /* set TFI/USF value for one given timeslot number (index) */
274 function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) {
275 a[idx] := tfi_usf;
276 }
277
Harald Welte7024baa2018-03-02 23:37:51 +0100278 template (value) RLCMAC_ph_data_req ts_PH_DATA_ABS(uint8_t tbf_id, GprsCodingScheme cs,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +0700279 uint8_t ts, uint32_t fn,
280 GsmBandArfcn arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100281 RlcmacUlBlock block) := {
282 abs := {
283 tbf_id := tbf_id,
284 cs := CS1, /* FIXME */
285 ts_nr := ts,
286 fn := fn,
287 arfcn := arfcn,
288 block := block
289 }
290 }
291
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200292 private function f_establish_tbf(uint8_t ra) runs on lapdm_CT return boolean {
293 var template GsmRrMessage imm_ass_rr;
Harald Welte4b6c7722017-08-01 00:07:12 +0200294 var ImmediateAssignment imm_ass;
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200295 var PacketUlAssign pkt_ul_ass;
Harald Welte4b6c7722017-08-01 00:07:12 +0200296 var GsmFrameNumber rach_fn;
297 var TfiUsfArr tua := f_TfiUsfArrInit();
298
299 /* send RACH request and obtain FN at which it was sent */
300 rach_fn := f_L1CTL_RACH(L1CTL, ra);
301
302 /* wait for receiving matching IMM ASS */
303 imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn);
304
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200305 /* make sure we got *Packet* (Uplink) Immediate Assignment */
306 imm_ass_rr := tr_IMM_TBF_ASS(dl := false, ra := ra, fn := rach_fn,
307 rest := tr_IaRestOctets_ULAss(?));
308 if (not match(imm_ass, imm_ass_rr.payload.imm_ass)) {
309 log("Failed to match Packet Immediate Assignment");
310 return false;
311 }
312
313 /* decapsulate PacketUlAssign for further matching */
314 pkt_ul_ass := imm_ass.rest_octets.hh.pa.uldl.ass.ul;
315
316 /* Dynamic Block Allocation */
317 if (match(pkt_ul_ass, tr_PacketUlDynAssign)) {
Harald Welte4b6c7722017-08-01 00:07:12 +0200318 set_ph_state(PH_STATE_TBF);
319
320 /* store/save channel description */
321 //chan_desc := imm_ass.chan_desc;
322
323 /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200324 f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, pkt_ul_ass.dynamic.usf);
Harald Welte4b6c7722017-08-01 00:07:12 +0200325 f_L1CTL_TBF_CFG(L1CTL, true, tua);
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200326 return true;
327 /* FIXME: Single Block Allocation */
328 } else if (match(pkt_ul_ass, tr_PacketUlSglAssign)) {
Harald Welte4b6c7722017-08-01 00:07:12 +0200329 log("Non-dynamic UL TBF assignment not supported yet");
Vadim Yanitskiyc9b2ba22019-09-09 16:02:10 +0200330 return false;
331 } else {
332 log("Failed to match Uplink Block Allocation: ", pkt_ul_ass);
333 return false;
Harald Welte4b6c7722017-08-01 00:07:12 +0200334 }
335 }
336
337 private function f_release_tbf() runs on lapdm_CT {
338 var TfiUsfArr tua := f_TfiUsfArrInit();
339 /* send "all timeslots unused" for both UL and DL */
340 f_L1CTL_TBF_CFG(L1CTL, true, tua);
341 f_L1CTL_TBF_CFG(L1CTL, false, tua);
342 /* L1 will then fall back to BCCH/CCCH */
343 set_ph_state(PH_STATE_BCH);
344 }
345
Harald Welteb669ee02018-03-09 12:50:02 +0100346 /* Establish TBF / packet transfer mode */
347 private altstep as_tbf_ul_est() runs on lapdm_CT {
348 var TBF_UL_establish_req tbf_ul_req;
349 [] LAPDM_SP.receive(TBF_UL_establish_req:?) -> value tbf_ul_req {
350 var TbfNr tbf_nr := tbf_ul_req.tbf_nr;
351 var TBF_UL_establish_res res;
352 if (isvalue(g_tbf_ul[tbf_nr].arfcn)) {
353 setverdict(fail, "Cannot establish UL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200354 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100355 }
356 f_establish_tbf(tbf_ul_req.ra);
357 if (ph_state == PH_STATE_TBF) {
358 g_tbf_ul[tbf_nr] := valueof(t_TbfParsInit); /* FIXME: Actual TFI[s] */
359 log("Established UL TBF ", tbf_nr);
360 res := { pars := g_tbf_ul[tbf_nr], err := omit };
361 } else {
362 res := { pars := omit, err := "Unable to establish UL TBF" };
363 }
364 LAPDM_SP.send(res);
365 }
366 }
367
368 private altstep as_tbf_dl_est() runs on lapdm_CT {
369 var TBF_DL_establish_req tbf_dl_req;
370 [] LAPDM_SP.receive(TBF_DL_establish_req:?) -> value tbf_dl_req {
371 var TbfNr tbf_nr := tbf_dl_req.tbf_nr;
372 if (isvalue(g_tbf_dl[tbf_nr].arfcn)) {
373 setverdict(fail, "Cannot establish DL TBF ID ", tbf_nr, ": BUSY");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200374 mtc.stop;
Harald Welteb669ee02018-03-09 12:50:02 +0100375 }
376 g_tbf_dl[tbf_nr] := tbf_dl_req.pars;
377 f_L1CTL_TBF_CFG(L1CTL, false, tbf_dl_req.pars.tfi);
378 set_ph_state(PH_STATE_TBF);
379 log("Established DL TBF ", tbf_nr, ": ", tbf_dl_req.pars);
380 }
381 }
382
383 private function f_init_tbf() runs on lapdm_CT {
384 var integer i;
385 for (i := 0; i < 8; i := i+1) {
386 g_tbf_ul[i] := valueof(t_TbfParsInit);
387 g_tbf_dl[i] := valueof(t_TbfParsInit);
388 }
389 }
390
Harald Welted4ba7ff2017-07-17 21:00:48 +0200391 function ScanEvents() runs on lapdm_CT {
392 var L1ctlDlMessage dl;
393 var BCCH_tune_req bt;
394 var LAPDm_ph_data lpd;
Harald Welte4b6c7722017-08-01 00:07:12 +0200395 var RLCMAC_ph_data_ind rpdi;
396 var RLCMAC_ph_data_req rpdr;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200397 var DCCH_establish_req est_req;
Harald Welte66f07542019-05-30 17:35:31 +0200398 var DCCH_switch_req sw_req;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200399 var DCCH_establish_res est_res;
Harald Welteb669ee02018-03-09 12:50:02 +0100400
401 f_init_tbf();
Harald Welted4ba7ff2017-07-17 21:00:48 +0200402
403 while (true) {
404 if (ph_state == PH_STATE_NULL) {
405 alt {
406 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
407 f_tune_bcch(bt.arfcn, bt.combined_ccch);
408 }
409
410 [] LAPDM_SP.receive {}
411 [] L1CTL.receive {}
412
413 }
414 } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) {
415 alt {
416 [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt {
417 f_tune_bcch(bt.arfcn, bt.combined_ccch);
418 }
419
420 /* forward CCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100421 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200422 lpd.sacch := false;
423 lpd.sapi := 0;
424 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
425 LAPDM_SP.send(lpd);
426 }
427
428 /* forward BCCH SAPI from L1CTL to User */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100429 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
Harald Welted4ba7ff2017-07-17 21:00:48 +0200430 lpd.sacch := false;
431 lpd.sapi := 0;
432 lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload);
433 LAPDM_SP.send(lpd);
434 }
435
436 /* Establish dedicated channel */
437 [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req {
438 var DCCH_establish_res res;
439 f_establish_dcch(est_req.ra);
440 if (ph_state == PH_STATE_DCH) {
441 res := { chan_desc, omit };
442 } else {
443 res := { omit, "Unable to esetablish DCCH" };
444 }
445 LAPDM_SP.send(res);
446 }
Harald Welte66f07542019-05-30 17:35:31 +0200447 [] LAPDM_SP.receive(DCCH_switch_req:?) -> value sw_req {
448 var DCCH_switch_res res;
Vadim Yanitskiyeec14f02020-07-14 19:04:18 +0700449 f_switch_dcch(sw_req);
Harald Welte66f07542019-05-30 17:35:31 +0200450 if (ph_state == PH_STATE_DCH) {
451 res := { omit };
452 } else {
453 res := { "Unable to switch to DCCH" };
454 }
455 LAPDM_SP.send(res);
456 }
457
Harald Welted4ba7ff2017-07-17 21:00:48 +0200458
Harald Welteb669ee02018-03-09 12:50:02 +0100459 [] as_tbf_ul_est();
460 [] as_tbf_dl_est();
Harald Welte4b6c7722017-08-01 00:07:12 +0200461
Harald Welted4ba7ff2017-07-17 21:00:48 +0200462 [] LAPDM_SP.receive {}
463 [] L1CTL.receive {}
464
465 }
466
467 } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) {
468 alt {
469
470 /* decode any received DATA frames for the dedicated channel and pass them up */
Harald Weltef8df4cb2018-03-10 15:15:08 +0100471 [] L1CTL.receive(tr_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl {
Harald Welte1365a4e2019-06-01 23:04:43 +0200472 var octetstring l2;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200473 if (dl.dl_info.link_id.c == SACCH) {
474 lpd.sacch := true;
Harald Welte1365a4e2019-06-01 23:04:43 +0200475 var octetstring l1 := substr(dl.payload.data_ind.payload, 0, 2);
476 l2 := substr(dl.payload.data_ind.payload, 2,
477 lengthof(dl.payload.data_ind.payload)-2);
478 ms_power_lvl := oct2int(l1[0] and4b '1F'O);
479 timing_adv := oct2int(l1[1]);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200480 /* FIXME: how to deal with UI frames in B4 format (lo length!) */
481 } else {
482 lpd.sacch := false;
Harald Welte1365a4e2019-06-01 23:04:43 +0200483 l2 := dl.payload.data_ind.payload;
Harald Welted4ba7ff2017-07-17 21:00:48 +0200484 }
485 lpd.sapi := dl.dl_info.link_id.sapi;
Harald Welte1365a4e2019-06-01 23:04:43 +0200486 lpd.lapdm.ab := dec_LapdmFrameAB(l2);
Harald Welted4ba7ff2017-07-17 21:00:48 +0200487 LAPDM_SP.send(lpd);
488 }
489
490 /* encode any LAPDm record from user and pass it on to L1CTL */
491 [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd {
492 var octetstring buf;
493 var RslLinkId link_id;
494 if (lpd.sacch) {
495 link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200496 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 21, '2B'O);
Harald Welte1365a4e2019-06-01 23:04:43 +0200497 var SacchL1Header l1h := valueof(ts_SacchL1Header(ms_power_lvl,
Vadim Yanitskiy68c4cff2022-07-30 23:45:02 +0700498 timing_adv));
Harald Welte1365a4e2019-06-01 23:04:43 +0200499 L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(chan_desc.chan_nr, link_id,
500 l1h, buf));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200501 } else {
502 link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi));
Harald Welte9de7f642019-05-30 15:08:57 +0200503 buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 23, '2B'O);
Harald Welte1365a4e2019-06-01 23:04:43 +0200504 L1CTL.send(ts_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf));
Harald Welted4ba7ff2017-07-17 21:00:48 +0200505 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200506 }
507
508 /* Release dedicated channel */
509 [] LAPDM_SP.receive(DCCH_release_req:?) {
510 /* go back to BCCH */
511 f_release_dcch();
512 }
513
514 [] LAPDM_SP.receive {}
515 [] L1CTL.receive {}
516
517
518 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200519 } else if (ph_state == PH_STATE_TBF) {
520 alt {
521
522 /* decode + forward any blocks from L1 to L23*/
Harald Weltef8df4cb2018-03-10 15:15:08 +0100523 [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl {
Harald Welte4b6c7722017-08-01 00:07:12 +0200524 rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload);
Harald Welteb669ee02018-03-09 12:50:02 +0100525 /* FIXME: Filter based on g_tbf_dl */
Harald Welte7024baa2018-03-02 23:37:51 +0100526 rpdi.fn := dl.dl_info.frame_nr;
527 rpdi.ts_nr := dl.dl_info.chan_nr.tn;
Harald Welte4b6c7722017-08-01 00:07:12 +0200528 rpdi.cs := CS1; /* FIXME */
529 log("RPDI: ", rpdi);
530 LAPDM_SP.send(rpdi);
531 }
532
533 [] L1CTL.receive { }
534
535 /* encode + forward any blocks from L23 to L1 */
536 [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr {
537 var octetstring buf;
Harald Welte7024baa2018-03-02 23:37:51 +0100538 if (ischosen(rpdr.dyn)) {
539 buf := enc_RlcmacUlBlock(rpdr.dyn.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100540 L1CTL.send(ts_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.dyn.tbf_id));
Harald Welte7024baa2018-03-02 23:37:51 +0100541 } else {
542 buf := enc_RlcmacUlBlock(rpdr.abs.block);
Harald Weltef8df4cb2018-03-10 15:15:08 +0100543 L1CTL.send(ts_L1CTL_DATA_ABS_REQ(buf, rpdr.abs.arfcn,
Harald Welte7024baa2018-03-02 23:37:51 +0100544 rpdr.abs.ts_nr, rpdr.abs.fn,
545 L1CTL_CS1, rpdr.abs.tbf_id));
546 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200547 }
548
Harald Welteb669ee02018-03-09 12:50:02 +0100549 [] as_tbf_ul_est();
550 [] as_tbf_dl_est();
551
Harald Welte4b6c7722017-08-01 00:07:12 +0200552 /* FIXME: release TBF mode */
553 [] LAPDM_SP.receive(DCCH_release_req:?) {
554 /* go back to BCCH */
555 f_release_tbf();
Harald Welteb669ee02018-03-09 12:50:02 +0100556 f_init_tbf();
Harald Welte4b6c7722017-08-01 00:07:12 +0200557 }
558
559 }
Harald Welted4ba7ff2017-07-17 21:00:48 +0200560 }
Harald Welte4b6c7722017-08-01 00:07:12 +0200561
Harald Welted4ba7ff2017-07-17 21:00:48 +0200562 } /* while (1) */
563 }
564}