Move struct gsm_bts: gsm_data.* => bts.*

Place all code related to the object into the related file.

Having all the data model in one file made sense in early stage of
development to make progress quickly, but nowadays it hurts more than
helps, due to constantly growing size and more and more bits being
added to the model, gaining in complexity.

Currently, having lots of different objects mixed up in gsm_data.h is a hole
of despair, where nobody can make any sense were to properly put new stuff
in, ending up with functions related to same object in different files
or with wrong prefixes, declarations of non-existing functions, etc.
because people cannot make up their mind on strict relation to objects
in the data model.
Splitting them in files really helps finding code operating on a
specific object and helping with logically splitting in the future.

Change-Id: I00c15f5285b5c1a0109279b7ab192d5467a04ece
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index 145ea39..273530c 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -46,6 +46,7 @@
 
 # FIXME: resolve the bogus dependencies patched around here:
 ipaccess_config_LDADD = \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/abis_nm.o \
 	$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \
 	$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
@@ -57,9 +58,10 @@
 ipaccess_proxy_SOURCES = \
 	ipaccess-proxy.c \
 	stubs.c \
-	$(top_srcdir)/src/osmo-bsc/gsm_data.c \
 	$(NULL)
 
 ipaccess_proxy_LDADD = \
+	$(top_builddir)/src/osmo-bsc/bts.o \
+	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(OSMO_LIBS) \
 	$(NULL)
diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c
index f7d7332..34ad57f 100644
--- a/src/ipaccess/ipaccess-config.c
+++ b/src/ipaccess/ipaccess-config.c
@@ -56,6 +56,7 @@
 #include <osmocom/abis/abis.h>
 #include <osmocom/gsm/protocol/gsm_12_21.h>
 #include <osmocom/bsc/bss.h>
+#include <osmocom/bsc/bts.h>
 
 struct gsm_network *bsc_gsmnet;
 
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index 0665af6..f66421a 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -43,6 +43,7 @@
 	bsc_subscr_conn_fsm.c \
 	bsc_subscriber.c \
 	bsc_vty.c \
+	bts.c \
 	bts_ericsson_rbs2000.c \
 	bts_init.c \
 	bts_ipaccess_nanobts.c \
diff --git a/src/osmo-bsc/abis_nm.c b/src/osmo-bsc/abis_nm.c
index 8dde8de..4ce27f3 100644
--- a/src/osmo-bsc/abis_nm.c
+++ b/src/osmo-bsc/abis_nm.c
@@ -48,6 +48,7 @@
 #include <osmocom/bsc/signal.h>
 #include <osmocom/abis/e1_input.h>
 #include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/gsm/bts_features.h>
 
 #define OM_ALLOC_SIZE		1024
@@ -853,14 +854,6 @@
 	return true;
 }
 
-char *get_model_oml_status(const struct gsm_bts *bts)
-{
-	if (bts->model->oml_status)
-		return bts->model->oml_status(bts);
-
-	return "unknown";
-}
-
 void abis_nm_queue_send_next(struct gsm_bts *bts)
 {
 	int wait = 0;
diff --git a/src/osmo-bsc/abis_om2000.c b/src/osmo-bsc/abis_om2000.c
index 0aea684..1e4ef97 100644
--- a/src/osmo-bsc/abis_om2000.c
+++ b/src/osmo-bsc/abis_om2000.c
@@ -44,6 +44,7 @@
 #include <osmocom/bsc/abis_om2000.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/abis/e1_input.h>
 
 /* FIXME: move to libosmocore */
diff --git a/src/osmo-bsc/abis_om2000_vty.c b/src/osmo-bsc/abis_om2000_vty.c
index 222546f..b00c7dd 100644
--- a/src/osmo-bsc/abis_om2000_vty.c
+++ b/src/osmo-bsc/abis_om2000_vty.c
@@ -33,6 +33,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/abis_om2000.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/bsc/vty.h>
 
 #include <osmocom/vty/vty.h>
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index f6e564b..1ffdd06 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -54,7 +54,7 @@
 #include <osmocom/bsc/lchan_rtp_fsm.h>
 #include <osmocom/bsc/handover_fsm.h>
 #include <osmocom/bsc/smscb.h>
-
+#include <osmocom/bsc/bts.h>
 #define RSL_ALLOC_SIZE		1024
 #define RSL_ALLOC_HEADROOM	128
 
diff --git a/src/osmo-bsc/acc_ramp.c b/src/osmo-bsc/acc_ramp.c
index b79c0c2..761ab09 100644
--- a/src/osmo-bsc/acc_ramp.c
+++ b/src/osmo-bsc/acc_ramp.c
@@ -29,6 +29,7 @@
 #include <osmocom/bsc/chan_alloc.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
 
 /*
  * Check if an ACC has been permanently barred for a BTS,
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index cd5abc8..ca29daa 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -34,6 +34,7 @@
 #include <osmocom/bsc/gsm_08_08.h>
 #include <osmocom/bsc/lchan_select.h>
 #include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/bsc/assignment_fsm.h>
 
diff --git a/src/osmo-bsc/bsc_ctrl_commands.c b/src/osmo-bsc/bsc_ctrl_commands.c
index 774ded2..9383167 100644
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ b/src/osmo-bsc/bsc_ctrl_commands.c
@@ -30,6 +30,7 @@
 #include <osmocom/bsc/chan_alloc.h>
 #include <osmocom/bsc/osmo_bsc_rf.h>
 #include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
 
 CTRL_CMD_DEFINE(net_mcc, "mcc");
 static int get_net_mcc(struct ctrl_cmd *cmd, void *_data)
diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c
index 7333533..4fc0ab1 100644
--- a/src/osmo-bsc/bsc_init.c
+++ b/src/osmo-bsc/bsc_init.c
@@ -36,6 +36,7 @@
 #include <osmocom/bsc/handover_cfg.h>
 #include <osmocom/bsc/gsm_04_08_rr.h>
 #include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/bsc/smscb.h>
 #include <osmocom/gsm/protocol/gsm_48_049.h>
@@ -68,24 +69,6 @@
 	return 0;
 }
 
-unsigned long long bts_uptime(const struct gsm_bts *bts)
-{
-	struct timespec tp;
-
-	if (!bts->uptime || !bts->oml_link) {
-		LOGP(DNM, LOGL_ERROR, "BTS %u OML link uptime unavailable\n", bts->nr);
-		return 0;
-	}
-
-	if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
-		LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno));
-		return 0;
-	}
-
-	/* monotonic clock helps to ensure that the conversion is valid */
-	return difftime(tp.tv_sec, bts->uptime);
-}
-
 static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
 {
 	struct gsm_bts *bts = trx->bts;
diff --git a/src/osmo-bsc/bsc_rf_ctrl.c b/src/osmo-bsc/bsc_rf_ctrl.c
index c72154a..1e04f21 100644
--- a/src/osmo-bsc/bsc_rf_ctrl.c
+++ b/src/osmo-bsc/bsc_rf_ctrl.c
@@ -26,6 +26,7 @@
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/ipaccess.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/utils.h>
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 36b16a2..aa08e8b 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -73,6 +73,7 @@
 #include <osmocom/bsc/lchan_select.h>
 #include <osmocom/bsc/smscb.h>
 #include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
 
 #include <inttypes.h>
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c
new file mode 100644
index 0000000..71968f6
--- /dev/null
+++ b/src/osmo-bsc/bts.c
@@ -0,0 +1,748 @@
+/* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@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/gsm/abis_nm.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/debug.h>
+
+const struct value_string bts_attribute_names[] = {
+	OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
+	OSMO_VALUE_STRING(BTS_SUB_MODEL),
+	OSMO_VALUE_STRING(TRX_PHY_VERSION),
+	{ 0, NULL }
+};
+
+enum bts_attribute str2btsattr(const char *s)
+{
+	return get_string_value(bts_attribute_names, s);
+}
+
+const char *btsatttr2str(enum bts_attribute v)
+{
+	return get_value_string(bts_attribute_names, v);
+}
+
+const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
+	{ BTS_UNKNOWN,		"unknown" },
+	{ BTS_OSMO_LITECELL15,	"osmo-bts-lc15" },
+	{ BTS_OSMO_OCTPHY,	"osmo-bts-octphy" },
+	{ BTS_OSMO_SYSMO,	"osmo-bts-sysmo" },
+	{ BTS_OSMO_TRX,		"omso-bts-trx" },
+	{ 0, NULL }
+};
+
+enum gsm_bts_type_variant str2btsvariant(const char *arg)
+{
+	return get_string_value(osmo_bts_variant_names, arg);
+}
+
+const char *btsvariant2str(enum gsm_bts_type_variant v)
+{
+	return get_value_string(osmo_bts_variant_names, v);
+}
+
+const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
+	{ GSM_BTS_TYPE_UNKNOWN,		"unknown" },
+	{ GSM_BTS_TYPE_BS11,		"bs11" },
+	{ GSM_BTS_TYPE_NANOBTS,		"nanobts" },
+	{ GSM_BTS_TYPE_RBS2000,		"rbs2000" },
+	{ GSM_BTS_TYPE_NOKIA_SITE,	"nokia_site" },
+	{ GSM_BTS_TYPE_OSMOBTS,		"sysmobts" },
+	{ 0, NULL }
+};
+
+const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
+	{ GSM_BTS_TYPE_UNKNOWN,		"Unknown BTS Type" },
+	{ GSM_BTS_TYPE_BS11,		"Siemens BTS (BS-11 or compatible)" },
+	{ GSM_BTS_TYPE_NANOBTS,		"ip.access nanoBTS or compatible" },
+	{ GSM_BTS_TYPE_RBS2000,		"Ericsson RBS2000 Series" },
+	{ GSM_BTS_TYPE_NOKIA_SITE,	"Nokia {Metro,Ultra,In}Site" },
+	{ GSM_BTS_TYPE_OSMOBTS,		"sysmocom sysmoBTS" },
+	{ 0,				NULL }
+};
+
+enum gsm_bts_type str2btstype(const char *arg)
+{
+	return get_string_value(bts_type_names, arg);
+}
+
+const char *btstype2str(enum gsm_bts_type type)
+{
+	return get_value_string(bts_type_names, type);
+}
+
+static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)
+{
+	cstate->bts = bts;
+	INIT_LLIST_HEAD(&cstate->messages);
+}
+
+static LLIST_HEAD(bts_models);
+
+struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
+{
+	struct gsm_bts_model *model;
+
+	llist_for_each_entry(model, &bts_models, list) {
+		if (model->type == type)
+			return model;
+	}
+
+	return NULL;
+}
+
+int gsm_bts_model_register(struct gsm_bts_model *model)
+{
+	if (bts_model_find(model->type))
+		return -EEXIST;
+
+	tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef);
+	llist_add_tail(&model->list, &bts_models);
+	return 0;
+}
+
+static const struct osmo_stat_item_desc bts_stat_desc[] = {
+	[BTS_STAT_CHAN_LOAD_AVERAGE] =			{ "chanloadavg", "Channel load average", "%", 16, 0 },
+	[BTS_STAT_CHAN_CCCH_SDCCH4_USED] =		{ "chan_ccch_sdcch4:used",
+							  "Number of CCCH+SDCCH4 channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL] =		{ "chan_ccch_sdcch4:total",
+							  "Number of CCCH+SDCCH4 channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_USED] =			{ "chan_tch_f:used",
+							  "Number of TCH/F channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_TOTAL] =			{ "chan_tch_f:total",
+							  "Number of TCH/F channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_H_USED] =			{ "chan_tch_h:used",
+							  "Number of TCH/H channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_H_TOTAL] =			{ "chan_tch_h:total",
+							  "Number of TCH/H channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_SDCCH8_USED] =			{ "chan_sdcch8:used",
+							  "Number of SDCCH8 channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_SDCCH8_TOTAL] =			{ "chan_sdcch8:total",
+							  "Number of SDCCH8 channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_PDCH_USED] =		{ "chan_tch_f_pdch:used",
+							  "Number of TCH/F_PDCH channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] =		{ "chan_tch_f_pdch:total",
+							  "Number of TCH/F_PDCH channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED] =		{ "chan_ccch_sdcch4_cbch:used",
+							  "Number of CCCH+SDCCH4+CBCH channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL] =	{ "chan_ccch_sdcch4_cbch:total",
+							  "Number of CCCH+SDCCH4+CBCH channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_SDCCH8_CBCH_USED] =		{ "chan_sdcch8_cbch:used",
+							  "Number of SDCCH8+CBCH channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL] =		{ "chan_sdcch8_cbch:total",
+							  "Number of SDCCH8+CBCH channels total", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED] =		{ "chan_tch_f_tch_h_pdch:used",
+							  "Number of TCH/F_TCH/H_PDCH channels used", "", 16, 0 },
+	[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL] =	{ "chan_tch_f_tch_h_pdch:total",
+							  "Number of TCH/F_TCH/H_PDCH channels total", "", 16, 0 },
+	[BTS_STAT_T3122] =				{ "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator",
+							  "s", 16, GSM_T3122_DEFAULT },
+	[BTS_STAT_RACH_BUSY] =				{ "rach_busy",
+							  "RACH slots with signal above threshold", "%", 16, 0 },
+	[BTS_STAT_RACH_ACCESS] =			{ "rach_access",
+							  "RACH slots with access bursts in them", "%", 16, 0 },
+	[BTS_STAT_OML_CONNECTED] =			{ "oml_connected", "Number of OML links connected", "", 16, 0 },
+	[BTS_STAT_RSL_CONNECTED] =			{ "rsl_connected", "Number of RSL links connected", "", 16, 0 },
+	[BTS_STAT_LCHAN_BORKEN] =			{ "lchan_borken",
+							  "Number of lchans in the BORKEN state", "", 16, 0 },
+	[BTS_STAT_TS_BORKEN] =				{ "ts_borken",
+							  "Number of timeslots in the BORKEN state", "", 16, 0 },
+};
+
+static const struct osmo_stat_item_group_desc bts_statg_desc = {
+	.group_name_prefix = "bts",
+	.group_description = "base transceiver station",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_items = ARRAY_SIZE(bts_stat_desc),
+	.item_desc = bts_stat_desc,
+};
+
+static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
+static const uint8_t bts_cell_timer_default[] =
+				{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
+static const struct gprs_rlc_cfg rlc_cfg_default = {
+	.parameter = {
+		[RLC_T3142] = 20,
+		[RLC_T3169] = 5,
+		[RLC_T3191] = 5,
+		[RLC_T3193] = 160, /* 10ms */
+		[RLC_T3195] = 5,
+		[RLC_N3101] = 10,
+		[RLC_N3103] = 4,
+		[RLC_N3105] = 8,
+		[CV_COUNTDOWN] = 15,
+		[T_DL_TBF_EXT] = 250 * 10, /* ms */
+		[T_UL_TBF_EXT] = 250 * 10, /* ms */
+	},
+	.paging = {
+		.repeat_time = 5 * 50, /* ms */
+		.repeat_count = 3,
+	},
+	.cs_mask = 0x1fff,
+	.initial_cs = 2,
+	.initial_mcs = 6,
+};
+
+/* Initialize those parts that don't require osmo-bsc specific dependencies.
+ * This part is shared among the thin programs in osmo-bsc/src/utils/.
+ * osmo-bsc requires further initialization that pulls in more dependencies (see
+ * bsc_bts_alloc_register()). */
+struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
+{
+	struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
+	struct gsm48_multi_rate_conf mr_cfg;
+	int i;
+
+	if (!bts)
+		return NULL;
+
+	bts->nr = bts_num;
+	bts->num_trx = 0;
+	INIT_LLIST_HEAD(&bts->trx_list);
+	bts->network = net;
+
+	bts->ms_max_power = 15;	/* dBm */
+
+	gsm_mo_init(&bts->mo, bts, NM_OC_BTS,
+			bts->nr, 0xff, 0xff);
+	gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER,
+			0xff, 0xff, 0xff);
+
+	for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
+		bts->gprs.nsvc[i].bts = bts;
+		bts->gprs.nsvc[i].id = i;
+		gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC,
+				bts->nr, i, 0xff);
+	}
+	memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
+		sizeof(bts->gprs.nse.timer));
+	gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE,
+			bts->nr, 0xff, 0xff);
+	memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
+		sizeof(bts->gprs.cell.timer));
+	gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL,
+			bts->nr, 0xff, 0xff);
+	memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default,
+		sizeof(bts->gprs.cell.rlc_cfg));
+
+	/* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */
+	bts->gprs.cell.bvci = 2;
+
+	/* init statistics */
+	bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr);
+	if (!bts->bts_ctrs) {
+		talloc_free(bts);
+		return NULL;
+	}
+	bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr);
+
+	/* create our primary TRX */
+	bts->c0 = gsm_bts_trx_alloc(bts);
+	if (!bts->c0) {
+		rate_ctr_group_free(bts->bts_ctrs);
+		osmo_stat_item_group_free(bts->bts_statg);
+		talloc_free(bts);
+		return NULL;
+	}
+	bts->c0->ts[0].pchan_from_config = GSM_PCHAN_CCCH_SDCCH4; /* TODO: really?? */
+
+	bts->ccch_load_ind_thresh = 10; /* 10% of Load: Start sending CCCH LOAD IND */
+	bts->rach_b_thresh = -1;
+	bts->rach_ldavg_slots = -1;
+
+	bts->paging.free_chans_need = -1;
+	INIT_LLIST_HEAD(&bts->paging.pending_requests);
+
+	bts->features.data = &bts->_features_data[0];
+	bts->features.data_len = sizeof(bts->_features_data);
+
+	/* si handling */
+	bts->bcch_change_mark = 1;
+	bts->chan_load_avg = 0;
+
+	/* timer overrides */
+	bts->T3122 = 0; /* not overridden by default */
+	bts->T3113_dynamic = true; /* dynamic by default */
+
+	bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
+	bts->dtxd = false;
+	bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */
+	bts->neigh_list_manual_mode = NL_MODE_AUTOMATIC;
+	bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */
+	bts->si_unused_send_empty = true;
+	bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
+	bts->si_common.cell_sel_par.rxlev_acc_min = 0;
+	bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list;
+	bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list;
+	bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST;
+	bts->si_common.si2quater_neigh_list.thresh_hi = 0;
+	osmo_earfcn_init(&bts->si_common.si2quater_neigh_list);
+	bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
+	bts->si_common.neigh_list.data_len =
+				sizeof(bts->si_common.data.neigh_list);
+	bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list;
+	bts->si_common.si5_neigh_list.data_len =
+				sizeof(bts->si_common.data.si5_neigh_list);
+	bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
+	bts->si_common.cell_alloc.data_len =
+				sizeof(bts->si_common.data.cell_alloc);
+	bts->si_common.rach_control.re = 1; /* no re-establishment */
+	bts->si_common.rach_control.tx_integer = 9;  /* 12 slots spread - 217/115 slots delay */
+	bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
+	bts->si_common.rach_control.t2 = 4; /* no emergency calls */
+	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 = osmo_tdef_get(net->T_defs, 3212, OSMO_TDEF_CUSTOM, -1);
+	gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
+
+	INIT_LLIST_HEAD(&bts->abis_queue);
+	INIT_LLIST_HEAD(&bts->loc_list);
+	INIT_LLIST_HEAD(&bts->local_neighbors);
+	INIT_LLIST_HEAD(&bts->oml_fail_rep);
+
+	/* Enable all codecs by default. These get reset to a more fine grained selection IF a
+	 * 'codec-support' config appears in the config file (see bsc_vty.c). */
+	bts->codec = (struct bts_codec_conf){
+		.hr = 1,
+		.efr = 1,
+		.amr = 1,
+	};
+
+	/* Set reasonable defaults for AMR-FR and AMR-HR rate configuration.
+	 * (see also 3GPP TS 28.062, Table 7.11.3.1.3-2) */
+	mr_cfg = (struct gsm48_multi_rate_conf) {
+		.m4_75 = 1,
+		.m5_15 = 0,
+		.m5_90 = 1,
+		.m6_70 = 0,
+		.m7_40 = 1,
+		.m7_95 = 0,
+		.m10_2 = 0,
+		.m12_2 = 1
+	};
+	memcpy(bts->mr_full.gsm48_ie, &mr_cfg, sizeof(bts->mr_full.gsm48_ie));
+	bts->mr_full.ms_mode[0].mode = 0;
+	bts->mr_full.ms_mode[1].mode = 2;
+	bts->mr_full.ms_mode[2].mode = 4;
+	bts->mr_full.ms_mode[3].mode = 7;
+	bts->mr_full.bts_mode[0].mode = 0;
+	bts->mr_full.bts_mode[1].mode = 2;
+	bts->mr_full.bts_mode[2].mode = 4;
+	bts->mr_full.bts_mode[3].mode = 7;
+	for (i = 0; i < 3; i++) {
+		bts->mr_full.ms_mode[i].hysteresis = 8;
+		bts->mr_full.ms_mode[i].threshold = 32;
+		bts->mr_full.bts_mode[i].hysteresis = 8;
+		bts->mr_full.bts_mode[i].threshold = 32;
+	}
+	bts->mr_full.num_modes = 4;
+
+	mr_cfg = (struct gsm48_multi_rate_conf) {
+		.m4_75 = 1,
+		.m5_15 = 0,
+		.m5_90 = 1,
+		.m6_70 = 0,
+		.m7_40 = 1,
+		.m7_95 = 0,
+		.m10_2 = 0,
+		.m12_2 = 0
+	};
+	memcpy(bts->mr_half.gsm48_ie, &mr_cfg, sizeof(bts->mr_half.gsm48_ie));
+	bts->mr_half.ms_mode[0].mode = 0;
+	bts->mr_half.ms_mode[1].mode = 2;
+	bts->mr_half.ms_mode[2].mode = 4;
+	bts->mr_half.ms_mode[3].mode = 7;
+	bts->mr_half.bts_mode[0].mode = 0;
+	bts->mr_half.bts_mode[1].mode = 2;
+	bts->mr_half.bts_mode[2].mode = 4;
+	bts->mr_half.bts_mode[3].mode = 7;
+	for (i = 0; i < 3; i++) {
+		bts->mr_half.ms_mode[i].hysteresis = 8;
+		bts->mr_half.ms_mode[i].threshold = 32;
+		bts->mr_half.bts_mode[i].hysteresis = 8;
+		bts->mr_half.bts_mode[i].threshold = 32;
+	}
+	bts->mr_half.num_modes = 3;
+
+	bts_init_cbch_state(&bts->cbch_basic, bts);
+	bts_init_cbch_state(&bts->cbch_extended, bts);
+
+	return bts;
+}
+
+static char ts2str[255];
+
+char *gsm_bts_name(const struct gsm_bts *bts)
+{
+	if (!bts)
+		snprintf(ts2str, sizeof(ts2str), "(bts=NULL)");
+	else
+		snprintf(ts2str, sizeof(ts2str), "(bts=%d)", bts->nr);
+
+	return ts2str;
+}
+
+bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai)
+{
+	return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0
+		&& lai->lac == bts->location_area_code;
+}
+
+bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id)
+{
+	const union gsm0808_cell_id_u *id = &cell_id->id;
+	if (!bts || !cell_id)
+		return false;
+
+	switch (cell_id->id_discr) {
+	case CELL_IDENT_WHOLE_GLOBAL:
+		return gsm_bts_matches_lai(bts, &id->global.lai)
+			&& id->global.cell_identity == bts->cell_identity;
+	case CELL_IDENT_LAC_AND_CI:
+		return id->lac_and_ci.lac == bts->location_area_code
+			&& id->lac_and_ci.ci == bts->cell_identity;
+	case CELL_IDENT_CI:
+		return id->ci == bts->cell_identity;
+	case CELL_IDENT_NO_CELL:
+		return false;
+	case CELL_IDENT_LAI_AND_LAC:
+		return gsm_bts_matches_lai(bts, &id->lai_and_lac);
+	case CELL_IDENT_LAC:
+		return id->lac == bts->location_area_code;
+	case CELL_IDENT_BSS:
+		return true;
+	case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
+	case CELL_IDENT_UTRAN_RNC:
+	case CELL_IDENT_UTRAN_LAC_RNC:
+		return false;
+	default:
+		OSMO_ASSERT(false);
+	}
+}
+
+static struct gsm_bts_ref *gsm_bts_ref_find(const struct llist_head *list, const struct gsm_bts *bts)
+{
+	struct gsm_bts_ref *ref;
+	if (!bts)
+		return NULL;
+	llist_for_each_entry(ref, list, entry) {
+		if (ref->bts == bts)
+			return ref;
+	}
+	return NULL;
+}
+
+/* Add a BTS reference to the local_neighbors list.
+ * Return 1 if added, 0 if such an entry already existed, and negative on errors. */
+int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor)
+{
+	struct gsm_bts_ref *ref;
+	if (!bts || !neighbor)
+		return -ENOMEM;
+
+	if (bts == neighbor)
+		return -EINVAL;
+
+	/* Already got this entry? */
+	ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
+	if (ref)
+		return 0;
+
+	ref = talloc_zero(bts, struct gsm_bts_ref);
+	if (!ref)
+		return -ENOMEM;
+	ref->bts = neighbor;
+	llist_add_tail(&ref->entry, &bts->local_neighbors);
+	return 1;
+}
+
+/* Remove a BTS reference from the local_neighbors list.
+ * Return 1 if removed, 0 if no such entry existed, and negative on errors. */
+int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor)
+{
+	struct gsm_bts_ref *ref;
+	if (!bts || !neighbor)
+		return -ENOMEM;
+
+	ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
+	if (!ref)
+		return 0;
+
+	llist_del(&ref->entry);
+	talloc_free(ref);
+	return 1;
+}
+
+/* return the gsm_lchan for the CBCH (if it exists at all) */
+struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts)
+{
+	struct gsm_lchan *lchan = NULL;
+	struct gsm_bts_trx *trx = bts->c0;
+
+	if (trx->ts[0].pchan_from_config == GSM_PCHAN_CCCH_SDCCH4_CBCH)
+		lchan = &trx->ts[0].lchan[2];
+	else {
+		int i;
+		for (i = 0; i < 8; i++) {
+			if (trx->ts[i].pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) {
+				lchan = &trx->ts[i].lchan[2];
+				break;
+			}
+		}
+	}
+
+	return lchan;
+}
+
+int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
+{
+	struct gsm_bts_model *model;
+
+	model = bts_model_find(type);
+	if (!model)
+		return -EINVAL;
+
+	bts->type = type;
+	bts->model = model;
+
+	if (model->start && !model->started) {
+		int ret = model->start(bts->network);
+		if (ret < 0)
+			return ret;
+
+		model->started = true;
+	}
+
+	switch (bts->type) {
+	case GSM_BTS_TYPE_NANOBTS:
+	case GSM_BTS_TYPE_OSMOBTS:
+		/* Set the default OML Stream ID to 0xff */
+		bts->oml_tei = 0xff;
+		bts->c0->nominal_power = 23;
+		break;
+	case GSM_BTS_TYPE_RBS2000:
+		INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups);
+		INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups);
+		break;
+	case GSM_BTS_TYPE_BS11:
+	case GSM_BTS_TYPE_UNKNOWN:
+	case GSM_BTS_TYPE_NOKIA_SITE:
+		/* Set default BTS reset timer */
+		bts->nokia.bts_reset_timer_cnf = 15;
+	case _NUM_GSM_BTS_TYPE:
+		break;
+	}
+
+	return 0;
+}
+
+int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode)
+{
+	if (mode != BTS_GPRS_NONE &&
+	    !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_GPRS)) {
+		return 0;
+	}
+	if (mode == BTS_GPRS_EGPRS &&
+	    !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) {
+		return 0;
+	}
+
+	return 1;
+}
+
+struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
+{
+	struct gsm_bts_trx *trx;
+
+	llist_for_each_entry(trx, &bts->trx_list, list) {
+		if (trx->nr == nr)
+			return trx;
+	}
+	return NULL;
+}
+
+unsigned long long bts_uptime(const struct gsm_bts *bts)
+{
+	struct timespec tp;
+
+	if (!bts->uptime || !bts->oml_link) {
+		LOGP(DNM, LOGL_ERROR, "BTS %u OML link uptime unavailable\n", bts->nr);
+		return 0;
+	}
+
+	if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
+		LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno));
+		return 0;
+	}
+
+	/* monotonic clock helps to ensure that the conversion is valid */
+	return difftime(tp.tv_sec, bts->uptime);
+}
+
+char *get_model_oml_status(const struct gsm_bts *bts)
+{
+	if (bts->model->oml_status)
+		return bts->model->oml_status(bts);
+
+	return "unknown";
+}
+
+/* reset the state of all MO in the BTS */
+void gsm_bts_mo_reset(struct gsm_bts *bts)
+{
+	struct gsm_bts_trx *trx;
+	unsigned int i;
+
+	gsm_abis_mo_reset(&bts->mo);
+	gsm_abis_mo_reset(&bts->site_mgr.mo);
+	for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++)
+		gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo);
+	gsm_abis_mo_reset(&bts->gprs.nse.mo);
+	gsm_abis_mo_reset(&bts->gprs.cell.mo);
+
+	llist_for_each_entry(trx, &bts->trx_list, list) {
+		gsm_abis_mo_reset(&trx->mo);
+		gsm_abis_mo_reset(&trx->bb_transc.mo);
+
+		for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+			struct gsm_bts_trx_ts *ts = &trx->ts[i];
+			gsm_abis_mo_reset(&ts->mo);
+		}
+	}
+}
+
+/* Assume there are only 256 possible bts */
+osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
+static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
+{
+	*idx = bts_nr / (8 * 4);
+	*bit = bts_nr % (8 * 4);
+}
+
+void bts_depend_mark(struct gsm_bts *bts, int dep)
+{
+	int idx, bit;
+	depends_calc_index_bit(dep, &idx, &bit);
+
+	bts->depends_on[idx] |= 1 << bit;
+}
+
+void bts_depend_clear(struct gsm_bts *bts, int dep)
+{
+	int idx, bit;
+	depends_calc_index_bit(dep, &idx, &bit);
+
+	bts->depends_on[idx] &= ~(1 << bit);
+}
+
+int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
+{
+	int idx, bit;
+	depends_calc_index_bit(other->nr, &idx, &bit);
+
+	/* Check if there is a depends bit */
+	return (base->depends_on[idx] & (1 << bit)) > 0;
+}
+
+static int bts_is_online(struct gsm_bts *bts)
+{
+	/* TODO: support E1 BTS too */
+	if (!is_ipaccess_bts(bts))
+		return 1;
+
+	if (!bts->oml_link)
+		return 0;
+
+	return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
+}
+
+int bts_depend_check(struct gsm_bts *bts)
+{
+	struct gsm_bts *other_bts;
+
+	llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
+		if (!bts_depend_is_depedency(bts, other_bts))
+			continue;
+		if (bts_is_online(other_bts))
+			continue;
+		return 0;
+	}
+	return 1;
+}
+
+/* get the radio link timeout (based on SACCH decode errors, according
+ * to algorithm specified in TS 05.08 section 5.2.  A value of -1
+ * indicates we should use an infinitely long timeout, which only works
+ * with OsmoBTS as the BTS implementation */
+int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts)
+{
+	const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
+
+	if (bts->infinite_radio_link_timeout)
+		return -1;
+	else {
+		/* Encoding as per Table 10.5.21 of TS 04.08 */
+		return (cell_options->radio_link_timeout + 1) << 2;
+	}
+}
+
+/* set the radio link timeout (based on SACCH decode errors, according
+ * to algorithm specified in TS 05.08 Section 5.2.  A value of -1
+ * indicates we should use an infinitely long timeout, which only works
+ * with OsmoBTS as the BTS implementation */
+void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
+{
+	struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
+
+	if (value < 0)
+		bts->infinite_radio_link_timeout = true;
+	else {
+		bts->infinite_radio_link_timeout = false;
+		/* Encoding as per Table 10.5.21 of TS 04.08 */
+		if (value < 4)
+			value = 4;
+		if (value > 64)
+			value = 64;
+		cell_options->radio_link_timeout = (value >> 2) - 1;
+	}
+}
+
+void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
+{
+	struct gsm_bts_trx *trx;
+	llist_for_each_entry(trx, &bts->trx_list, list)
+		gsm_trx_all_ts_dispatch(trx, ts_ev, data);
+}
+
+
+/* Count number of free TS of given pchan type */
+int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
+{
+	struct gsm_bts_trx *trx;
+	int count = 0;
+
+	llist_for_each_entry(trx, &bts->trx_list, list)
+		count += trx_count_free_ts(trx, pchan);
+
+	return count;
+}
diff --git a/src/osmo-bsc/bts_ericsson_rbs2000.c b/src/osmo-bsc/bts_ericsson_rbs2000.c
index 02ef463..1297b30 100644
--- a/src/osmo-bsc/bts_ericsson_rbs2000.c
+++ b/src/osmo-bsc/bts_ericsson_rbs2000.c
@@ -29,6 +29,7 @@
 #include <osmocom/abis/e1_input.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/abis/lapd.h>
 
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts.c b/src/osmo-bsc/bts_ipaccess_nanobts.c
index 5b60367..49720e5 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts.c
@@ -45,6 +45,7 @@
 #include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
 #include <osmocom/bsc/paging.h>
 #include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 static int bts_model_nanobts_start(struct gsm_network *net);
 static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line);
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
index be823ae..b979cc7 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
@@ -23,7 +23,7 @@
 #include <osmocom/core/msgb.h>
 #include <osmocom/bsc/gsm_data.h>
 #include <osmocom/bsc/abis_nm.h>
-
+#include <osmocom/bsc/bts.h>
 
 struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
 {
diff --git a/src/osmo-bsc/bts_nokia_site.c b/src/osmo-bsc/bts_nokia_site.c
index 523c9ed..2b6f918 100644
--- a/src/osmo-bsc/bts_nokia_site.c
+++ b/src/osmo-bsc/bts_nokia_site.c
@@ -37,6 +37,7 @@
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/timeslot_fsm.h>
 #include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/timer.h>
 
diff --git a/src/osmo-bsc/bts_siemens_bs11.c b/src/osmo-bsc/bts_siemens_bs11.c
index 057a0e1..c808421 100644
--- a/src/osmo-bsc/bts_siemens_bs11.c
+++ b/src/osmo-bsc/bts_siemens_bs11.c
@@ -30,6 +30,7 @@
 #include <osmocom/abis/e1_input.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 static int bts_model_bs11_start(struct gsm_network *net);
 
diff --git a/src/osmo-bsc/bts_sysmobts.c b/src/osmo-bsc/bts_sysmobts.c
index 6f9dc77..df43d76 100644
--- a/src/osmo-bsc/bts_sysmobts.c
+++ b/src/osmo-bsc/bts_sysmobts.c
@@ -26,6 +26,7 @@
 #include <osmocom/bsc/gsm_data.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/abis/e1_input.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/core/msgb.h>
diff --git a/src/osmo-bsc/bts_unknown.c b/src/osmo-bsc/bts_unknown.c
index b6b56a8..b5471ce 100644
--- a/src/osmo-bsc/bts_unknown.c
+++ b/src/osmo-bsc/bts_unknown.c
@@ -21,6 +21,7 @@
 
 
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/bsc/abis_nm.h>
 
diff --git a/src/osmo-bsc/cbch_scheduler.c b/src/osmo-bsc/cbch_scheduler.c
index ef93b7a..8021804 100644
--- a/src/osmo-bsc/cbch_scheduler.c
+++ b/src/osmo-bsc/cbch_scheduler.c
@@ -28,6 +28,7 @@
 #include <osmocom/bsc/gsm_data.h>
 #include <osmocom/bsc/smscb.h>
 #include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
 
 /* add all pages of given SMSCB so they appear as soon as possible *after* (included) base_idx. */
 static int bts_smscb_sched_add_after(struct bts_smscb_page **sched_arr, int sched_arr_size,
diff --git a/src/osmo-bsc/chan_alloc.c b/src/osmo-bsc/chan_alloc.c
index 9b80df3..6829251 100644
--- a/src/osmo-bsc/chan_alloc.c
+++ b/src/osmo-bsc/chan_alloc.c
@@ -34,6 +34,7 @@
 #include <osmocom/bsc/timeslot_fsm.h>
 #include <osmocom/bsc/lchan_fsm.h>
 #include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/talloc.h>
 
diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c
index 6ec6d05..58a7867 100644
--- a/src/osmo-bsc/codec_pref.c
+++ b/src/osmo-bsc/codec_pref.c
@@ -27,6 +27,7 @@
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/codec_pref.h>
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 
 /* Determine whether a permitted speech value is specifies a half rate or full
  * rate codec */
diff --git a/src/osmo-bsc/e1_config.c b/src/osmo-bsc/e1_config.c
index 4389f66..9ccbbfd 100644
--- a/src/osmo-bsc/e1_config.c
+++ b/src/osmo-bsc/e1_config.c
@@ -30,6 +30,7 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
 
 #define SAPI_L2ML	0
 #define SAPI_OML	62
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index 07978de..e54a937 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -44,6 +44,7 @@
 #include <osmocom/bsc/gsm_08_08.h>
 #include <osmocom/bsc/gsm_data.h>
 #include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/bts.h>
 
 
 int gsm48_sendmsg(struct msgb *msg)
diff --git a/src/osmo-bsc/gsm_08_08.c b/src/osmo-bsc/gsm_08_08.c
index 2829b67..d5915f4 100644
--- a/src/osmo-bsc/gsm_08_08.c
+++ b/src/osmo-bsc/gsm_08_08.c
@@ -36,6 +36,7 @@
 #include <osmocom/gsm/gsm23236.h>
 
 #include <osmocom/bsc/osmo_bsc_sigtran.h>
+#include <osmocom/bsc/bts.h>
 
 #define LOG_COMPL_L3(pdisc, mtype, loglevel, format, args...) \
 	LOGP(DRSL, loglevel, "%s %s: " format, gsm48_pdisc_name(pdisc), gsm48_pdisc_msgtype_name(pdisc, mtype), ##args)
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index a4f6b7f..1295592 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -44,11 +44,10 @@
 #include <osmocom/bsc/handover_cfg.h>
 #include <osmocom/bsc/timeslot_fsm.h>
 #include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 void *tall_bsc_ctx = NULL;
 
-static LLIST_HEAD(bts_models);
-
 void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
 		   uint8_t e1_ts, uint8_t e1_ts_ss)
 {
@@ -57,49 +56,6 @@
 	ts->e1_link.e1_ts_ss = e1_ts_ss;
 }
 
-static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
-{
-	struct gsm_bts_model *model;
-
-	llist_for_each_entry(model, &bts_models, list) {
-		if (model->type == type)
-			return model;
-	}
-
-	return NULL;
-}
-
-int gsm_bts_model_register(struct gsm_bts_model *model)
-{
-	if (bts_model_find(model->type))
-		return -EEXIST;
-
-	tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef);
-	llist_add_tail(&model->list, &bts_models);
-	return 0;
-}
-
-const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
-	{ GSM_BTS_TYPE_UNKNOWN,		"Unknown BTS Type" },
-	{ GSM_BTS_TYPE_BS11,		"Siemens BTS (BS-11 or compatible)" },
-	{ GSM_BTS_TYPE_NANOBTS,		"ip.access nanoBTS or compatible" },
-	{ GSM_BTS_TYPE_RBS2000,		"Ericsson RBS2000 Series" },
-	{ GSM_BTS_TYPE_NOKIA_SITE,	"Nokia {Metro,Ultra,In}Site" },
-	{ GSM_BTS_TYPE_OSMOBTS,		"sysmocom sysmoBTS" },
-	{ 0,				NULL }
-};
-
-struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
-{
-	struct gsm_bts_trx *trx;
-
-	llist_for_each_entry(trx, &bts->trx_list, list) {
-		if (trx->nr == nr)
-			return trx;
-	}
-	return NULL;
-}
-
 /* Search for a BTS in the given Location Area; optionally start searching
  * with start_bts (for continuing to search after the first result) */
 struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
@@ -149,62 +105,6 @@
 	return get_value_string(bts_gprs_mode_names, mode);
 }
 
-int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode)
-{
-	if (mode != BTS_GPRS_NONE &&
-	    !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_GPRS)) {
-		return 0;
-	}
-	if (mode == BTS_GPRS_EGPRS &&
-	    !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) {
-		return 0;
-	}
-
-	return 1;
-}
-
-int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
-{
-	struct gsm_bts_model *model;
-
-	model = bts_model_find(type);
-	if (!model)
-		return -EINVAL;
-
-	bts->type = type;
-	bts->model = model;
-
-	if (model->start && !model->started) {
-		int ret = model->start(bts->network);
-		if (ret < 0)
-			return ret;
-
-		model->started = true;
-	}
-
-	switch (bts->type) {
-	case GSM_BTS_TYPE_NANOBTS:
-	case GSM_BTS_TYPE_OSMOBTS:
-		/* Set the default OML Stream ID to 0xff */
-		bts->oml_tei = 0xff;
-		bts->c0->nominal_power = 23;
-		break;
-	case GSM_BTS_TYPE_RBS2000:
-		INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups);
-		INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups);
-		break;
-	case GSM_BTS_TYPE_BS11:
-	case GSM_BTS_TYPE_UNKNOWN:
-	case GSM_BTS_TYPE_NOKIA_SITE:
-		/* Set default BTS reset timer */
-		bts->nokia.bts_reset_timer_cnf = 15;
-	case _NUM_GSM_BTS_TYPE:
-		break;
-	}
-
-	return 0;
-}
-
 struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type,
 					uint8_t bsic)
 {
@@ -248,166 +148,14 @@
 	gsm48_encode_ra(buf, &raid);
 }
 
-/* Assume there are only 256 possible bts */
-osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
-static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
-{
-	*idx = bts_nr / (8 * 4);
-	*bit = bts_nr % (8 * 4);
-}
-
-void bts_depend_mark(struct gsm_bts *bts, int dep)
-{
-	int idx, bit;
-	depends_calc_index_bit(dep, &idx, &bit);
-
-	bts->depends_on[idx] |= 1 << bit;
-}
-
-void bts_depend_clear(struct gsm_bts *bts, int dep)
-{
-	int idx, bit;
-	depends_calc_index_bit(dep, &idx, &bit);
-
-	bts->depends_on[idx] &= ~(1 << bit);
-}
-
-int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
-{
-	int idx, bit;
-	depends_calc_index_bit(other->nr, &idx, &bit);
-
-	/* Check if there is a depends bit */
-	return (base->depends_on[idx] & (1 << bit)) > 0;
-}
-
-static int bts_is_online(struct gsm_bts *bts)
-{
-	/* TODO: support E1 BTS too */
-	if (!is_ipaccess_bts(bts))
-		return 1;
-
-	if (!bts->oml_link)
-		return 0;
-
-	return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
-}
-
-int bts_depend_check(struct gsm_bts *bts)
-{
-	struct gsm_bts *other_bts;
-
-	llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
-		if (!bts_depend_is_depedency(bts, other_bts))
-			continue;
-		if (bts_is_online(other_bts))
-			continue;
-		return 0;
-	}
-	return 1;
-}
-
-/* get the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 section 5.2.  A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts)
-{
-	const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
-	if (bts->infinite_radio_link_timeout)
-		return -1;
-	else {
-		/* Encoding as per Table 10.5.21 of TS 04.08 */
-		return (cell_options->radio_link_timeout + 1) << 2;
-	}
-}
-
-/* set the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 Section 5.2.  A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
-{
-	struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
-	if (value < 0)
-		bts->infinite_radio_link_timeout = true;
-	else {
-		bts->infinite_radio_link_timeout = false;
-		/* Encoding as per Table 10.5.21 of TS 04.08 */
-		if (value < 4)
-			value = 4;
-		if (value > 64)
-			value = 64;
-		cell_options->radio_link_timeout = (value >> 2) - 1;
-	}
-}
-
-static const struct osmo_stat_item_desc bts_stat_desc[] = {
-	[BTS_STAT_CHAN_LOAD_AVERAGE] =			{ "chanloadavg", "Channel load average", "%", 16, 0 },
-	[BTS_STAT_CHAN_CCCH_SDCCH4_USED] =		{ "chan_ccch_sdcch4:used",
-							  "Number of CCCH+SDCCH4 channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL] =		{ "chan_ccch_sdcch4:total",
-							  "Number of CCCH+SDCCH4 channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_USED] =			{ "chan_tch_f:used",
-							  "Number of TCH/F channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_TOTAL] =			{ "chan_tch_f:total",
-							  "Number of TCH/F channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_H_USED] =			{ "chan_tch_h:used",
-							  "Number of TCH/H channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_H_TOTAL] =			{ "chan_tch_h:total",
-							  "Number of TCH/H channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_SDCCH8_USED] =			{ "chan_sdcch8:used",
-							  "Number of SDCCH8 channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_SDCCH8_TOTAL] =			{ "chan_sdcch8:total",
-							  "Number of SDCCH8 channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_PDCH_USED] =		{ "chan_tch_f_pdch:used",
-							  "Number of TCH/F_PDCH channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] =		{ "chan_tch_f_pdch:total",
-							  "Number of TCH/F_PDCH channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED] =		{ "chan_ccch_sdcch4_cbch:used",
-							  "Number of CCCH+SDCCH4+CBCH channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL] =	{ "chan_ccch_sdcch4_cbch:total",
-							  "Number of CCCH+SDCCH4+CBCH channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_SDCCH8_CBCH_USED] =		{ "chan_sdcch8_cbch:used",
-							  "Number of SDCCH8+CBCH channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL] =		{ "chan_sdcch8_cbch:total",
-							  "Number of SDCCH8+CBCH channels total", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED] =		{ "chan_tch_f_tch_h_pdch:used",
-							  "Number of TCH/F_TCH/H_PDCH channels used", "", 16, 0 },
-	[BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL] =	{ "chan_tch_f_tch_h_pdch:total",
-							  "Number of TCH/F_TCH/H_PDCH channels total", "", 16, 0 },
-	[BTS_STAT_T3122] =				{ "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator",
-							  "s", 16, GSM_T3122_DEFAULT },
-	[BTS_STAT_RACH_BUSY] =				{ "rach_busy",
-							  "RACH slots with signal above threshold", "%", 16, 0 },
-	[BTS_STAT_RACH_ACCESS] =			{ "rach_access",
-							  "RACH slots with access bursts in them", "%", 16, 0 },
-	[BTS_STAT_OML_CONNECTED] =			{ "oml_connected", "Number of OML links connected", "", 16, 0 },
-	[BTS_STAT_RSL_CONNECTED] =			{ "rsl_connected", "Number of RSL links connected", "", 16, 0 },
-	[BTS_STAT_LCHAN_BORKEN] =			{ "lchan_borken",
-							  "Number of lchans in the BORKEN state", "", 16, 0 },
-	[BTS_STAT_TS_BORKEN] =				{ "ts_borken",
-							  "Number of timeslots in the BORKEN state", "", 16, 0 },
-};
-
-static const struct osmo_stat_item_group_desc bts_statg_desc = {
-	.group_name_prefix = "bts",
-	.group_description = "base transceiver station",
-	.class_id = OSMO_STATS_CLASS_GLOBAL,
-	.num_items = ARRAY_SIZE(bts_stat_desc),
-	.item_desc = bts_stat_desc,
-};
-
 void gsm_abis_mo_reset(struct gsm_abis_mo *mo)
 {
 	mo->nm_state.operational = NM_OPSTATE_NULL;
 	mo->nm_state.availability = NM_AVSTATE_POWER_OFF;
 }
 
-static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
-			uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3)
+void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
+		 uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3)
 {
 	mo->bts = bts;
 	mo->obj_class = obj_class;
@@ -417,62 +165,6 @@
 	gsm_abis_mo_reset(mo);
 }
 
-const struct value_string bts_attribute_names[] = {
-	OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
-	OSMO_VALUE_STRING(BTS_SUB_MODEL),
-	OSMO_VALUE_STRING(TRX_PHY_VERSION),
-	{ 0, NULL }
-};
-
-enum bts_attribute str2btsattr(const char *s)
-{
-	return get_string_value(bts_attribute_names, s);
-}
-
-const char *btsatttr2str(enum bts_attribute v)
-{
-	return get_value_string(bts_attribute_names, v);
-}
-
-const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
-	{ BTS_UNKNOWN,		"unknown" },
-	{ BTS_OSMO_LITECELL15,	"osmo-bts-lc15" },
-	{ BTS_OSMO_OCTPHY,	"osmo-bts-octphy" },
-	{ BTS_OSMO_SYSMO,	"osmo-bts-sysmo" },
-	{ BTS_OSMO_TRX,		"omso-bts-trx" },
-	{ 0, NULL }
-};
-
-enum gsm_bts_type_variant str2btsvariant(const char *arg)
-{
-	return get_string_value(osmo_bts_variant_names, arg);
-}
-
-const char *btsvariant2str(enum gsm_bts_type_variant v)
-{
-	return get_value_string(osmo_bts_variant_names, v);
-}
-
-const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
-	{ GSM_BTS_TYPE_UNKNOWN,		"unknown" },
-	{ GSM_BTS_TYPE_BS11,		"bs11" },
-	{ GSM_BTS_TYPE_NANOBTS,		"nanobts" },
-	{ GSM_BTS_TYPE_RBS2000,		"rbs2000" },
-	{ GSM_BTS_TYPE_NOKIA_SITE,	"nokia_site" },
-	{ GSM_BTS_TYPE_OSMOBTS,		"sysmobts" },
-	{ 0, NULL }
-};
-
-enum gsm_bts_type str2btstype(const char *arg)
-{
-	return get_string_value(bts_type_names, arg);
-}
-
-const char *btstype2str(enum gsm_bts_type type)
-{
-	return get_value_string(bts_type_names, type);
-}
-
 const struct value_string gsm_chreq_descs[] = {
 	{ GSM_CHREQ_REASON_EMERG,	"emergency call" },
 	{ GSM_CHREQ_REASON_PAG,		"answer to paging" },
@@ -577,44 +269,6 @@
 	return NULL;
 }
 
-bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai)
-{
-	return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0
-		&& lai->lac == bts->location_area_code;
-}
-
-bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id)
-{
-	const union gsm0808_cell_id_u *id = &cell_id->id;
-	if (!bts || !cell_id)
-		return false;
-
-	switch (cell_id->id_discr) {
-	case CELL_IDENT_WHOLE_GLOBAL:
-		return gsm_bts_matches_lai(bts, &id->global.lai)
-			&& id->global.cell_identity == bts->cell_identity;
-	case CELL_IDENT_LAC_AND_CI:
-		return id->lac_and_ci.lac == bts->location_area_code
-			&& id->lac_and_ci.ci == bts->cell_identity;
-	case CELL_IDENT_CI:
-		return id->ci == bts->cell_identity;
-	case CELL_IDENT_NO_CELL:
-		return false;
-	case CELL_IDENT_LAI_AND_LAC:
-		return gsm_bts_matches_lai(bts, &id->lai_and_lac);
-	case CELL_IDENT_LAC:
-		return id->lac == bts->location_area_code;
-	case CELL_IDENT_BSS:
-		return true;
-	case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
-	case CELL_IDENT_UTRAN_RNC:
-	case CELL_IDENT_UTRAN_LAC_RNC:
-		return false;
-	default:
-		OSMO_ASSERT(false);
-	}
-}
-
 /* From a list of local BTSes that match the cell_id, return the Nth one, or NULL if there is no such
  * match. */
 struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
@@ -636,59 +290,6 @@
 	return NULL;
 }
 
-struct gsm_bts_ref *gsm_bts_ref_find(const struct llist_head *list, const struct gsm_bts *bts)
-{
-	struct gsm_bts_ref *ref;
-	if (!bts)
-		return NULL;
-	llist_for_each_entry(ref, list, entry) {
-		if (ref->bts == bts)
-			return ref;
-	}
-	return NULL;
-}
-
-/* Add a BTS reference to the local_neighbors list.
- * Return 1 if added, 0 if such an entry already existed, and negative on errors. */
-int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor)
-{
-	struct gsm_bts_ref *ref;
-	if (!bts || !neighbor)
-		return -ENOMEM;
-
-	if (bts == neighbor)
-		return -EINVAL;
-
-	/* Already got this entry? */
-	ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
-	if (ref)
-		return 0;
-
-	ref = talloc_zero(bts, struct gsm_bts_ref);
-	if (!ref)
-		return -ENOMEM;
-	ref->bts = neighbor;
-	llist_add_tail(&ref->entry, &bts->local_neighbors);
-	return 1;
-}
-
-/* Remove a BTS reference from the local_neighbors list.
- * Return 1 if removed, 0 if no such entry existed, and negative on errors. */
-int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor)
-{
-	struct gsm_bts_ref *ref;
-	if (!bts || !neighbor)
-		return -ENOMEM;
-
-	ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor);
-	if (!ref)
-		return 0;
-
-	llist_del(&ref->entry);
-	talloc_free(ref);
-	return 1;
-}
-
 struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
 {
 	struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
@@ -748,251 +349,6 @@
 	return trx;
 }
 
-
-static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
-static const uint8_t bts_cell_timer_default[] =
-				{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
-static const struct gprs_rlc_cfg rlc_cfg_default = {
-	.parameter = {
-		[RLC_T3142] = 20,
-		[RLC_T3169] = 5,
-		[RLC_T3191] = 5,
-		[RLC_T3193] = 160, /* 10ms */
-		[RLC_T3195] = 5,
-		[RLC_N3101] = 10,
-		[RLC_N3103] = 4,
-		[RLC_N3105] = 8,
-		[CV_COUNTDOWN] = 15,
-		[T_DL_TBF_EXT] = 250 * 10, /* ms */
-		[T_UL_TBF_EXT] = 250 * 10, /* ms */
-	},
-	.paging = {
-		.repeat_time = 5 * 50, /* ms */
-		.repeat_count = 3,
-	},
-	.cs_mask = 0x1fff,
-	.initial_cs = 2,
-	.initial_mcs = 6,
-};
-
-static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)
-{
-	cstate->bts = bts;
-	INIT_LLIST_HEAD(&cstate->messages);
-}
-
-/* Initialize those parts that don't require osmo-bsc specific dependencies.
- * This part is shared among the thin programs in osmo-bsc/src/utils/.
- * osmo-bsc requires further initialization that pulls in more dependencies (see
- * bsc_bts_alloc_register()). */
-struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
-{
-	struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
-	struct gsm48_multi_rate_conf mr_cfg;
-	int i;
-
-	if (!bts)
-		return NULL;
-
-	bts->nr = bts_num;
-	bts->num_trx = 0;
-	INIT_LLIST_HEAD(&bts->trx_list);
-	bts->network = net;
-
-	bts->ms_max_power = 15;	/* dBm */
-
-	gsm_mo_init(&bts->mo, bts, NM_OC_BTS,
-			bts->nr, 0xff, 0xff);
-	gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER,
-			0xff, 0xff, 0xff);
-
-	for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
-		bts->gprs.nsvc[i].bts = bts;
-		bts->gprs.nsvc[i].id = i;
-		gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC,
-				bts->nr, i, 0xff);
-	}
-	memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
-		sizeof(bts->gprs.nse.timer));
-	gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE,
-			bts->nr, 0xff, 0xff);
-	memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
-		sizeof(bts->gprs.cell.timer));
-	gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL,
-			bts->nr, 0xff, 0xff);
-	memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default,
-		sizeof(bts->gprs.cell.rlc_cfg));
-
-	/* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */
-	bts->gprs.cell.bvci = 2;
-
-	/* init statistics */
-	bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr);
-	if (!bts->bts_ctrs) {
-		talloc_free(bts);
-		return NULL;
-	}
-	bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr);
-
-	/* create our primary TRX */
-	bts->c0 = gsm_bts_trx_alloc(bts);
-	if (!bts->c0) {
-		rate_ctr_group_free(bts->bts_ctrs);
-		osmo_stat_item_group_free(bts->bts_statg);
-		talloc_free(bts);
-		return NULL;
-	}
-	bts->c0->ts[0].pchan_from_config = GSM_PCHAN_CCCH_SDCCH4; /* TODO: really?? */
-
-	bts->ccch_load_ind_thresh = 10; /* 10% of Load: Start sending CCCH LOAD IND */
-	bts->rach_b_thresh = -1;
-	bts->rach_ldavg_slots = -1;
-
-	bts->paging.free_chans_need = -1;
-	INIT_LLIST_HEAD(&bts->paging.pending_requests);
-
-	bts->features.data = &bts->_features_data[0];
-	bts->features.data_len = sizeof(bts->_features_data);
-
-	/* si handling */
-	bts->bcch_change_mark = 1;
-	bts->chan_load_avg = 0;
-
-	/* timer overrides */
-	bts->T3122 = 0; /* not overridden by default */
-	bts->T3113_dynamic = true; /* dynamic by default */
-
-	bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
-	bts->dtxd = false;
-	bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */
-	bts->neigh_list_manual_mode = NL_MODE_AUTOMATIC;
-	bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */
-	bts->si_unused_send_empty = true;
-	bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
-	bts->si_common.cell_sel_par.rxlev_acc_min = 0;
-	bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list;
-	bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list;
-	bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST;
-	bts->si_common.si2quater_neigh_list.thresh_hi = 0;
-	osmo_earfcn_init(&bts->si_common.si2quater_neigh_list);
-	bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
-	bts->si_common.neigh_list.data_len =
-				sizeof(bts->si_common.data.neigh_list);
-	bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list;
-	bts->si_common.si5_neigh_list.data_len =
-				sizeof(bts->si_common.data.si5_neigh_list);
-	bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
-	bts->si_common.cell_alloc.data_len =
-				sizeof(bts->si_common.data.cell_alloc);
-	bts->si_common.rach_control.re = 1; /* no re-establishment */
-	bts->si_common.rach_control.tx_integer = 9;  /* 12 slots spread - 217/115 slots delay */
-	bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
-	bts->si_common.rach_control.t2 = 4; /* no emergency calls */
-	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 = osmo_tdef_get(net->T_defs, 3212, OSMO_TDEF_CUSTOM, -1);
-	gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
-
-	INIT_LLIST_HEAD(&bts->abis_queue);
-	INIT_LLIST_HEAD(&bts->loc_list);
-	INIT_LLIST_HEAD(&bts->local_neighbors);
-	INIT_LLIST_HEAD(&bts->oml_fail_rep);
-
-	/* Enable all codecs by default. These get reset to a more fine grained selection IF a
-	 * 'codec-support' config appears in the config file (see bsc_vty.c). */
-	bts->codec = (struct bts_codec_conf){
-		.hr = 1,
-		.efr = 1,
-		.amr = 1,
-	};
-
-	/* Set reasonable defaults for AMR-FR and AMR-HR rate configuration.
-	 * (see also 3GPP TS 28.062, Table 7.11.3.1.3-2) */
-	mr_cfg = (struct gsm48_multi_rate_conf) {
-		.m4_75 = 1,
-		.m5_15 = 0,
-		.m5_90 = 1,
-		.m6_70 = 0,
-		.m7_40 = 1,
-		.m7_95 = 0,
-		.m10_2 = 0,
-		.m12_2 = 1
-	};
-	memcpy(bts->mr_full.gsm48_ie, &mr_cfg, sizeof(bts->mr_full.gsm48_ie));
-	bts->mr_full.ms_mode[0].mode = 0;
-	bts->mr_full.ms_mode[1].mode = 2;
-	bts->mr_full.ms_mode[2].mode = 4;
-	bts->mr_full.ms_mode[3].mode = 7;
-	bts->mr_full.bts_mode[0].mode = 0;
-	bts->mr_full.bts_mode[1].mode = 2;
-	bts->mr_full.bts_mode[2].mode = 4;
-	bts->mr_full.bts_mode[3].mode = 7;
-	for (i = 0; i < 3; i++) {
-		bts->mr_full.ms_mode[i].hysteresis = 8;
-		bts->mr_full.ms_mode[i].threshold = 32;
-		bts->mr_full.bts_mode[i].hysteresis = 8;
-		bts->mr_full.bts_mode[i].threshold = 32;
-	}
-	bts->mr_full.num_modes = 4;
-
-	mr_cfg = (struct gsm48_multi_rate_conf) {
-		.m4_75 = 1,
-		.m5_15 = 0,
-		.m5_90 = 1,
-		.m6_70 = 0,
-		.m7_40 = 1,
-		.m7_95 = 0,
-		.m10_2 = 0,
-		.m12_2 = 0
-	};
-	memcpy(bts->mr_half.gsm48_ie, &mr_cfg, sizeof(bts->mr_half.gsm48_ie));
-	bts->mr_half.ms_mode[0].mode = 0;
-	bts->mr_half.ms_mode[1].mode = 2;
-	bts->mr_half.ms_mode[2].mode = 4;
-	bts->mr_half.ms_mode[3].mode = 7;
-	bts->mr_half.bts_mode[0].mode = 0;
-	bts->mr_half.bts_mode[1].mode = 2;
-	bts->mr_half.bts_mode[2].mode = 4;
-	bts->mr_half.bts_mode[3].mode = 7;
-	for (i = 0; i < 3; i++) {
-		bts->mr_half.ms_mode[i].hysteresis = 8;
-		bts->mr_half.ms_mode[i].threshold = 32;
-		bts->mr_half.bts_mode[i].hysteresis = 8;
-		bts->mr_half.bts_mode[i].threshold = 32;
-	}
-	bts->mr_half.num_modes = 3;
-
-	bts_init_cbch_state(&bts->cbch_basic, bts);
-	bts_init_cbch_state(&bts->cbch_extended, bts);
-
-	return bts;
-}
-
-/* reset the state of all MO in the BTS */
-void gsm_bts_mo_reset(struct gsm_bts *bts)
-{
-	struct gsm_bts_trx *trx;
-	unsigned int i;
-
-	gsm_abis_mo_reset(&bts->mo);
-	gsm_abis_mo_reset(&bts->site_mgr.mo);
-	for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++)
-		gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo);
-	gsm_abis_mo_reset(&bts->gprs.nse.mo);
-	gsm_abis_mo_reset(&bts->gprs.cell.mo);
-
-	llist_for_each_entry(trx, &bts->trx_list, list) {
-		gsm_abis_mo_reset(&trx->mo);
-		gsm_abis_mo_reset(&trx->bb_transc.mo);
-
-		for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
-			struct gsm_bts_trx_ts *ts = &trx->ts[i];
-			gsm_abis_mo_reset(&ts->mo);
-		}
-	}
-}
-
 struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num)
 {
 	struct gsm_bts_trx *trx;
@@ -1010,16 +366,6 @@
 
 static char ts2str[255];
 
-char *gsm_bts_name(const struct gsm_bts *bts)
-{
-	if (!bts)
-		snprintf(ts2str, sizeof(ts2str), "(bts=NULL)");
-	else
-		snprintf(ts2str, sizeof(ts2str), "(bts=%d)", bts->nr);
-
-	return ts2str;
-}
-
 char *gsm_trx_name(const struct gsm_bts_trx *trx)
 {
 	if (!trx)
@@ -1289,27 +635,6 @@
 	return gsm_pchan2chan_nr(lchan->ts->pchan_is, lchan->ts->nr, lchan->nr);
 }
 
-/* return the gsm_lchan for the CBCH (if it exists at all) */
-struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts)
-{
-	struct gsm_lchan *lchan = NULL;
-	struct gsm_bts_trx *trx = bts->c0;
-
-	if (trx->ts[0].pchan_from_config == GSM_PCHAN_CCCH_SDCCH4_CBCH)
-		lchan = &trx->ts[0].lchan[2];
-	else {
-		int i;
-		for (i = 0; i < 8; i++) {
-			if (trx->ts[i].pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) {
-				lchan = &trx->ts[i].lchan[2];
-				break;
-			}
-		}
-	}
-
-	return lchan;
-}
-
 /* determine logical channel based on TRX and channel number IE */
 struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
 				   int *rc)
@@ -1392,6 +717,12 @@
 	return pchan_is_tch(ts->pchan_is);
 }
 
+struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
+	if (!conn || !conn->lchan)
+		return NULL;
+	return conn->lchan->ts->trx->bts;
+}
+
 bool trx_is_usable(const struct gsm_bts_trx *trx)
 {
 	/* FIXME: How does this behave for BS-11 ? */
@@ -1417,13 +748,6 @@
 	}
 }
 
-void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data)
-{
-	struct gsm_bts_trx *trx;
-	llist_for_each_entry(trx, &bts->trx_list, list)
-		gsm_trx_all_ts_dispatch(trx, ts_ev, data);
-}
-
 static void _chan_desc_fill_tail(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan)
 {
 	if (!lchan->ts->hopping.enabled) {
@@ -1458,6 +782,14 @@
 	_chan_desc_fill_tail(cd, lchan);
 }
 
+uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts)
+{
+	if (ts->tsc != -1)
+		return ts->tsc;
+	else
+		return ts->trx->bts->bsic & 7;
+}
+
 bool nm_is_running(const struct gsm_nm_state *s) {
 	if (s->operational != NM_OPSTATE_ENABLED)
 		return false;
@@ -1633,7 +965,7 @@
 	}
 }
 
-static int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
+int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
 {
 	struct gsm_bts_trx_ts *ts;
 	struct gsm_lchan *lchan;
@@ -1681,18 +1013,6 @@
 	return count;
 }
 
-/* Count number of free TS of given pchan type */
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
-{
-	struct gsm_bts_trx *trx;
-	int count = 0;
-
-	llist_for_each_entry(trx, &bts->trx_list, list)
-		count += trx_count_free_ts(trx, pchan);
-
-	return count;
-}
-
 bool ts_is_usable(const struct gsm_bts_trx_ts *ts)
 {
 	if (!trx_is_usable(ts->trx)) {
diff --git a/src/osmo-bsc/handover_decision.c b/src/osmo-bsc/handover_decision.c
index 5338400..7eb8f31 100644
--- a/src/osmo-bsc/handover_decision.c
+++ b/src/osmo-bsc/handover_decision.c
@@ -34,6 +34,7 @@
 
 #include <osmocom/bsc/handover_fsm.h>
 #include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/bts.h>
 
 /* did we get a RXLEV for a given cell in the given report? */
 static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index ab507e8..8caa2e3 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -37,6 +37,7 @@
 #include <osmocom/bsc/penalty_timers.h>
 #include <osmocom/bsc/neighbor_ident.h>
 #include <osmocom/bsc/timeslot_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 #define LOGPHOBTS(bts, level, fmt, args...) \
 	LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args)
diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c
index 0b88b27..8ed5945 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -43,6 +43,7 @@
 #include <osmocom/bsc/osmo_bsc_lcls.h>
 #include <osmocom/bsc/codec_pref.h>
 #include <osmocom/bsc/gsm_08_08.h>
+#include <osmocom/bsc/bts.h>
 
 #define LOG_FMT_BTS "bts %u lac-ci %u-%u arfcn-bsic %d-%d"
 #define LOG_ARGS_BTS(bts) \
diff --git a/src/osmo-bsc/handover_logic.c b/src/osmo-bsc/handover_logic.c
index 5be8383..071228e 100644
--- a/src/osmo-bsc/handover_logic.c
+++ b/src/osmo-bsc/handover_logic.c
@@ -43,6 +43,7 @@
 #include <osmocom/bsc/bsc_subscr_conn_fsm.h>
 #include <osmocom/bsc/neighbor_ident.h>
 #include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/gsm/gsm0808.h>
 #include <osmocom/gsm/gsm0808_utils.h>
 
diff --git a/src/osmo-bsc/handover_vty.c b/src/osmo-bsc/handover_vty.c
index a418aa7..81313e2 100644
--- a/src/osmo-bsc/handover_vty.c
+++ b/src/osmo-bsc/handover_vty.c
@@ -27,6 +27,7 @@
 #include <osmocom/bsc/vty.h>
 #include <osmocom/bsc/handover_cfg.h>
 #include <osmocom/bsc/handover_decision_2.h>
+#include <osmocom/bsc/bts.h>
 
 static struct handover_cfg *ho_cfg_from_vty(struct vty *vty)
 {
diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c
index bcf7c4b..e879059 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -39,7 +39,7 @@
 #include <osmocom/bsc/handover_fsm.h>
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/codec_pref.h>
-
+#include <osmocom/bsc/bts.h>
 
 static struct osmo_fsm lchan_fsm;
 
diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c
index 4be9790..2def2f5 100644
--- a/src/osmo-bsc/lchan_rtp_fsm.c
+++ b/src/osmo-bsc/lchan_rtp_fsm.c
@@ -30,6 +30,7 @@
 #include <osmocom/bsc/bsc_subscr_conn_fsm.h>
 #include <osmocom/bsc/abis_rsl.h>
 #include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
 
 static struct osmo_fsm lchan_rtp_fsm;
 
diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c
index d63db22..d2dba1b 100644
--- a/src/osmo-bsc/lchan_select.c
+++ b/src/osmo-bsc/lchan_select.c
@@ -28,6 +28,7 @@
 #include <osmocom/bsc/lchan_fsm.h>
 
 #include <osmocom/bsc/lchan_select.h>
+#include <osmocom/bsc/bts.h>
 
 static struct gsm_lchan *
 _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan,
diff --git a/src/osmo-bsc/meas_feed.c b/src/osmo-bsc/meas_feed.c
index f9a3626..889f6ef 100644
--- a/src/osmo-bsc/meas_feed.c
+++ b/src/osmo-bsc/meas_feed.c
@@ -19,6 +19,7 @@
 #include <osmocom/bsc/meas_feed.h>
 #include <osmocom/bsc/vty.h>
 #include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bts.h>
 
 struct meas_feed_state {
 	struct osmo_wqueue wqueue;
diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c
index 54d6944..7feed2a 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -30,6 +30,7 @@
 #include <osmocom/bsc/vty.h>
 #include <osmocom/bsc/neighbor_ident.h>
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 
 static struct gsm_network *g_net = NULL;
 static struct neighbor_ident_list *g_neighbor_cells = NULL;
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 1cacfe9..bf38d10 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -33,6 +33,7 @@
 #include <osmocom/bsc/codec_pref.h>
 #include <osmocom/bsc/abis_rsl.h>
 #include <osmocom/bsc/handover_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/gsm/protocol/gsm_08_08.h>
 #include <osmocom/gsm/gsm0808.h>
diff --git a/src/osmo-bsc/osmo_bsc_ctrl.c b/src/osmo-bsc/osmo_bsc_ctrl.c
index b94a749..5ebe597 100644
--- a/src/osmo-bsc/osmo_bsc_ctrl.c
+++ b/src/osmo-bsc/osmo_bsc_ctrl.c
@@ -27,6 +27,7 @@
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/a_reset.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/signal.h>
diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c
index 08c3a50..f664231 100644
--- a/src/osmo-bsc/osmo_bsc_filter.c
+++ b/src/osmo-bsc/osmo_bsc_filter.c
@@ -26,6 +26,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/paging.h>
 #include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts.h>
 
 #include <stdlib.h>
 
diff --git a/src/osmo-bsc/osmo_bsc_grace.c b/src/osmo-bsc/osmo_bsc_grace.c
index 56edee5..a26a716 100644
--- a/src/osmo-bsc/osmo_bsc_grace.c
+++ b/src/osmo-bsc/osmo_bsc_grace.c
@@ -25,6 +25,7 @@
 #include <osmocom/bsc/paging.h>
 #include <osmocom/bsc/signal.h>
 #include <osmocom/bsc/lchan_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts)
 {
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index ada6047..84b44f7 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -64,6 +64,7 @@
 #include <osmocom/bsc/e1_config.h>
 #include <osmocom/bsc/codec_pref.h>
 #include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/mgcp_client/mgcp_client.h>
 
diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c
index f2a1e5b..5e02b4a 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -29,6 +29,7 @@
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/osmo_bsc_sigtran.h>
 #include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/socket.h>
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
index afc6c8d..de4be26 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -34,6 +34,7 @@
 #include <osmocom/bsc/a_reset.h>
 #include <osmocom/bsc/bsc_subscr_conn_fsm.h>
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/mgcp_client/mgcp_common.h>
 
 /* A pointer to a list with all involved MSCs
diff --git a/src/osmo-bsc/paging.c b/src/osmo-bsc/paging.c
index 7859c69..521598f 100644
--- a/src/osmo-bsc/paging.c
+++ b/src/osmo-bsc/paging.c
@@ -53,6 +53,7 @@
 #include <osmocom/bsc/gsm_08_08.h>
 #include <osmocom/bsc/gsm_04_08_rr.h>
 #include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/bts.h>
 
 void *tall_paging_ctx = NULL;
 
diff --git a/src/osmo-bsc/pcu_sock.c b/src/osmo-bsc/pcu_sock.c
index 3e0b7bf..ae4ac8a 100644
--- a/src/osmo-bsc/pcu_sock.c
+++ b/src/osmo-bsc/pcu_sock.c
@@ -44,6 +44,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/abis_rsl.h>
 #include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/bts.h>
 
 static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg);
 uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
diff --git a/src/osmo-bsc/rest_octets.c b/src/osmo-bsc/rest_octets.c
index 0d806f3..7307cb8 100644
--- a/src/osmo-bsc/rest_octets.c
+++ b/src/osmo-bsc/rest_octets.c
@@ -33,6 +33,7 @@
 #include <osmocom/bsc/rest_octets.h>
 #include <osmocom/bsc/arfcn_range_encode.h>
 #include <osmocom/bsc/system_information.h>
+#include <osmocom/bsc/bts.h>
 
 /* generate SI1 rest octets */
 int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
diff --git a/src/osmo-bsc/smscb.c b/src/osmo-bsc/smscb.c
index 8d48af9..436826a 100644
--- a/src/osmo-bsc/smscb.c
+++ b/src/osmo-bsc/smscb.c
@@ -41,6 +41,7 @@
 #include <osmocom/bsc/gsm_04_08_rr.h>
 #include <osmocom/bsc/lchan_fsm.h>
 #include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
 
 /*********************************************************************************
  * Helper Functions
diff --git a/src/osmo-bsc/system_information.c b/src/osmo-bsc/system_information.c
index b17a909..13d0f46 100644
--- a/src/osmo-bsc/system_information.c
+++ b/src/osmo-bsc/system_information.c
@@ -41,6 +41,7 @@
 #include <osmocom/bsc/gsm_04_08_rr.h>
 #include <osmocom/bsc/acc_ramp.h>
 #include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/bts.h>
 
 struct gsm0808_cell_id_list2;
 
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index 4816daf..0aecfae 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -28,6 +28,7 @@
 #include <osmocom/bsc/lchan_fsm.h>
 #include <osmocom/bsc/abis_rsl.h>
 #include <osmocom/bsc/pcu_if.h>
+#include <osmocom/bsc/bts.h>
 
 static struct osmo_fsm ts_fsm;
 
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index e585e0d..6696679 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -48,6 +48,7 @@
 
 bs11_config_LDADD = \
 	$(top_builddir)/src/osmo-bsc/abis_nm.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/bts_siemens_bs11.o \
 	$(top_builddir)/src/osmo-bsc/e1_config.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
@@ -117,6 +118,7 @@
 	$(NULL)
 
 meas_json_LDADD = \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(LIBOSMOCORE_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c
index 1156233..c43c008 100644
--- a/src/utils/bs11_config.c
+++ b/src/utils/bs11_config.c
@@ -39,6 +39,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/core/select.h>
 #include <osmocom/bsc/rs232.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/core/application.h>
 #include <osmocom/core/talloc.h>
 #include <osmocom/abis/abis.h>