Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 1 | /* 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 Welte | 34b5a95 | 2019-05-27 11:54:11 +0200 | [diff] [blame] | 4 | |
| 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 Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 14 | module LAPDm_RAW_PT { |
| 15 | import from GSM_Types all; |
Harald Welte | ffcad68 | 2017-07-30 22:51:04 +0200 | [diff] [blame] | 16 | import from GSM_RR_Types all; |
Vadim Yanitskiy | 150d6d1 | 2022-10-20 19:10:04 +0700 | [diff] [blame] | 17 | import from GSM_RestOctets all; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 18 | import from Osmocom_Types all; |
| 19 | import from L1CTL_Types all; |
| 20 | import from L1CTL_PortType all; |
| 21 | import from LAPDm_Types all; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 22 | import from RLCMAC_Types all; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 23 | |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 24 | type record length(8) of uint8_t TfiUsfArr; |
| 25 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 26 | /* request to tune to a given ARFCN and start BCCH decoding */ |
| 27 | type record BCCH_tune_req { |
Vadim Yanitskiy | 1acc7bb | 2020-11-14 04:24:57 +0700 | [diff] [blame] | 28 | GsmBandArfcn arfcn, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 29 | boolean combined_ccch |
| 30 | } |
| 31 | |
| 32 | /* ask for a dedicated channel to be established */ |
| 33 | type record DCCH_establish_req { |
| 34 | uint8_t ra |
| 35 | } |
| 36 | |
| 37 | type record DCCH_establish_res { |
| 38 | ChannelDescription chan_desc optional, |
| 39 | charstring err optional |
| 40 | } |
| 41 | |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 42 | /* directly switch to a dedicated channel (without RACH/IMM.ASS */ |
| 43 | type record DCCH_switch_req { |
Vadim Yanitskiy | eec14f0 | 2020-07-14 19:04:18 +0700 | [diff] [blame] | 44 | ChannelDescription chan_desc, |
Vadim Yanitskiy | ca81392 | 2020-09-12 19:08:31 +0700 | [diff] [blame] | 45 | L1ctlMA ma optional |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 46 | } |
| 47 | |
| 48 | type record DCCH_switch_res { |
| 49 | charstring err optional |
| 50 | } |
| 51 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 52 | type record length(8) of uint8_t TfiList; |
| 53 | type record TbfPars { |
| 54 | GsmArfcn arfcn optional, |
| 55 | /* Temporary Flow Identifier for each TN */ |
| 56 | TfiList tfi |
| 57 | } |
| 58 | type record length(8) of TbfPars TbfParsPerTs; |
| 59 | |
| 60 | template TbfPars t_TbfParsInit := { |
| 61 | arfcn := omit, |
| 62 | tfi := { 255, 255, 255, 255, 255, 255, 255, 255 } |
| 63 | } |
| 64 | |
| 65 | type record TBF_UL_establish_res { |
| 66 | TbfPars pars optional, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 67 | charstring err optional |
| 68 | } |
| 69 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 70 | type record DCCH_release_req { |
| 71 | } |
| 72 | |
| 73 | /* PH-DATA.ind / PH-DATA.req */ |
| 74 | type record LAPDm_ph_data { |
| 75 | boolean sacch, |
| 76 | GsmSapi sapi, |
| 77 | LapdmFrame lapdm |
| 78 | } |
| 79 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 80 | type integer TbfNr (0..7); /* maximum of 8 concurrent TBF per direction */ |
| 81 | type record TBF_UL_establish_req { |
| 82 | TbfNr tbf_nr, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 83 | uint8_t ra |
| 84 | } |
| 85 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 86 | type record TBF_DL_establish_req { |
| 87 | TbfNr tbf_nr, |
| 88 | TbfPars pars |
| 89 | } |
| 90 | |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 91 | /* PH-DATA.ind / PH-DATA.req */ |
| 92 | type record RLCMAC_ph_data_ind { |
| 93 | GprsCodingScheme cs, |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 94 | uint8_t ts_nr, |
| 95 | GsmFrameNumber fn, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 96 | RlcmacDlBlock block |
| 97 | } |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 98 | type record RLCMAC_ph_data_req_dyn { |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 99 | uint8_t tbf_id, |
| 100 | GprsCodingScheme cs, |
| 101 | RlcmacUlBlock block |
| 102 | } |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 103 | type record RLCMAC_ph_data_req_abs { |
| 104 | uint8_t tbf_id, |
| 105 | GprsCodingScheme cs, |
| 106 | uint8_t ts_nr, |
| 107 | GsmFrameNumber fn, |
Vadim Yanitskiy | 1acc7bb | 2020-11-14 04:24:57 +0700 | [diff] [blame] | 108 | GsmBandArfcn arfcn, |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 109 | RlcmacUlBlock block |
| 110 | } |
| 111 | type union RLCMAC_ph_data_req { |
| 112 | RLCMAC_ph_data_req_dyn dyn, |
| 113 | RLCMAC_ph_data_req_abs abs |
| 114 | } |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 115 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 116 | /* port from our (internal) point of view */ |
| 117 | type port LAPDm_SP_PT message { |
| 118 | in BCCH_tune_req, |
| 119 | DCCH_establish_req, |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 120 | DCCH_switch_req, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 121 | DCCH_release_req, |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 122 | TBF_UL_establish_req, |
| 123 | TBF_DL_establish_req, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 124 | RLCMAC_ph_data_req, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 125 | LAPDm_ph_data; |
| 126 | out DCCH_establish_res, |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 127 | DCCH_switch_res, |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 128 | TBF_UL_establish_res, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 129 | RLCMAC_ph_data_ind, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 130 | LAPDm_ph_data; |
| 131 | } with {extension "internal"}; |
| 132 | |
| 133 | /* port from user (external) point of view */ |
| 134 | type port LAPDm_PT message { |
| 135 | in DCCH_establish_res, |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 136 | DCCH_switch_res, |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 137 | TBF_UL_establish_res, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 138 | RLCMAC_ph_data_ind, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 139 | LAPDm_ph_data; |
| 140 | out BCCH_tune_req, |
| 141 | DCCH_establish_req, |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 142 | DCCH_switch_req, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 143 | DCCH_release_req, |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 144 | TBF_UL_establish_req, |
| 145 | TBF_DL_establish_req, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 146 | RLCMAC_ph_data_req, |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 147 | LAPDm_ph_data; |
| 148 | } with {extension "internal"}; |
| 149 | |
| 150 | function LAPDmStart() runs on lapdm_CT { |
| 151 | f_init(); |
| 152 | ScanEvents(); |
| 153 | } |
| 154 | |
| 155 | /* TS 44.004 Figure 5.1 */ |
| 156 | type enumerated ph_state_enum { |
| 157 | PH_STATE_NULL, |
| 158 | PH_STATE_BCH, |
| 159 | PH_STATE_SEARCHING_BCH, |
| 160 | PH_STATE_TUNING_DCH, |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 161 | PH_STATE_DCH, |
| 162 | PH_STATE_TBF |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | type component lapdm_CT { |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 166 | |
| 167 | /* L1CTL port towards the bottom */ |
| 168 | port L1CTL_PT L1CTL; |
| 169 | /* Port towards L2 */ |
| 170 | port LAPDm_SP_PT LAPDM_SP; |
| 171 | |
| 172 | /* physical layer state */ |
| 173 | var ph_state_enum ph_state := PH_STATE_NULL; |
| 174 | |
| 175 | /* channel description of the currently active DCH */ |
| 176 | var ChannelDescription chan_desc; |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 177 | |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 178 | /* last SACCH downlink L1 header we received */ |
| 179 | var uint5_t ms_power_lvl := 0; |
| 180 | var uint8_t timing_adv := 0; |
| 181 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 182 | var TbfParsPerTs g_tbf_ul; |
| 183 | var TbfParsPerTs g_tbf_dl; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 184 | }; |
| 185 | |
| 186 | /* wrapper function to log state transitions */ |
| 187 | private function set_ph_state(ph_state_enum new_state) runs on lapdm_CT { |
| 188 | log("PH-STATE ", ph_state, " -> ", new_state); |
| 189 | ph_state := new_state; |
| 190 | } |
| 191 | |
| 192 | private function f_init() runs on lapdm_CT { |
Harald Welte | b26cfff | 2017-08-25 09:56:47 +0200 | [diff] [blame] | 193 | f_connect_reset(L1CTL); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 194 | set_ph_state(PH_STATE_NULL); |
| 195 | } |
| 196 | |
| 197 | /* release the dedicated radio channel */ |
| 198 | private function f_release_dcch() runs on lapdm_CT { |
Harald Welte | f8df4cb | 2018-03-10 15:15:08 +0100 | [diff] [blame] | 199 | L1CTL.send(ts_L1CTL_DM_REL_REQ(chan_desc.chan_nr)); |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 200 | set_ph_state(PH_STATE_BCH); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | /* tune to given ARFCN and start BCCH/CCCH decoding */ |
Vadim Yanitskiy | 1acc7bb | 2020-11-14 04:24:57 +0700 | [diff] [blame] | 204 | private function f_tune_bcch(GsmBandArfcn arfcn, boolean combined) |
| 205 | runs on lapdm_CT { |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 206 | var L1ctlCcchMode mode := CCCH_MODE_NON_COMBINED; |
| 207 | if (combined) { |
| 208 | mode := CCCH_MODE_COMBINED; |
| 209 | } |
| 210 | |
| 211 | if (ph_state == PH_STATE_DCH) { |
| 212 | /* release any previous DCH */ |
| 213 | f_release_dcch(); |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 214 | } else if (ph_state == PH_STATE_TBF) { |
| 215 | f_release_tbf(); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | set_ph_state(PH_STATE_SEARCHING_BCH); |
| 219 | |
| 220 | /* send FB/SB req to sync to cell */ |
| 221 | f_L1CTL_FBSB(L1CTL, arfcn, mode); |
| 222 | set_ph_state(PH_STATE_BCH); |
| 223 | } |
| 224 | |
| 225 | /* master function establishing a dedicated radio channel */ |
| 226 | private function f_establish_dcch(uint8_t ra) runs on lapdm_CT { |
| 227 | var ImmediateAssignment imm_ass; |
| 228 | var GsmFrameNumber rach_fn; |
| 229 | |
| 230 | /* send RACH request and obtain FN at which it was sent */ |
| 231 | rach_fn := f_L1CTL_RACH(L1CTL, ra); |
| 232 | //if (not rach_fn) { return; } |
| 233 | |
| 234 | /* wait for receiving matching IMM ASS */ |
| 235 | imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn) |
| 236 | //if (not imm_ass) { return; } |
| 237 | set_ph_state(PH_STATE_TUNING_DCH); |
| 238 | |
| 239 | /* store/save channel description */ |
| 240 | chan_desc := imm_ass.chan_desc; |
| 241 | |
Vadim Yanitskiy | eec14f0 | 2020-07-14 19:04:18 +0700 | [diff] [blame] | 242 | /* send DM_EST_REQ, TODO: Mobile Allocation */ |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 243 | f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass); |
| 244 | set_ph_state(PH_STATE_DCH); |
| 245 | } |
| 246 | |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 247 | /* switching directly to a dedicated channel *without RACH/IMM-ASS */ |
Vadim Yanitskiy | eec14f0 | 2020-07-14 19:04:18 +0700 | [diff] [blame] | 248 | private function f_switch_dcch(in DCCH_switch_req sw_req) runs on lapdm_CT { |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 249 | set_ph_state(PH_STATE_TUNING_DCH); |
| 250 | /* store/save channel description */ |
Vadim Yanitskiy | eec14f0 | 2020-07-14 19:04:18 +0700 | [diff] [blame] | 251 | chan_desc := sw_req.chan_desc; |
| 252 | |
| 253 | /* tune the L1 to the indicated channel */ |
| 254 | if (chan_desc.h) { |
| 255 | L1CTL.send(ts_L1CTL_DM_EST_REQ_H1(chan_desc.chan_nr, |
| 256 | chan_desc.tsc, |
| 257 | chan_desc.maio_hsn.hsn, |
| 258 | chan_desc.maio_hsn.maio, |
| 259 | sw_req.ma)); |
| 260 | } else { |
| 261 | L1CTL.send(ts_L1CTL_DM_EST_REQ_H0(chan_desc.chan_nr, |
| 262 | chan_desc.tsc, |
| 263 | chan_desc.arfcn)); |
| 264 | } |
| 265 | |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 266 | set_ph_state(PH_STATE_DCH); |
| 267 | } |
| 268 | |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 269 | /* initialize a tfi_usf array with "not used" value 255 for all TN */ |
| 270 | function f_TfiUsfArrInit() return TfiUsfArr { |
| 271 | var TfiUsfArr tua := { 255, 255, 255, 255, 255, 255, 255, 255 }; |
| 272 | return tua; |
| 273 | } |
| 274 | |
| 275 | /* set TFI/USF value for one given timeslot number (index) */ |
| 276 | function f_TfiUsfArrSet(inout TfiUsfArr a, in uint8_t idx, in uint8_t tfi_usf) { |
| 277 | a[idx] := tfi_usf; |
| 278 | } |
| 279 | |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 280 | template (value) RLCMAC_ph_data_req ts_PH_DATA_ABS(uint8_t tbf_id, GprsCodingScheme cs, |
Vadim Yanitskiy | 1acc7bb | 2020-11-14 04:24:57 +0700 | [diff] [blame] | 281 | uint8_t ts, uint32_t fn, |
| 282 | GsmBandArfcn arfcn, |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 283 | RlcmacUlBlock block) := { |
| 284 | abs := { |
| 285 | tbf_id := tbf_id, |
| 286 | cs := CS1, /* FIXME */ |
| 287 | ts_nr := ts, |
| 288 | fn := fn, |
| 289 | arfcn := arfcn, |
| 290 | block := block |
| 291 | } |
| 292 | } |
| 293 | |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 294 | private function f_establish_tbf(uint8_t ra) runs on lapdm_CT return boolean { |
| 295 | var template GsmRrMessage imm_ass_rr; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 296 | var ImmediateAssignment imm_ass; |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 297 | var PacketUlAssign pkt_ul_ass; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 298 | var GsmFrameNumber rach_fn; |
| 299 | var TfiUsfArr tua := f_TfiUsfArrInit(); |
| 300 | |
| 301 | /* send RACH request and obtain FN at which it was sent */ |
| 302 | rach_fn := f_L1CTL_RACH(L1CTL, ra); |
| 303 | |
| 304 | /* wait for receiving matching IMM ASS */ |
| 305 | imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, rach_fn); |
| 306 | |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 307 | /* make sure we got *Packet* (Uplink) Immediate Assignment */ |
| 308 | imm_ass_rr := tr_IMM_TBF_ASS(dl := false, ra := ra, fn := rach_fn, |
| 309 | rest := tr_IaRestOctets_ULAss(?)); |
| 310 | if (not match(imm_ass, imm_ass_rr.payload.imm_ass)) { |
| 311 | log("Failed to match Packet Immediate Assignment"); |
| 312 | return false; |
| 313 | } |
| 314 | |
| 315 | /* decapsulate PacketUlAssign for further matching */ |
| 316 | pkt_ul_ass := imm_ass.rest_octets.hh.pa.uldl.ass.ul; |
| 317 | |
| 318 | /* Dynamic Block Allocation */ |
| 319 | if (match(pkt_ul_ass, tr_PacketUlDynAssign)) { |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 320 | set_ph_state(PH_STATE_TBF); |
| 321 | |
| 322 | /* store/save channel description */ |
| 323 | //chan_desc := imm_ass.chan_desc; |
| 324 | |
| 325 | /* Important: ARFCN, TN, TSC, USF, USF_GRANULARITY, CH_CODING_CMD */ |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 326 | f_TfiUsfArrSet(tua, imm_ass.pkt_chan_desc.tn, pkt_ul_ass.dynamic.usf); |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 327 | // FIXME: f_L1CTL_TBF_CFG(L1CTL, true, tua); |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 328 | return true; |
| 329 | /* FIXME: Single Block Allocation */ |
| 330 | } else if (match(pkt_ul_ass, tr_PacketUlSglAssign)) { |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 331 | log("Non-dynamic UL TBF assignment not supported yet"); |
Vadim Yanitskiy | c9b2ba2 | 2019-09-09 16:02:10 +0200 | [diff] [blame] | 332 | return false; |
| 333 | } else { |
| 334 | log("Failed to match Uplink Block Allocation: ", pkt_ul_ass); |
| 335 | return false; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 336 | } |
| 337 | } |
| 338 | |
| 339 | private function f_release_tbf() runs on lapdm_CT { |
| 340 | var TfiUsfArr tua := f_TfiUsfArrInit(); |
| 341 | /* send "all timeslots unused" for both UL and DL */ |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 342 | // FIXME: f_L1CTL_TBF_CFG(L1CTL, true, tua); |
| 343 | // FIXME: f_L1CTL_TBF_CFG(L1CTL, false, tua); |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 344 | /* L1 will then fall back to BCCH/CCCH */ |
| 345 | set_ph_state(PH_STATE_BCH); |
| 346 | } |
| 347 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 348 | /* Establish TBF / packet transfer mode */ |
| 349 | private altstep as_tbf_ul_est() runs on lapdm_CT { |
| 350 | var TBF_UL_establish_req tbf_ul_req; |
| 351 | [] LAPDM_SP.receive(TBF_UL_establish_req:?) -> value tbf_ul_req { |
| 352 | var TbfNr tbf_nr := tbf_ul_req.tbf_nr; |
| 353 | var TBF_UL_establish_res res; |
| 354 | if (isvalue(g_tbf_ul[tbf_nr].arfcn)) { |
| 355 | setverdict(fail, "Cannot establish UL TBF ID ", tbf_nr, ": BUSY"); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 356 | mtc.stop; |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 357 | } |
| 358 | f_establish_tbf(tbf_ul_req.ra); |
| 359 | if (ph_state == PH_STATE_TBF) { |
| 360 | g_tbf_ul[tbf_nr] := valueof(t_TbfParsInit); /* FIXME: Actual TFI[s] */ |
| 361 | log("Established UL TBF ", tbf_nr); |
| 362 | res := { pars := g_tbf_ul[tbf_nr], err := omit }; |
| 363 | } else { |
| 364 | res := { pars := omit, err := "Unable to establish UL TBF" }; |
| 365 | } |
| 366 | LAPDM_SP.send(res); |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | private altstep as_tbf_dl_est() runs on lapdm_CT { |
| 371 | var TBF_DL_establish_req tbf_dl_req; |
| 372 | [] LAPDM_SP.receive(TBF_DL_establish_req:?) -> value tbf_dl_req { |
| 373 | var TbfNr tbf_nr := tbf_dl_req.tbf_nr; |
| 374 | if (isvalue(g_tbf_dl[tbf_nr].arfcn)) { |
| 375 | setverdict(fail, "Cannot establish DL TBF ID ", tbf_nr, ": BUSY"); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 376 | mtc.stop; |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 377 | } |
| 378 | g_tbf_dl[tbf_nr] := tbf_dl_req.pars; |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 379 | // FIXME: f_L1CTL_TBF_CFG(L1CTL, false, tbf_dl_req.pars.tfi); |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 380 | set_ph_state(PH_STATE_TBF); |
| 381 | log("Established DL TBF ", tbf_nr, ": ", tbf_dl_req.pars); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | private function f_init_tbf() runs on lapdm_CT { |
| 386 | var integer i; |
| 387 | for (i := 0; i < 8; i := i+1) { |
| 388 | g_tbf_ul[i] := valueof(t_TbfParsInit); |
| 389 | g_tbf_dl[i] := valueof(t_TbfParsInit); |
| 390 | } |
| 391 | } |
| 392 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 393 | function ScanEvents() runs on lapdm_CT { |
Vadim Yanitskiy | af0aae6 | 2023-03-18 06:49:18 +0700 | [diff] [blame] | 394 | var L1ctlMessage dl; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 395 | var BCCH_tune_req bt; |
| 396 | var LAPDm_ph_data lpd; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 397 | var RLCMAC_ph_data_ind rpdi; |
| 398 | var RLCMAC_ph_data_req rpdr; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 399 | var DCCH_establish_req est_req; |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 400 | var DCCH_switch_req sw_req; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 401 | var DCCH_establish_res est_res; |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 402 | |
| 403 | f_init_tbf(); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 404 | |
| 405 | while (true) { |
| 406 | if (ph_state == PH_STATE_NULL) { |
| 407 | alt { |
| 408 | [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt { |
| 409 | f_tune_bcch(bt.arfcn, bt.combined_ccch); |
| 410 | } |
| 411 | |
| 412 | [] LAPDM_SP.receive {} |
| 413 | [] L1CTL.receive {} |
| 414 | |
| 415 | } |
| 416 | } else if (ph_state == PH_STATE_BCH or ph_state == PH_STATE_SEARCHING_BCH) { |
| 417 | alt { |
| 418 | [] LAPDM_SP.receive(BCCH_tune_req:?) -> value bt { |
| 419 | f_tune_bcch(bt.arfcn, bt.combined_ccch); |
| 420 | } |
| 421 | |
| 422 | /* forward CCCH SAPI from L1CTL to User */ |
Harald Welte | f8df4cb | 2018-03-10 15:15:08 +0100 | [diff] [blame] | 423 | [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0))) -> value dl { |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 424 | lpd.sacch := false; |
| 425 | lpd.sapi := 0; |
| 426 | lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload); |
| 427 | LAPDM_SP.send(lpd); |
| 428 | } |
| 429 | |
| 430 | /* forward BCCH SAPI from L1CTL to User */ |
Harald Welte | f8df4cb | 2018-03-10 15:15:08 +0100 | [diff] [blame] | 431 | [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl { |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 432 | lpd.sacch := false; |
| 433 | lpd.sapi := 0; |
| 434 | lpd.lapdm.bbis := dec_LapdmFrameBbis(dl.payload.data_ind.payload); |
| 435 | LAPDM_SP.send(lpd); |
| 436 | } |
| 437 | |
| 438 | /* Establish dedicated channel */ |
| 439 | [] LAPDM_SP.receive(DCCH_establish_req:?) -> value est_req { |
| 440 | var DCCH_establish_res res; |
| 441 | f_establish_dcch(est_req.ra); |
| 442 | if (ph_state == PH_STATE_DCH) { |
| 443 | res := { chan_desc, omit }; |
| 444 | } else { |
| 445 | res := { omit, "Unable to esetablish DCCH" }; |
| 446 | } |
| 447 | LAPDM_SP.send(res); |
| 448 | } |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 449 | [] LAPDM_SP.receive(DCCH_switch_req:?) -> value sw_req { |
| 450 | var DCCH_switch_res res; |
Vadim Yanitskiy | eec14f0 | 2020-07-14 19:04:18 +0700 | [diff] [blame] | 451 | f_switch_dcch(sw_req); |
Harald Welte | 66f0754 | 2019-05-30 17:35:31 +0200 | [diff] [blame] | 452 | if (ph_state == PH_STATE_DCH) { |
| 453 | res := { omit }; |
| 454 | } else { |
| 455 | res := { "Unable to switch to DCCH" }; |
| 456 | } |
| 457 | LAPDM_SP.send(res); |
| 458 | } |
| 459 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 460 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 461 | [] as_tbf_ul_est(); |
| 462 | [] as_tbf_dl_est(); |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 463 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 464 | [] LAPDM_SP.receive {} |
| 465 | [] L1CTL.receive {} |
| 466 | |
| 467 | } |
| 468 | |
| 469 | } else if (ph_state == PH_STATE_TUNING_DCH or ph_state == PH_STATE_DCH) { |
| 470 | alt { |
| 471 | |
| 472 | /* decode any received DATA frames for the dedicated channel and pass them up */ |
Harald Welte | f8df4cb | 2018-03-10 15:15:08 +0100 | [diff] [blame] | 473 | [] L1CTL.receive(tr_L1CTL_DATA_IND(chan_desc.chan_nr)) -> value dl { |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 474 | var octetstring l2; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 475 | if (dl.dl_info.link_id.c == SACCH) { |
| 476 | lpd.sacch := true; |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 477 | var octetstring l1 := substr(dl.payload.data_ind.payload, 0, 2); |
| 478 | l2 := substr(dl.payload.data_ind.payload, 2, |
| 479 | lengthof(dl.payload.data_ind.payload)-2); |
| 480 | ms_power_lvl := oct2int(l1[0] and4b '1F'O); |
| 481 | timing_adv := oct2int(l1[1]); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 482 | /* FIXME: how to deal with UI frames in B4 format (lo length!) */ |
| 483 | } else { |
| 484 | lpd.sacch := false; |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 485 | l2 := dl.payload.data_ind.payload; |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 486 | } |
| 487 | lpd.sapi := dl.dl_info.link_id.sapi; |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 488 | lpd.lapdm.ab := dec_LapdmFrameAB(l2); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 489 | LAPDM_SP.send(lpd); |
| 490 | } |
| 491 | |
| 492 | /* encode any LAPDm record from user and pass it on to L1CTL */ |
| 493 | [] LAPDM_SP.receive(LAPDm_ph_data:?) -> value lpd { |
| 494 | var octetstring buf; |
| 495 | var RslLinkId link_id; |
| 496 | if (lpd.sacch) { |
| 497 | link_id := valueof(ts_RslLinkID_SACCH(lpd.sapi)); |
Harald Welte | 9de7f64 | 2019-05-30 15:08:57 +0200 | [diff] [blame] | 498 | buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 21, '2B'O); |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 499 | var SacchL1Header l1h := valueof(ts_SacchL1Header(ms_power_lvl, |
Vadim Yanitskiy | 68c4cff | 2022-07-30 23:45:02 +0700 | [diff] [blame] | 500 | timing_adv)); |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 501 | L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(chan_desc.chan_nr, link_id, |
| 502 | l1h, buf)); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 503 | } else { |
| 504 | link_id := valueof(ts_RslLinkID_DCCH(lpd.sapi)); |
Harald Welte | 9de7f64 | 2019-05-30 15:08:57 +0200 | [diff] [blame] | 505 | buf := f_pad_oct(enc_LapdmFrame(lpd.lapdm), 23, '2B'O); |
Harald Welte | 1365a4e | 2019-06-01 23:04:43 +0200 | [diff] [blame] | 506 | L1CTL.send(ts_L1CTL_DATA_REQ(chan_desc.chan_nr, link_id, buf)); |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 507 | } |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 508 | } |
| 509 | |
| 510 | /* Release dedicated channel */ |
| 511 | [] LAPDM_SP.receive(DCCH_release_req:?) { |
| 512 | /* go back to BCCH */ |
| 513 | f_release_dcch(); |
| 514 | } |
| 515 | |
| 516 | [] LAPDM_SP.receive {} |
| 517 | [] L1CTL.receive {} |
| 518 | |
| 519 | |
| 520 | } |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 521 | } else if (ph_state == PH_STATE_TBF) { |
| 522 | alt { |
| 523 | |
| 524 | /* decode + forward any blocks from L1 to L23*/ |
Harald Welte | f8df4cb | 2018-03-10 15:15:08 +0100 | [diff] [blame] | 525 | [] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(?))) -> value dl { |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 526 | rpdi.block := dec_RlcmacDlBlock(dl.payload.data_ind.payload); |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 527 | /* FIXME: Filter based on g_tbf_dl */ |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 528 | rpdi.fn := dl.dl_info.frame_nr; |
| 529 | rpdi.ts_nr := dl.dl_info.chan_nr.tn; |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 530 | rpdi.cs := CS1; /* FIXME */ |
| 531 | log("RPDI: ", rpdi); |
| 532 | LAPDM_SP.send(rpdi); |
| 533 | } |
| 534 | |
| 535 | [] L1CTL.receive { } |
| 536 | |
| 537 | /* encode + forward any blocks from L23 to L1 */ |
| 538 | [] LAPDM_SP.receive(RLCMAC_ph_data_req:?) -> value rpdr { |
| 539 | var octetstring buf; |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 540 | if (ischosen(rpdr.dyn)) { |
| 541 | buf := enc_RlcmacUlBlock(rpdr.dyn.block); |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 542 | // FIXME: L1CTL.send(ts_L1CTL_DATA_TBF_REQ(buf, L1CTL_CS1, rpdr.dyn.tbf_id)); |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 543 | } else { |
| 544 | buf := enc_RlcmacUlBlock(rpdr.abs.block); |
Vadim Yanitskiy | 72a76b5 | 2023-03-18 00:12:09 +0700 | [diff] [blame] | 545 | // FIXME: L1CTL.send(ts_L1CTL_DATA_ABS_REQ(buf, rpdr.abs.arfcn, |
| 546 | // rpdr.abs.ts_nr, rpdr.abs.fn, |
| 547 | // L1CTL_CS1, rpdr.abs.tbf_id)); |
Harald Welte | 7024baa | 2018-03-02 23:37:51 +0100 | [diff] [blame] | 548 | } |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 549 | } |
| 550 | |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 551 | [] as_tbf_ul_est(); |
| 552 | [] as_tbf_dl_est(); |
| 553 | |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 554 | /* FIXME: release TBF mode */ |
| 555 | [] LAPDM_SP.receive(DCCH_release_req:?) { |
| 556 | /* go back to BCCH */ |
| 557 | f_release_tbf(); |
Harald Welte | b669ee0 | 2018-03-09 12:50:02 +0100 | [diff] [blame] | 558 | f_init_tbf(); |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | } |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 562 | } |
Harald Welte | 4b6c772 | 2017-08-01 00:07:12 +0200 | [diff] [blame] | 563 | |
Harald Welte | d4ba7ff | 2017-07-17 21:00:48 +0200 | [diff] [blame] | 564 | } /* while (1) */ |
| 565 | } |
| 566 | } |