blob: 3bda958b40be6c0f78d8f41de4d568670a26adf6 [file] [log] [blame]
Harald Weltec676c682017-12-24 20:38:20 +01001module IuUP_Emulation {
2
Harald Welte35bb7162018-01-03 21:07:52 +01003/* IuUP emulation, uses the encoding/decoding from IuUP_Types.
4 *
5 * rather than running in a separate component with related primitives,
6 * we just implement a set of functions and data types which can be used
7 * by other code (such as an RTP endpoint) to implement IuUP support.
8 *
9 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
10 * All rights reserved.
11 *
12 * Released under the terms of GNU General Public License, Version 2 or
13 * (at your option) any later version.
Harald Welte34b5a952019-05-27 11:54:11 +020014 *
15 * SPDX-License-Identifier: GPL-2.0-or-later
Harald Welte35bb7162018-01-03 21:07:52 +010016 */
17
Harald Weltec676c682017-12-24 20:38:20 +010018import from Osmocom_Types all;
19import from IuUP_Types all;
20
21
22type record IuUP_RabFlowCombination {
23 IuUP_RFCI rfci,
24 /* number of bits per sub-flow */
25 RecOfU8 sub_flow_bits,
26 /* IPTI value in number of ITIs for the corresponding RFCI */
27 uint8_t ipti
28};
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020029type record of IuUP_RabFlowCombination IuUP_RabFlowCombinationList;
Harald Weltec676c682017-12-24 20:38:20 +010030
31template (value) IuUP_RabFlowCombination t_IuUP_RFC(IuUP_RFCI rfci, RecOfU8 subflow_bits, uint8_t ipti) := {
32 rfci := rfci,
33 sub_flow_bits := subflow_bits,
34 ipti := ipti
35}
36
37template (value) IuUP_RabFlowCombination t_IuUP_RFC_AMR_12_2(IuUP_RFCI rfci) := t_IuUP_RFC(rfci, {81, 103, 60}, 1);
Neels Hofmeyr99f9c8a2023-09-27 01:59:20 +020038template (value) IuUP_RabFlowCombination t_IuUP_RFC_AMR_SID(IuUP_RFCI rfci) := t_IuUP_RFC(rfci, {39, 0, 0}, 7);
Pau Espin Pedrol52ee5182022-05-25 17:34:44 +020039template (value) IuUP_RabFlowCombination t_IuUP_RFC_AMR_NO_DATA(IuUP_RFCI rfci) := t_IuUP_RFC(rfci, {0, 0, 0}, 1);
Harald Weltec676c682017-12-24 20:38:20 +010040
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020041
42const IuUP_RabFlowCombinationList c_IuUP_Config_RabFlowCombination_def := {
43 {
44 rfci := 0,
45 sub_flow_bits := {81, 103, 60},
46 ipti := 1
47 }, {
48 rfci := 1,
Neels Hofmeyr99f9c8a2023-09-27 01:59:20 +020049 sub_flow_bits := {39, 0, 0},
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020050 ipti := 7
51 }, {
52 rfci := 2,
53 sub_flow_bits := {0, 0, 0},
54 ipti := 1
55 }
56};
Harald Weltec676c682017-12-24 20:38:20 +010057type record IuUP_Config {
58 /* actively send INIT (true) or only passively respond (false) */
59 boolean active_init,
60 boolean data_pdu_type_0,
61 /* RAB Flow Combinations */
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020062 IuUP_RabFlowCombinationList rab_flow_combs
Harald Weltec676c682017-12-24 20:38:20 +010063};
64
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020065template (value) IuUP_Config t_IuUP_Config(boolean active_init := true,
66 boolean data_pdu_type_0 := true,
67 template (value) IuUP_RabFlowCombinationList rab_flow_combs :=
68 c_IuUP_Config_RabFlowCombination_def) := {
69 active_init := active_init,
70 data_pdu_type_0 := true,
71 rab_flow_combs := rab_flow_combs
72}
73
74const IuUP_Config c_IuUP_Config_def := {
75 active_init := true,
76 data_pdu_type_0 := true,
77 rab_flow_combs := c_IuUP_Config_RabFlowCombination_def
78}
79
Harald Weltec676c682017-12-24 20:38:20 +010080type enumerated IuUP_Em_State {
81 ST_INIT,
82 ST_DATA_TRANSFER_READY
83};
84
Harald Weltec676c682017-12-24 20:38:20 +010085type record IuUP_Entity {
86 IuUP_Config cfg,
87 IuUP_Em_State state,
88 IuUP_FrameNr tx_next_frame_nr,
89 IuUP_FrameNr rx_last_frame_nr optional,
90 IuUP_PDU pending_tx_pdu optional
91};
92
Pau Espin Pedrol6ed76302022-05-25 18:09:37 +020093template (value) IuUP_Entity t_IuUP_Entity(template (value) IuUP_Config cfg) := {
94 cfg := cfg,
Harald Weltec676c682017-12-24 20:38:20 +010095 state := ST_INIT,
96 tx_next_frame_nr := 0,
97 rx_last_frame_nr := omit,
98 pending_tx_pdu := omit
99}
100
101
102function f_IuUP_Em_rx_decaps(inout IuUP_Entity st, octetstring inp) return octetstring {
103 var IuUP_PDU pdu := dec_IuUP_PDU(inp);
104 if (ischosen(pdu.type_0)) {
105 if (st.cfg.data_pdu_type_0) {
106 /* FIXME: check header / CRC */
107 st.rx_last_frame_nr := pdu.type_0.frame_nr;
108 return pdu.type_0.payload;
109 } else {
110 setverdict(fail, "PDU Type 0 received but 1 configured");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200111 mtc.stop;
Harald Weltec676c682017-12-24 20:38:20 +0100112 }
113 } else if (ischosen(pdu.type_1)) {
114 if (st.cfg.data_pdu_type_0 == false) {
115 /* FIXME: check header / CRC */
116 st.rx_last_frame_nr := pdu.type_1.frame_nr;
117 return pdu.type_1.payload;
118 } else {
119 setverdict(fail, "PDU Type 1 received but 0 configured");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200120 mtc.stop;
Harald Weltec676c682017-12-24 20:38:20 +0100121 }
122 } else if (ischosen(pdu.type_14)) {
123 if (match(pdu, tr_IuUP_INIT)) {
124 if (st.cfg.active_init == true) {
125 setverdict(fail, "INIT received in ACTIVE role");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200126 mtc.stop;
Harald Weltec676c682017-12-24 20:38:20 +0100127 } else {
128 /* store an INIT_ACK to be transmitted later */
129 st.pending_tx_pdu := valueof(ts_IuUP_INIT_ACK(pdu.type_14.frame_nr,
Pau Espin Pedrol5b4f2c62022-05-25 17:07:17 +0200130 pdu.type_14.u.proc.hdr.iuup_version));
Harald Weltec676c682017-12-24 20:38:20 +0100131 }
132 } else if (match(pdu, tr_IuUP_INIT_ACK)) {
133 if (st.cfg.active_init == true) {
134 log("IuUP INIT_ACK Received");
Pau Espin Pedrolb204a4e2021-12-24 14:18:39 +0100135 st.pending_tx_pdu := omit; /* Drop pending Init retrans */
Harald Weltec676c682017-12-24 20:38:20 +0100136 st.state := ST_DATA_TRANSFER_READY;
137 } else {
138 setverdict(fail, "INIT_ACK received in PASSIVE role");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200139 mtc.stop;
Harald Weltec676c682017-12-24 20:38:20 +0100140 }
141 }
142 return ''O;
143 } else {
144 setverdict(fail, "Impossible IuUP PDU decoded from ", inp);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200145 mtc.stop;
Harald Weltec676c682017-12-24 20:38:20 +0100146 }
147 self.stop;
148}
149
Pau Espin Pedrol52ee5182022-05-25 17:34:44 +0200150private function f_ts_IuUP_INIT(inout IuUP_Entity st) return IuUP_PDU
151{
152 var IuUP_PDU pdu;
153 var uint4_t data_pdu_type;
154 var template (omit) IuUP_InitRfci rfci := omit;
155 var IuUP_IPTI_List IPTIs := {};
156 var uint4_t num_rfci := lengthof(st.cfg.rab_flow_combs);
157
158 if (st.cfg.data_pdu_type_0 == true) {
159 data_pdu_type := 0;
160 } else {
161 data_pdu_type := 1;
162 }
163
164 /* Build RFCI list: */
165 for (var integer remain := num_rfci; remain > 0; remain := remain - 1) {
166 var IuUP_RabFlowCombination comb := st.cfg.rab_flow_combs[remain - 1];
167 var boolean lri := false;
168 if (remain == num_rfci) {
169 lri := true;
170 }
171 rfci := ts_IuUP_InitRfci(lri, false, comb.rfci, comb.sub_flow_bits, omit, rfci)
172 }
173
174 /* Build IPTI list: */
175 for (var integer i := 0; i < num_rfci; i := i + 1) {
176 IPTIs := IPTIs & { st.cfg.rab_flow_combs[i].ipti };
177 }
178
179 template (value) IuUP_PDU14_ProcSending_INIT tpl := ts_IuUP_PDU14_ProcSending_INIT(
180 ti := true,
181 subflows_per_rfci := num_rfci,
182 chain_ind := false,
183 rfci := rfci,
184 IPTIs := IPTIs,
185 versions_supported := '0000000000000001'B,
186 data_pdu_type := data_pdu_type
187 );
188 pdu := valueof(ts_IuUP_INIT(tpl));
189 return pdu;
190}
191
Harald Weltec676c682017-12-24 20:38:20 +0100192function f_IuUP_Em_tx_encap(inout IuUP_Entity st, in octetstring payload) return octetstring {
193 var IuUP_PDU pdu;
194 select (st.state) {
195 case (ST_INIT) {
196 if (st.cfg.active_init) {
Pau Espin Pedrolb204a4e2021-12-24 14:18:39 +0100197 if (not isvalue(st.pending_tx_pdu)) {
198 /* send INIT */
Pau Espin Pedrol52ee5182022-05-25 17:34:44 +0200199 pdu := f_ts_IuUP_INIT(st);
Pau Espin Pedrolb204a4e2021-12-24 14:18:39 +0100200 st.pending_tx_pdu := pdu;
201 } /* else: wait for INIT-ACK return ''O at the end */
202
Harald Weltec676c682017-12-24 20:38:20 +0100203 } else {
204 /* wait for INIT */
205 if (isvalue(st.pending_tx_pdu)) {
206 /* we're waiting to transmit the INIT_ACK in response to an
207 * init (passive) */
208 pdu := st.pending_tx_pdu;
209 st.pending_tx_pdu := omit;
210 st.state := ST_DATA_TRANSFER_READY;
211 }
212 }
213 }
214 case (ST_DATA_TRANSFER_READY) {
215 if (st.cfg.data_pdu_type_0) {
216 pdu := valueof(ts_IuUP_Type0(st.tx_next_frame_nr, 0, payload));
217 } else {
218 pdu := valueof(ts_IuUP_Type1(st.tx_next_frame_nr, 0, payload));
219 }
Pau Espin Pedrol64fd02b2021-12-21 18:19:42 +0100220 st.tx_next_frame_nr := (st.tx_next_frame_nr + 1) mod 16;
Harald Weltec676c682017-12-24 20:38:20 +0100221 }
222 }
223 if (isvalue(pdu)) {
224 return f_enc_IuUP_PDU(pdu);
225 } else {
226 return ''O;
227 }
228}
229
230
231}