[PATCH] openbsc[master]: mscsplit: various preparations to separate MSC from BSC

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/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Wed May 10 12:16:57 UTC 2017


Review at  https://gerrit.osmocom.org/2567

mscsplit: various preparations to separate MSC from BSC

Disable large parts of the code that depend on BSC presence.

Don't set msg->lchan nor msg->dst.
Don't use lchan in libmsc.
Decouple lac from bts.

Prepare entry/exit point for MSC -> BSC and MSC -> RNC communication:
Add msc_ifaces.[hc], a_iface.c, with a general msc_tx_dtap() to redirect to
different interfaces depending on the actual subscriber connection.
While iu_tx() is going to be functional fairly soon, the a_tx() is going to be
just a dummy for some time (see comment).
Add Iu specific fields in gsm_subscriber_connection: the UE connection pointer
and an indicator for the Integrity Protection status on Iu (to be fully
implemented in later commits).
Add lac member to gsm_subscriber_connection, to allow decoupling from
bts->location_area_code. The conn->lac will actually be set in iu.c in an
upcoming commit ("add iucs.[hc]").

move to libcommon-cs: gsm48_extract_mi(), gsm48_paging_extract_mi().

libmsc: duplicate gsm0808 / gsm48 functions (towards BSC).
In osmo-nitb, libmsc would directly call the functions on the BSC level, not
always via the bsc_api. When separating libmsc from libbsc, some functions are
missing from the linkage.
Hence duplicate these functions to libmsc, add an msc_ prefix for clarity, also
add a _tx to gsm0808_cipher_mode():
* add msc_gsm0808_tx_cipher_mode() (dummy/stub)
* add msc_gsm48_tx_mm_serv_ack()
* add msc_gsm48_tx_mm_serv_rej()
Call these from libmsc instead of
* gsm0808_cipher_mode()
* gsm48_tx_mm_serv_ack()
* gsm48_tx_mm_serv_rej()
Also add a comment related to msc_gsm0808_tx_cipher_mode() in two places.

Temporarily disable all paging to be able to link libmsc without libbsc.
Skip the paging part of channel_test because the paging is now disabled.
In osmo-nitb, paging is done on BSC level and MSC level "at the same time".
When the new MSC is fully operational, paging will be controlled separately on
the MSC level, and the BSC (RNC) level will be instructed over an IuCS or
A-interface to negotiate paging with the MS (UE). This MSC level paging does
not yet exist and will be added in subsequent commits.

msc_compl_l3(): publish in .h, tweak return value.  Use new libmsc enum values
for return val, to avoid dependency on libbsc headers.  Make callable from
other scopes: publish in osmo_msc.h and remove 'static' in osmo_msc.c

add gsm_encr to subscr_conn
move subscr_request to gsm_subscriber.h
subscr_request_channel() -> subscr_request_conn()
move to libmsc: osmo_stats_vty_add_cmds()
gsm_04_08: remove apply_codec_restrictions()
gsm0408_test: use NULL for root ctx
move to libbsc: gsm_bts_neighbor()
move to libbsc: lchan_next_meas_rep()
move vty config for t3212 to network level (periodic lu)
remove unneccessary linking from some tests
remove handle_abisip_signal()
abis_rsl.c: don't use libvlr from libbsc

Change-Id: I9cf80f9c2c8a53a29e42f000029e680a9922cb41
---
M openbsc/include/openbsc/Makefile.am
M openbsc/include/openbsc/gsm_04_08.h
M openbsc/include/openbsc/gsm_data.h
M openbsc/include/openbsc/gsm_subscriber.h
A openbsc/include/openbsc/msc_ifaces.h
M openbsc/include/openbsc/osmo_msc.h
M openbsc/src/libbsc/abis_rsl.c
M openbsc/src/libbsc/bsc_vty.c
M openbsc/src/libbsc/gsm_04_08_utils.c
M openbsc/src/libbsc/handover_decision.c
M openbsc/src/libcommon-cs/common_cs.c
M openbsc/src/libcommon-cs/common_cs_vty.c
M openbsc/src/libcommon/gsm_data.c
M openbsc/src/libmsc/Makefile.am
A openbsc/src/libmsc/a_iface.c
M openbsc/src/libmsc/gsm_04_08.c
M openbsc/src/libmsc/gsm_04_11.c
M openbsc/src/libmsc/gsm_subscriber.c
M openbsc/src/libmsc/mncc_builtin.c
A openbsc/src/libmsc/msc_ifaces.c
M openbsc/src/libmsc/osmo_msc.c
M openbsc/src/libmsc/silent_call.c
M openbsc/src/libmsc/smpp_openbsc.c
M openbsc/src/libmsc/vty_interface_layer3.c
M openbsc/tests/bsc/Makefile.am
M openbsc/tests/channel/Makefile.am
M openbsc/tests/db/Makefile.am
M openbsc/tests/gsm0408/gsm0408_test.c
28 files changed, 446 insertions(+), 268 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/67/2567/1

diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index b903782..1091e8a 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -54,6 +54,7 @@
 	misdn.h \
 	mncc.h \
 	mncc_int.h \
+	msc_ifaces.h \
 	nat_rewrite_trie.h \
 	network_listen.h \
 	oap_client.h \
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index ceaed7c..bf3f8423 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -79,6 +79,8 @@
 int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv);
 int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type);
 int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type);
+
+/* TODO MSCSPLIT remove gsm48_handle_paging_resp() */
 int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
 			     struct msgb *msg, struct bsc_subscr *bsub);
 
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index f2b7fa3..da61070 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -26,6 +26,7 @@
 struct bsc_subscr;
 struct vlr_instance;
 struct vlr_subscr;
+struct ue_conn_ctx;
 
 #define OBSC_LINKID_CB(__msgb)	(__msgb)->cb[3]
 
@@ -110,6 +111,12 @@
 	uint8_t classmark3[14];
 };
 
+enum integrity_protection_state {
+	INTEGRITY_PROTECTION_NONE	= 0,
+	INTEGRITY_PROTECTION_IK		= 1,
+	INTEGRITY_PROTECTION_IK_CK	= 2,
+};
+
 /* active radio connection of a mobile subscriber */
 struct gsm_subscriber_connection {
 	/* global linked list of subscriber_connections */
@@ -169,6 +176,15 @@
 	enum ran_type via_ran;
 
 	struct gsm_classmark classmark;
+
+	uint16_t lac;
+	struct gsm_encr encr;
+
+	/* which Iu-CS connection, if any. */
+	struct {
+		struct ue_conn_ctx *ue_ctx;
+		int integrity_protection;
+	} iu;
 };
 
 
@@ -304,6 +320,12 @@
 };
 
 struct gsm_network {
+	/* TODO MSCSPLIT the gsm_network struct is basically a kitchen sink for
+	 * global settings and variables, "madly" mixing BSC and MSC stuff. Split
+	 * this in e.g. struct osmo_bsc and struct osmo_msc, with the things
+	 * these have in common, like country and network code, put in yet
+	 * separate structs and placed as members in osmo_bsc and osmo_msc. */
+
 	/* global parameters */
 	uint16_t country_code;
 	uint16_t network_code;
@@ -414,6 +436,9 @@
 	uint16_t gsup_server_port;
 
 	struct vlr_instance *vlr;
+
+	/* Periodic location update default value */
+	uint8_t t3212;
 };
 
 struct osmo_esme;
@@ -463,10 +488,6 @@
 extern void talloc_ctx_init(void *ctx_root);
 
 int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
-
-/* Get reference to a neighbor cell on a given BCCH ARFCN */
-struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
-				 uint16_t arfcn, uint8_t bsic);
 
 enum gsm_bts_type parse_btstype(const char *arg);
 const char *btstype2str(enum gsm_bts_type type);
@@ -550,7 +571,6 @@
 
 int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts);
 void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
-struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
 
 int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat);
 int gsm_bts_model_register(struct gsm_bts_model *model);
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index c65b8a3..4124e06 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -19,8 +19,6 @@
 
 #define GSM_SUBSCRIBER_NO_EXPIRATION	0x0
 
-struct subscr_request;
-
 enum gsm_subscriber_field {
 	GSM_SUBSCRIBER_IMSI,
 	GSM_SUBSCRIBER_TMSI,
@@ -34,14 +32,29 @@
 	GSM_SUBSCRIBER_UPDATE_EQUIPMENT,
 };
 
-int subscr_update(struct vlr_subscr *vsub, struct gsm_bts *bts, int reason);
+/*
+ * Struct for pending channel requests. This is managed in the
+ * llist_head requests of each subscriber. The reference counting
+ * should work in such a way that a subscriber with a pending request
+ * remains in memory.
+ */
+struct subscr_request {
+       struct llist_head entry;
+
+       /* the callback data */
+       gsm_cbfn *cbfn;
+       void *param;
+};
+
+int subscr_update(struct vlr_subscr *vsub, int reason);
 
 /*
  * Paging handling with authentication
  */
-struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub,
-					      int channel_type,
-					      gsm_cbfn *cbfn, void *param);
+struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub,
+					   int channel_type,
+					   gsm_cbfn *cbfn, void *param);
+
 void subscr_remove_request(struct subscr_request *req);
 
 int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
diff --git a/openbsc/include/openbsc/msc_ifaces.h b/openbsc/include/openbsc/msc_ifaces.h
new file mode 100644
index 0000000..83aad92
--- /dev/null
+++ b/openbsc/include/openbsc/msc_ifaces.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <osmocom/core/msgb.h>
+#include <openbsc/gsm_data.h>
+
+/* These are the interfaces of the MSC layer towards (from?) the BSC and RNC,
+ * i.e. in the direction towards the mobile device (MS aka UE).
+ *
+ * 2G will use the A-interface,
+ * 3G aka UMTS will use the Iu-interface (for the MSC, it's IuCS).
+ *
+ * To allow linking parts of the MSC code without having to include entire
+ * infrastructures of external libraries, the core transmitting and receiving
+ * functions are left unimplemented. For example, a unit test does not need to
+ * link against external ASN1 libraries if it is never going to encode actual
+ * outgoing messages. It is up to each building scope to implement real world
+ * functions or to plug mere dummy implementations.
+ *
+ * For example, msc_tx_dtap(conn, msg), depending on conn->via_iface, will call
+ * either iu_tx() or a_tx() [note: at time of writing, the A-interface is not
+ * yet implemented]. When you try to link against libmsc, you will find that
+ * the compiler complains about an undefined reference to iu_tx(). If you,
+ * however, link against libiu as well as the osmo-iuh libs (etc.), iu_tx() is
+ * available. A unit test may instead simply implement a dummy iu_tx() function
+ * and not link against osmo-iuh.
+ */
+
+/* Each main linkage must implement this function (see comment above). */
+extern int iu_tx(struct msgb *msg, uint8_t sapi);
+
+/* So far this is a dummy implemented in libmsc/a_iface.c. When A-interface
+ * gets implemented, it should be in a separate lib (like libiu), this function
+ * should move there, and the following comment should remain here: "
+ * Each main linkage must implement this function (see comment above).
+ * " */
+extern int a_tx(struct msgb *msg);
+
+int msc_tx_dtap(struct gsm_subscriber_connection *conn,
+		struct msgb *msg);
+
+int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn);
+int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
+			     enum gsm48_reject_value value);
+
+/* TODO: specific to A interface, move this away */
+int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
+			       const uint8_t *key, int len, int include_imeisv);
diff --git a/openbsc/include/openbsc/osmo_msc.h b/openbsc/include/openbsc/osmo_msc.h
index 166732e..6096e0b 100644
--- a/openbsc/include/openbsc/osmo_msc.h
+++ b/openbsc/include/openbsc/osmo_msc.h
@@ -50,6 +50,11 @@
 	return get_value_string(subscr_conn_from_names, val);
 }
 
+enum msc_compl_l3_rc {
+	MSC_CONN_ACCEPT = 0,
+	MSC_CONN_REJECT = 1,
+};
+
 
 struct bsc_api *msc_bsc_api();
 
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index 6227c0c..d4fdaed 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -1373,8 +1373,6 @@
 	if (lchan && lchan->conn) {
 		if (lchan->conn->bsub)
 			name = bsc_subscr_name(lchan->conn->bsub);
-		else if (lchan->conn->subscr)
-			name = lchan->conn->subscr->imsi;
 		else
 			name = lchan->name;
 	}
@@ -1416,6 +1414,19 @@
 	}
 }
 
+static struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
+{
+	struct gsm_meas_rep *meas_rep;
+
+	meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
+	memset(meas_rep, 0, sizeof(*meas_rep));
+	meas_rep->lchan = lchan;
+	lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
+					% ARRAY_SIZE(lchan->meas_rep);
+
+	return meas_rep;
+}
+
 static int rsl_rx_meas_res(struct msgb *msg)
 {
 	struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 106874e..46f27e8 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -594,13 +594,6 @@
 				(sp->penalty_time*20)+20, VTY_NEWLINE);
 	}
 
-	/* Is periodic LU enabled or disabled? */
-	if (bts->si_common.chan_desc.t3212 == 0)
-		vty_out(vty, "  no periodic location update%s", VTY_NEWLINE);
-	else
-		vty_out(vty, "  periodic location update %u%s",
-			bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE);
-
 	vty_out(vty, "  radio-link-timeout %d%s",
 		get_radio_link_timeout(&bts->si_common.cell_options),
 		VTY_NEWLINE);
@@ -843,6 +836,11 @@
 			vty_out(vty, " timezone %d %d%s",
 				gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
 	}
+	if (gsmnet->t3212 == 0)
+		vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
+	else
+		vty_out(vty, " periodic location update %u%s",
+			gsmnet->t3212 * 6, VTY_NEWLINE);
 
 	return CMD_SUCCESS;
 }
@@ -3951,7 +3949,6 @@
 	install_element_ve(&show_paging_group_cmd);
 
 	logging_vty_add_cmds(NULL);
-	osmo_stats_vty_add_cmds();
 
 	install_element(GSMNET_NODE, &cfg_net_neci_cmd);
 	install_element(GSMNET_NODE, &cfg_net_handover_cmd);
diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c
index db9dabb..52c6b27 100644
--- a/openbsc/src/libbsc/gsm_04_08_utils.c
+++ b/openbsc/src/libbsc/gsm_04_08_utils.c
@@ -270,30 +270,6 @@
 	return rsl_siemens_mrpci(lchan, &mrpci);
 }
 
-int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type)
-{
-	/* Check the size for the classmark */
-	if (length < 1 + *classmark2_lv)
-		return -1;
-
-	uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
-	if (length < 2 + *classmark2_lv + mi_lv[0])
-		return -2;
-
-	*mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
-	return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv);
-}
-
-int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
-			    char *mi_string, uint8_t *mi_type)
-{
-	static const uint32_t classmark_offset =
-		offsetof(struct gsm48_pag_resp, classmark2);
-	uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2;
-	return gsm48_extract_mi(classmark2_lv, length - classmark_offset,
-				mi_string, mi_type);
-}
-
 int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
 			     struct msgb *msg, struct bsc_subscr *bsub)
 {
diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c
index 0f07bca..8d7e047 100644
--- a/openbsc/src/libbsc/handover_decision.c
+++ b/openbsc/src/libbsc/handover_decision.c
@@ -33,6 +33,27 @@
 #include <openbsc/handover.h>
 #include <osmocom/gsm/gsm_utils.h>
 
+/* Get reference to a neighbor cell on a given BCCH ARFCN */
+static struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
+					uint16_t arfcn, uint8_t bsic)
+{
+	struct gsm_bts *neigh;
+	/* FIXME: use some better heuristics here to determine which cell
+	 * using this ARFCN really is closest to the target cell.  For
+	 * now we simply assume that each ARFCN will only be used by one
+	 * cell */
+
+	llist_for_each_entry(neigh, &bts->network->bts_list, list) {
+		/* FIXME: this is probably returning the same bts again!? */
+		if (neigh->c0->arfcn == arfcn &&
+		    neigh->bsic == bsic)
+			return neigh;
+	}
+
+	return NULL;
+}
+
+
 /* issue handover to a cell identified by ARFCN and BSIC */
 static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
 				  uint16_t arfcn, uint8_t bsic)
diff --git a/openbsc/src/libcommon-cs/common_cs.c b/openbsc/src/libcommon-cs/common_cs.c
index 20fbab8..d94381b 100644
--- a/openbsc/src/libcommon-cs/common_cs.c
+++ b/openbsc/src/libcommon-cs/common_cs.c
@@ -59,6 +59,9 @@
 	net->country_code = country_code;
 	net->network_code = network_code;
 
+	/* Use 30 min periodic update interval as sane default */
+	net->t3212 = 5;
+
 	INIT_LLIST_HEAD(&net->trans_list);
 	INIT_LLIST_HEAD(&net->upqueue);
 	INIT_LLIST_HEAD(&net->subscr_conns);
@@ -112,6 +115,30 @@
 	return msg;
 }
 
+int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type)
+{
+	/* Check the size for the classmark */
+	if (length < 1 + *classmark2_lv)
+		return -1;
+
+	uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
+	if (length < 2 + *classmark2_lv + mi_lv[0])
+		return -2;
+
+	*mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
+	return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv);
+}
+
+int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
+			    char *mi_string, uint8_t *mi_type)
+{
+	static const uint32_t classmark_offset =
+		offsetof(struct gsm48_pag_resp, classmark2);
+	uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2;
+	return gsm48_extract_mi(classmark2_lv, length - classmark_offset,
+				mi_string, mi_type);
+}
+
 uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref)
 {
 	const uint8_t rp_msg_ref = *next_rp_ref;
diff --git a/openbsc/src/libcommon-cs/common_cs_vty.c b/openbsc/src/libcommon-cs/common_cs_vty.c
index 10fdc11..00d55c5 100644
--- a/openbsc/src/libcommon-cs/common_cs_vty.c
+++ b/openbsc/src/libcommon-cs/common_cs_vty.c
@@ -305,6 +305,8 @@
 	OSMO_ASSERT(vty_global_gsm_network == NULL);
 	vty_global_gsm_network = network;
 
+	osmo_stats_vty_add_cmds();
+
 	install_element(CONFIG_NODE, &cfg_net_cmd);
 	install_node(&net_node, config_write_net);
 	vty_install_default(GSMNET_NODE);
diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c
index 89acc6b..8377421 100644
--- a/openbsc/src/libcommon/gsm_data.c
+++ b/openbsc/src/libcommon/gsm_data.c
@@ -71,25 +71,6 @@
 	return 0;
 }
 
-/* Get reference to a neighbor cell on a given BCCH ARFCN */
-struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
-				 uint16_t arfcn, uint8_t bsic)
-{
-	struct gsm_bts *neigh;
-	/* FIXME: use some better heuristics here to determine which cell
-	 * using this ARFCN really is closest to the target cell.  For
-	 * now we simply assume that each ARFCN will only be used by one
-	 * cell */
-
-	llist_for_each_entry(neigh, &bts->network->bts_list, list) {
-		if (neigh->c0->arfcn == arfcn &&
-		    neigh->bsic == bsic)
-			return neigh;
-	}
-
-	return NULL;
-}
-
 const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1] = {
 	{ GSM_BTS_TYPE_UNKNOWN,	"unknown" },
 	{ GSM_BTS_TYPE_BS11,	"bs11" },
@@ -230,19 +211,6 @@
 	return 1;
 }
 
-struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
-{
-	struct gsm_meas_rep *meas_rep;
-
-	meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
-	memset(meas_rep, 0, sizeof(*meas_rep));
-	meas_rep->lchan = lchan;
-	lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
-					% ARRAY_SIZE(lchan->meas_rep);
-
-	return meas_rep;
-}
-
 int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat)
 {
 	return bitvec_set_bit_pos(&bts->features, feat, 1);
@@ -340,7 +308,7 @@
 	bts->si_common.chan_desc.att = 1; /* attachment required */
 	bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */
 	bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */
-	bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */
+	bts->si_common.chan_desc.t3212 = net->t3212; /* Use network's current value */
 	set_radio_link_timeout(&bts->si_common.cell_options, 32);
 				/* Use RADIO LINK TIMEOUT of 32 seconds */
 
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am
index 3f4174c..7ab30d0 100644
--- a/openbsc/src/libmsc/Makefile.am
+++ b/openbsc/src/libmsc/Makefile.am
@@ -23,6 +23,7 @@
 	$(NULL)
 
 libmsc_a_SOURCES = \
+	a_iface.c \
 	auth.c \
 	db.c \
 	gsm_04_08.c \
@@ -32,6 +33,7 @@
 	mncc.c \
 	mncc_builtin.c \
 	mncc_sock.c \
+	msc_ifaces.c \
 	rrlp.c \
 	silent_call.c \
 	sms_queue.c \
diff --git a/openbsc/src/libmsc/a_iface.c b/openbsc/src/libmsc/a_iface.c
new file mode 100644
index 0000000..1f471f9
--- /dev/null
+++ b/openbsc/src/libmsc/a_iface.c
@@ -0,0 +1,45 @@
+/* A-interface implementation, from MSC to BSC */
+
+/* (C) 2016 by sysmocom s.m.f.c GmbH <info at sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * 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/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#include <openbsc/debug.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/msc_ifaces.h>
+#include <openbsc/debug.h>
+
+int a_tx(struct msgb *msg)
+{
+	LOGP(DMSC, LOGL_ERROR, "message to be sent to BSC, but A-interface"
+	     " not implemented.\n%s\n", osmo_hexdump(msg->data, msg->len));
+	return -1;
+}
+
+int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
+			       const uint8_t *key, int len, int include_imeisv)
+{
+	/* TODO generalize for A- and Iu interfaces, don't name after 08.08 */
+	LOGP(DMSC, LOGL_ERROR, "gsm0808_cipher_mode(): message to be sent to"
+	     " BSC, but A interface not yet implemented.\n");
+	return -1;
+}
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 30c2cc3..e1e5f6f 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -59,6 +59,7 @@
 #include <osmocom/abis/e1_input.h>
 #include <osmocom/core/bitvec.h>
 #include <openbsc/vlr.h>
+#include <openbsc/msc_ifaces.h>
 
 #include <osmocom/gsm/gsm48.h>
 #include <osmocom/gsm/gsm0480.h>
@@ -71,10 +72,17 @@
 
 #include <assert.h>
 
+
+/* These debug statements were removed during the BSC/MSC split. It may make
+ * sense to replace them with debug statements that do not access BTS data. */
+#define BEFORE_MSCSPLIT 0
+
 void *tall_locop_ctx;
 void *tall_authciphop_ctx;
 
+#if BEFORE_MSCSPLIT
 static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
+#endif
 
 static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
 			       uint32_t send_tmsi);
@@ -86,29 +94,6 @@
 	uint16_t mnc;
 	uint16_t lac;
 };
-
-static int apply_codec_restrictions(struct gsm_bts *bts,
-	struct gsm_mncc_bearer_cap *bcap)
-{
-	int i, j;
-
-	/* remove unsupported speech versions from list */
-	for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) {
-		if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR)
-			bcap->speech_ver[j++] = GSM48_BCAP_SV_FR;
-		if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr)
-			bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR;
-		if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr)
-			bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F;
-		if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr)
-			bcap->speech_ver[j++] = GSM48_BCAP_SV_HR;
-		if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr)
-			bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H;
-	}
-	bcap->speech_ver[j] = -1;
-
-	return 0;
-}
 
 static uint32_t new_callref = 0x80000001;
 
@@ -126,27 +111,6 @@
 	 * work that the caller no longer has to do */
 	if (trans) {
 		gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
-		msg->lchan = trans->conn->lchan;
-	}
-
-	if (msg->lchan) {
-		struct e1inp_sign_link *sign_link =
-				msg->lchan->ts->trx->rsl_link;
-
-		msg->dst = sign_link;
-		if (gsm48_hdr_pdisc(gh) == GSM48_PDISC_CC)
-			DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) "
-				"Sending '%s' to MS.\n",
-				sign_link->trx->bts->nr,
-				sign_link->trx->nr, msg->lchan->ts->nr,
-				gh->proto_discr & 0xf0,
-				gsm48_cc_msg_name(gh->msg_type));
-		else
-			DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
-				"Sending 0x%02x to MS.\n",
-				sign_link->trx->bts->nr,
-				sign_link->trx->nr, msg->lchan->ts->nr,
-				gh->proto_discr, gh->msg_type);
 	}
 
 	return gsm0808_submit_dtap(conn, msg, 0, 0);
@@ -187,7 +151,6 @@
 /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
 int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
 {
-	struct gsm_bts *bts = conn->bts;
 	struct msgb *msg;
 
 	msg = gsm48_create_loc_upd_rej(cause);
@@ -196,11 +159,8 @@
 		return -1;
 	}
 
-	msg->lchan = conn->lchan;
-
-	LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
-	     "LAC=%u BTS=%u\n", vlr_subscr_name(conn->vsub),
-	     bts->location_area_code, bts->nr);
+	LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT\n",
+	     vlr_subscr_name(conn->vsub));
 
 	return gsm48_conn_sendmsg(msg, conn, NULL);
 }
@@ -214,8 +174,6 @@
 	struct gsm48_loc_area_id *lai;
 	uint8_t *mid;
 
-	msg->lchan = conn->lchan;
-
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	gh->proto_discr = GSM48_PDISC_MM;
 	gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
@@ -223,7 +181,7 @@
 	lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
 	gsm48_generate_lai(lai, conn->network->country_code,
 			   conn->network->network_code,
-			   conn->bts->location_area_code);
+			   conn->lac);
 
 	if (send_tmsi == GSM_RESERVED_TMSI) {
 		/* we did not allocate a TMSI to the MS, so we need to
@@ -257,8 +215,6 @@
 {
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ");
 	struct gsm48_hdr *gh;
-
-	msg->lchan = conn->lchan;
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
 	gh->proto_discr = GSM48_PDISC_MM;
@@ -437,8 +393,6 @@
 	int tzunits;
 	int dst = 0;
 
-	msg->lchan = conn->lchan;
-
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	gh->proto_discr = GSM48_PDISC_MM;
 	gh->msg_type = GSM48_MT_MM_INFO;
@@ -588,7 +542,6 @@
 	if (autn)
 		DEBUGP(DMM, "   AUTH REQ (autn = %s)\n", osmo_hexdump_nospc(autn, 16));
 
-	msg->lchan = conn->lchan;
 	gh->proto_discr = GSM48_PDISC_MM;
 	gh->msg_type = GSM48_MT_MM_AUTH_REQ;
 
@@ -669,7 +622,7 @@
  * b) Try to parse the TMSI. If we do not have one reject
  * c) Check that we know the subscriber with the TMSI otherwise reject
  *    with a HLR cause
- * d) Set the subscriber on the gsm_lchan and accept
+ * d) Set the subscriber on the conn and accept
  *
  * Keep this function non-static for direct invocation by unit tests.
  */
@@ -699,14 +652,14 @@
 	DEBUGP(DMM, "<- CM SERVICE REQUEST ");
 	if (msg->data_len < sizeof(struct gsm48_service_request*)) {
 		DEBUGPC(DMM, "wrong sized message\n");
-		return gsm48_tx_mm_serv_rej(conn,
-					    GSM48_REJECT_INCORRECT_MESSAGE);
+		return msc_gsm48_tx_mm_serv_rej(conn,
+						GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
 	if (msg->data_len < req->mi_len + 6) {
 		DEBUGPC(DMM, "does not fit in packet\n");
-		return gsm48_tx_mm_serv_rej(conn,
-					    GSM48_REJECT_INCORRECT_MESSAGE);
+		return msc_gsm48_tx_mm_serv_rej(conn,
+						GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
 	gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
@@ -722,8 +675,8 @@
 			mi_string);
 	} else {
 		DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type);
-		return gsm48_tx_mm_serv_rej(conn,
-					    GSM48_REJECT_INCORRECT_MESSAGE);
+		return msc_gsm48_tx_mm_serv_rej(conn,
+						GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
 	osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
@@ -748,8 +701,12 @@
 		return rc;
 	}
 
+#if BEFORE_MSCSPLIT
+	/* see mail on openbsc@ 9 Feb 2016 22:30:15 +0100
+	 * We need to hook sending of MRPCI to Siemens BS11 somewhere else */
 	if (is_siemens_bts(conn->bts))
 		send_siemens_mrpci(msg->lchan, classmark2-1);
+#endif
 
 	vlr_proc_acc_req(conn->conn_fsm,
 			 SUBSCR_CONN_E_ACCEPTED,
@@ -800,7 +757,6 @@
 		DEBUGPC(DMM, ": unknown mobile identity type\n");
 		break;
 	}
-
 
 	/* TODO? We used to remember the subscriber's classmark1 here and
 	 * stored it in the old sqlite db, but now we store it in a conn that
@@ -1195,8 +1151,6 @@
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 APP INF");
 	struct gsm48_hdr *gh;
 
-	msg->lchan = conn->lchan;
-
 	DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n",
 		apdu_id, apdu_len);
 
@@ -1288,8 +1242,6 @@
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 TX SIMPLE");
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	msg->lchan = conn->lchan;
-
 	gh->proto_discr = pdisc;
 	gh->msg_type = msg_type;
 
@@ -1311,6 +1263,7 @@
 	struct msgb *msg;
 	unsigned char *data;
 
+#if BEFORE_MSCSPLIT
 	if (trans)
 		if (trans->conn && trans->conn->lchan)
 			DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
@@ -1328,6 +1281,7 @@
 	else
 		DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) "
 			"Sending '%s' to MNCC.\n", get_mncc_name(msg_type));
+#endif
 
 	mncc->msg_type = msg_type;
 
@@ -1371,8 +1325,10 @@
 	}
 	if (trans->cc.state != GSM_CSTATE_NULL)
 		new_cc_state(trans, GSM_CSTATE_NULL);
+#if BEFORE_MSCSPLIT
 	if (trans->conn)
 		trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref);
+#endif
 }
 
 static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
@@ -1386,13 +1342,12 @@
 
 	OSMO_ASSERT(!transt->conn);
 
-	/* check all tranactions (without lchan) for subscriber */
 	switch (event) {
 	case GSM_PAGING_SUCCEEDED:
 		DEBUGP(DCC, "Paging subscr %s succeeded!\n",
 		       vlr_subscr_msisdn_or_name(transt->vsub));
 		OSMO_ASSERT(conn);
-		/* Assign lchan */
+		/* Assign conn */
 		transt->conn = conn;
 		/* send SETUP request to called party */
 		gsm48_cc_tx_setup(transt, &transt->cc.msg);
@@ -1421,6 +1376,7 @@
 
 static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable);
 
+#if BEFORE_MSCSPLIT
 /* handle audio path for handover */
 static int switch_for_handover(struct gsm_lchan *old_lchan,
 			struct gsm_lchan *new_lchan)
@@ -1488,77 +1444,6 @@
 		switch_for_handover(old_lchan, lchan);
 }
 
-/* some other part of the code sends us a signal */
-static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
-				 void *handler_data, void *signal_data)
-{
-	struct gsm_lchan *lchan = signal_data;
-	int rc;
-	struct gsm_network *net;
-	struct gsm_trans *trans;
-
-	if (subsys != SS_ABISIP)
-		return 0;
-
-	/* RTP bridge handling */
-	if (lchan->conn && lchan->conn->mncc_rtp_bridge)
-		return tch_rtp_signal(lchan, signal);
-
-	/* in case we use direct BTS-to-BTS RTP */
-	if (ipacc_rtp_direct)
-		return 0;
-
-	switch (signal) {
-	case S_ABISIP_CRCX_ACK:
-		/* in case we don't use direct BTS-to-BTS RTP */
-		/* the BTS has successfully bound a TCH to a local ip/port,
-		 * which means we can connect our UDP socket to it */
-		if (lchan->abis_ip.rtp_socket) {
-			rtp_socket_free(lchan->abis_ip.rtp_socket);
-			lchan->abis_ip.rtp_socket = NULL;
-		}
-
-		lchan->abis_ip.rtp_socket = rtp_socket_create();
-		if (!lchan->abis_ip.rtp_socket)
-			return -EIO;
-
-		rc = rtp_socket_connect(lchan->abis_ip.rtp_socket,
-				   lchan->abis_ip.bound_ip,
-				   lchan->abis_ip.bound_port);
-		if (rc < 0)
-			return -EIO;
-
-		/* check if any transactions on this lchan still have
-		 * a tch_recv_mncc request pending */
-		net = lchan->ts->trx->bts->network;
-		llist_for_each_entry(trans, &net->trans_list, entry) {
-			if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) {
-				DEBUGP(DCC, "pending tch_recv_mncc request\n");
-				tch_recv_mncc(net, trans->callref, 1);
-			}
-		}
-
-		/*
-		 * TODO: this appears to be too early? Why not until after
-		 * the handover detect or the handover complete?
-		 *
-		 * Do we have a handover pending for this new lchan? In that
-		 * case re-route the audio from the old channel to the new one.
-		 */
-		maybe_switch_for_handover(lchan);
-		break;
-	case S_ABISIP_DLCX_IND:
-		/* the BTS tells us a RTP stream has been disconnected */
-		if (lchan->abis_ip.rtp_socket) {
-			rtp_socket_free(lchan->abis_ip.rtp_socket);
-			lchan->abis_ip.rtp_socket = NULL;
-		}
-
-		break;
-	}
-
-	return 0;
-}
 
 /* map two ipaccess RTP streams onto each other */
 static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
@@ -1647,6 +1532,7 @@
 
 	return 0;
 }
+#endif
 
 /* bridge channels of two transactions */
 static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge)
@@ -1663,13 +1549,19 @@
 	/* Which subscriber do we want to track trans1 or trans2? */
 	log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub);
 
+#if BEFORE_MSCSPLIT
 	/* through-connect channel */
 	return tch_map(trans1->conn->lchan, trans2->conn->lchan);
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
 /* enable receive of channels to MNCC upqueue */
 static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable)
 {
+#if BEFORE_MSCSPLIT
 	struct gsm_trans *trans;
 	struct gsm_lchan *lchan;
 	struct gsm_bts *bts;
@@ -1738,6 +1630,10 @@
 	}
 
 	return 0;
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
 static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
@@ -1878,7 +1774,11 @@
 
 	memset(&setup, 0, sizeof(struct gsm_mncc));
 	setup.callref = trans->callref;
+#if BEFORE_MSCSPLIT
 	setup.lchan_type = trans->conn->lchan->type;
+#else
+	setup.lchan_type = GSM_LCHAN_NONE;
+#endif
 	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* emergency setup is identified by msg_type */
 	if (msg_type == GSM48_MT_CC_EMERG_SETUP)
@@ -1894,7 +1794,6 @@
 		setup.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&setup.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
-		apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
@@ -2034,7 +1933,11 @@
 
 	memset(&call_conf, 0, sizeof(struct gsm_mncc));
 	call_conf.callref = trans->callref;
+#if BEFORE_MSCSPLIT
 	call_conf.lchan_type = trans->conn->lchan->type;
+#else
+	call_conf.lchan_type = GSM_LCHAN_NONE;
+#endif
 	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 #if 0
 	/* repeat */
@@ -2048,7 +1951,6 @@
 		call_conf.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&call_conf.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
-		apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
@@ -2738,7 +2640,6 @@
 		modify.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
-		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 
 	new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
@@ -2781,7 +2682,6 @@
 		modify.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
-		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
@@ -2822,7 +2722,6 @@
 		modify.fields |= GSM48_IE_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
-		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
@@ -2927,6 +2826,7 @@
 
 static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
 {
+#if BEFORE_MSCSPLIT
 	struct gsm_mncc *mode = arg;
 	struct gsm_lchan *lchan = trans->conn->lchan;
 
@@ -2942,8 +2842,14 @@
 
 	return gsm0808_assign_req(trans->conn, mode->lchan_mode,
 		trans->conn->lchan->type != GSM_LCHAN_TCH_H);
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
+
 }
 
+#if BEFORE_MSCSPLIT
 static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
 		int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
 		uint32_t payload_msg_type)
@@ -3000,9 +2906,11 @@
 {
 	return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
 }
+#endif
 
 static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
 {
+#if BEFORE_MSCSPLIT
 	struct gsm_bts *bts;
 	struct gsm_lchan *lchan;
 	struct gsm_trans *trans;
@@ -3056,10 +2964,15 @@
 
 	mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE);
 	return 0;
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
 static int tch_rtp_connect(struct gsm_network *net, void *arg)
 {
+#if BEFORE_MSCSPLIT
 	struct gsm_lchan *lchan;
 	struct gsm_trans *trans;
 	struct gsm_mncc_rtp *rtp = arg;
@@ -3097,8 +3010,13 @@
 	 */
 	trans->conn->mncc_rtp_connect_pending = 1;
 	return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
+#if BEFORE_MSCSPLIT
 static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
 {
 	struct gsm_network *net;
@@ -3146,6 +3064,7 @@
 
 	return 0;
 }
+#endif
 
 
 static struct downstate {
@@ -3215,7 +3134,9 @@
 	int i, rc = 0;
 	struct gsm_trans *trans = NULL, *transt;
 	struct gsm_subscriber_connection *conn = NULL;
+#if BEFORE_MSCSPLIT
 	struct gsm_bts *bts = NULL;
+#endif
 	struct gsm_mncc *data = arg, rel;
 
 	DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type));
@@ -3253,6 +3174,7 @@
 			LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n");
 			return 0;
 		}
+#if BEFORE_MSCSPLIT
 		if (!trans->conn->lchan) {
 			LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without lchan\n");
 			return 0;
@@ -3282,6 +3204,10 @@
 			LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
 		}
 		return -EINVAL;
+#else
+		/* not implemented yet! */
+		return -1;
+#endif
 	}
 
 	memset(&rel, 0, sizeof(struct gsm_mncc));
@@ -3357,14 +3283,15 @@
 					 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
 			return -ENOMEM;
 		}
-		/* Find lchan */
+
+		/* Find conn */
 		conn = connection_for_subscr(vsub);
 
-		/* If subscriber has no lchan */
+		/* If subscriber has no conn */
 		if (!conn) {
 			/* find transaction with this subscriber already paging */
 			llist_for_each_entry(transt, &net->trans_list, entry) {
-				/* Transaction of our lchan? */
+				/* Transaction of our conn? */
 				if (transt == trans ||
 				    transt->vsub != vsub)
 					continue;
@@ -3396,6 +3323,7 @@
 			vlr_subscr_put(vsub);
 			return 0;
 		}
+
 		/* Assign lchan */
 		trans->conn = msc_subscr_conn_get(conn);
 		vlr_subscr_put(vsub);
@@ -3409,7 +3337,7 @@
 
 	/* if paging did not respond yet */
 	if (!conn) {
-		DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+		DEBUGP(DCC, "(sub %s) "
 			"Received '%s' from MNCC in paging state\n",
 			vlr_subscr_msisdn_or_name(trans->vsub),
 			get_mncc_name(msg_type));
@@ -3424,9 +3352,8 @@
 		return rc;
 	}
 
-	DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+	DEBUGP(DCC, "(ti %02x sub %s) "
 		"Received '%s' from MNCC in state %d (%s)\n",
-		conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
 		trans->transaction_id,
 		vlr_subscr_msisdn_or_name(trans->conn->vsub),
 		get_mncc_name(msg_type), trans->cc.state,
@@ -3523,12 +3450,14 @@
 	/* Find transaction */
 	trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id);
 
+#if BEFORE_MSCSPLIT
 	DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
 		"Received '%s' from MS in state %d (%s)\n",
 		conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
 		transaction_id, vlr_subscr_msisdn_or_name(conn->vsub),
 		gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
 		gsm48_cc_state_name(trans?(trans->cc.state):0));
+#endif
 
 	/* Create transaction */
 	if (!trans) {
@@ -3846,6 +3775,7 @@
 			 net->gsup_server_port);
 }
 
+#if BEFORE_MSCSPLIT
 /*
  * This will be run by the linker when loading the DSO. We use it to
  * do system initialization, e.g. registration of signal handlers.
@@ -3854,3 +3784,4 @@
 {
 	osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL);
 }
+#endif
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index a67a7c6..7183d23 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -872,7 +872,7 @@
 }
 
 /* Take a SMS in gsm_sms structure and send it through an already
- * existing lchan. We also assume that the caller ensured this lchan already
+ * existing conn. We also assume that the caller ensured this conn already
  * has a SAPI3 RLL connection! */
 int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
 {
@@ -998,7 +998,7 @@
 	struct gsm_subscriber_connection *conn;
 	void *res;
 
-	/* check if we already have an open lchan to the subscriber.
+	/* check if we already have an open conn to the subscriber.
 	 * if yes, send the SMS this way */
 	conn = connection_for_subscr(vsub);
 	if (conn) {
@@ -1010,8 +1010,8 @@
 	/* if not, we have to start paging */
 	LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n",
 	     vlr_subscr_name(vsub));
-	res = subscr_request_channel(vsub, RSL_CHANNEED_SDCCH,
-				     paging_cb_send_sms, sms);
+	res = subscr_request_conn(vsub, RSL_CHANNEED_SDCCH, paging_cb_send_sms,
+				  sms);
 	if (!res) {
 		send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY);
 		sms_free(sms);
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
index e9b2e0e..f425058 100644
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ b/openbsc/src/libmsc/gsm_subscriber.c
@@ -46,21 +46,6 @@
 int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
                          gsm_cbfn *cb, void *cb_data);
 
-
-/*
- * Struct for pending channel requests. This is managed in the
- * llist_head requests of each subscriber. The reference counting
- * should work in such a way that a subscriber with a pending request
- * remains in memory.
- */
-struct subscr_request {
-	struct llist_head entry;
-
-	/* the callback data */
-	gsm_cbfn *cbfn;
-	void *param;
-};
-
 static struct bsc_subscr *vlr_subscr_to_bsc_sub(struct llist_head *bsc_subscribers,
 						struct vlr_subscr *vsub)
 {
@@ -73,6 +58,10 @@
 	sub->lac = vsub->lac;
 	return sub;
 }
+
+#if 0
+TODO implement paging response in libmsc!
+Excluding this to be able to link without libbsc:
 
 /*
  * We got the channel assigned and can now hand this channel
@@ -139,9 +128,16 @@
 	if (!vsub->cs.is_paging) {
 		LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n",
 		     vlr_subscr_name(vsub));
+#if 0
+		TODO implement paging response in libmsc!
+		Excluding this to be able to link without libbsc:
+
 		bsub = vlr_subscr_to_bsc_sub(net->bsc_subscribers, vsub);
 		rc = paging_request(net, bsub, channel_type, NULL, NULL);
 		bsc_subscr_put(bsub);
+#else
+		rc = -ENOTSUP;
+#endif
 		if (rc <= 0) {
 			LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
 			     vlr_subscr_name(vsub), rc);
@@ -181,3 +177,4 @@
 
 	return NULL;
 }
+#endif
diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c
index 067cc92..7f613c4 100644
--- a/openbsc/src/libmsc/mncc_builtin.c
+++ b/openbsc/src/libmsc/mncc_builtin.c
@@ -207,9 +207,11 @@
 	bridge.callref[1] = call->remote_ref;
 	DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
 
+#if BEFORE_MSCSPLIT
 	/* in direct mode, we always have to bridge the channels */
 	if (ipacc_rtp_direct)
 		return mncc_tx_to_cc(call->net, MNCC_BRIDGE, &bridge);
+#endif
 
 	/* proxy mode */
 	if (!net->handover.active) {
@@ -293,11 +295,16 @@
 		return -EIO;
 	}
 
+#if BEFORE_MSCSPLIT
 	/* RTP socket of remote end has meanwhile died */
 	if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
 		return -EIO;
 
 	return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
 
diff --git a/openbsc/src/libmsc/msc_ifaces.c b/openbsc/src/libmsc/msc_ifaces.c
new file mode 100644
index 0000000..500c99c
--- /dev/null
+++ b/openbsc/src/libmsc/msc_ifaces.c
@@ -0,0 +1,83 @@
+/* Implementation for MSC decisions which interface to send messages out on. */
+
+/* (C) 2016 by sysmocom s.m.f.c GmbH <info at sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * 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/core/logging.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/msc_ifaces.h>
+
+static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+	switch (conn->via_ran) {
+	case RAN_GERAN_A:
+		msg->dst = conn;
+		return a_tx(msg);
+
+	case RAN_UTRAN_IU:
+		msg->dst = conn->iu.ue_ctx;
+		return iu_tx(msg, 0);
+
+	default:
+		LOGP(DMSC, LOGL_ERROR,
+		     "msc_tx(): conn->via_ran invalid (%d)\n",
+		     conn->via_ran);
+		return -1;
+	}
+}
+
+
+int msc_tx_dtap(struct gsm_subscriber_connection *conn,
+		struct msgb *msg)
+{
+	return msc_tx(conn, msg);
+}
+
+
+/* 9.2.5 CM service accept */
+int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
+{
+	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACC");
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_MM;
+	gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
+
+	DEBUGP(DMM, "-> CM SERVICE ACCEPT\n");
+
+	return msc_tx_dtap(conn, msg);
+}
+
+/* 9.2.6 CM service reject */
+int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
+			     enum gsm48_reject_value value)
+{
+	struct msgb *msg;
+
+	msg = gsm48_create_mm_serv_rej(value);
+	if (!msg) {
+		LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n");
+		return -1;
+	}
+
+	DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
+
+	return msc_tx_dtap(conn, msg);
+}
diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c
index 21c8ec2..ab53b0b 100644
--- a/openbsc/src/libmsc/osmo_msc.c
+++ b/openbsc/src/libmsc/osmo_msc.c
@@ -21,6 +21,7 @@
  *
  */
 
+#include <openbsc/osmo_msc.h>
 #include <openbsc/bsc_api.h>
 #include <openbsc/debug.h>
 #include <openbsc/transaction.h>
@@ -69,9 +70,10 @@
 	osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL);
 }
 
-/* Receive a COMPLETE LAYER3 INFO from BSC */
-static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
-			uint16_t chosen_channel)
+/* receive a Level 3 Complete message and return MSC_CONN_ACCEPT or
+ * MSC_CONN_REJECT */
+enum msc_compl_l3_rc msc_compl_l3(struct gsm_subscriber_connection *conn,
+				  struct msgb *msg, uint16_t chosen_channel)
 {
 	/* Ownership of the gsm_subscriber_connection is still a bit mucky
 	 * between libbsc and libmsc. In libmsc, we use ref counting, but not
@@ -87,7 +89,7 @@
 		/* keep the use_count reserved, libbsc will discard. If we
 		 * released the ref count and discarded here, libbsc would
 		 * double-free. And we will not change bsc_api semantics. */
-		return BSC_API_CONN_POL_REJECT;
+		return MSC_CONN_REJECT;
 	}
 	DEBUGP(DMM, "compl_l3: Keeping conn\n");
 
@@ -96,7 +98,7 @@
 
 	/* If this should be kept, the conn->conn_fsm has placed a use_count */
 	msc_subscr_conn_put(conn);
-	return BSC_API_CONN_POL_ACCEPT;
+	return MSC_CONN_ACCEPT;
 
 #if 0
 	/*
@@ -105,14 +107,14 @@
 	 * pending transaction or ongoing operation.
 	 */
 	if (conn->silent_call)
-		return BSC_API_CONN_POL_ACCEPT;
-	if (conn->sec_operation || conn->anch_operation)
-		return BSC_API_CONN_POL_ACCEPT;
+		return MSC_CONN_ACCEPT;
+	if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
+		return MSC_CONN_ACCEPT;
 	if (trans_has_conn(conn))
-		return BSC_API_CONN_POL_ACCEPT;
+		return MSC_CONN_ACCEPT;
 
 	LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n");
-	return BSC_API_CONN_POL_REJECT;
+	return MSC_CONN_REJECT;
 #endif
 }
 
diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c
index 6f3fbf2..76816c2 100644
--- a/openbsc/src/libmsc/silent_call.c
+++ b/openbsc/src/libmsc/silent_call.c
@@ -52,8 +52,10 @@
 
 	switch (event) {
 	case GSM_PAGING_SUCCEEDED:
+#if BEFORE_MSCSPLIT
 		DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n",
 			conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
+#endif
 		conn->silent_call = 1;
 		msc_subscr_conn_get(conn);
 		/* increment lchan reference count */
@@ -126,7 +128,10 @@
 {
 	struct subscr_request *req;
 
-	req = subscr_request_channel(vsub, type, paging_cb_silent, data);
+	/* FIXME the VTY command allows selecting a silent call channel type.
+	 * This doesn't apply to the situation after MSCSPLIT with an
+	 * A-interface. */
+	req = subscr_request_conn(vsub, type, paging_cb_silent, data);
 	return req != NULL;
 }
 
@@ -143,8 +148,10 @@
 	if (!conn->silent_call)
 		return -EINVAL;
 
+#if BEFORE_MSCSPLIT
 	DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n",
 		conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
+#endif
 
 	conn->silent_call = 0;
 	msc_subscr_conn_put(conn);
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index 8281071..d232cff 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -422,6 +422,7 @@
 	build_tlv(req_tlv, &tlv);
 }
 
+#if BEFORE_MSCSPLIT
 /* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */
 static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan)
 {
@@ -460,6 +461,7 @@
 				   (uint8_t *)vsub->imei, imei_len+1);
 	}
 }
+#endif
 
 static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
 			   struct gsm_subscriber_connection *conn)
@@ -535,8 +537,10 @@
 		memcpy(deliver.short_message, sms->user_data, deliver.sm_length);
 	}
 
+#if BEFORE_MSCSPLIT
 	if (esme->acl && esme->acl->osmocom_ext && conn->lchan)
 		append_osmo_tlvs(&deliver.tlv, conn->lchan);
+#endif
 
 	return smpp_tx_deliver(esme, &deliver);
 }
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index faadbb1..df2d1e4 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -559,6 +559,7 @@
 	SUBSCR_HELP "Handover the active connection\n"
 	"Number of the BTS to handover to\n")
 {
+#if BEFORE_MSCSPLIT
 	int ret;
 	struct gsm_subscriber_connection *conn;
 	struct gsm_bts *bts;
@@ -602,6 +603,10 @@
 
 	vlr_subscr_put(vsub);
 	return CMD_SUCCESS;
+#else
+	vty_out(vty, "%% Not implemented!%s", VTY_NEWLINE);
+	return -1;
+#endif
 }
 
 #define A3A8_ALG_TYPES "(none|xor|comp128v1)"
@@ -647,6 +652,7 @@
 static int scall_cbfn(unsigned int subsys, unsigned int signal,
 			void *handler_data, void *signal_data)
 {
+#if BEFORE_MSCSPLIT
 	struct scall_signal_data *sigdata = signal_data;
 	struct vty *vty = sigdata->data;
 
@@ -661,6 +667,10 @@
 		break;
 	}
 	return 0;
+#else
+	/* not implemented yet! */
+	return -1;
+#endif
 }
 
 DEFUN(show_stats,
@@ -670,7 +680,11 @@
 {
 	struct gsm_network *net = gsmnet_from_vty(vty);
 
+#if 0
+	TODO implement statistics specifically for libmsc!
+	Excluding this to be able to link without libbsc:
 	openbsc_vty_print_statistics(vty, net);
+#endif
 	vty_out(vty, "Location Update         : %lu attach, %lu normal, %lu periodic%s",
 		net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH].current,
 		net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL].current,
diff --git a/openbsc/tests/bsc/Makefile.am b/openbsc/tests/bsc/Makefile.am
index 9de4145..904bdfc 100644
--- a/openbsc/tests/bsc/Makefile.am
+++ b/openbsc/tests/bsc/Makefile.am
@@ -32,7 +32,6 @@
 
 bsc_test_LDADD = \
 	$(top_builddir)/src/libbsc/libbsc.a \
-	$(top_builddir)/src/libmsc/libmsc.a \
 	$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
 	$(top_builddir)/src/libmgcp/libmgcp.a \
 	$(top_builddir)/src/libtrau/libtrau.a \
diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am
index c7164b4..dd78bdc 100644
--- a/openbsc/tests/channel/Makefile.am
+++ b/openbsc/tests/channel/Makefile.am
@@ -24,7 +24,6 @@
 	$(NULL)
 
 channel_test_LDADD = \
-	$(top_builddir)/src/libmsc/libmsc.a \
 	$(top_builddir)/src/libbsc/libbsc.a \
 	$(top_builddir)/src/libvlr/libvlr.a \
 	$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am
index 0eed5cd..df421d8 100644
--- a/openbsc/tests/db/Makefile.am
+++ b/openbsc/tests/db/Makefile.am
@@ -32,9 +32,7 @@
 	$(NULL)
 
 db_test_LDADD = \
-	$(top_builddir)/src/libbsc/libbsc.a \
 	$(top_builddir)/src/libmsc/libmsc.a \
-	$(top_builddir)/src/libbsc/libbsc.a \
 	$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
 	$(top_builddir)/src/libtrau/libtrau.a \
 	$(top_builddir)/src/libcommon/libcommon.a \
diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c
index 81dc177..01b7bf5 100644
--- a/openbsc/tests/gsm0408/gsm0408_test.c
+++ b/openbsc/tests/gsm0408/gsm0408_test.c
@@ -157,7 +157,7 @@
 static inline void test_si2q_u(void)
 {
 	struct gsm_bts *bts;
-	struct gsm_network *network = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
+	struct gsm_network *network = bsc_network_init(NULL, 1, 1, NULL);
 	printf("Testing SYSINFO_TYPE_2quater UARFCN generation:\n");
 
 	if (!network)
@@ -184,7 +184,7 @@
 static inline void test_si2q_e(void)
 {
 	struct gsm_bts *bts;
-	struct gsm_network *network = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
+	struct gsm_network *network = bsc_network_init(NULL, 1, 1, NULL);
 	printf("Testing SYSINFO_TYPE_2quater EARFCN generation:\n");
 
 	if (!network)

-- 
To view, visit https://gerrit.osmocom.org/2567
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9cf80f9c2c8a53a29e42f000029e680a9922cb41
Gerrit-PatchSet: 1
Gerrit-Project: openbsc
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list