blob: 7f30c1df03bb8f0c29fc92162f6fce848f5cd259 [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 Maier60be6272020-09-22 16:22:34 +0200368static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
Philipp Maier889fe7f2020-07-06 17:44:12 +0200369{
370 /*! Each timeslot needs only to be configured once. The Timeslot then
371 * stays open and permanently receives data. It is then up to the
372 * I.460 demultiplexer to add/remove subchannels as needed. It is
373 * allowed to call this function multiple times since we check if the
374 * timeslot is already configured. */
375
376 struct e1inp_line *e1_line;
377 int rc;
378
379 OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
380 cfg = trunk->cfg;
381
382 if (trunk->e1.ts_in_use[ts_nr - 1]) {
Keithb2064422021-05-10 17:45:49 -0500383 LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200384 return 0;
385 }
386
387 /* Get 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) {
404 LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %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 Maier889fe7f2020-07-06 17:44:12 +0200409 trunk->e1.ts_in_use[ts_nr - 1] = true;
410
411 return 0;
412}
413
414/* Determine a suitable TRAU frame type for a given codec */
415static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
416 uint8_t amr_ft, struct mgcp_endpoint *endp)
417{
418 if (strcmp(sdp_subtype_name, "GSM") == 0)
419 return OSMO_TRAU16_FT_FR;
420 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
421 return OSMO_TRAU16_FT_EFR;
422 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
423 return OSMO_TRAU16_FT_HR;
424 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
425 if (i460_rate == OSMO_I460_RATE_8k) {
426 switch (amr_ft) {
427 case AMR_4_75:
428 case AMR_5_15:
429 case AMR_5_90:
430 return OSMO_TRAU8_AMR_LOW;
431 case AMR_6_70:
432 return OSMO_TRAU8_AMR_6k7;
433 case AMR_7_40:
434 return OSMO_TRAU8_AMR_7k4;
435 default:
436 LOGPENDP(endp, DE1, LOGL_ERROR,
437 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
438 return OSMO_TRAU_FT_NONE;
439 }
440 }
441 return OSMO_TRAU16_FT_AMR;
442 } else {
443 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
444 sdp_subtype_name);
445 return OSMO_TRAU_FT_NONE;
446 }
447}
448
449/* Determine a suitable TRAU frame type for a given codec */
450static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
451 uint8_t amr_ft, struct mgcp_endpoint *endp)
452{
453 if (strcmp(sdp_subtype_name, "GSM") == 0)
454 return OSMO_TRAU_SYNCP_16_FR_EFR;
455 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
456 return OSMO_TRAU_SYNCP_16_FR_EFR;
457 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
458 return OSMO_TRAU_SYNCP_8_HR;
459 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
460 if (i460_rate == OSMO_I460_RATE_8k) {
461 switch (amr_ft) {
462 case AMR_4_75:
463 case AMR_5_15:
464 case AMR_5_90:
465 return OSMO_TRAU_SYNCP_8_AMR_LOW;
466 case AMR_6_70:
467 return OSMO_TRAU_SYNCP_8_AMR_6K7;
468 case AMR_7_40:
469 return OSMO_TRAU_SYNCP_8_AMR_7K4;
470 default:
471 LOGPENDP(endp, DE1, LOGL_ERROR,
472 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
473 return OSMO_TRAU_SYNCP_16_FR_EFR;
474 }
475 }
476 return OSMO_TRAU_SYNCP_16_FR_EFR;
477 } else {
478 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
479 sdp_subtype_name);
480 return OSMO_TRAU_SYNCP_16_FR_EFR;
481 }
482}
483
484/* Find out if a given TRAU frame type is AMR */
485static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
486{
487
488 switch (ft) {
489 case OSMO_TRAU16_FT_AMR:
490 case OSMO_TRAU8_AMR_LOW:
491 case OSMO_TRAU8_AMR_6k7:
492 case OSMO_TRAU8_AMR_7k4:
493 return true;
494 default:
495 return false;
496 }
497}
498
Philipp Maier0fdc08c2022-08-10 14:13:11 +0200499/*! Equip E1 endpoint with I.460 mux resources.
Philipp Maier889fe7f2020-07-06 17:44:12 +0200500 * \param[in] endp endpoint to equip
501 * \param[in] ts E1 timeslot number.
502 * \param[in] ss E1 subslot number.
503 * \param[in] offset E1 bit offset.
504 * \returns 0 on success, -EINVAL on error. */
505int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
506{
507 int rc;
508 enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
509
510 OSMO_ASSERT(ts != 0);
511 OSMO_ASSERT(ts != 0xFF);
512 OSMO_ASSERT(ss != 0xFF);
513 OSMO_ASSERT(offs != 0xFF);
514
515 memset(&endp->e1, 0, sizeof(endp->e1));
516
517 endp->e1.last_amr_ft = AMR_4_75;
518
519 /* Set up E1 line / timeslot */
Philipp Maier60be6272020-09-22 16:22:34 +0200520 rc = e1_init(endp->trunk, ts);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200521 if (rc != 0)
522 return -EINVAL;
523
524 /* Set up I.460 mux */
525 switch (e1_rates[ss]) {
526 case 64:
527 endp->e1.scd.rate = OSMO_I460_RATE_64k;
528 endp->e1.scd.demux.num_bits = 160 * 8;
529 break;
530 case 32:
531 endp->e1.scd.rate = OSMO_I460_RATE_32k;
532 endp->e1.scd.demux.num_bits = 80 * 8;
533 break;
534 case 16:
535 endp->e1.scd.rate = OSMO_I460_RATE_16k;
536 endp->e1.scd.demux.num_bits = 40 * 8;
537 sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
538 break;
539 case 8:
540 endp->e1.scd.rate = OSMO_I460_RATE_8k;
541 endp->e1.scd.demux.num_bits = 20 * 8;
542 sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
543 break;
544 }
545 endp->e1.scd.bit_offset = offs;
546 endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
547 endp->e1.scd.demux.out_cb_bytes = NULL;
548 endp->e1.scd.demux.user_data = endp;
549 endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
550 endp->e1.scd.mux.user_data = endp;
551
Keithb2064422021-05-10 17:45:49 -0500552 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 +0200553 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
554 endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
555 if (!endp->e1.schan) {
556 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
557 return -EINVAL;
558 }
559
560 if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
561 /* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
562 * types the concept of TRAU frames does not apply. However, at the moment this
563 * is the only format we currently support in osmo-mgw */
564 endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
565 if (!endp->e1.trau_sync_fi) {
566 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
567 return -EINVAL;
568 }
569 endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
570 endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
571 } else {
572 LOGPENDP(endp, DE1, LOGL_ERROR,
573 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
574 return -EINVAL;
575 }
576
577 return 0;
578}
579
580/*! Update E1 related parameters (codec and sync pattern).
581 * \param[in] endp endpoint to update. */
582void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
583{
584 struct mgcp_conn *conn;
585 struct mgcp_rtp_codec *codec;
586 enum osmo_tray_sync_pat_id sync_pat_id;
587
588 /* In order to determine the codec, find the oldest connection on
589 * the endpoint and use its codec information. Normally on an E1
590 * endpoint no more than one connection should exist. */
591 conn = mgcp_conn_get_oldest(endp);
592 OSMO_ASSERT(conn);
593 codec = conn->u.rtp.end.codec;
594 OSMO_ASSERT(codec);
595
596 /* Update codec information */
597 endp->e1.trau_rtp_st->type =
598 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
599 endp->e1.last_codec = codec;
600
601 /* Update sync pattern */
602 sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
603 osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
604}
605
606/*! Remove E1 resources from endpoint
607 * \param[in] endp endpoint to release. */
608void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
609{
610 LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
611
612 if (endp->e1.schan)
613 osmo_i460_subchan_del(endp->e1.schan);
614 if (endp->e1.trau_rtp_st)
615 talloc_free(endp->e1.trau_rtp_st);
616 if (endp->e1.trau_sync_fi)
617 osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
618
619 memset(&endp->e1, 0, sizeof(endp->e1));
620}
621
622/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
623 * \param[in] endp related endpoint (does not take ownership).
624 * \param[in] codec configuration.
625 * \param[in] msg RTP message buffer (including RTP header).
626 * \returns 0 on success, -1 on ERROR. */
627int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
628{
Eric8f333032021-08-13 00:00:43 +0200629 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 +0200630 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
631 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
632 struct osmo_trau_frame tf;
633 uint8_t amr_ft;
634 int rc;
635
636 /* Extract AMR frame type from AMR head (if AMR is used) */
637 if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
638 amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
639 else
640 amr_ft = 0xff;
641
642 /* Adapt TRAU frame type on codec changes */
643 OSMO_ASSERT(endp->e1.last_codec);
644 if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
645 endp->e1.trau_rtp_st->type =
646 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
647 endp->e1.last_codec = codec;
648 endp->e1.last_amr_ft = amr_ft;
649 }
650 if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
651 goto skip;
652 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
653 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
654
655 /* Convert from RTP to TRAU format */
656 msg->l2h = msgb_data(msg) + rtp_hdr_len;
657 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
658 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
659 memset(&tf, 0, sizeof(tf));
660 tf.dir = OSMO_TRAU_DIR_DL;
661 rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
662 if (rc < 0) {
663 LOGPENDP(endp, DE1, LOGL_DEBUG,
664 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
665 goto skip;
666 }
667 rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
668 if (rc < 0) {
669 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
670 goto skip;
671 }
672 msgb_put(msg_tf, rc);
673 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
674 osmo_ubit_dump(msgb_data(msg_tf),
675 msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
676
677 /* Enqueue data to I.460 multiplexer */
678 OSMO_ASSERT(endp->e1.schan);
679 OSMO_ASSERT(endp->e1.trau_sync_fi);
680
681 osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
682 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
683
684 return 0;
685skip:
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200686 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200687 msgb_free(msg_tf);
688 return -1;
689}