blob: c1aeadd2041562879430fa3dc318af2ffe7b691f [file] [log] [blame]
Philipp Maier889fe7f2020-07-06 17:44:12 +02001/* E1 traffic handling */
2
3/*
4 * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
5 * All Rights Reserved
6 *
7 * Author: Philipp Maier
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
Philipp Maier993ea6b2020-08-04 18:26:50 +020024#include <inttypes.h>
25
26#include <osmocom/mgcp/mgcp_protocol.h>
27#include <osmocom/mgcp/mgcp.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020028#include <osmocom/mgcp/mgcp_endp.h>
29#include <osmocom/mgcp/mgcp_trunk.h>
30#include <osmocom/mgcp/mgcp_conn.h>
31#include <osmocom/core/msgb.h>
32#include <osmocom/abis/e1_input.h>
33#include <osmocom/abis/abis.h>
34
35#include <osmocom/trau/trau_sync.h>
36#include <osmocom/trau/trau_frame.h>
37#include <osmocom/trau/trau_rtp.h>
38#include <osmocom/mgcp/mgcp_conn.h>
39#include <osmocom/netif/rtp.h>
40#include <osmocom/mgcp/debug.h>
41#include <osmocom/mgcp/mgcp_e1.h>
42#include <osmocom/codec/codec.h>
43
44#define DEBUG_BITS_MAX 80
45#define DEBUG_BYTES_MAX 40
46#define DEBUG_E1_TS 0
47#define E1_TS_BYTES 160
48#define E1_TRAU_BITS 320
49#define E1_TRAU_BITS_MSGB 2048
50
51static struct mgcp_config *cfg;
52
53static const struct e1inp_line_ops dummy_e1_line_ops = {
54 .sign_link_up = NULL,
55 .sign_link_down = NULL,
56 .sign_link = NULL,
57};
58
59/* EFR idle frame */
60static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0,
62 1, 1, 1, 0, 1, 0, 0, 0,
63 0, 0, 0, 0, 1, 0, 0, 0,
64 1, 0, 0, 0, 0, 0, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0,
66 1, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0,
68 1, 0, 0, 0, 0, 0, 0, 0,
69 0, 0, 0, 0, 0, 0, 0, 0,
70 1, 0, 0, 0, 0, 0, 0, 0,
71 0, 0, 0, 0, 0, 0, 0, 0,
72 1, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0,
74 1, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0,
76 1, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0,
78 1, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0,
80 1, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0,
82 1, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0,
84 1, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0,
86 1, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0,
88 1, 0, 0, 0, 0, 0, 0, 0,
89 0, 0, 0, 0, 0, 0, 0, 0,
90 1, 0, 0, 0, 0, 0, 0, 0,
91 0, 0, 0, 0, 0, 0, 0, 0,
92 1, 0, 0, 0, 0, 0, 0, 0,
93 0, 0, 0, 0, 0, 0, 0, 0,
94 1, 0, 0, 0, 0, 0, 0, 0,
95 0, 0, 0, 0, 0, 0, 0, 0,
96 1, 0, 0, 0, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0,
98 1, 0, 0, 0, 0, 0, 1, 0,
99 1, 1, 1, 1, 1, 1, 1, 1,
100};
101
102/* FR idle frame */
103static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0,
105 1, 1, 1, 1, 0, 0, 0, 0,
106 0, 0, 0, 0, 1, 0, 0, 0,
107 1, 0, 0, 0, 0, 0, 0, 0,
108 0, 0, 0, 0, 0, 0, 0, 0,
109 1, 0, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0,
111 1, 0, 0, 0, 0, 0, 0, 0,
112 0, 0, 0, 0, 0, 0, 0, 0,
113 1, 0, 0, 0, 0, 0, 0, 0,
114 0, 0, 0, 0, 0, 0, 0, 0,
115 1, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0,
117 1, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0,
119 1, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0,
121 1, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0,
123 1, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0,
125 1, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0,
127 1, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0,
129 1, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0,
131 1, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0,
133 1, 0, 0, 0, 0, 0, 0, 0,
134 0, 0, 0, 0, 0, 0, 0, 0,
135 1, 0, 0, 0, 0, 0, 0, 0,
136 0, 0, 0, 0, 0, 0, 0, 0,
137 1, 0, 0, 0, 0, 0, 0, 0,
138 0, 0, 0, 0, 0, 0, 0, 0,
139 1, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0,
141 1, 0, 0, 0, 0, 0, 1, 0,
142 1, 1, 1, 1, 1, 1, 1, 1,
143};
144
145/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
146static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
147 0, 0, 0, 0, 0, 0, 0, 0,
148 1, 0, 1, 1, 1, 0, 0, 0,
149 0, 0, 0, 0, 1, 0, 0, 0,
150 1, 1, 1, 1, 1, 1, 1, 1,
151 1, 1, 1, 1, 1, 1, 1, 1,
152 1, 1, 1, 1, 1, 1, 1, 1,
153 1, 1, 1, 1, 1, 1, 1, 1,
154 1, 1, 1, 1, 1, 1, 1, 1,
155 1, 1, 1, 1, 1, 1, 1, 1,
156 1, 1, 1, 1, 1, 1, 1, 1,
157 1, 1, 1, 1, 1, 1, 1, 1,
158 1, 1, 1, 1, 1, 1, 1, 1,
159 1, 1, 1, 1, 1, 1, 1, 1,
160 1, 1, 1, 1, 1, 1, 1, 1,
161 1, 1, 1, 1, 1, 1, 1, 1,
162 1, 1, 1, 1, 1, 1, 1, 1,
163 1, 1, 1, 1, 1, 1, 1, 1,
164 1, 1, 1, 1, 1, 1, 1, 1,
165 1, 1, 1, 1, 1, 1, 1, 1,
166 1, 1, 1, 1, 1, 1, 1, 1,
167 1, 1, 1, 1, 1, 1, 1, 1,
168 1, 1, 1, 1, 1, 1, 1, 1,
169 1, 1, 1, 1, 1, 1, 1, 1,
170 1, 1, 1, 1, 1, 1, 1, 1,
171 1, 1, 1, 1, 1, 1, 1, 1,
172 1, 1, 1, 1, 1, 1, 1, 1,
173 1, 1, 1, 1, 1, 1, 1, 1,
174 1, 1, 1, 1, 1, 1, 1, 1,
175 1, 1, 1, 1, 1, 1, 1, 1,
176 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1,
178 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1,
182 1, 1, 1, 1, 1, 1, 1, 1,
183 1, 1, 1, 1, 1, 1, 1, 1,
184 1, 1, 1, 1, 1, 1, 1, 0,
185 1, 1, 1, 1, 1, 1, 1, 1,
186};
187
188/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
189 * run empty. In order to make sure that the TRAU frame transmission continues
190 * we generate idle TRAU frames here. */
191static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
192{
193 struct mgcp_endpoint *endp = user_data;
194 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
Eric8f333032021-08-13 00:00:43 +0200195 struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
Philipp Maier889fe7f2020-07-06 17:44:12 +0200196 uint8_t *ptr;
197 const uint8_t *ptr_ft;
198 enum osmo_trau_frame_type ft;
199
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200200 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200201
202 /* Choose an appropiate idle frame type */
203 ft = endp->e1.trau_rtp_st->type;
204 switch (ft) {
205 case OSMO_TRAU16_FT_FR:
206 ptr_ft = idle_tf_fr;
207 break;
208 case OSMO_TRAU16_FT_EFR:
209 ptr_ft = idle_tf_efr;
210 break;
211 default:
212 /* FIXME: What about 8k subslots and AMR frames? */
213 ptr_ft = idle_tf_spch;
214 }
215
216 /* Put the replacement into a message buffer and enqueue it into the
217 * I.460 multiplexer */
218 ptr = msgb_put(msg, E1_TRAU_BITS);
219 memcpy(ptr, ptr_ft, E1_TRAU_BITS);
220 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
221 osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
222 osmo_i460_mux_enqueue(endp->e1.schan, msg);
223}
224
225/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
226static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
227 unsigned int num_bits)
228{
229 struct mgcp_endpoint *endp = user_data;
230 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
231 osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
232
233 OSMO_ASSERT(endp->e1.trau_sync_fi);
234 osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
235}
236
237/* called for each synchronized TRAU frame received; decode frame + convert to RTP
238 * (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
239static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
240{
Philipp Maier889fe7f2020-07-06 17:44:12 +0200241 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
242 struct mgcp_endpoint *endp = user_data;
Eric8f333032021-08-13 00:00:43 +0200243 struct msgb *msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx-from-E1");
Philipp Maier889fe7f2020-07-06 17:44:12 +0200244 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
245 struct mgcp_conn *conn_dst;
246 struct osmo_trau_frame fr;
247 int rc;
248
249 if (!bits || num_bits == 0)
250 goto skip;
251
252 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
253 num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
254
255 /* Decode TRAU frame */
256 switch (endp->e1.scd.rate) {
257 case OSMO_I460_RATE_8k:
258 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
259 rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
260 break;
261 case OSMO_I460_RATE_16k:
262 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
263 rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
264 break;
265 default:
266 /* TRAU frames only exist in 8K or 16K subslots. */
267 OSMO_ASSERT(false);
268 break;
269 }
270 if (rc != 0) {
271 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
272 goto skip;
273 }
274
275 /* Check if the payload type is supported and what the expected lenth
276 * of the RTP payload will be. */
277 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
278 osmo_trau_frame_type_name(fr.type));
279
280 /* Convert decoded trau frame to RTP frame */
281 struct osmo_trau2rtp_state t2rs = {
282 .type = fr.type,
283 };
284 rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
285 if (rc <= 0) {
286 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
287 goto skip;
288 }
289 msgb_put(msg, rtp_hdr_len + rc);
290 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
291 osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
292
293 /* Forward RTP data to IP */
294 conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
295 if (!conn_dst) {
296 LOGPENDP(endp, DE1, LOGL_DEBUG,
297 "E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
298 goto skip;
299 }
300 OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
301
302 mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
303
304 msgb_free(msg);
305 return;
306skip:
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200307 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200308 msgb_free(msg);
309 return;
310}
311
312/* Function to handle outgoing E1 traffic */
313static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
314{
Eric8f333032021-08-13 00:00:43 +0200315 struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
Philipp Maier889fe7f2020-07-06 17:44:12 +0200316 uint8_t *ptr;
317
318 /* Get E1 frame from I.460 multiplexer */
319 ptr = msgb_put(msg, E1_TS_BYTES);
320 osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
321
322#if DEBUG_E1_TS == 1
323 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
324 osmo_hexdump_nospc(msgb_data(msg),
325 msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
326#endif
327 /* Hand data over to the E1 stack */
328 msgb_enqueue(&ts->raw.tx_queue, msg);
329 return;
330}
331
332/* Callback function to handle incoming E1 traffic */
333static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
334{
335 struct mgcp_trunk *trunk;
336
337 /* Find associated trunk */
338 trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
339 if (!trunk) {
Keithb2064422021-05-10 17:45:49 -0500340 LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
Vadim Yanitskiy0b6faa42022-04-18 02:29:37 +0300341 msgb_free(msg);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200342 return;
343 }
344
345 /* Check if the incoming data looks sane */
346 if (msgb_length(msg) != E1_TS_BYTES) {
347 LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
348 "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
349 msgb_length(msg));
350 }
351#if DEBUG_E1_TS == 1
352 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
353 msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
354 msgb_length(msg) >
355 DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
356#endif
357
358 /* Hand data over to the I.460 demultiplexer. */
359 osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
360
361 /* Trigger sending of pending E1 traffic */
362 e1_send(ts, trunk);
Vadim Yanitskiy0b6faa42022-04-18 02:29:37 +0300363
364 /* e1inp_rx_ts() does not free() msgb */
365 msgb_free(msg);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200366}
367
Philipp Maier2a9ba662023-02-09 13:39:31 +0100368static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
Philipp Maier889fe7f2020-07-06 17:44:12 +0200369{
Philipp Maier2a9ba662023-02-09 13:39:31 +0100370 /*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
371 * opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
372 * be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
Philipp Maier889fe7f2020-07-06 17:44:12 +0200373
374 struct e1inp_line *e1_line;
375 int rc;
376
377 OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
378 cfg = trunk->cfg;
379
Philipp Maier2a9ba662023-02-09 13:39:31 +0100380 if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
381 LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
382 ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
383 trunk->e1.ts_usecount[ts_nr - 1]++;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200384 return 0;
385 }
386
Philipp Maier2a9ba662023-02-09 13:39:31 +0100387 /* Find E1 line */
Philipp Maier2f34b532020-08-18 17:22:58 +0200388 e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
389 if (!e1_line) {
Keithb2064422021-05-10 17:45:49 -0500390 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
Philipp Maier2f34b532020-08-18 17:22:58 +0200391 trunk->e1.vty_line_nr);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200392 return -EINVAL;
Philipp Maier2f34b532020-08-18 17:22:58 +0200393 }
394 e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200395
396 /* Configure E1 timeslot */
397 rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
Philipp Maierad79f9e2020-08-18 17:02:02 +0200398 if (rc < 0) {
399 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to put E1 timeslot %u in raw mode.\n", ts_nr);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200400 return -EINVAL;
Philipp Maierad79f9e2020-08-18 17:02:02 +0200401 }
Philipp Maierc8acee22020-08-13 22:26:29 +0200402 rc = e1inp_line_update(e1_line);
Philipp Maierad79f9e2020-08-18 17:02:02 +0200403 if (rc < 0) {
Philipp Maierf8abd0e2023-02-13 11:56:38 +0100404 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200405 return -EINVAL;
Philipp Maierad79f9e2020-08-18 17:02:02 +0200406 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200407
Keithb2064422021-05-10 17:45:49 -0500408 LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
Philipp Maier2a9ba662023-02-09 13:39:31 +0100409 trunk->e1.ts_usecount[ts_nr - 1]++;
410 OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
411
412 return 0;
413}
414
415static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
416{
417 /* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
418
419 struct e1inp_line *e1_line;
420 int rc;
421
422 OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
423 cfg = trunk->cfg;
424
425 if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
426 trunk->e1.ts_usecount[ts_nr - 1]--;
427 LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
428 ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
429 return 0;
430 } else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
431 /* This should not be as it means we close the timeslot too often. */
432 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
433 return -EINVAL;
434 }
435
436 /* Find E1 line */
437 e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
438 if (!e1_line) {
439 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
440 trunk->e1.vty_line_nr);
441 return -EINVAL;
442 }
443
444 /* Release E1 timeslot */
445 rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
446 if (rc < 0) {
447 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
448 return -EINVAL;
449 }
450 rc = e1inp_line_update(e1_line);
451 if (rc < 0) {
452 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
453 return -EINVAL;
454 }
455
456 LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
457 trunk->e1.ts_usecount[ts_nr - 1]--;
458 OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200459
460 return 0;
461}
462
463/* Determine a suitable TRAU frame type for a given codec */
464static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
465 uint8_t amr_ft, struct mgcp_endpoint *endp)
466{
467 if (strcmp(sdp_subtype_name, "GSM") == 0)
468 return OSMO_TRAU16_FT_FR;
469 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
470 return OSMO_TRAU16_FT_EFR;
471 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
472 return OSMO_TRAU16_FT_HR;
473 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
474 if (i460_rate == OSMO_I460_RATE_8k) {
475 switch (amr_ft) {
476 case AMR_4_75:
477 case AMR_5_15:
478 case AMR_5_90:
479 return OSMO_TRAU8_AMR_LOW;
480 case AMR_6_70:
481 return OSMO_TRAU8_AMR_6k7;
482 case AMR_7_40:
483 return OSMO_TRAU8_AMR_7k4;
484 default:
485 LOGPENDP(endp, DE1, LOGL_ERROR,
486 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
487 return OSMO_TRAU_FT_NONE;
488 }
489 }
490 return OSMO_TRAU16_FT_AMR;
491 } else {
492 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
493 sdp_subtype_name);
494 return OSMO_TRAU_FT_NONE;
495 }
496}
497
498/* Determine a suitable TRAU frame type for a given codec */
499static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
500 uint8_t amr_ft, struct mgcp_endpoint *endp)
501{
502 if (strcmp(sdp_subtype_name, "GSM") == 0)
503 return OSMO_TRAU_SYNCP_16_FR_EFR;
504 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
505 return OSMO_TRAU_SYNCP_16_FR_EFR;
506 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
507 return OSMO_TRAU_SYNCP_8_HR;
508 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
509 if (i460_rate == OSMO_I460_RATE_8k) {
510 switch (amr_ft) {
511 case AMR_4_75:
512 case AMR_5_15:
513 case AMR_5_90:
514 return OSMO_TRAU_SYNCP_8_AMR_LOW;
515 case AMR_6_70:
516 return OSMO_TRAU_SYNCP_8_AMR_6K7;
517 case AMR_7_40:
518 return OSMO_TRAU_SYNCP_8_AMR_7K4;
519 default:
520 LOGPENDP(endp, DE1, LOGL_ERROR,
521 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
522 return OSMO_TRAU_SYNCP_16_FR_EFR;
523 }
524 }
525 return OSMO_TRAU_SYNCP_16_FR_EFR;
526 } else {
527 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
528 sdp_subtype_name);
529 return OSMO_TRAU_SYNCP_16_FR_EFR;
530 }
531}
532
533/* Find out if a given TRAU frame type is AMR */
534static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
535{
536
537 switch (ft) {
538 case OSMO_TRAU16_FT_AMR:
539 case OSMO_TRAU8_AMR_LOW:
540 case OSMO_TRAU8_AMR_6k7:
541 case OSMO_TRAU8_AMR_7k4:
542 return true;
543 default:
544 return false;
545 }
546}
547
Philipp Maier2a9ba662023-02-09 13:39:31 +0100548/*! Equip E1 endpoint with I.460 mux and E1 timeslot resources.
Philipp Maier889fe7f2020-07-06 17:44:12 +0200549 * \param[in] endp endpoint to equip
550 * \param[in] ts E1 timeslot number.
551 * \param[in] ss E1 subslot number.
552 * \param[in] offset E1 bit offset.
553 * \returns 0 on success, -EINVAL on error. */
554int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
555{
556 int rc;
557 enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
558
559 OSMO_ASSERT(ts != 0);
560 OSMO_ASSERT(ts != 0xFF);
561 OSMO_ASSERT(ss != 0xFF);
562 OSMO_ASSERT(offs != 0xFF);
563
564 memset(&endp->e1, 0, sizeof(endp->e1));
565
566 endp->e1.last_amr_ft = AMR_4_75;
567
568 /* Set up E1 line / timeslot */
Philipp Maier2a9ba662023-02-09 13:39:31 +0100569 rc = e1_open(endp->trunk, ts);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200570 if (rc != 0)
571 return -EINVAL;
572
573 /* Set up I.460 mux */
574 switch (e1_rates[ss]) {
575 case 64:
576 endp->e1.scd.rate = OSMO_I460_RATE_64k;
577 endp->e1.scd.demux.num_bits = 160 * 8;
578 break;
579 case 32:
580 endp->e1.scd.rate = OSMO_I460_RATE_32k;
581 endp->e1.scd.demux.num_bits = 80 * 8;
582 break;
583 case 16:
584 endp->e1.scd.rate = OSMO_I460_RATE_16k;
585 endp->e1.scd.demux.num_bits = 40 * 8;
586 sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
587 break;
588 case 8:
589 endp->e1.scd.rate = OSMO_I460_RATE_8k;
590 endp->e1.scd.demux.num_bits = 20 * 8;
591 sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
592 break;
593 }
594 endp->e1.scd.bit_offset = offs;
595 endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
596 endp->e1.scd.demux.out_cb_bytes = NULL;
597 endp->e1.scd.demux.user_data = endp;
598 endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
599 endp->e1.scd.mux.user_data = endp;
600
Keithb2064422021-05-10 17:45:49 -0500601 LOGPENDP(endp, DE1, LOGL_INFO, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
Philipp Maier889fe7f2020-07-06 17:44:12 +0200602 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
603 endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
604 if (!endp->e1.schan) {
605 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
606 return -EINVAL;
607 }
608
609 if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
610 /* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
611 * types the concept of TRAU frames does not apply. However, at the moment this
612 * is the only format we currently support in osmo-mgw */
613 endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
614 if (!endp->e1.trau_sync_fi) {
615 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
616 return -EINVAL;
617 }
618 endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
619 endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
620 } else {
621 LOGPENDP(endp, DE1, LOGL_ERROR,
622 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
623 return -EINVAL;
624 }
625
626 return 0;
627}
628
629/*! Update E1 related parameters (codec and sync pattern).
630 * \param[in] endp endpoint to update. */
631void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
632{
633 struct mgcp_conn *conn;
634 struct mgcp_rtp_codec *codec;
635 enum osmo_tray_sync_pat_id sync_pat_id;
636
637 /* In order to determine the codec, find the oldest connection on
638 * the endpoint and use its codec information. Normally on an E1
639 * endpoint no more than one connection should exist. */
640 conn = mgcp_conn_get_oldest(endp);
641 OSMO_ASSERT(conn);
642 codec = conn->u.rtp.end.codec;
643 OSMO_ASSERT(codec);
644
645 /* Update codec information */
646 endp->e1.trau_rtp_st->type =
647 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
648 endp->e1.last_codec = codec;
649
650 /* Update sync pattern */
651 sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
652 osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
653}
654
655/*! Remove E1 resources from endpoint
Philipp Maier2a9ba662023-02-09 13:39:31 +0100656 * \param[in] endp endpoint to release.
657 * \param[in] ts E1 timeslot number. */
658void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
Philipp Maier889fe7f2020-07-06 17:44:12 +0200659{
Philipp Maier2a9ba662023-02-09 13:39:31 +0100660 /* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
661 * is done. */
662 if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
663 return;
664
Philipp Maier889fe7f2020-07-06 17:44:12 +0200665 LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
666
667 if (endp->e1.schan)
668 osmo_i460_subchan_del(endp->e1.schan);
669 if (endp->e1.trau_rtp_st)
670 talloc_free(endp->e1.trau_rtp_st);
671 if (endp->e1.trau_sync_fi)
672 osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200673 memset(&endp->e1, 0, sizeof(endp->e1));
Philipp Maier2a9ba662023-02-09 13:39:31 +0100674
675 /* Close E1 timeslot */
676 e1_close(endp->trunk, ts);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200677}
678
679/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
680 * \param[in] endp related endpoint (does not take ownership).
681 * \param[in] codec configuration.
682 * \param[in] msg RTP message buffer (including RTP header).
683 * \returns 0 on success, -1 on ERROR. */
684int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
685{
Eric8f333032021-08-13 00:00:43 +0200686 struct msgb *msg_tf = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
Philipp Maier889fe7f2020-07-06 17:44:12 +0200687 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
688 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
689 struct osmo_trau_frame tf;
690 uint8_t amr_ft;
691 int rc;
692
693 /* Extract AMR frame type from AMR head (if AMR is used) */
694 if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
695 amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
696 else
697 amr_ft = 0xff;
698
699 /* Adapt TRAU frame type on codec changes */
700 OSMO_ASSERT(endp->e1.last_codec);
701 if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
702 endp->e1.trau_rtp_st->type =
703 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
704 endp->e1.last_codec = codec;
705 endp->e1.last_amr_ft = amr_ft;
706 }
707 if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
708 goto skip;
709 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
710 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
711
712 /* Convert from RTP to TRAU format */
713 msg->l2h = msgb_data(msg) + rtp_hdr_len;
714 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
715 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
716 memset(&tf, 0, sizeof(tf));
717 tf.dir = OSMO_TRAU_DIR_DL;
718 rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
719 if (rc < 0) {
720 LOGPENDP(endp, DE1, LOGL_DEBUG,
721 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
722 goto skip;
723 }
724 rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
725 if (rc < 0) {
726 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
727 goto skip;
728 }
729 msgb_put(msg_tf, rc);
730 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
731 osmo_ubit_dump(msgb_data(msg_tf),
732 msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
733
734 /* Enqueue data to I.460 multiplexer */
735 OSMO_ASSERT(endp->e1.schan);
736 OSMO_ASSERT(endp->e1.trau_sync_fi);
737
738 osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
739 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
740
741 return 0;
742skip:
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200743 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200744 msgb_free(msg_tf);
745 return -1;
746}