Change in osmo-mgw[master]: mgcp_e1: finish E1 support, add E1 support from libosmoabis

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Wed Aug 12 10:00:21 UTC 2020


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-mgw/+/19524 )

Change subject: mgcp_e1: finish E1 support, add E1 support from libosmoabis
......................................................................

mgcp_e1: finish E1 support, add E1 support from libosmoabis

Currently only the endpoint handling for E1 exists, but there is no
actual code behind it that handles the E1 traffic.

Change-Id: I6b93809b5ac7d01af55888347dd787b0bc997ae1
Related: OS#2659
---
M configure.ac
M include/osmocom/mgcp/Makefile.am
M include/osmocom/mgcp/debug.h
M include/osmocom/mgcp/mgcp_conn.h
A include/osmocom/mgcp/mgcp_e1.h
M include/osmocom/mgcp/mgcp_endp.h
M include/osmocom/mgcp/mgcp_internal.h
M include/osmocom/mgcp/mgcp_ratectr.h
M include/osmocom/mgcp/mgcp_trunk.h
M src/libosmo-mgcp/Makefile.am
M src/libosmo-mgcp/mgcp_conn.c
A src/libosmo-mgcp/mgcp_e1.c
M src/libosmo-mgcp/mgcp_endp.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_protocol.c
M src/libosmo-mgcp/mgcp_ratectr.c
M src/libosmo-mgcp/mgcp_trunk.c
M src/libosmo-mgcp/mgcp_vty.c
M src/osmo-mgw/Makefile.am
M src/osmo-mgw/mgw_main.c
M tests/mgcp/Makefile.am
M tests/mgcp/mgcp_test.c
22 files changed, 1,076 insertions(+), 62 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved



diff --git a/configure.ac b/configure.ac
index 7c63437..db44893 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,6 +52,8 @@
 PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.1.0)
 PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0)
 PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
+PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
+PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.6.0)
 
 AC_ARG_ENABLE(sanitize,
 	[AS_HELP_STRING(
diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am
index fb7654f..549ba87 100644
--- a/include/osmocom/mgcp/Makefile.am
+++ b/include/osmocom/mgcp/Makefile.am
@@ -10,4 +10,5 @@
 	mgcp_trunk.h \
 	debug.h \
 	mgcp_ratectr.h \
+	mgcp_e1.h \
 	$(NULL)
diff --git a/include/osmocom/mgcp/debug.h b/include/osmocom/mgcp/debug.h
index ddeb0dc..7044c1e 100644
--- a/include/osmocom/mgcp/debug.h
+++ b/include/osmocom/mgcp/debug.h
@@ -29,6 +29,7 @@
 /* Debug Areas of the code */
 enum {
 	DRTP,
+	DE1,
 	Debug_LastEntry,
 };
 
diff --git a/include/osmocom/mgcp/mgcp_conn.h b/include/osmocom/mgcp/mgcp_conn.h
index ff5a779..78d5ea8 100644
--- a/include/osmocom/mgcp/mgcp_conn.h
+++ b/include/osmocom/mgcp/mgcp_conn.h
@@ -124,3 +124,4 @@
 void mgcp_conn_free_all(struct mgcp_endpoint *endp);
 char *mgcp_conn_dump(struct mgcp_conn *conn);
 struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
+struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
diff --git a/include/osmocom/mgcp/mgcp_e1.h b/include/osmocom/mgcp/mgcp_e1.h
new file mode 100644
index 0000000..a4ae854
--- /dev/null
+++ b/include/osmocom/mgcp/mgcp_e1.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/* A 64k timeslot on an E1 line can be subdevied into the following
+ * subslot combinations:
+ *
+ * subslot:                                          offset:
+ * [          ][          ][   16k    ][8k_subslot]  0
+ * [          ][   32k    ][_subslot__][8k_subslot]  1
+ * [          ][ subslot  ][   16k    ][8k_subslot]  2
+ * [   64k    ][__________][_subslot__][8k_subslot]  3
+ * [ timeslot ][          ][   16k    ][8k_subslot]  4
+ * [          ][   32K    ][_subslot__][8k_subslot]  5
+ * [          ][ subslot  ][   16k    ][8k_subslot]  6
+ * [          ][          ][ subslot  ][8k_subslot]  7
+ *
+ * Since overlapping assignment of subslots is not possible there is a limited
+ * set of subslot assignments possible. The e1_rates array lists the possible
+ * assignments as depicted above. Also each subslot assignment comes along with
+ * a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
+ * offsets. */
+static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
+static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
+
+int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr);
+int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
+void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
+void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
+int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index c16ea4b..065494f 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -24,6 +24,7 @@
 #pragma once
 
 #include <osmocom/core/msgb.h>
+#include <osmocom/gsm/i460_mux.h>
 
 struct sockaddr_in;
 struct mgcp_conn;
@@ -116,10 +117,23 @@
 
 	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
 	uint32_t x_osmo_ign;
+
+	/* E1 specific */
+	struct {
+		struct osmo_i460_schan_desc scd;
+		struct osmo_i460_subchan *schan;
+		struct osmo_fsm_inst *trau_sync_fi;
+		struct osmo_trau2rtp_state *trau_rtp_st;
+		uint8_t last_amr_ft;
+		struct mgcp_rtp_codec *last_codec;
+	} e1;
+
 };
 
 struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
 void mgcp_endp_release(struct mgcp_endpoint *endp);
+int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
+void mgcp_endp_update(struct mgcp_endpoint *endp);
 struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
 					      const struct mgcp_trunk *trunk);
 struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index 3d7224e..86b2a57 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -71,6 +71,10 @@
 	/* duration of a packet (FIXME: in which unit?) */
 	uint32_t packet_duration;
 
+	/* Note: These states are not continuously updated, they serve as an
+	 * information source to patch certain values in the RTP header. Do
+	 * not use this state if constantly updated data about the RTP stream
+	 * is needed. (see also mgcp_patch_and_count() */
 	struct mgcp_rtp_stream_state in_stream;
 	struct mgcp_rtp_stream_state out_stream;
 
@@ -85,6 +89,13 @@
 		int cycles;
 	} stats;
 
+	/* Alternative values for RTP tx, in case no sufficient header
+	 * information is available so the header needs to be generated
+	 * locally (when just forwarding packets, the header of incoming
+	 * data is just re-used) */
+	uint16_t alt_rtp_tx_sequence;
+	uint32_t alt_rtp_tx_ssrc;
+
 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
 };
 
@@ -296,3 +307,5 @@
 			  struct mgcp_rtp_state *state,
 			  struct mgcp_rtp_end *rtp_end,
 			  struct sockaddr_in *addr, struct msgb *msg);
+
+#define RTP_BUF_SIZE		4096
diff --git a/include/osmocom/mgcp/mgcp_ratectr.h b/include/osmocom/mgcp/mgcp_ratectr.h
index d0bc628..ff59ea4 100644
--- a/include/osmocom/mgcp/mgcp_ratectr.h
+++ b/include/osmocom/mgcp/mgcp_ratectr.h
@@ -29,6 +29,7 @@
 	MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
 	MGCP_CRCX_FAIL_BIND_PORT,
 	MGCP_CRCX_FAIL_AVAIL,
+	MGCP_CRCX_FAIL_CLAIM,
 };
 
 /* Global MCGP MDCX related rate counters */
@@ -63,6 +64,13 @@
 	MGCP_DLCX_FAIL_AVAIL,
 };
 
+/* Trunk-global E1 related counters */
+enum {
+        E1_I460_TRAU_RX_FAIL_CTR,
+        E1_I460_TRAU_TX_FAIL_CTR,
+        E1_I460_TRAU_MUX_EMPTY_CTR,
+};
+
 /* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
 
 struct mgcp_ratectr_global {
@@ -79,6 +87,8 @@
 	struct rate_ctr_group *mgcp_dlcx_ctr_group;
 	/* Rate counter group which aggregates stats of individual RTP connections. */
 	struct rate_ctr_group *all_rtp_conn_stats;
+	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
+	struct rate_ctr_group *e1_stats;
 };
 
 int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h
index aa6dd29..d99f801 100644
--- a/include/osmocom/mgcp/mgcp_trunk.h
+++ b/include/osmocom/mgcp/mgcp_trunk.h
@@ -1,5 +1,13 @@
 #pragma once
 
+#include <osmocom/gsm/i460_mux.h>
+
+#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
+LOGP(cat, level, "trunk:%u " fmt, \
+     trunk ? trunk->trunk_nr : 0, \
+     ## args)
+
+
 enum mgcp_trunk_type {
 	MGCP_TRUNK_VIRTUAL,
 	MGCP_TRUNK_E1,
@@ -39,18 +47,32 @@
 	int rtp_accept_all;
 
 	unsigned int number_endpoints;
-	unsigned int vty_number_endpoints;
 	struct mgcp_endpoint **endpoints;
 
 	/* global rate counters to measure the trunks overall performance and health */
 	struct mgcp_ratectr_trunk ratectr;
+
+	union {
+		/* Virtual trunk specific */
+		struct {
+			unsigned int vty_number_endpoints;
+		} v;
+		/* E1 specific */
+		struct {
+			unsigned int vty_line_nr;
+			struct e1inp_line *line;
+			bool ts_in_use[31];
+			struct osmo_i460_timeslot i460_ts[31];
+		} e1;
+	};
 };
 
 struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
-int mgcp_trunk_alloc_endpts(struct mgcp_trunk *tcfg);
+int mgcp_trunk_equip(struct mgcp_trunk *trunk);
 struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
 struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
 int e1_trunk_nr_from_epname(const char *epname);
+struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
 
 /* The virtual trunk is always created on trunk id 0 for historical reasons,
  * use this define constant as ID when allocating a virtual trunk. Other
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
index 77d0cdf..91b2bf6 100644
--- a/src/libosmo-mgcp/Makefile.am
+++ b/src/libosmo-mgcp/Makefile.am
@@ -10,6 +10,8 @@
 	$(LIBOSMOGSM_CFLAGS) \
 	$(LIBOSMOVTY_CFLAGS) \
 	$(LIBOSMONETIF_CFLAGS) \
+	$(LIBOSMOABIS_CFLAGS) \
+	$(LIBOSMOTRAU_CFLAGS) \
 	$(COVERAGE_CFLAGS) \
 	$(NULL)
 
@@ -18,6 +20,8 @@
 	$(LIBOSMOGSM_LIBS) \
 	$(LIBOSMOVTY_LIBS) \
 	$(LIBOSMONETIF_LIBS) \
+	$(LIBOSMOABIS_LIBS) \
+	$(LIBOSMOTRAU_LIBS) \
 	$(COVERAGE_LDFLAGS) \
 	$(NULL)
 
@@ -43,4 +47,5 @@
 	mgcp_trunk.c \
 	mgcp_ctrl.c \
 	mgcp_ratectr.c \
+	mgcp_e1.c \
 	$(NULL)
diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c
index 6802b91..8c7918e 100644
--- a/src/libosmo-mgcp/mgcp_conn.c
+++ b/src/libosmo-mgcp/mgcp_conn.c
@@ -397,3 +397,13 @@
 
 	return NULL;
 }
+
+/*! get oldest connection in the list.
+ *  \param[in] endp associated endpoint */
+struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
+{
+	if (llist_empty(&endp->conns))
+		return NULL;
+
+	return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
+}
diff --git a/src/libosmo-mgcp/mgcp_e1.c b/src/libosmo-mgcp/mgcp_e1.c
new file mode 100644
index 0000000..1e227dc
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_e1.c
@@ -0,0 +1,687 @@
+/* E1 traffic handling */
+
+/*
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info at sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/mgcp/mgcp_internal.h>
+#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
+#include <osmocom/mgcp/mgcp_conn.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/abis.h>
+
+#include <osmocom/trau/trau_sync.h>
+#include <osmocom/trau/trau_frame.h>
+#include <osmocom/trau/trau_rtp.h>
+#include <osmocom/mgcp/mgcp_conn.h>
+#include <osmocom/netif/rtp.h>
+#include <osmocom/mgcp/debug.h>
+#include <osmocom/mgcp/mgcp_e1.h>
+#include <osmocom/codec/codec.h>
+
+#define DEBUG_BITS_MAX 80
+#define DEBUG_BYTES_MAX 40
+#define DEBUG_E1_TS 0
+#define E1_TS_BYTES 160
+#define E1_TRAU_BITS 320
+#define E1_TRAU_BITS_MSGB 2048
+
+static struct mgcp_config *cfg;
+
+static const struct e1inp_line_ops dummy_e1_line_ops = {
+	.sign_link_up = NULL,
+	.sign_link_down = NULL,
+	.sign_link = NULL,
+};
+
+/* EFR idle frame */
+static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 1, 1, 0, 1, 0, 0, 0,
+				      0, 0, 0, 0, 1, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 0, 0,
+				      0, 0, 0, 0, 0, 0, 0, 0,
+				      1, 0, 0, 0, 0, 0, 1, 0,
+				      1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+/* FR idle frame */
+static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 1, 1, 1, 0, 0, 0, 0,
+				     0, 0, 0, 0, 1, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     1, 0, 0, 0, 0, 0, 1, 0,
+				     1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
+static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+				       0, 0, 0, 0, 0, 0, 0, 0,
+				       1, 0, 1, 1, 1, 0, 0, 0,
+				       0, 0, 0, 0, 1, 0, 0, 0,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+				       1, 1, 1, 1, 1, 1, 1, 0,
+				       1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
+ * run empty. In order to make sure that the TRAU frame transmission continues
+ * we generate idle TRAU frames here. */
+static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
+{
+	struct mgcp_endpoint *endp = user_data;
+	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
+	struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
+	uint8_t *ptr;
+	const uint8_t *ptr_ft;
+	enum osmo_trau_frame_type ft;
+
+	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
+
+	/* Choose an appropiate idle frame type */
+	ft = endp->e1.trau_rtp_st->type;
+	switch (ft) {
+	case OSMO_TRAU16_FT_FR:
+		ptr_ft = idle_tf_fr;
+		break;
+	case OSMO_TRAU16_FT_EFR:
+		ptr_ft = idle_tf_efr;
+		break;
+	default:
+		/* FIXME: What about 8k subslots and AMR frames? */
+		ptr_ft = idle_tf_spch;
+	}
+
+	/* Put the replacement into a message buffer and enqueue it into the
+	 * I.460 multiplexer */
+	ptr = msgb_put(msg, E1_TRAU_BITS);
+	memcpy(ptr, ptr_ft, E1_TRAU_BITS);
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
+		 osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
+	osmo_i460_mux_enqueue(endp->e1.schan, msg);
+}
+
+/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
+static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
+				  unsigned int num_bits)
+{
+	struct mgcp_endpoint *endp = user_data;
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
+		 osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
+
+	OSMO_ASSERT(endp->e1.trau_sync_fi);
+	osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
+}
+
+/* called for each synchronized TRAU frame received; decode frame + convert to RTP
+ * (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
+static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
+{
+	struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
+	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
+	struct mgcp_endpoint *endp = user_data;
+	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
+	struct mgcp_conn *conn_dst;
+	struct osmo_trau_frame fr;
+	int rc;
+
+	if (!bits || num_bits == 0)
+		goto skip;
+
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
+		 num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
+
+	/* Decode TRAU frame */
+	switch (endp->e1.scd.rate) {
+	case OSMO_I460_RATE_8k:
+		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
+		rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
+		break;
+	case OSMO_I460_RATE_16k:
+		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
+		rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
+		break;
+	default:
+		/* TRAU frames only exist in 8K or 16K subslots. */
+		OSMO_ASSERT(false);
+		break;
+	}
+	if (rc != 0) {
+		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
+		goto skip;
+	}
+
+	/* Check if the payload type is supported and what the expected lenth
+	 * of the RTP payload will be. */
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
+		 osmo_trau_frame_type_name(fr.type));
+
+	/* Convert decoded trau frame to RTP frame */
+	struct osmo_trau2rtp_state t2rs = {
+		.type = fr.type,
+	};
+	rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
+	if (rc <= 0) {
+		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
+		goto skip;
+	}
+	msgb_put(msg, rtp_hdr_len + rc);
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
+		 osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
+
+	/* Forward RTP data to IP */
+	conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
+	if (!conn_dst) {
+		LOGPENDP(endp, DE1, LOGL_DEBUG,
+			 "E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
+		goto skip;
+	}
+	OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
+
+	mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
+
+	msgb_free(msg);
+	return;
+skip:
+	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
+	msgb_free(msg);
+	return;
+}
+
+/* Function to handle outgoing E1 traffic */
+static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
+{
+	struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
+	uint8_t *ptr;
+
+	/* Get E1 frame from I.460 multiplexer */
+	ptr = msgb_put(msg, E1_TS_BYTES);
+	osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
+
+#if DEBUG_E1_TS == 1
+	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
+		  osmo_hexdump_nospc(msgb_data(msg),
+				     msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
+#endif
+	/* Hand data over to the E1 stack */
+	msgb_enqueue(&ts->raw.tx_queue, msg);
+	return;
+}
+
+/* Callback function to handle incoming E1 traffic */
+static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
+{
+	struct mgcp_trunk *trunk;
+
+	/* Find associated trunk */
+	trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
+	if (!trunk) {
+		LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
+		return;
+	}
+
+	/* Check if the incoming data looks sane */
+	if (msgb_length(msg) != E1_TS_BYTES) {
+		LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
+			  "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
+			  msgb_length(msg));
+	}
+#if DEBUG_E1_TS == 1
+	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
+		  msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
+						       msgb_length(msg) >
+						       DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
+#endif
+
+	/* Hand data over to the I.460 demultiplexer. */
+	osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
+
+	/* Trigger sending of pending E1 traffic */
+	e1_send(ts, trunk);
+}
+
+/*! Find an endpoint by its name on a specified trunk.
+ *  \param[in] trunk trunk configuration.
+ *  \param[in] ts_nr E1 timeslot number.
+ *  \returns -EINVAL on failure, 0 on success. */
+int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
+{
+	/*! Each timeslot needs only to be configured once. The Timeslot then
+	 *  stays open and permanently receives data. It is then up to the
+	 *  I.460 demultiplexer to add/remove subchannels as needed. It is
+	 *  allowed to call this function multiple times since we check if the
+	 *  timeslot is already configured. */
+
+	struct e1inp_line *e1_line;
+	int rc;
+
+	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
+	cfg = trunk->cfg;
+
+	if (trunk->e1.ts_in_use[ts_nr - 1]) {
+		LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
+		return 0;
+	}
+
+	/* Get E1 line */
+	if (!trunk->e1.line) {
+		e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
+		if (!e1_line) {
+			LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
+				  trunk->e1.vty_line_nr);
+			return -EINVAL;
+		}
+		e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
+	} else
+		e1_line = trunk->e1.line;
+	if (!e1_line)
+		return -EINVAL;
+
+	/* Configure E1 timeslot */
+	rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
+	if (rc < 0)
+		return -EINVAL;
+	e1inp_line_update(e1_line);
+	if (rc < 0)
+		return -EINVAL;
+
+	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
+	trunk->e1.ts_in_use[ts_nr - 1] = true;
+
+	return 0;
+}
+
+/* Determine a suitable TRAU frame type for a given codec */
+static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
+							uint8_t amr_ft, struct mgcp_endpoint *endp)
+{
+	if (strcmp(sdp_subtype_name, "GSM") == 0)
+		return OSMO_TRAU16_FT_FR;
+	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
+		return OSMO_TRAU16_FT_EFR;
+	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
+		return OSMO_TRAU16_FT_HR;
+	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
+		if (i460_rate == OSMO_I460_RATE_8k) {
+			switch (amr_ft) {
+			case AMR_4_75:
+			case AMR_5_15:
+			case AMR_5_90:
+				return OSMO_TRAU8_AMR_LOW;
+			case AMR_6_70:
+				return OSMO_TRAU8_AMR_6k7;
+			case AMR_7_40:
+				return OSMO_TRAU8_AMR_7k4;
+			default:
+				LOGPENDP(endp, DE1, LOGL_ERROR,
+					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
+				return OSMO_TRAU_FT_NONE;
+			}
+		}
+		return OSMO_TRAU16_FT_AMR;
+	} else {
+		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
+			 sdp_subtype_name);
+		return OSMO_TRAU_FT_NONE;
+	}
+}
+
+/* Determine a suitable TRAU frame type for a given codec */
+static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
+							  uint8_t amr_ft, struct mgcp_endpoint *endp)
+{
+	if (strcmp(sdp_subtype_name, "GSM") == 0)
+		return OSMO_TRAU_SYNCP_16_FR_EFR;
+	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
+		return OSMO_TRAU_SYNCP_16_FR_EFR;
+	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
+		return OSMO_TRAU_SYNCP_8_HR;
+	else if (strcmp(sdp_subtype_name, "AMR") == 0) {
+		if (i460_rate == OSMO_I460_RATE_8k) {
+			switch (amr_ft) {
+			case AMR_4_75:
+			case AMR_5_15:
+			case AMR_5_90:
+				return OSMO_TRAU_SYNCP_8_AMR_LOW;
+			case AMR_6_70:
+				return OSMO_TRAU_SYNCP_8_AMR_6K7;
+			case AMR_7_40:
+				return OSMO_TRAU_SYNCP_8_AMR_7K4;
+			default:
+				LOGPENDP(endp, DE1, LOGL_ERROR,
+					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
+				return OSMO_TRAU_SYNCP_16_FR_EFR;
+			}
+		}
+		return OSMO_TRAU_SYNCP_16_FR_EFR;
+	} else {
+		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
+			 sdp_subtype_name);
+		return OSMO_TRAU_SYNCP_16_FR_EFR;
+	}
+}
+
+/* Find out if a given TRAU frame type is AMR */
+static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
+{
+
+	switch (ft) {
+	case OSMO_TRAU16_FT_AMR:
+	case OSMO_TRAU8_AMR_LOW:
+	case OSMO_TRAU8_AMR_6k7:
+	case OSMO_TRAU8_AMR_7k4:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* !Equip E1 endpoint with I.460 mux resources.
+ *  \param[in] endp endpoint to equip
+ *  \param[in] ts E1 timeslot number.
+ *  \param[in] ss E1 subslot number.
+ *  \param[in] offset E1 bit offset.
+ *  \returns 0 on success, -EINVAL on error. */
+int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
+{
+	int rc;
+	enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
+
+	OSMO_ASSERT(ts != 0);
+	OSMO_ASSERT(ts != 0xFF);
+	OSMO_ASSERT(ss != 0xFF);
+	OSMO_ASSERT(offs != 0xFF);
+
+	memset(&endp->e1, 0, sizeof(endp->e1));
+
+	endp->e1.last_amr_ft = AMR_4_75;
+
+	/* Set up E1 line / timeslot */
+	rc = mgcp_e1_init(endp->trunk, ts);
+	if (rc != 0)
+		return -EINVAL;
+
+	/* Set up I.460 mux */
+	switch (e1_rates[ss]) {
+	case 64:
+		endp->e1.scd.rate = OSMO_I460_RATE_64k;
+		endp->e1.scd.demux.num_bits = 160 * 8;
+		break;
+	case 32:
+		endp->e1.scd.rate = OSMO_I460_RATE_32k;
+		endp->e1.scd.demux.num_bits = 80 * 8;
+		break;
+	case 16:
+		endp->e1.scd.rate = OSMO_I460_RATE_16k;
+		endp->e1.scd.demux.num_bits = 40 * 8;
+		sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
+		break;
+	case 8:
+		endp->e1.scd.rate = OSMO_I460_RATE_8k;
+		endp->e1.scd.demux.num_bits = 20 * 8;
+		sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
+		break;
+	}
+	endp->e1.scd.bit_offset = offs;
+	endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
+	endp->e1.scd.demux.out_cb_bytes = NULL;
+	endp->e1.scd.demux.user_data = endp;
+	endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
+	endp->e1.scd.mux.user_data = endp;
+
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
+		 offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
+	endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
+	if (!endp->e1.schan) {
+		LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
+		return -EINVAL;
+	}
+
+	if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
+		/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
+		 * types the concept of TRAU frames does not apply. However, at the moment this
+		 * is the only format we currently support in osmo-mgw */
+		endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
+		if (!endp->e1.trau_sync_fi) {
+			LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
+			return -EINVAL;
+		}
+		endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
+		endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
+	} else {
+		LOGPENDP(endp, DE1, LOGL_ERROR,
+			 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*! Update E1 related parameters (codec and sync pattern).
+ *  \param[in] endp endpoint to update. */
+void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
+{
+	struct mgcp_conn *conn;
+	struct mgcp_rtp_codec *codec;
+	enum osmo_tray_sync_pat_id sync_pat_id;
+
+	/* In order to determine the codec, find the oldest connection on
+	 * the endpoint and use its codec information. Normally on an E1
+	 * endpoint no more than one connection should exist. */
+	conn = mgcp_conn_get_oldest(endp);
+	OSMO_ASSERT(conn);
+	codec = conn->u.rtp.end.codec;
+	OSMO_ASSERT(codec);
+
+	/* Update codec information */
+	endp->e1.trau_rtp_st->type =
+	    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
+	endp->e1.last_codec = codec;
+
+	/* Update sync pattern */
+	sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
+	osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
+}
+
+/*! Remove E1 resources from endpoint
+ *  \param[in] endp endpoint to release. */
+void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
+{
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
+
+	if (endp->e1.schan)
+		osmo_i460_subchan_del(endp->e1.schan);
+	if (endp->e1.trau_rtp_st)
+		talloc_free(endp->e1.trau_rtp_st);
+	if (endp->e1.trau_sync_fi)
+		osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
+
+	memset(&endp->e1, 0, sizeof(endp->e1));
+}
+
+/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
+ *  \param[in] endp related endpoint (does not take ownership).
+ *  \param[in] codec configuration.
+ *  \param[in] msg RTP message buffer (including RTP header).
+ *  \returns 0 on success, -1 on ERROR. */
+int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
+{
+	struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
+	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
+	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
+	struct osmo_trau_frame tf;
+	uint8_t amr_ft;
+	int rc;
+
+	/* Extract AMR frame type from AMR head (if AMR is used) */
+	if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
+		amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
+	else
+		amr_ft = 0xff;
+
+	/* Adapt TRAU frame type on codec changes */
+	OSMO_ASSERT(endp->e1.last_codec);
+	if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
+		endp->e1.trau_rtp_st->type =
+		    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
+		endp->e1.last_codec = codec;
+		endp->e1.last_amr_ft = amr_ft;
+	}
+	if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
+		goto skip;
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
+		 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
+
+	/* Convert from RTP to TRAU format */
+	msg->l2h = msgb_data(msg) + rtp_hdr_len;
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
+		 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
+	memset(&tf, 0, sizeof(tf));
+	tf.dir = OSMO_TRAU_DIR_DL;
+	rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
+	if (rc < 0) {
+		LOGPENDP(endp, DE1, LOGL_DEBUG,
+			 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
+		goto skip;
+	}
+	rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
+	if (rc < 0) {
+		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
+		goto skip;
+	}
+	msgb_put(msg_tf, rc);
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
+		 osmo_ubit_dump(msgb_data(msg_tf),
+				msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
+
+	/* Enqueue data to I.460 multiplexer */
+	OSMO_ASSERT(endp->e1.schan);
+	OSMO_ASSERT(endp->e1.trau_sync_fi);
+
+	osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
+	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
+
+	return 0;
+skip:
+	rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
+	msgb_free(msg_tf);
+	return -1;
+}
diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c
index 5d9ec27..f0ad0a7 100644
--- a/src/libosmo-mgcp/mgcp_endp.c
+++ b/src/libosmo-mgcp/mgcp_endp.c
@@ -25,33 +25,12 @@
 #include <osmocom/mgcp/mgcp_endp.h>
 #include <osmocom/mgcp/mgcp_trunk.h>
 
+#include <osmocom/mgcp/mgcp_e1.h>
+
 #define E1_TIMESLOTS 32
 #define E1_RATE_MAX 64
 #define E1_OFFS_MAX 8
 
-/* A 64k timeslot on an E1 line can be subdevied into the following
- * subslot combinations:
- *
- * subslot:                                          offset:
- * [          ][          ][   16k    ][8k_subslot]  0
- * [          ][   32k    ][_subslot__][8k_subslot]  1
- * [          ][ subslot  ][   16k    ][8k_subslot]  2
- * [   64k    ][__________][_subslot__][8k_subslot]  3
- * [ timeslot ][          ][   16k    ][8k_subslot]  4
- * [          ][   32K    ][_subslot__][8k_subslot]  5
- * [          ][ subslot  ][   16k    ][8k_subslot]  6
- * [          ][          ][ subslot  ][8k_subslot]  7
- *
- * Since overlapping assignment of subslots is not possible there is a limited
- * set of subslot assignments possible. The e1_rates array lists the possible
- * assignments as depicted above. Also each subslot assignment comes along with
- * a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
- * offsets. */
-static const uint8_t e1_rates[] =
-		{ 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
-static const uint8_t e1_offsets[] =
-		{ 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
-
 /* Endpoint typeset definition */
 const struct mgcp_endpoint_typeset ep_typeset = {
 	/* Specify endpoint properties for RTP endpoint */
@@ -116,7 +95,7 @@
 		endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
 		break;
 	case MGCP_TRUNK_E1:
-		endp->type = &ep_typeset.rtp;
+		endp->type = &ep_typeset.e1;
 		endp->name = gen_e1_epname(endp, trunk->cfg->domain,
 					   trunk->trunk_nr,
 					   index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
@@ -149,6 +128,9 @@
 	talloc_free(endp->local_options.codec);
 	endp->local_options.codec = NULL;
 	endp->wildcarded_req = false;
+
+	if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
+		mgcp_e1_endp_release(endp);
 }
 
 /* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
@@ -614,3 +596,71 @@
 
 	return false;
 }
+
+/*! claim endpoint, sets callid and activates endpoint, should be called at the
+ *  beginning of the CRCX procedure when it is clear that a new call should be
+ *  created.
+ *  \param[in] endp endpoint to claim.
+ *  \param[in] callid that is assingned to this endpoint. */
+int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
+{
+	int rc = 0;
+	uint8_t ts;
+	uint8_t ss;
+	uint8_t offs;
+
+	/* TODO: Make this function more intelligent, it should run the
+	 * call id checks we currently have in protocol.c directly here. */
+
+	/* Set the callid, creation of another connection will only be possible
+	 * when the callid matches up. (Connections are distinguished by their
+	 * connection ids) */
+	endp->callid = talloc_strdup(endp, callid);
+	OSMO_ASSERT(endp->callid);
+
+	/* Allocate resources */
+	switch (endp->trunk->trunk_type) {
+	case MGCP_TRUNK_VIRTUAL:
+		/* No additional initaliziation required here, virtual
+		 * endpoints will open/close network sockets themselves
+		 * on demand. */
+		break;
+	case MGCP_TRUNK_E1:
+		ts = e1_ts_nr_from_epname(endp->name);
+		ss = e1_ss_nr_from_epname(endp->name);
+		offs = e1_offs_from_epname(endp->name);
+		OSMO_ASSERT(ts != 0xFF);
+		OSMO_ASSERT(ts != 0);
+		OSMO_ASSERT(ss != 0xFF);
+		OSMO_ASSERT(offs != 0xFF);
+		rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
+		break;
+	default:
+		OSMO_ASSERT(false);
+	}
+
+	/* Make sure the endpoint is released when claiming the endpoint
+	 * failes. */
+	if (rc < 0)
+		mgcp_endp_release(endp);
+
+	return rc;
+}
+
+/*! update endpoint, updates internal endpoint specific data, should be
+ *  after when MDCX or CRCX has been executed successuflly.
+ *  \param[in] endp endpoint to update. */
+void mgcp_endp_update(struct mgcp_endpoint *endp)
+{
+	/* Allocate resources */
+	switch (endp->trunk->trunk_type) {
+	case MGCP_TRUNK_VIRTUAL:
+		/* No updating initaliziation required for virtual endpoints. */
+		break;
+	case MGCP_TRUNK_E1:
+		mgcp_e1_endp_update(endp);
+		break;
+	default:
+		OSMO_ASSERT(false);
+	}
+}
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 155ed20..e0aa42e 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -46,12 +46,11 @@
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/debug.h>
 #include <osmocom/codec/codec.h>
-
+#include <osmocom/mgcp/mgcp_e1.h>
 
 #define RTP_SEQ_MOD		(1 << 16)
 #define RTP_MAX_DROPOUT		3000
 #define RTP_MAX_MISORDER	100
-#define RTP_BUF_SIZE		4096
 
 enum rtp_proto {
 	MGCP_PROTO_RTP,
@@ -798,6 +797,23 @@
 		     "Forwarding tapped (debug) voice data failed.\n");
 }
 
+/* Generate an RTP header if it is missing */
+static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
+			   struct mgcp_rtp_state *state)
+{
+	struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
+
+	if (hdr->version > 0)
+		return;
+
+	hdr->version = 2;
+	hdr->payload_type = rtp_end->codec->payload_type;
+	hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
+	hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
+	hdr->ssrc = state->alt_rtp_tx_ssrc;
+}
+
+
 /*! Send RTP/RTCP data to a specified destination connection.
  *  \param[in] endp associated endpoint (for configuration, logging).
  *  \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
@@ -857,6 +873,11 @@
 	rtp_state = &conn_src->state;
 	dest_name = conn_dst->conn->name;
 
+	/* Ensure we have an alternative SSRC in case we need it, see also
+	 * gen_rtp_header() */
+	if (rtp_state->alt_rtp_tx_ssrc == 0)
+		rtp_state->alt_rtp_tx_ssrc = rand();
+
 	if (!rtp_end->output_enabled) {
 		rtpconn_rate_ctr_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
 		LOGPENDP(endp, DRTP, LOGL_DEBUG,
@@ -870,6 +891,11 @@
 		int cont;
 		int nbytes = 0;
 		int buflen = msgb_length(msg);
+
+		/* Make sure we have a valid RTP header, in cases where no RTP
+		 * header is present, we will generate one. */
+		gen_rtp_header(msg, rtp_end, rtp_state);
+
 		do {
 			/* Run transcoder */
 			cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
@@ -938,6 +964,7 @@
 
 			rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
 			rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
+			rtp_state->alt_rtp_tx_sequence++;
 
 			nbytes += len;
 			buflen = cont;
@@ -956,6 +983,7 @@
 
 		rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
 		rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
+		rtp_state->alt_rtp_tx_sequence++;
 
 		return len;
 	}
@@ -1236,12 +1264,24 @@
 	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
 	struct mgcp_conn_rtp *conn_src = mc->conn_src;
 	struct mgcp_conn *conn = conn_src->conn;
+	struct sockaddr_in *from_addr = mc->from_addr;
 
-	/* FIXME: integrate E1 support from libsomoabis, also implement
-	 * handling for RTCP packets, which can not converted to E1. */
-	LOGPCONN(conn, DRTP, LOGL_FATAL,
-		 "cannot dispatch! E1 support is not implemented yet!\n");
-	return -1;
+	/* Check if the connection is in loopback mode, if yes, just send the
+	 * incoming data back to the origin */
+	if (conn->mode == MGCP_CONN_LOOPBACK) {
+		/* When we are in loopback mode, we loop back all incoming
+		 * packets back to their origin. We will use the originating
+		 * address data from the UDP packet header to patch the
+		 * outgoing address in connection on the fly */
+		if (conn->u.rtp.end.rtp_port == 0) {
+			conn->u.rtp.end.addr = from_addr->sin_addr;
+			conn->u.rtp.end.rtp_port = from_addr->sin_port;
+		}
+		return mgcp_send_rtp(conn_src, msg);
+	}
+
+	/* Forward to E1 */
+	return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
 }
 
 /*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1267,8 +1307,9 @@
  *  \param[in] conn Connection that is about to be removed (ignored). */
 void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
 {
-	LOGPCONN(conn, DRTP, LOGL_FATAL,
-		 "cannot dispatch! E1 support is not implemented yet!\n");
+	/* Cleanup tasks for E1 are the same as for regular endpoint. The
+	 * shut down of the E1 part is handled separately. */
+	mgcp_cleanup_rtp_bridge_cb(endp, conn);
 }
 
 static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index bc96462..2a6581e 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -849,10 +849,16 @@
 		}
 	}
 
-	/* Set the callid, creation of another connection will only be possible
-	 * when the callid matches up. (Connections are distinguished by their
-	 * connection ids) */
-	endp->callid = talloc_strdup(trunk->endpoints, callid);
+	if (!endp->callid) {
+		/* Claim endpoint resources. This will also set the callid,
+		 * creating additional connections will only be possible if
+		 * the callid matches up (see above). */
+		rc = mgcp_endp_claim(endp, callid);
+		if (rc != 0) {
+			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CLAIM]);
+			return create_err_response(endp, 502, "CRCX", p->trans);
+		}
+	}
 
 	snprintf(conn_name, sizeof(conn_name), "%s", callid);
 	_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
@@ -863,6 +869,7 @@
 		goto error2;
 
 	}
+
 	conn = mgcp_conn_get_rtp(endp, _conn->id);
 	OSMO_ASSERT(conn);
 
@@ -979,6 +986,7 @@
 	LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
 		 "CRCX: connection successfully created\n");
 	rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
+	mgcp_endp_update(endp);
 	return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
 error2:
 	mgcp_endp_release(endp);
@@ -1206,6 +1214,7 @@
 
 	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 		 "MDCX: connection successfully modified\n");
+	mgcp_endp_update(endp);
 	return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
 error3:
 	return create_err_response(endp, error_code, "MDCX", p->trans);
diff --git a/src/libosmo-mgcp/mgcp_ratectr.c b/src/libosmo-mgcp/mgcp_ratectr.c
index 1a89c83..aacdd85 100644
--- a/src/libosmo-mgcp/mgcp_ratectr.c
+++ b/src/libosmo-mgcp/mgcp_ratectr.c
@@ -64,6 +64,7 @@
 	[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
 	[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
 	[MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." },
+	[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
 };
 
 const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
@@ -124,6 +125,20 @@
 	.ctr_desc = mgcp_dlcx_ctr_desc
 };
 
+static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
+	[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
+	[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
+	[E1_I460_TRAU_MUX_EMPTY_CTR] = {"e1:i460", "Outbound I.460 MUX queue empty."}
+};
+
+const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
+	.group_name_prefix = "e1",
+	.group_description = "e1 statistics",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
+	.ctr_desc = e1_rate_ctr_desc
+};
+
 const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 	.group_name_prefix = "all_rtp_conn",
 	.group_description = "aggregated statistics for all rtp connections",
@@ -203,5 +218,12 @@
 		talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
 		all_rtp_conn_rate_ctr_index++;
 	}
+	if (ratectr->e1_stats == NULL) {
+		ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
+		if (!ratectr->e1_stats)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
+		mdcx_rate_ctr_index++;
+	}
 	return 0;
 }
diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c
index 0d7b385..dfedc4b 100644
--- a/src/libosmo-mgcp/mgcp_trunk.c
+++ b/src/libosmo-mgcp/mgcp_trunk.c
@@ -24,6 +24,8 @@
 #include <osmocom/mgcp/mgcp_internal.h>
 #include <osmocom/mgcp/mgcp_endp.h>
 #include <osmocom/mgcp/mgcp_trunk.h>
+#include <osmocom/mgcp/mgcp_e1.h>
+#include <osmocom/abis/e1_input.h>
 
 /*! allocate trunk and add it (if required) to the trunk list.
  *  (called once at startup by VTY).
@@ -47,7 +49,7 @@
 
 	trunk->audio_send_ptime = 1;
 	trunk->audio_send_name = 1;
-	trunk->vty_number_endpoints = 32;
+	trunk->v.vty_number_endpoints = 32;
 	trunk->omit_rtcp = 0;
 
 	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
@@ -81,13 +83,13 @@
 		/* Due to historical reasons the endpoints on the virtual
 		 * trunk start counting at 1. */
 		first_endpoint_nr = 1;
-		number_endpoints = trunk->vty_number_endpoints;
+		number_endpoints = trunk->v.vty_number_endpoints;
 		break;
 	case MGCP_TRUNK_E1:
 		/* The first timeslot on an E1 line is reserved for framing
 		 * and alignment and can not be used for audio transport */
 	        first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
-		number_endpoints = 31 * MGCP_ENDP_E1_SUBSLOTS;
+		number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
 		break;
 	default:
 		OSMO_ASSERT(false);
@@ -122,6 +124,41 @@
 	return 0;
 }
 
+/*! Equip trunk with endpoints and resources
+ *  (called once at startup by VTY).
+ *  \param[in] trunk trunk configuration.
+ *  \returns 0 on success, -1 on failure. */
+int mgcp_trunk_equip(struct mgcp_trunk *trunk)
+{
+	unsigned int i;
+
+	/* Allocate endpoints */
+	if(mgcp_trunk_alloc_endpts(trunk) != 0)
+		return -1;
+
+	/* Allocate resources */
+	switch (trunk->trunk_type) {
+	case MGCP_TRUNK_VIRTUAL:
+		/* No additional initaliziation required here, virtual
+		 * endpoints will open/close network sockets themselves
+		 * on demand. */
+		break;
+	case MGCP_TRUNK_E1:
+		/* The TS initalization happens once on startup for all
+		 * timeslots. This only affects the i460 multiplexer. Until
+		 * now no E1 resources are claimed yet. This happens on demand
+		 * when the related endpoint is actually used */
+		memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
+		for (i = 0; i < (NUM_E1_TS-1); i++)
+			osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
+		break;
+	default:
+		OSMO_ASSERT(false);
+	}
+
+	return 0;
+}
+
 /*! get trunk configuration by trunk number (index).
  *  \param[in] cfg mgcp configuration.
  *  \param[in] ttype trunk type.
@@ -199,3 +236,19 @@
 	LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
 	return NULL;
 }
+
+/*! Find a trunk (E1) by its associated E1 line number.
+ *  \param[in] num e1 line number.
+ *  \returns trunk or NULL if trunk was not found. */
+struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
+{
+	/*! When used on trunks other than E1, the result will always be NULL. */
+	struct mgcp_trunk *trunk;
+
+	llist_for_each_entry(trunk, &cfg->trunks, entry) {
+		if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
+			return trunk;
+	}
+
+	return NULL;
+}
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 35b75fb..b8ec241 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -112,7 +112,7 @@
 		trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
 	vty_out(vty, " loop %u%s", ! !trunk->audio_loop, VTY_NEWLINE);
 	vty_out(vty, " number endpoints %u%s",
-		trunk->vty_number_endpoints, VTY_NEWLINE);
+		trunk->v.vty_number_endpoints, VTY_NEWLINE);
 	vty_out(vty, " %sallow-transcoding%s",
 		trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
 	if (g_cfg->call_agent_addr)
@@ -243,8 +243,10 @@
 	}
 }
 
-static void dump_ratectr_trunk(struct vty *vty, struct mgcp_ratectr_trunk *ratectr)
+static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
 {
+	struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr;
+
 	vty_out(vty, "%s", VTY_NEWLINE);
 	vty_out(vty, "Rate counters (trunk):%s", VTY_NEWLINE);
 
@@ -280,6 +282,15 @@
 					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
 					   ratectr->all_rtp_conn_stats);
 	}
+
+	if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
+		vty_out(vty, "   %s:%s",
+			ratectr->e1_stats->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->e1_stats);
+	}
 }
 
 
@@ -305,7 +316,7 @@
 	}
 
 	if (show_stats)
-		dump_ratectr_trunk(vty, &trunk->ratectr);
+		dump_ratectr_trunk(vty, trunk);
 }
 
 #define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
@@ -715,7 +726,7 @@
 {
 	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 	OSMO_ASSERT(trunk);
-	trunk->vty_number_endpoints = atoi(argv[0]);
+	trunk->v.vty_number_endpoints = atoi(argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -894,6 +905,7 @@
 			continue;
 
 		vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
+		vty_out(vty, "  line %u%s", trunk->e1.vty_line_nr, VTY_NEWLINE);
 		vty_out(vty, "  %ssdp audio-payload send-ptime%s",
 			trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
 		vty_out(vty, "  %ssdp audio-payload send-name%s",
@@ -1159,6 +1171,19 @@
 	return CMD_SUCCESS;
 }
 
+#define LINE_STR "Configure trunk for given Line\nE1/T1 Line Number\n"
+
+DEFUN(cfg_trunk_line,
+      cfg_trunk_line_cmd,
+      "line <0-255>",
+      LINE_STR)
+{
+	struct mgcp_trunk *trunk = vty->index;
+	int line_nr = atoi(argv[0]);
+	trunk->e1.vty_line_nr = line_nr;
+	return CMD_SUCCESS;
+}
+
 DEFUN(loop_conn,
       loop_conn_cmd,
       "loop-endpoint <0-64> NAME (0|1)",
@@ -1549,6 +1574,7 @@
 	install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_name_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_allow_transcoding_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_no_allow_transcoding_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_line_cmd);
 
 	return 0;
 }
@@ -1577,7 +1603,7 @@
 	}
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
-		if (mgcp_trunk_alloc_endpts(trunk) != 0) {
+		if (mgcp_trunk_equip(trunk) != 0) {
 			LOGP(DLMGCP, LOGL_ERROR,
 			     "Failed to initialize trunk %d (%d endpoints)\n",
 			     trunk->trunk_nr, trunk->number_endpoints);
diff --git a/src/osmo-mgw/Makefile.am b/src/osmo-mgw/Makefile.am
index d38df9f..928390b 100644
--- a/src/osmo-mgw/Makefile.am
+++ b/src/osmo-mgw/Makefile.am
@@ -11,6 +11,8 @@
 	$(LIBOSMOGSM_CFLAGS) \
 	$(LIBOSMOCTRL_CFLAGS) \
 	$(LIBOSMONETIF_CFLAGS) \
+	$(LIBOSMOABIS_CFLAGS) \
+	$(LIBOSMOTRAU_CFLAGS) \
 	$(COVERAGE_CFLAGS) \
 	$(NULL)
 
@@ -29,4 +31,6 @@
 	$(LIBOSMOGSM_LIBS) \
 	$(LIBOSMOCTRL_LIBS) \
 	$(LIBOSMONETIF_LIBS) \
+	$(LIBOSMOABIS_LIBS) \
+	$(LIBOSMOTRAU_LIBS) \
 	$(NULL)
diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c
index 6ca1800..036e0c0 100644
--- a/src/osmo-mgw/mgw_main.c
+++ b/src/osmo-mgw/mgw_main.c
@@ -30,6 +30,8 @@
 #include <limits.h>
 #include <unistd.h>
 #include <errno.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/abis/e1_input.h>
 
 #include <sys/socket.h>
 
@@ -58,6 +60,7 @@
 #include <osmocom/vty/command.h>
 #include <osmocom/vty/stats.h>
 #include <osmocom/vty/misc.h>
+#include <osmocom/abis/abis.h>
 
 #include "../../bscconfig.h"
 
@@ -268,7 +271,13 @@
 		  .description = "RTP stream handling",
 		  .color = "\033[1;30m",
 		  .enabled = 1,.loglevel = LOGL_NOTICE,
-		  },
+	},
+	[DE1] = {
+		  .name = "DE1",
+		  .description = "E1 line handling",
+		  .color = "\033[1;31m",
+		  .enabled = 1,.loglevel = LOGL_NOTICE,
+	},
 };
 
 const struct log_info log_info = {
@@ -288,6 +297,7 @@
 
 	osmo_init_ignore_signals();
 	osmo_init_logging2(tall_bsc_ctx, &log_info);
+	libosmo_abis_init(tall_bsc_ctx);
 
 	cfg = mgcp_config_alloc();
 	if (!cfg)
@@ -300,6 +310,7 @@
 	osmo_stats_vty_add_cmds();
 	mgcp_vty_init();
 	ctrl_vty_init(cfg);
+	e1inp_vty_init();
 
 	handle_options(argc, argv);
 
diff --git a/tests/mgcp/Makefile.am b/tests/mgcp/Makefile.am
index 95444b5..1224c0a 100644
--- a/tests/mgcp/Makefile.am
+++ b/tests/mgcp/Makefile.am
@@ -11,6 +11,8 @@
 	$(LIBOSMOVTY_CFLAGS) \
 	$(LIBOSMOGSM_CFLAGS) \
 	$(LIBOSMONETIF_CFLAGS) \
+	$(LIBOSMOABIS_CFLAGS) \
+	$(LIBOSMOTRAU_CFLAGS) \
 	$(COVERAGE_CFLAGS) \
 	$(NULL)
 
@@ -35,6 +37,8 @@
 	$(LIBOSMOCORE_LIBS) \
 	$(LIBOSMOVTY_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
+	$(LIBOSMOABIS_LIBS) \
+	$(LIBOSMOTRAU_LIBS) \
 	$(LIBRARY_DL) \
 	$(LIBRARY_DLSYM) \
 	$(LIBOSMONETIF_LIBS) \
diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c
index 458f6c9..a050a0b 100644
--- a/tests/mgcp/mgcp_test.c
+++ b/tests/mgcp/mgcp_test.c
@@ -771,8 +771,8 @@
 	cfg = mgcp_config_alloc();
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 
-	trunk->vty_number_endpoints = 64;
-        mgcp_trunk_alloc_endpts(trunk);
+	trunk->v.vty_number_endpoints = 64;
+        mgcp_trunk_equip(trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
@@ -908,8 +908,8 @@
 	cfg = mgcp_config_alloc();
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 
-	trunk->vty_number_endpoints = 64;
-        mgcp_trunk_alloc_endpts(trunk);
+	trunk->v.vty_number_endpoints = 64;
+        mgcp_trunk_equip(trunk);
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
 
@@ -973,8 +973,8 @@
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 	cfg->rqnt_cb = rqnt_cb;
 
-	trunk->vty_number_endpoints = 64;
-        mgcp_trunk_alloc_endpts(trunk);
+	trunk->v.vty_number_endpoints = 64;
+        mgcp_trunk_equip(trunk);
 
 	inp = create_msg(CRCX, NULL);
 	msg = mgcp_handle_message(cfg, inp);
@@ -1050,7 +1050,7 @@
 
 	endp.cfg = &cfg;
 	endp.type = &ep_typeset.rtp;
-	trunk.vty_number_endpoints = 1;
+	trunk.v.vty_number_endpoints = 1;
 	trunk.endpoints = endpoints;
 	trunk.endpoints[0] = &endp;
 	endp.trunk = &trunk;
@@ -1301,7 +1301,7 @@
 	endp.cfg = &cfg;
 	endp.type = &ep_typeset.rtp;
 
-	trunk.vty_number_endpoints = 1;
+	trunk.v.vty_number_endpoints = 1;
 	trunk.endpoints = endpoints;
 	trunk.endpoints[0] = &endp;
 	trunk.force_constant_ssrc = patch_ssrc;
@@ -1382,8 +1382,8 @@
 
 	cfg = mgcp_config_alloc();
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
-	trunk->vty_number_endpoints = 64;
-        mgcp_trunk_alloc_endpts(trunk);
+	trunk->v.vty_number_endpoints = 64;
+        mgcp_trunk_equip(trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	/* Allocate endpoint 1 at mgw with two codecs */
@@ -1531,8 +1531,8 @@
 
 	cfg = mgcp_config_alloc();
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
-	trunk->vty_number_endpoints = 64;
-        mgcp_trunk_alloc_endpts(trunk);
+	trunk->v.vty_number_endpoints = 64;
+        mgcp_trunk_equip(trunk);
 
 	endp = mgcp_endp_by_name(NULL, "rtpbridge/1 at mgw", cfg);
 	OSMO_ASSERT(endp);
@@ -1581,9 +1581,9 @@
 	cfg = mgcp_config_alloc();
 	trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 
-	trunk->vty_number_endpoints = 64;
+	trunk->v.vty_number_endpoints = 64;
 	trunk->audio_send_name = 0;
-        mgcp_trunk_alloc_endpts(trunk);
+        mgcp_trunk_equip(trunk);
 
 	cfg->policy_cb = mgcp_test_policy_cb;
 

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/19524
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: I6b93809b5ac7d01af55888347dd787b0bc997ae1
Gerrit-Change-Number: 19524
Gerrit-PatchSet: 3
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200812/d64c5ecc/attachment.htm>


More information about the gerrit-log mailing list