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