src: port openBSC over libosmo-abis

This is a big patch that ports openBSC over libosmo-abis.
Sorry, the changes that are included here are all dependent
of libosmo-abis, splitting them into smaller pieces would
leave the repository in some intermediate state, which is
not desired.

The main changes are:

- The directory libabis/ has been removed as it now lives in
  libosmo-abis.

- new configuration file format for nanoBTS and HSL femto, we
  need to define the virtual e1_line and attach it to the OML
  link.

- all the existing BTS drivers (nanoBTS, hsl femto, Nokia site,
  BS11 and rbs2000) now use the new libosmo-abis framework.

- use r232 input driver available in libosmo-abis for bs11_config.

- use ipa_msg_recv instead of old ipaccess_read_msg function.

- delete definition of gsm_e1_subslot and input_signal_data.
  These structures now lives in libosmo-abis.

Most of this patch are deletions of libabis/ which has been
moved to libosmo-abis.

This patch also modifies openBSC to use all the new definitions
available in libosmocore and libosmo-abis. In order to do that,
we have replaced the following:

- DINP, DMI, DMIB and DMUX by their respective DL* correspondences.
- SS_GLOBAL by SS_L_GLOBAL
- SS_INPUT by SS_L_INPUT
- S_GLOBAL_SHUTDOWN by S_L_GLOBAL_SHUTDOWN
- SS_INPUT by SS_L_INPUT
- S_INP_* by S_L_INP_* sub-signals
- E1INP_NODE by L_E1INP_NODE vty node

This patch has been tested with:
- one nanoBTS
- the HSL femto with the examples available under libosmo-abis
- BS11 with both dahdi and misdn drivers.
diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c
index a41414a..027a263 100644
--- a/openbsc/src/libbsc/abis_nm.c
+++ b/openbsc/src/libbsc/abis_nm.c
@@ -43,7 +43,7 @@
 #include <openbsc/abis_nm.h>
 #include <openbsc/misdn.h>
 #include <openbsc/signal.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 
 #define OM_ALLOC_SIZE		1024
 #define OM_HEADROOM_SIZE	128
@@ -114,6 +114,26 @@
 				   "OML");
 }
 
+int _abis_nm_sendmsg(struct msgb *msg, int to_trx_oml)
+{
+	struct e1inp_sign_link *sign_link = msg->dst;
+
+	msg->l2h = msg->data;
+
+	if (!msg->dst) {
+		LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Check for TRX-specific OML link first */
+	if (to_trx_oml) {
+		if (!sign_link->trx->oml_link)
+			return -ENODEV;
+		msg->dst = sign_link->trx->oml_link;
+	}
+	return abis_sendmsg(msg);
+}
+
 /* Send a OML NM Message from BSC to BTS */
 static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg)
 {
@@ -480,7 +500,7 @@
 	return 0;
 }
 
-static void abis_nm_queue_send_next(struct gsm_bts *bts)
+void abis_nm_queue_send_next(struct gsm_bts *bts)
 {
 	int wait = 0;
 	struct msgb *msg;
diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c
index a4a3f23..2216462 100644
--- a/openbsc/src/libbsc/abis_om2000.c
+++ b/openbsc/src/libbsc/abis_om2000.c
@@ -40,7 +40,7 @@
 #include <openbsc/abis_nm.h>
 #include <openbsc/abis_om2000.h>
 #include <openbsc/signal.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 
 #define OM_ALLOC_SIZE		1024
 #define OM_HEADROOM_SIZE	128
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index 8645268..b4babc3 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -38,7 +38,7 @@
 #include <openbsc/signal.h>
 #include <openbsc/meas_rep.h>
 #include <openbsc/rtp_proxy.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 #include <osmocom/gsm/rsl.h>
 #include <osmocom/core/talloc.h>
 
diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c
index 1d885db..84dfa6e 100644
--- a/openbsc/src/libbsc/bsc_init.c
+++ b/openbsc/src/libbsc/bsc_init.c
@@ -82,7 +82,7 @@
 
 	llist_for_each_entry(bts, &net->bts_list, list) {
 		LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
-		osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts);
+		osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts);
 	}
 
 	return 0;
@@ -274,11 +274,11 @@
 	struct gsm_bts_trx *trx = isd->trx;
 	int ts_no, lchan_no;
 
-	if (subsys != SS_INPUT)
+	if (subsys != SS_L_INPUT)
 		return -EINVAL;
 
 	switch (signal) {
-	case S_INP_TEI_UP:
+	case S_L_INP_TEI_UP:
 		if (isd->link_type == E1INP_SIGN_OML) {
 			/* TODO: this is required for the Nokia BTS, hopping is configured
 			   during OML, other MA is not set.  */
@@ -300,8 +300,8 @@
 		if (isd->link_type == E1INP_SIGN_RSL)
 			bootstrap_rsl(trx);
 		break;
-	case S_INP_TEI_DN:
-		LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx);
+	case S_L_INP_TEI_DN:
+		LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx);
 
 		if (isd->link_type == E1INP_SIGN_OML)
 			osmo_counter_inc(trx->bts->network->stats.bts.oml_fail);
@@ -469,7 +469,7 @@
 		return rc;
 
 	osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
-	osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
 
 	llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
 		rc = bootstrap_bts(bts);
@@ -477,14 +477,7 @@
 			LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n");
 			return rc;
 		}
-		switch (bts->type) {
-		case GSM_BTS_TYPE_NANOBTS:
-		case GSM_BTS_TYPE_HSL_FEMTO:
-			break;
-		default:
-			rc = e1_reconfig_bts(bts);
-			break;
-		}
+		rc = e1_reconfig_bts(bts);
 		if (rc < 0) {
 			LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n");
 			return rc;
diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c
index e9ffce3..66288a3 100644
--- a/openbsc/src/libbsc/bsc_msc.c
+++ b/openbsc/src/libbsc/bsc_msc.c
@@ -21,11 +21,13 @@
 
 #include <openbsc/bsc_msc.h>
 #include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
+#include <osmocom/abis/ipaccess.h>
 
 #include <osmocom/core/write_queue.h>
 #include <osmocom/core/talloc.h>
 
+#include <osmocom/gsm/tlv.h>
+
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <errno.h>
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 8a5c7fd..a98779d 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -30,7 +30,7 @@
 
 #include <osmocom/core/linuxlist.h>
 #include <openbsc/gsm_data.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 #include <openbsc/abis_nm.h>
 #include <openbsc/abis_om2000.h>
 #include <osmocom/core/utils.h>
@@ -506,10 +506,13 @@
 	case GSM_BTS_TYPE_NANOBTS:
 		vty_out(vty, "  ip.access unit_id %u %u%s",
 			bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
-		vty_out(vty, "  oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE);
+		vty_out(vty, "  oml ip.access stream_id %u line %u%s",
+			bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE);
 		break;
 	case GSM_BTS_TYPE_HSL_FEMTO:
 		vty_out(vty, "  hsl serial-number %lu%s", bts->hsl.serno, VTY_NEWLINE);
+		vty_out(vty, "  oml hsl line %u%s",
+			bts->oml_e1_link.e1_nr, VTY_NEWLINE);
 		break;
 	default:
 		config_write_e1_link(vty, &bts->oml_e1_link, "  oml ");
@@ -1507,12 +1510,12 @@
 
 DEFUN(cfg_bts_stream_id,
       cfg_bts_stream_id_cmd,
-      "oml ip.access stream_id <0-255>",
+      "oml ip.access stream_id <0-255> line E1_LINE",
 	OML_STR IPA_STR
       "Set the ip.access Stream ID of the OML link of this BTS\n")
 {
 	struct gsm_bts *bts = vty->index;
-	int stream_id = atoi(argv[0]);
+	int stream_id = atoi(argv[0]), linenr = atoi(argv[1]);
 
 	if (!is_ipaccess_bts(bts)) {
 		vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
@@ -1520,6 +1523,27 @@
 	}
 
 	bts->oml_tei = stream_id;
+	/* This is used by e1inp_bind_ops callback for each BTS model. */
+	bts->oml_e1_link.e1_nr = linenr;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_hsl_oml,
+      cfg_bts_hsl_oml_cmd,
+      "oml hsl line E1_LINE",
+      OML_STR "HSL femto Specific Options"
+      "Set OML link of this HSL femto BTS\n")
+{
+	struct gsm_bts *bts = vty->index;
+	int linenr = atoi(argv[0]);
+
+	if (!(bts->type == GSM_BTS_TYPE_HSL_FEMTO)) {
+		vty_out(vty, "%% BTS is not of HSL type%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->oml_e1_link.e1_nr = linenr;
 
 	return CMD_SUCCESS;
 }
@@ -2624,6 +2648,7 @@
 	install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
 	install_element(BTS_NODE, &cfg_bts_serno_cmd);
 	install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
+	install_element(BTS_NODE, &cfg_bts_hsl_oml_cmd);
 	install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
 	install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
 	install_element(BTS_NODE, &cfg_bts_challoc_cmd);
diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c
index 9c98a9f..28e7df4 100644
--- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c
+++ b/openbsc/src/libbsc/bts_ericsson_rbs2000.c
@@ -26,10 +26,10 @@
 #include <openbsc/gsm_data.h>
 #include <openbsc/abis_om2000.h>
 #include <openbsc/abis_nm.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 #include <openbsc/signal.h>
 
-#include "../libabis/input/lapd.h"
+#include <osmocom/abis/lapd.h>
 
 static void bootstrap_om_bts(struct gsm_bts *bts)
 {
@@ -83,7 +83,7 @@
 {
 	struct gsm_bts *bts;
 
-	if (subsys != SS_GLOBAL)
+	if (subsys != SS_L_GLOBAL)
 		return 0;
 
 	switch (signal) {
@@ -103,11 +103,11 @@
 {
 	struct input_signal_data *isd = signal_data;
 
-	if (subsys != SS_INPUT)
+	if (subsys != SS_L_INPUT)
 		return 0;
 
 	switch (signal) {
-	case S_INP_TEI_UP:
+	case S_L_INP_TEI_UP:
 		switch (isd->link_type) {
 		case E1INP_SIGN_OML:
 			if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000)
@@ -119,18 +119,18 @@
 			break;
 		}
 		break;
-	case S_INP_LINE_INIT:
+	case S_L_INP_LINE_INIT:
 		/* Right now Ericsson RBS are only supported on DAHDI */
 		if (strcasecmp(isd->line->driver->name, "DAHDI"))
 			break;
 		start_sabm_in_line(isd->line, 1);
 		break;
-	case S_INP_LINE_ALARM:
+	case S_L_INP_LINE_ALARM:
 		if (strcasecmp(isd->line->driver->name, "DAHDI"))
 			break;
 		start_sabm_in_line(isd->line, 0);
 		break;
-	case S_INP_LINE_NOALARM:
+	case S_L_INP_LINE_NOALARM:
 		if (strcasecmp(isd->line->driver->name, "DAHDI"))
 			break;
 		start_sabm_in_line(isd->line, 1);
@@ -244,12 +244,18 @@
 
 static int bts_model_rbs2k_start(struct gsm_network *net);
 
+static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line)
+{
+	e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
+}
+
 static struct gsm_bts_model model_rbs2k = {
 	.type = GSM_BTS_TYPE_RBS2000,
 	.name = "rbs2000",
 	.start = bts_model_rbs2k_start,
 	.oml_rcvmsg = &abis_om2k_rcvmsg,
 	.config_write_bts = &config_write_bts,
+	.e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops,
 };
 
 static int bts_model_rbs2k_start(struct gsm_network *net)
@@ -260,8 +266,8 @@
 	gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING);
 	gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD);
 
-	osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
-	osmo_signal_register_handler(SS_GLOBAL, gbl_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
 	osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
 
 	return 0;
diff --git a/openbsc/src/libbsc/bts_hsl_femtocell.c b/openbsc/src/libbsc/bts_hsl_femtocell.c
index 8463202..ebab34a 100644
--- a/openbsc/src/libbsc/bts_hsl_femtocell.c
+++ b/openbsc/src/libbsc/bts_hsl_femtocell.c
@@ -28,13 +28,18 @@
 #include <openbsc/abis_nm.h>
 #include <openbsc/abis_rsl.h>
 #include <openbsc/signal.h>
-#include <openbsc/e1_input.h>
+#include <openbsc/debug.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/ipaccess.h>
 
 static int bts_model_hslfemto_start(struct gsm_network *net);
+static void bts_model_hslfemto_e1line_bind_ops(struct e1inp_line *line);
 
 static struct gsm_bts_model model_hslfemto = {
 	.type = GSM_BTS_TYPE_HSL_FEMTO,
 	.start = bts_model_hslfemto_start,
+	.e1line_bind_ops = &bts_model_hslfemto_e1line_bind_ops,
 	.nm_att_tlvdef = {
 		.def = {
 			/* no HSL specific OML attributes that we know of */
@@ -121,7 +126,7 @@
 	cur = msgb_put(msg, sizeof(oml_arfcn_bsic));
 	memcpy(msg->data, oml_arfcn_bsic, sizeof(oml_arfcn_bsic));
 	msg->dst = bts->c0->rsl_link;
-	_abis_nm_sendmsg(msg, 0);
+	abis_sendmsg(msg);
 
 	/* Delay the OPSTART until after SI have been set via RSL */
 	//abis_nm_opstart(bts, NM_OC_BTS, 255, 255, 255);
@@ -135,11 +140,11 @@
 {
 	struct input_signal_data *isd = signal_data;
 
-	if (subsys != SS_INPUT)
+	if (subsys != SS_L_INPUT)
 		return 0;
 
 	switch (signal) {
-	case S_INP_TEI_UP:
+	case S_L_INP_TEI_UP:
 		switch (isd->link_type) {
 		case E1INP_SIGN_OML:
 			if (isd->trx->bts->type == GSM_BTS_TYPE_HSL_FEMTO)
@@ -151,6 +156,8 @@
 	return 0;
 }
 
+static struct gsm_network *hsl_gsmnet;
+
 static int bts_model_hslfemto_start(struct gsm_network *net)
 {
 	model_hslfemto.features.data = &model_hslfemto._features_data[0];
@@ -159,13 +166,147 @@
 	gsm_btsmodel_set_feature(&model_hslfemto, BTS_FEAT_GPRS);
 	gsm_btsmodel_set_feature(&model_hslfemto, BTS_FEAT_EGPRS);
 
-	osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
 
-	/* Call A-bis input driver, start socket for OML and RSL. */
-	return hsl_setup(net);
+	hsl_gsmnet = net;
+	return 0;
 }
 
 int bts_model_hslfemto_init(void)
 {
 	return gsm_bts_model_register(&model_hslfemto);
 }
+
+#define OML_UP		0x0001
+#define RSL_UP		0x0002
+
+struct gsm_bts *find_bts_by_serno(struct gsm_network *net, uint64_t serno)
+{
+	struct gsm_bts *bts;
+
+	llist_for_each_entry(bts, &net->bts_list, list) {
+		if (bts->type != GSM_BTS_TYPE_HSL_FEMTO)
+			continue;
+
+		if (serno == bts->hsl.serno)
+			return bts;
+	}
+	return NULL;
+}
+
+/* This function is called once the OML/RSL link becomes up. */
+static struct e1inp_sign_link *
+hsl_sign_link_up(void *unit_data, struct e1inp_line *line,
+		 enum e1inp_sign_type type)
+{
+	struct hsl_unit *dev = unit_data;
+	struct gsm_bts *bts;
+
+	bts = find_bts_by_serno(hsl_gsmnet, dev->serno);
+	if (!bts) {
+		LOGP(DLINP, LOGL_ERROR, "Unable to find BTS config for "
+				"serial number %lx\n", dev->serno);
+		return NULL;
+	}
+	DEBUGP(DLINP, "Identified HSL BTS Serial Number %lx\n", dev->serno);
+
+	/* we shouldn't hardcode it, but HSL femto also hardcodes it... */
+	bts->oml_tei = 255;
+	bts->c0->rsl_tei = 0;
+	bts->oml_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML-1],
+					       E1INP_SIGN_OML, bts->c0,
+					       bts->oml_tei, 0);
+	bts->c0->rsl_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML-1],
+						   E1INP_SIGN_RSL, bts->c0,
+						   bts->c0->rsl_tei, 0);
+	e1inp_event(&line->ts[E1INP_SIGN_OML-1], S_L_INP_TEI_UP, 255, 0);
+	e1inp_event(&line->ts[E1INP_SIGN_OML-1], S_L_INP_TEI_UP, 0, 0);
+	bts->ip_access.flags |= OML_UP;
+	bts->ip_access.flags |= (RSL_UP << 0);
+
+	return bts->oml_link;
+}
+
+void hsl_drop_oml(struct gsm_bts *bts)
+{
+	struct e1inp_line *line;
+
+	if (!bts->oml_link)
+		return;
+
+	line = bts->oml_link->ts->line;
+	e1inp_sign_link_destroy(bts->oml_link);
+	bts->oml_link = NULL;
+
+	e1inp_sign_link_destroy(bts->c0->rsl_link);
+	bts->c0->rsl_link = NULL;
+
+	bts->ip_access.flags = 0;
+}
+
+static void hsl_sign_link_down(struct e1inp_line *line)
+{
+	/* No matter what link went down, we close both signal links. */
+	struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1];
+	struct e1inp_sign_link *link;
+
+	llist_for_each_entry(link, &ts->sign.sign_links, list) {
+		struct gsm_bts *bts = link->trx->bts;
+
+		hsl_drop_oml(bts);
+		/* Yes, we only use the first element of the list. */
+		break;
+       }
+}
+
+/* This function is called if we receive one OML/RSL message. */
+static int hsl_sign_link(struct msgb *msg)
+{
+	int ret = 0;
+	struct e1inp_sign_link *link = msg->dst;
+	struct e1inp_ts *e1i_ts = link->ts;
+
+	switch (link->type) {
+	case E1INP_SIGN_OML:
+		if (!(link->trx->bts->ip_access.flags & OML_UP)) {
+			e1inp_event(e1i_ts, S_L_INP_TEI_UP,
+					link->tei, link->sapi);
+			link->trx->bts->ip_access.flags |= OML_UP;
+		}
+		ret = abis_nm_rcvmsg(msg);
+		break;
+	case E1INP_SIGN_RSL:
+		if (!(link->trx->bts->ip_access.flags &
+			(RSL_UP << link->trx->nr))) {
+			e1inp_event(e1i_ts, S_L_INP_TEI_UP,
+					link->tei, link->sapi);
+			link->trx->bts->ip_access.flags |=
+					(RSL_UP << link->trx->nr);
+		}
+		ret = abis_rsl_rcvmsg(msg);
+		break;
+	default:
+		LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n",
+			link->type);
+		msgb_free(msg);
+	break;
+	}
+	return ret;
+}
+
+static struct e1inp_line_ops hsl_e1inp_line_ops = {
+	.cfg = {
+		.ipa = {
+			.addr	= "0.0.0.0",
+			.role	= E1INP_LINE_R_BSC,
+		},
+	},
+       .sign_link_up	= hsl_sign_link_up,
+       .sign_link_down	= hsl_sign_link_down,
+       .sign_link	= hsl_sign_link,
+};
+
+static void bts_model_hslfemto_e1line_bind_ops(struct e1inp_line *line)
+{
+	e1inp_line_bind_ops(line, &hsl_e1inp_line_ops);
+}
diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c
index c86b1e1..42ac6b4 100644
--- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c
+++ b/openbsc/src/libbsc/bts_ipaccess_nanobts.c
@@ -26,15 +26,27 @@
 #include <openbsc/gsm_data.h>
 #include <openbsc/signal.h>
 #include <openbsc/abis_nm.h>
-#include <openbsc/e1_input.h> /* for ipaccess_setup() */
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/debug.h>
+#include <osmocom/abis/subchan_demux.h>
+#include <osmocom/abis/ipaccess.h>
+#include <osmocom/core/logging.h>
 
 static int bts_model_nanobts_start(struct gsm_network *net);
+static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line);
 
 static struct gsm_bts_model model_nanobts = {
 	.type = GSM_BTS_TYPE_NANOBTS,
 	.name = "nanobts",
 	.start = bts_model_nanobts_start,
 	.oml_rcvmsg = &abis_nm_rcvmsg,
+	.e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, 
 	.nm_att_tlvdef = {
 		.def = {
 			/* ip.access specifics */
@@ -440,6 +452,8 @@
 	return 0;
 }
 
+static struct gsm_network *ipaccess_gsmnet;
+
 static int bts_model_nanobts_start(struct gsm_network *net)
 {
 	model_nanobts.features.data = &model_nanobts._features_data[0];
@@ -450,11 +464,185 @@
 
 	osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
 
-	/* Call A-bis input driver, start server sockets for OML and RSL. */
-	return ipaccess_setup(net);
+	ipaccess_gsmnet = net;
+	return 0;
 }
 
 int bts_model_nanobts_init(void)
 {
 	return gsm_bts_model_register(&model_nanobts);
 }
+
+#define OML_UP         0x0001
+#define RSL_UP         0x0002
+
+static struct gsm_bts *
+find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id)
+{
+	struct gsm_bts *bts;
+
+	llist_for_each_entry(bts, &net->bts_list, list) {
+		if (!is_ipaccess_bts(bts))
+			continue;
+
+		if (bts->ip_access.site_id == site_id &&
+		    bts->ip_access.bts_id == bts_id)
+			return bts;
+	}
+	return NULL;
+}
+
+/* These are exported because they are used by the VTY interface. */
+void ipaccess_drop_rsl(struct gsm_bts_trx *trx)
+{
+	if (!trx->rsl_link)
+		return;
+
+	e1inp_sign_link_destroy(trx->rsl_link);
+	trx->rsl_link = NULL;
+}
+
+void ipaccess_drop_oml(struct gsm_bts *bts)
+{
+	struct gsm_bts_trx *trx;
+	struct e1inp_line *line;
+
+	if (!bts->oml_link)
+		return;
+
+	line = bts->oml_link->ts->line;
+
+	e1inp_sign_link_destroy(bts->oml_link);
+	bts->oml_link = NULL;
+
+	/* we have issues reconnecting RSL, drop everything. */
+	llist_for_each_entry(trx, &bts->trx_list, list)
+		ipaccess_drop_rsl(trx);
+
+	bts->ip_access.flags = 0;
+}
+
+/* This function is called once the OML/RSL link becomes up. */
+static struct e1inp_sign_link *
+ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
+		      enum e1inp_sign_type type)
+{
+	struct gsm_bts *bts;
+	struct ipaccess_unit *dev = unit_data;
+	struct e1inp_sign_link *sign_link = NULL;
+
+	bts = find_bts_by_unitid(ipaccess_gsmnet, dev->site_id, dev->bts_id);
+	if (!bts) {
+		LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for "
+			" %u/%u/%u, disconnecting\n", dev->site_id,
+			dev->bts_id, dev->trx_id);
+		return NULL;
+	}
+	DEBUGP(DLINP, "Identified BTS %u/%u/%u\n",
+			dev->site_id, dev->bts_id, dev->trx_id);
+
+	switch(type) {
+	case E1INP_SIGN_OML:
+		/* remove old OML signal link for this BTS. */
+		ipaccess_drop_oml(bts);
+
+		/* create new OML link. */
+		sign_link = bts->oml_link =
+			e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
+						E1INP_SIGN_OML, bts->c0,
+						bts->oml_tei, 0);
+		break;
+	case E1INP_SIGN_RSL: {
+		struct e1inp_ts *ts;
+		struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id);
+
+		/* no OML link set yet? give up. */
+		if (!bts->oml_link)
+			return NULL;
+
+		/* remove old RSL link for this TRX. */
+		ipaccess_drop_rsl(trx);
+
+		/* set new RSL link for this TRX. */
+		line = bts->oml_link->ts->line;
+		ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1];
+		e1inp_ts_config_sign(ts, line);
+		sign_link = trx->rsl_link =
+				e1inp_sign_link_create(ts, E1INP_SIGN_RSL,
+						       trx, trx->rsl_tei, 0);
+		trx->rsl_link->ts->sign.delay = 0;
+		break;
+	}
+	default:
+		break;
+	}
+	return sign_link;
+}
+
+static void ipaccess_sign_link_down(struct e1inp_line *line)
+{
+	/* No matter what link went down, we close both signal links. */
+	struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1];
+	struct e1inp_sign_link *link;
+
+	llist_for_each_entry(link, &ts->sign.sign_links, list) {
+		struct gsm_bts *bts = link->trx->bts;
+
+		ipaccess_drop_oml(bts);
+		/* Yes, we only use the first element of the list. */
+		break;
+	}
+}
+
+/* This function is called if we receive one OML/RSL message. */
+static int ipaccess_sign_link(struct msgb *msg)
+{
+	int ret = 0;
+	struct e1inp_sign_link *link = msg->dst;
+	struct e1inp_ts *e1i_ts = link->ts;
+
+	switch (link->type) {
+	case E1INP_SIGN_RSL:
+		if (!(link->trx->bts->ip_access.flags &
+					(RSL_UP << link->trx->nr))) {
+			e1inp_event(e1i_ts, S_L_INP_TEI_UP,
+					link->tei, link->sapi);
+			link->trx->bts->ip_access.flags |=
+					(RSL_UP << link->trx->nr);
+		}
+	        ret = abis_rsl_rcvmsg(msg);
+	        break;
+	case E1INP_SIGN_OML:
+		if (!(link->trx->bts->ip_access.flags & OML_UP)) {
+			e1inp_event(e1i_ts, S_L_INP_TEI_UP,
+					link->tei, link->sapi);
+			link->trx->bts->ip_access.flags |= OML_UP;
+		}
+	        ret = abis_nm_rcvmsg(msg);
+	        break;
+	default:
+		LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n",
+			link->type);
+		msgb_free(msg);
+		break;
+	}
+	return ret;
+}
+
+/* not static, ipaccess-config needs it. */
+struct e1inp_line_ops ipaccess_e1inp_line_ops = {
+	.cfg = {
+		.ipa = {
+			.addr = "0.0.0.0",
+			.role = E1INP_LINE_R_BSC,
+		},
+	},
+	.sign_link_up	= ipaccess_sign_link_up,
+	.sign_link_down	= ipaccess_sign_link_down,
+	.sign_link	= ipaccess_sign_link,
+};
+
+static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line)
+{
+        e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops);
+}
diff --git a/openbsc/src/libbsc/bts_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c
index 8ba01d0..fa8f43e 100644
--- a/openbsc/src/libbsc/bts_nokia_site.c
+++ b/openbsc/src/libbsc/bts_nokia_site.c
@@ -33,12 +33,12 @@
 #include <openbsc/debug.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/abis_nm.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 #include <openbsc/signal.h>
 
 #include <osmocom/core/timer.h>
 
-#include "../libabis/input/lapd.h"
+#include <osmocom/abis/lapd.h>
 
 /* TODO: put in a separate file ? */
 
@@ -48,7 +48,7 @@
 /* was static in system_information.c */
 extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts);
 
-static void abis_nm_queue_send_next(struct gsm_bts *bts);
+static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts);
 static void reset_timer_cb(void *_bts);
 static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref);
 static int dump_elements(uint8_t * data, int len);
@@ -120,7 +120,7 @@
 {
 	struct gsm_bts *bts;
 
-	if (subsys != SS_GLOBAL)
+	if (subsys != SS_L_GLOBAL)
 		return 0;
 
 	switch (signal) {
@@ -140,16 +140,16 @@
 {
 	struct input_signal_data *isd = signal_data;
 
-	if (subsys != SS_INPUT)
+	if (subsys != SS_L_INPUT)
 		return 0;
 
 	switch (signal) {
-	case S_INP_LINE_INIT:
+	case S_L_INP_LINE_INIT:
 		start_sabm_in_line(isd->line, 1, SAPI_OML);	/* start only OML */
 		break;
-	case S_INP_TEI_DN:
+	case S_L_INP_TEI_DN:
 		break;
-	case S_INP_TEI_UP:
+	case S_L_INP_TEI_UP:
 		switch (isd->link_type) {
 		case E1INP_SIGN_OML:
 			if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE)
@@ -162,7 +162,7 @@
 			break;
 		}
 		break;
-	case S_INP_TEI_UNKNOWN:
+	case S_L_INP_TEI_UNKNOWN:
 		/* We are receiving LAPD frames with one TEI that we do not
 		 * seem to know, likely that we (the BSC) stopped working
 		 * and lost our local states. However, the BTS is already
@@ -1235,7 +1235,7 @@
 		if (ret < 0)
 			return ret;
 
-		abis_nm_queue_send_next(bts);
+		nokia_abis_nm_queue_send_next(bts);
 
 		/* next segment */
 		len_remain -= len_to_send;
@@ -1415,7 +1415,7 @@
 
 /* taken from abis_nm.c */
 
-static void abis_nm_queue_send_next(struct gsm_bts *bts)
+static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts)
 {
 	int wait = 0;
 	struct msgb *msg;
@@ -1423,7 +1423,7 @@
 	while (!llist_empty(&bts->abis_queue)) {
 		msg = msgb_dequeue(&bts->abis_queue);
 		wait = OBSC_NM_W_ACK_CB(msg);
-		_abis_nm_sendmsg(msg, 0);
+		abis_sendmsg(msg);
 
 		if (wait)
 			break;
@@ -1445,9 +1445,9 @@
 	bts->nokia.wait_reset = 0;
 
 	/* OML link */
-	line = e1inp_line_get(e1_link->e1_nr);
+	line = e1inp_line_find(e1_link->e1_nr);
 	if (!line) {
-		LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to "
+		LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to "
 		     "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
 		return;
 	}
@@ -1522,7 +1522,7 @@
 	case NOKIA_MSG_CONF_REQ:
 		/* send ACK */
 		abis_nm_ack(bts, ref);
-		abis_nm_queue_send_next(bts);
+		nokia_abis_nm_queue_send_next(bts);
 		/* send CONF_DATA */
 		abis_nm_send_config(bts, bts->nokia.bts_type);
 		bts->nokia.configured = 1;
@@ -1564,9 +1564,9 @@
 			struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
 			struct e1inp_line *line;
 			/* OML link */
-			line = e1inp_line_get(e1_link->e1_nr);
+			line = e1inp_line_find(e1_link->e1_nr);
 			if (!line) {
-				LOGP(DINP, LOGL_ERROR,
+				LOGP(DLINP, LOGL_ERROR,
 				     "BTS %u OML link referring to "
 				     "non-existing E1 line %u\n", bts->nr,
 				     e1_link->e1_nr);
@@ -1587,9 +1587,9 @@
 			bts->nokia.configured = 0;
 
 			/* RSL Link */
-			line = e1inp_line_get(e1_link->e1_nr);
+			line = e1inp_line_find(e1_link->e1_nr);
 			if (!line) {
-				LOGP(DINP, LOGL_ERROR,
+				LOGP(DLINP, LOGL_ERROR,
 				     "TRX (%u/%u) RSL link referring "
 				     "to non-existing E1 line %u\n",
 				     sign_link->trx->bts->nr, sign_link->trx->nr,
@@ -1643,7 +1643,7 @@
 		break;
 	}
 
-	abis_nm_queue_send_next(bts);
+	nokia_abis_nm_queue_send_next(bts);
 
 	return ret;
 }
@@ -1696,11 +1696,17 @@
 
 static int bts_model_nokia_site_start(struct gsm_network *net);
 
+static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line)
+{
+	e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
+}
+
 static struct gsm_bts_model model_nokia_site = {
 	.type = GSM_BTS_TYPE_NOKIA_SITE,
 	.name = "nokia_site",
 	.start = bts_model_nokia_site_start,
-	.oml_rcvmsg = &abis_nokia_rcvmsg
+	.oml_rcvmsg = &abis_nokia_rcvmsg,
+	.e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops,
 };
 
 static struct gsm_network *my_net;
@@ -1714,8 +1720,8 @@
 	gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING);
 	gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD);
 
-	osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
-	osmo_signal_register_handler(SS_GLOBAL, gbl_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
 	osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
 
 	my_net = net;
diff --git a/openbsc/src/libbsc/bts_siemens_bs11.c b/openbsc/src/libbsc/bts_siemens_bs11.c
index df4a1dc..2514d99 100644
--- a/openbsc/src/libbsc/bts_siemens_bs11.c
+++ b/openbsc/src/libbsc/bts_siemens_bs11.c
@@ -25,16 +25,22 @@
 #include <openbsc/debug.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/abis_nm.h>
-#include <openbsc/e1_input.h>
+#include <osmocom/abis/e1_input.h>
 #include <openbsc/signal.h>
 
 static int bts_model_bs11_start(struct gsm_network *net);
 
+static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line)
+{
+	e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
+}
+
 static struct gsm_bts_model model_bs11 = {
 	.type = GSM_BTS_TYPE_BS11,
 	.name = "bs11",
 	.start = bts_model_bs11_start,
 	.oml_rcvmsg = &abis_nm_rcvmsg,
+	.e1line_bind_ops = bts_model_bs11_e1line_bind_ops,
 	.nm_att_tlvdef = {
 		.def = {
 			[NM_ATT_AVAIL_STATUS] =		{ TLV_TYPE_TLV },
@@ -542,7 +548,7 @@
 {
 	struct gsm_bts *bts;
 
-	if (subsys != SS_GLOBAL)
+	if (subsys != SS_L_GLOBAL)
 		return 0;
 
 	switch (signal) {
@@ -562,11 +568,11 @@
 {
 	struct input_signal_data *isd = signal_data;
 
-	if (subsys != SS_INPUT)
+	if (subsys != SS_L_INPUT)
 		return 0;
 
 	switch (signal) {
-	case S_INP_TEI_UP:
+	case S_L_INP_TEI_UP:
 		switch (isd->link_type) {
 		case E1INP_SIGN_OML:
 			if (isd->trx->bts->type == GSM_BTS_TYPE_BS11)
@@ -586,8 +592,8 @@
 	gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING);
 	gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD);
 
-	osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
-	osmo_signal_register_handler(SS_GLOBAL, gbl_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
+	osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
 
 	return 0;
 }
diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c
index bd6ebba..cda1318 100644
--- a/openbsc/src/libbsc/e1_config.c
+++ b/openbsc/src/libbsc/e1_config.c
@@ -24,13 +24,14 @@
 #include <netinet/in.h>
 
 #include <openbsc/gsm_data.h>
-#include <openbsc/e1_input.h>
-#include <openbsc/trau_frame.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/trau_frame.h>
 #include <openbsc/trau_mux.h>
 #include <openbsc/misdn.h>
-#include <openbsc/ipaccess.h>
+#include <osmocom/abis/ipaccess.h>
 #include <osmocom/core/talloc.h>
 #include <openbsc/debug.h>
+#include <openbsc/abis_rsl.h>
 
 #define SAPI_L2ML	0
 #define SAPI_OML	62
@@ -46,17 +47,17 @@
 	struct e1inp_line *line;
 	struct e1inp_ts *e1_ts;
 
-	DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
+	DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
 
 	if (!e1_link->e1_ts) {
-		LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n",
+		LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n",
 		     ts->nr, ts->trx->nr, ts->trx->bts->nr);
 		return 0;
 	}
 
-	line = e1inp_line_get(e1_link->e1_nr);
+	line = e1inp_line_find(e1_link->e1_nr);
 	if (!line) {
-		LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) referring to "
+		LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to "
 		     "non-existing E1 line %u\n", ts->nr, ts->trx->nr,
 		     ts->trx->bts->nr, e1_link->e1_nr);
 		return -ENOMEM;
@@ -66,7 +67,7 @@
 	case GSM_PCHAN_TCH_F:
 	case GSM_PCHAN_TCH_H:
 		e1_ts = &line->ts[e1_link->e1_ts-1];
-		e1inp_ts_config(e1_ts, line, E1INP_TS_TYPE_TRAU);
+		e1inp_ts_config_trau(e1_ts, line, subch_cb);
 		subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss);
 		break;
 	default:
@@ -85,28 +86,28 @@
 	int i;
 
 	if (!e1_link->e1_ts) {
-		LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link without "
+		LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without "
 		     "timeslot?\n", trx->bts->nr, trx->nr);
 		return -EINVAL;
 	}
 
 	/* RSL Link */
-	line = e1inp_line_get(e1_link->e1_nr);
+	line = e1inp_line_find(e1_link->e1_nr);
 	if (!line) {
-		LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring "
+		LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring "
 		     "to non-existing E1 line %u\n", trx->bts->nr,
 		     trx->nr, e1_link->e1_nr);
 		return -ENOMEM;
 	}
 	sign_ts = &line->ts[e1_link->e1_ts-1];
-	e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN);
+	e1inp_ts_config_sign(sign_ts, line);
 	/* Ericsson RBS have a per-TRX OML link in parallel to RSL */
 	if (trx->bts->type == GSM_BTS_TYPE_RBS2000) {
 		struct e1inp_sign_link *oml_link;
 		oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx,
 						  trx->rsl_tei, SAPI_OML);
 		if (!oml_link) {
-			LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) OML link creation "
+			LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation "
 				"failed\n", trx->bts->nr, trx->nr);
 			return -ENOMEM;
 		}
@@ -117,7 +118,7 @@
 	rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
 					  trx, trx->rsl_tei, SAPI_RSL);
 	if (!rsl_link) {
-		LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation "
+		LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation "
 		     "failed\n", trx->bts->nr, trx->nr);
 		return -ENOMEM;
 	}
@@ -131,6 +132,33 @@
 	return 0;
 }
 
+/* this is the generic callback for all ISDN-based BTS. */
+static int bts_isdn_sign_link(struct msgb *msg)
+{
+	int ret = -EINVAL;
+	struct e1inp_sign_link *link = msg->dst;
+	struct gsm_bts *bts;
+
+	log_set_context(BSC_CTX_BTS, link->trx->bts);
+	switch (link->type) {
+	case E1INP_SIGN_OML:
+		bts = link->trx->bts;
+		ret = bts->model->oml_rcvmsg(msg);
+		break;
+	case E1INP_SIGN_RSL:
+		ret = abis_rsl_rcvmsg(msg);
+		break;
+	default:
+		LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type);
+		break;
+	}
+	return ret;
+}
+
+struct e1inp_line_ops bts_isdn_e1inp_line_ops = {
+	.sign_link	= bts_isdn_sign_link,
+};
+
 int e1_reconfig_bts(struct gsm_bts *bts)
 {
 	struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
@@ -139,27 +167,40 @@
 	struct e1inp_sign_link *oml_link;
 	struct gsm_bts_trx *trx;
 
-	DEBUGP(DMI, "e1_reconfig_bts(%u)\n", bts->nr);
+	DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr);
 
+	line = e1inp_line_find(e1_link->e1_nr);
+	if (!line) {
+		LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to "
+		     "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
+		return -ENOMEM;
+	}
+
+	if (!bts->model->e1line_bind_ops) {
+		LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n");
+		return -EINVAL;
+	}
+	if (!line->ops)
+		bts->model->e1line_bind_ops(line);
+
+	/* skip signal link initialization, this is done later for these BTS. */
+	if (bts->type == GSM_BTS_TYPE_NANOBTS ||
+	    bts->type == GSM_BTS_TYPE_HSL_FEMTO)
+		return e1inp_line_update(line);
+
+	/* OML link */
 	if (!e1_link->e1_ts) {
-		LOGP(DINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n",
+		LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n",
 		     bts->nr);
 		return -EINVAL;
 	}
 
-	/* OML link */
-	line = e1inp_line_get(e1_link->e1_nr);
-	if (!line) {
-		LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to "
-		     "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
-		return -ENOMEM;
-	}
 	sign_ts = &line->ts[e1_link->e1_ts-1];
-	e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN);
+	e1inp_ts_config_sign(sign_ts, line);
 	oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
 					  bts->c0, bts->oml_tei, SAPI_OML);
 	if (!oml_link) {
-		LOGP(DINP, LOGL_ERROR, "BTS %u OML link creation failed\n",
+		LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n",
 		     bts->nr);
 		return -ENOMEM;
 	}
@@ -260,37 +301,3 @@
 	return mi_setup(cardnr, line, release_l2);
 }
 #endif
-
-/* configure pseudo E1 line in ip.access style and connect to BTS */
-int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
-{
-	struct e1inp_line *line;
-	struct e1inp_ts *sign_ts, *rsl_ts;
-	struct e1inp_sign_link *oml_link, *rsl_link;
-
-	line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
-	if (!line)
-		return -ENOMEM;
-
-	/* create E1 timeslots for signalling and TRAU frames */
-	e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
-	e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
-
-	/* create signalling links for TS1 */
-	sign_ts = &line->ts[1-1];
-	rsl_ts = &line->ts[2-1];
-	oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
-					  bts->c0, 0xff, 0);
-	rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
-					  bts->c0, 0, 0);
-
-	/* create back-links from bts/trx */
-	bts->oml_link = oml_link;
-	bts->c0->rsl_link = rsl_link;
-
-	/* default port at BTS for incoming connections is 3006 */
-	if (sin->sin_port == 0)
-		sin->sin_port = htons(3006);
-
-	return ipaccess_connect(line, sin);
-}