blob: 1e227dc95a2a024c6faf285302a2678a8b2e2ae1 [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
24#include <osmocom/mgcp/mgcp_internal.h>
25#include <osmocom/mgcp/mgcp_endp.h>
26#include <osmocom/mgcp/mgcp_trunk.h>
27#include <osmocom/mgcp/mgcp_conn.h>
28#include <osmocom/core/msgb.h>
29#include <osmocom/abis/e1_input.h>
30#include <osmocom/abis/abis.h>
31
32#include <osmocom/trau/trau_sync.h>
33#include <osmocom/trau/trau_frame.h>
34#include <osmocom/trau/trau_rtp.h>
35#include <osmocom/mgcp/mgcp_conn.h>
36#include <osmocom/netif/rtp.h>
37#include <osmocom/mgcp/debug.h>
38#include <osmocom/mgcp/mgcp_e1.h>
39#include <osmocom/codec/codec.h>
40
41#define DEBUG_BITS_MAX 80
42#define DEBUG_BYTES_MAX 40
43#define DEBUG_E1_TS 0
44#define E1_TS_BYTES 160
45#define E1_TRAU_BITS 320
46#define E1_TRAU_BITS_MSGB 2048
47
48static struct mgcp_config *cfg;
49
50static const struct e1inp_line_ops dummy_e1_line_ops = {
51 .sign_link_up = NULL,
52 .sign_link_down = NULL,
53 .sign_link = NULL,
54};
55
56/* EFR idle frame */
57static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0,
59 1, 1, 1, 0, 1, 0, 0, 0,
60 0, 0, 0, 0, 1, 0, 0, 0,
61 1, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0,
63 1, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0, 0,
65 1, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0,
67 1, 0, 0, 0, 0, 0, 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0,
69 1, 0, 0, 0, 0, 0, 0, 0,
70 0, 0, 0, 0, 0, 0, 0, 0,
71 1, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 1, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0,
77 1, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0,
79 1, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0, 0,
81 1, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0,
83 1, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0,
85 1, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0,
87 1, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0,
89 1, 0, 0, 0, 0, 0, 0, 0,
90 0, 0, 0, 0, 0, 0, 0, 0,
91 1, 0, 0, 0, 0, 0, 0, 0,
92 0, 0, 0, 0, 0, 0, 0, 0,
93 1, 0, 0, 0, 0, 0, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0,
95 1, 0, 0, 0, 0, 0, 1, 0,
96 1, 1, 1, 1, 1, 1, 1, 1,
97};
98
99/* FR idle frame */
100static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
101 0, 0, 0, 0, 0, 0, 0, 0,
102 1, 1, 1, 1, 0, 0, 0, 0,
103 0, 0, 0, 0, 1, 0, 0, 0,
104 1, 0, 0, 0, 0, 0, 0, 0,
105 0, 0, 0, 0, 0, 0, 0, 0,
106 1, 0, 0, 0, 0, 0, 0, 0,
107 0, 0, 0, 0, 0, 0, 0, 0,
108 1, 0, 0, 0, 0, 0, 0, 0,
109 0, 0, 0, 0, 0, 0, 0, 0,
110 1, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0,
112 1, 0, 0, 0, 0, 0, 0, 0,
113 0, 0, 0, 0, 0, 0, 0, 0,
114 1, 0, 0, 0, 0, 0, 0, 0,
115 0, 0, 0, 0, 0, 0, 0, 0,
116 1, 0, 0, 0, 0, 0, 0, 0,
117 0, 0, 0, 0, 0, 0, 0, 0,
118 1, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0,
120 1, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0,
122 1, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0,
124 1, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0,
126 1, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0,
128 1, 0, 0, 0, 0, 0, 0, 0,
129 0, 0, 0, 0, 0, 0, 0, 0,
130 1, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0,
132 1, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0,
134 1, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0,
136 1, 0, 0, 0, 0, 0, 0, 0,
137 0, 0, 0, 0, 0, 0, 0, 0,
138 1, 0, 0, 0, 0, 0, 1, 0,
139 1, 1, 1, 1, 1, 1, 1, 1,
140};
141
142/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
143static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0,
145 1, 0, 1, 1, 1, 0, 0, 0,
146 0, 0, 0, 0, 1, 0, 0, 0,
147 1, 1, 1, 1, 1, 1, 1, 1,
148 1, 1, 1, 1, 1, 1, 1, 1,
149 1, 1, 1, 1, 1, 1, 1, 1,
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, 0,
182 1, 1, 1, 1, 1, 1, 1, 1,
183};
184
185/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
186 * run empty. In order to make sure that the TRAU frame transmission continues
187 * we generate idle TRAU frames here. */
188static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
189{
190 struct mgcp_endpoint *endp = user_data;
191 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
192 struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
193 uint8_t *ptr;
194 const uint8_t *ptr_ft;
195 enum osmo_trau_frame_type ft;
196
197 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
198
199 /* Choose an appropiate idle frame type */
200 ft = endp->e1.trau_rtp_st->type;
201 switch (ft) {
202 case OSMO_TRAU16_FT_FR:
203 ptr_ft = idle_tf_fr;
204 break;
205 case OSMO_TRAU16_FT_EFR:
206 ptr_ft = idle_tf_efr;
207 break;
208 default:
209 /* FIXME: What about 8k subslots and AMR frames? */
210 ptr_ft = idle_tf_spch;
211 }
212
213 /* Put the replacement into a message buffer and enqueue it into the
214 * I.460 multiplexer */
215 ptr = msgb_put(msg, E1_TRAU_BITS);
216 memcpy(ptr, ptr_ft, E1_TRAU_BITS);
217 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
218 osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
219 osmo_i460_mux_enqueue(endp->e1.schan, msg);
220}
221
222/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
223static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
224 unsigned int num_bits)
225{
226 struct mgcp_endpoint *endp = user_data;
227 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
228 osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
229
230 OSMO_ASSERT(endp->e1.trau_sync_fi);
231 osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
232}
233
234/* called for each synchronized TRAU frame received; decode frame + convert to RTP
235 * (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
236static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
237{
238 struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
239 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
240 struct mgcp_endpoint *endp = user_data;
241 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
242 struct mgcp_conn *conn_dst;
243 struct osmo_trau_frame fr;
244 int rc;
245
246 if (!bits || num_bits == 0)
247 goto skip;
248
249 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
250 num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
251
252 /* Decode TRAU frame */
253 switch (endp->e1.scd.rate) {
254 case OSMO_I460_RATE_8k:
255 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
256 rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
257 break;
258 case OSMO_I460_RATE_16k:
259 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
260 rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
261 break;
262 default:
263 /* TRAU frames only exist in 8K or 16K subslots. */
264 OSMO_ASSERT(false);
265 break;
266 }
267 if (rc != 0) {
268 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
269 goto skip;
270 }
271
272 /* Check if the payload type is supported and what the expected lenth
273 * of the RTP payload will be. */
274 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
275 osmo_trau_frame_type_name(fr.type));
276
277 /* Convert decoded trau frame to RTP frame */
278 struct osmo_trau2rtp_state t2rs = {
279 .type = fr.type,
280 };
281 rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
282 if (rc <= 0) {
283 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
284 goto skip;
285 }
286 msgb_put(msg, rtp_hdr_len + rc);
287 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
288 osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
289
290 /* Forward RTP data to IP */
291 conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
292 if (!conn_dst) {
293 LOGPENDP(endp, DE1, LOGL_DEBUG,
294 "E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
295 goto skip;
296 }
297 OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
298
299 mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
300
301 msgb_free(msg);
302 return;
303skip:
304 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
305 msgb_free(msg);
306 return;
307}
308
309/* Function to handle outgoing E1 traffic */
310static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
311{
312 struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
313 uint8_t *ptr;
314
315 /* Get E1 frame from I.460 multiplexer */
316 ptr = msgb_put(msg, E1_TS_BYTES);
317 osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
318
319#if DEBUG_E1_TS == 1
320 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
321 osmo_hexdump_nospc(msgb_data(msg),
322 msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
323#endif
324 /* Hand data over to the E1 stack */
325 msgb_enqueue(&ts->raw.tx_queue, msg);
326 return;
327}
328
329/* Callback function to handle incoming E1 traffic */
330static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
331{
332 struct mgcp_trunk *trunk;
333
334 /* Find associated trunk */
335 trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
336 if (!trunk) {
337 LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
338 return;
339 }
340
341 /* Check if the incoming data looks sane */
342 if (msgb_length(msg) != E1_TS_BYTES) {
343 LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
344 "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
345 msgb_length(msg));
346 }
347#if DEBUG_E1_TS == 1
348 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
349 msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
350 msgb_length(msg) >
351 DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
352#endif
353
354 /* Hand data over to the I.460 demultiplexer. */
355 osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
356
357 /* Trigger sending of pending E1 traffic */
358 e1_send(ts, trunk);
359}
360
361/*! Find an endpoint by its name on a specified trunk.
362 * \param[in] trunk trunk configuration.
363 * \param[in] ts_nr E1 timeslot number.
364 * \returns -EINVAL on failure, 0 on success. */
365int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
366{
367 /*! Each timeslot needs only to be configured once. The Timeslot then
368 * stays open and permanently receives data. It is then up to the
369 * I.460 demultiplexer to add/remove subchannels as needed. It is
370 * allowed to call this function multiple times since we check if the
371 * timeslot is already configured. */
372
373 struct e1inp_line *e1_line;
374 int rc;
375
376 OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
377 cfg = trunk->cfg;
378
379 if (trunk->e1.ts_in_use[ts_nr - 1]) {
380 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
381 return 0;
382 }
383
384 /* Get E1 line */
385 if (!trunk->e1.line) {
386 e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
387 if (!e1_line) {
388 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
389 trunk->e1.vty_line_nr);
390 return -EINVAL;
391 }
392 e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
393 } else
394 e1_line = trunk->e1.line;
395 if (!e1_line)
396 return -EINVAL;
397
398 /* Configure E1 timeslot */
399 rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
400 if (rc < 0)
401 return -EINVAL;
402 e1inp_line_update(e1_line);
403 if (rc < 0)
404 return -EINVAL;
405
406 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
407 trunk->e1.ts_in_use[ts_nr - 1] = true;
408
409 return 0;
410}
411
412/* Determine a suitable TRAU frame type for a given codec */
413static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
414 uint8_t amr_ft, struct mgcp_endpoint *endp)
415{
416 if (strcmp(sdp_subtype_name, "GSM") == 0)
417 return OSMO_TRAU16_FT_FR;
418 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
419 return OSMO_TRAU16_FT_EFR;
420 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
421 return OSMO_TRAU16_FT_HR;
422 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
423 if (i460_rate == OSMO_I460_RATE_8k) {
424 switch (amr_ft) {
425 case AMR_4_75:
426 case AMR_5_15:
427 case AMR_5_90:
428 return OSMO_TRAU8_AMR_LOW;
429 case AMR_6_70:
430 return OSMO_TRAU8_AMR_6k7;
431 case AMR_7_40:
432 return OSMO_TRAU8_AMR_7k4;
433 default:
434 LOGPENDP(endp, DE1, LOGL_ERROR,
435 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
436 return OSMO_TRAU_FT_NONE;
437 }
438 }
439 return OSMO_TRAU16_FT_AMR;
440 } else {
441 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
442 sdp_subtype_name);
443 return OSMO_TRAU_FT_NONE;
444 }
445}
446
447/* Determine a suitable TRAU frame type for a given codec */
448static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
449 uint8_t amr_ft, struct mgcp_endpoint *endp)
450{
451 if (strcmp(sdp_subtype_name, "GSM") == 0)
452 return OSMO_TRAU_SYNCP_16_FR_EFR;
453 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
454 return OSMO_TRAU_SYNCP_16_FR_EFR;
455 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
456 return OSMO_TRAU_SYNCP_8_HR;
457 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
458 if (i460_rate == OSMO_I460_RATE_8k) {
459 switch (amr_ft) {
460 case AMR_4_75:
461 case AMR_5_15:
462 case AMR_5_90:
463 return OSMO_TRAU_SYNCP_8_AMR_LOW;
464 case AMR_6_70:
465 return OSMO_TRAU_SYNCP_8_AMR_6K7;
466 case AMR_7_40:
467 return OSMO_TRAU_SYNCP_8_AMR_7K4;
468 default:
469 LOGPENDP(endp, DE1, LOGL_ERROR,
470 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
471 return OSMO_TRAU_SYNCP_16_FR_EFR;
472 }
473 }
474 return OSMO_TRAU_SYNCP_16_FR_EFR;
475 } else {
476 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
477 sdp_subtype_name);
478 return OSMO_TRAU_SYNCP_16_FR_EFR;
479 }
480}
481
482/* Find out if a given TRAU frame type is AMR */
483static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
484{
485
486 switch (ft) {
487 case OSMO_TRAU16_FT_AMR:
488 case OSMO_TRAU8_AMR_LOW:
489 case OSMO_TRAU8_AMR_6k7:
490 case OSMO_TRAU8_AMR_7k4:
491 return true;
492 default:
493 return false;
494 }
495}
496
497/* !Equip E1 endpoint with I.460 mux resources.
498 * \param[in] endp endpoint to equip
499 * \param[in] ts E1 timeslot number.
500 * \param[in] ss E1 subslot number.
501 * \param[in] offset E1 bit offset.
502 * \returns 0 on success, -EINVAL on error. */
503int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
504{
505 int rc;
506 enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
507
508 OSMO_ASSERT(ts != 0);
509 OSMO_ASSERT(ts != 0xFF);
510 OSMO_ASSERT(ss != 0xFF);
511 OSMO_ASSERT(offs != 0xFF);
512
513 memset(&endp->e1, 0, sizeof(endp->e1));
514
515 endp->e1.last_amr_ft = AMR_4_75;
516
517 /* Set up E1 line / timeslot */
518 rc = mgcp_e1_init(endp->trunk, ts);
519 if (rc != 0)
520 return -EINVAL;
521
522 /* Set up I.460 mux */
523 switch (e1_rates[ss]) {
524 case 64:
525 endp->e1.scd.rate = OSMO_I460_RATE_64k;
526 endp->e1.scd.demux.num_bits = 160 * 8;
527 break;
528 case 32:
529 endp->e1.scd.rate = OSMO_I460_RATE_32k;
530 endp->e1.scd.demux.num_bits = 80 * 8;
531 break;
532 case 16:
533 endp->e1.scd.rate = OSMO_I460_RATE_16k;
534 endp->e1.scd.demux.num_bits = 40 * 8;
535 sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
536 break;
537 case 8:
538 endp->e1.scd.rate = OSMO_I460_RATE_8k;
539 endp->e1.scd.demux.num_bits = 20 * 8;
540 sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
541 break;
542 }
543 endp->e1.scd.bit_offset = offs;
544 endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
545 endp->e1.scd.demux.out_cb_bytes = NULL;
546 endp->e1.scd.demux.user_data = endp;
547 endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
548 endp->e1.scd.mux.user_data = endp;
549
550 LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
551 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
552 endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
553 if (!endp->e1.schan) {
554 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
555 return -EINVAL;
556 }
557
558 if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
559 /* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
560 * types the concept of TRAU frames does not apply. However, at the moment this
561 * is the only format we currently support in osmo-mgw */
562 endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
563 if (!endp->e1.trau_sync_fi) {
564 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
565 return -EINVAL;
566 }
567 endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
568 endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
569 } else {
570 LOGPENDP(endp, DE1, LOGL_ERROR,
571 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
572 return -EINVAL;
573 }
574
575 return 0;
576}
577
578/*! Update E1 related parameters (codec and sync pattern).
579 * \param[in] endp endpoint to update. */
580void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
581{
582 struct mgcp_conn *conn;
583 struct mgcp_rtp_codec *codec;
584 enum osmo_tray_sync_pat_id sync_pat_id;
585
586 /* In order to determine the codec, find the oldest connection on
587 * the endpoint and use its codec information. Normally on an E1
588 * endpoint no more than one connection should exist. */
589 conn = mgcp_conn_get_oldest(endp);
590 OSMO_ASSERT(conn);
591 codec = conn->u.rtp.end.codec;
592 OSMO_ASSERT(codec);
593
594 /* Update codec information */
595 endp->e1.trau_rtp_st->type =
596 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
597 endp->e1.last_codec = codec;
598
599 /* Update sync pattern */
600 sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
601 osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
602}
603
604/*! Remove E1 resources from endpoint
605 * \param[in] endp endpoint to release. */
606void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
607{
608 LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
609
610 if (endp->e1.schan)
611 osmo_i460_subchan_del(endp->e1.schan);
612 if (endp->e1.trau_rtp_st)
613 talloc_free(endp->e1.trau_rtp_st);
614 if (endp->e1.trau_sync_fi)
615 osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
616
617 memset(&endp->e1, 0, sizeof(endp->e1));
618}
619
620/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
621 * \param[in] endp related endpoint (does not take ownership).
622 * \param[in] codec configuration.
623 * \param[in] msg RTP message buffer (including RTP header).
624 * \returns 0 on success, -1 on ERROR. */
625int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
626{
627 struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
628 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
629 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
630 struct osmo_trau_frame tf;
631 uint8_t amr_ft;
632 int rc;
633
634 /* Extract AMR frame type from AMR head (if AMR is used) */
635 if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
636 amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
637 else
638 amr_ft = 0xff;
639
640 /* Adapt TRAU frame type on codec changes */
641 OSMO_ASSERT(endp->e1.last_codec);
642 if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
643 endp->e1.trau_rtp_st->type =
644 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
645 endp->e1.last_codec = codec;
646 endp->e1.last_amr_ft = amr_ft;
647 }
648 if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
649 goto skip;
650 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
651 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
652
653 /* Convert from RTP to TRAU format */
654 msg->l2h = msgb_data(msg) + rtp_hdr_len;
655 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
656 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
657 memset(&tf, 0, sizeof(tf));
658 tf.dir = OSMO_TRAU_DIR_DL;
659 rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
660 if (rc < 0) {
661 LOGPENDP(endp, DE1, LOGL_DEBUG,
662 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
663 goto skip;
664 }
665 rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
666 if (rc < 0) {
667 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
668 goto skip;
669 }
670 msgb_put(msg_tf, rc);
671 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
672 osmo_ubit_dump(msgb_data(msg_tf),
673 msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
674
675 /* Enqueue data to I.460 multiplexer */
676 OSMO_ASSERT(endp->e1.schan);
677 OSMO_ASSERT(endp->e1.trau_sync_fi);
678
679 osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
680 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
681
682 return 0;
683skip:
684 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
685 msgb_free(msg_tf);
686 return -1;
687}