blob: b3db0b72ca851570f78bdb8728ace86da6cac06b [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;
195 struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
196 uint8_t *ptr;
197 const uint8_t *ptr_ft;
198 enum osmo_trau_frame_type ft;
199
200 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
201
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{
241 struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
242 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
243 struct mgcp_endpoint *endp = user_data;
244 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:
307 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
308 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{
315 struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
316 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) {
340 LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
341 return;
342 }
343
344 /* Check if the incoming data looks sane */
345 if (msgb_length(msg) != E1_TS_BYTES) {
346 LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
347 "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
348 msgb_length(msg));
349 }
350#if DEBUG_E1_TS == 1
351 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
352 msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
353 msgb_length(msg) >
354 DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
355#endif
356
357 /* Hand data over to the I.460 demultiplexer. */
358 osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
359
360 /* Trigger sending of pending E1 traffic */
361 e1_send(ts, trunk);
362}
363
364/*! Find an endpoint by its name on a specified trunk.
365 * \param[in] trunk trunk configuration.
366 * \param[in] ts_nr E1 timeslot number.
367 * \returns -EINVAL on failure, 0 on success. */
368int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
369{
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]) {
383 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
384 return 0;
385 }
386
387 /* Get E1 line */
388 if (!trunk->e1.line) {
389 e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
390 if (!e1_line) {
391 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
392 trunk->e1.vty_line_nr);
393 return -EINVAL;
394 }
395 e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
396 } else
397 e1_line = trunk->e1.line;
398 if (!e1_line)
399 return -EINVAL;
400
401 /* Configure E1 timeslot */
402 rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
403 if (rc < 0)
404 return -EINVAL;
Philipp Maierc8acee22020-08-13 22:26:29 +0200405 rc = e1inp_line_update(e1_line);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200406 if (rc < 0)
407 return -EINVAL;
408
409 LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
410 trunk->e1.ts_in_use[ts_nr - 1] = true;
411
412 return 0;
413}
414
415/* Determine a suitable TRAU frame type for a given codec */
416static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
417 uint8_t amr_ft, struct mgcp_endpoint *endp)
418{
419 if (strcmp(sdp_subtype_name, "GSM") == 0)
420 return OSMO_TRAU16_FT_FR;
421 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
422 return OSMO_TRAU16_FT_EFR;
423 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
424 return OSMO_TRAU16_FT_HR;
425 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
426 if (i460_rate == OSMO_I460_RATE_8k) {
427 switch (amr_ft) {
428 case AMR_4_75:
429 case AMR_5_15:
430 case AMR_5_90:
431 return OSMO_TRAU8_AMR_LOW;
432 case AMR_6_70:
433 return OSMO_TRAU8_AMR_6k7;
434 case AMR_7_40:
435 return OSMO_TRAU8_AMR_7k4;
436 default:
437 LOGPENDP(endp, DE1, LOGL_ERROR,
438 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
439 return OSMO_TRAU_FT_NONE;
440 }
441 }
442 return OSMO_TRAU16_FT_AMR;
443 } else {
444 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
445 sdp_subtype_name);
446 return OSMO_TRAU_FT_NONE;
447 }
448}
449
450/* Determine a suitable TRAU frame type for a given codec */
451static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
452 uint8_t amr_ft, struct mgcp_endpoint *endp)
453{
454 if (strcmp(sdp_subtype_name, "GSM") == 0)
455 return OSMO_TRAU_SYNCP_16_FR_EFR;
456 else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
457 return OSMO_TRAU_SYNCP_16_FR_EFR;
458 else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
459 return OSMO_TRAU_SYNCP_8_HR;
460 else if (strcmp(sdp_subtype_name, "AMR") == 0) {
461 if (i460_rate == OSMO_I460_RATE_8k) {
462 switch (amr_ft) {
463 case AMR_4_75:
464 case AMR_5_15:
465 case AMR_5_90:
466 return OSMO_TRAU_SYNCP_8_AMR_LOW;
467 case AMR_6_70:
468 return OSMO_TRAU_SYNCP_8_AMR_6K7;
469 case AMR_7_40:
470 return OSMO_TRAU_SYNCP_8_AMR_7K4;
471 default:
472 LOGPENDP(endp, DE1, LOGL_ERROR,
473 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
474 return OSMO_TRAU_SYNCP_16_FR_EFR;
475 }
476 }
477 return OSMO_TRAU_SYNCP_16_FR_EFR;
478 } else {
479 LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
480 sdp_subtype_name);
481 return OSMO_TRAU_SYNCP_16_FR_EFR;
482 }
483}
484
485/* Find out if a given TRAU frame type is AMR */
486static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
487{
488
489 switch (ft) {
490 case OSMO_TRAU16_FT_AMR:
491 case OSMO_TRAU8_AMR_LOW:
492 case OSMO_TRAU8_AMR_6k7:
493 case OSMO_TRAU8_AMR_7k4:
494 return true;
495 default:
496 return false;
497 }
498}
499
500/* !Equip E1 endpoint with I.460 mux resources.
501 * \param[in] endp endpoint to equip
502 * \param[in] ts E1 timeslot number.
503 * \param[in] ss E1 subslot number.
504 * \param[in] offset E1 bit offset.
505 * \returns 0 on success, -EINVAL on error. */
506int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
507{
508 int rc;
509 enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
510
511 OSMO_ASSERT(ts != 0);
512 OSMO_ASSERT(ts != 0xFF);
513 OSMO_ASSERT(ss != 0xFF);
514 OSMO_ASSERT(offs != 0xFF);
515
516 memset(&endp->e1, 0, sizeof(endp->e1));
517
518 endp->e1.last_amr_ft = AMR_4_75;
519
520 /* Set up E1 line / timeslot */
521 rc = mgcp_e1_init(endp->trunk, ts);
522 if (rc != 0)
523 return -EINVAL;
524
525 /* Set up I.460 mux */
526 switch (e1_rates[ss]) {
527 case 64:
528 endp->e1.scd.rate = OSMO_I460_RATE_64k;
529 endp->e1.scd.demux.num_bits = 160 * 8;
530 break;
531 case 32:
532 endp->e1.scd.rate = OSMO_I460_RATE_32k;
533 endp->e1.scd.demux.num_bits = 80 * 8;
534 break;
535 case 16:
536 endp->e1.scd.rate = OSMO_I460_RATE_16k;
537 endp->e1.scd.demux.num_bits = 40 * 8;
538 sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
539 break;
540 case 8:
541 endp->e1.scd.rate = OSMO_I460_RATE_8k;
542 endp->e1.scd.demux.num_bits = 20 * 8;
543 sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
544 break;
545 }
546 endp->e1.scd.bit_offset = offs;
547 endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
548 endp->e1.scd.demux.out_cb_bytes = NULL;
549 endp->e1.scd.demux.user_data = endp;
550 endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
551 endp->e1.scd.mux.user_data = endp;
552
553 LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
554 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
555 endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
556 if (!endp->e1.schan) {
557 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
558 return -EINVAL;
559 }
560
561 if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
562 /* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
563 * types the concept of TRAU frames does not apply. However, at the moment this
564 * is the only format we currently support in osmo-mgw */
565 endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
566 if (!endp->e1.trau_sync_fi) {
567 LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
568 return -EINVAL;
569 }
570 endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
571 endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
572 } else {
573 LOGPENDP(endp, DE1, LOGL_ERROR,
574 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
575 return -EINVAL;
576 }
577
578 return 0;
579}
580
581/*! Update E1 related parameters (codec and sync pattern).
582 * \param[in] endp endpoint to update. */
583void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
584{
585 struct mgcp_conn *conn;
586 struct mgcp_rtp_codec *codec;
587 enum osmo_tray_sync_pat_id sync_pat_id;
588
589 /* In order to determine the codec, find the oldest connection on
590 * the endpoint and use its codec information. Normally on an E1
591 * endpoint no more than one connection should exist. */
592 conn = mgcp_conn_get_oldest(endp);
593 OSMO_ASSERT(conn);
594 codec = conn->u.rtp.end.codec;
595 OSMO_ASSERT(codec);
596
597 /* Update codec information */
598 endp->e1.trau_rtp_st->type =
599 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
600 endp->e1.last_codec = codec;
601
602 /* Update sync pattern */
603 sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
604 osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
605}
606
607/*! Remove E1 resources from endpoint
608 * \param[in] endp endpoint to release. */
609void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
610{
611 LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
612
613 if (endp->e1.schan)
614 osmo_i460_subchan_del(endp->e1.schan);
615 if (endp->e1.trau_rtp_st)
616 talloc_free(endp->e1.trau_rtp_st);
617 if (endp->e1.trau_sync_fi)
618 osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
619
620 memset(&endp->e1, 0, sizeof(endp->e1));
621}
622
623/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
624 * \param[in] endp related endpoint (does not take ownership).
625 * \param[in] codec configuration.
626 * \param[in] msg RTP message buffer (including RTP header).
627 * \returns 0 on success, -1 on ERROR. */
628int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
629{
630 struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
631 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
632 unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
633 struct osmo_trau_frame tf;
634 uint8_t amr_ft;
635 int rc;
636
637 /* Extract AMR frame type from AMR head (if AMR is used) */
638 if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
639 amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
640 else
641 amr_ft = 0xff;
642
643 /* Adapt TRAU frame type on codec changes */
644 OSMO_ASSERT(endp->e1.last_codec);
645 if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
646 endp->e1.trau_rtp_st->type =
647 determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
648 endp->e1.last_codec = codec;
649 endp->e1.last_amr_ft = amr_ft;
650 }
651 if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
652 goto skip;
653 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
654 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
655
656 /* Convert from RTP to TRAU format */
657 msg->l2h = msgb_data(msg) + rtp_hdr_len;
658 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
659 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
660 memset(&tf, 0, sizeof(tf));
661 tf.dir = OSMO_TRAU_DIR_DL;
662 rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
663 if (rc < 0) {
664 LOGPENDP(endp, DE1, LOGL_DEBUG,
665 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
666 goto skip;
667 }
668 rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
669 if (rc < 0) {
670 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
671 goto skip;
672 }
673 msgb_put(msg_tf, rc);
674 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
675 osmo_ubit_dump(msgb_data(msg_tf),
676 msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
677
678 /* Enqueue data to I.460 multiplexer */
679 OSMO_ASSERT(endp->e1.schan);
680 OSMO_ASSERT(endp->e1.trau_sync_fi);
681
682 osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
683 LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
684
685 return 0;
686skip:
687 rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
688 msgb_free(msg_tf);
689 return -1;
690}