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