Change in osmo-bsc[master]: Move struct gsm_bts: gsm_data.* => bts.*

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

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

laforge gerrit-no-reply at lists.osmocom.org
Sat Jul 18 21:45:32 UTC 2020


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

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

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
---
M include/osmocom/bsc/Makefile.am
A include/osmocom/bsc/bts.h
M include/osmocom/bsc/gsm_data.h
M src/ipaccess/Makefile.am
M src/ipaccess/ipaccess-config.c
M src/osmo-bsc/Makefile.am
M src/osmo-bsc/abis_nm.c
M src/osmo-bsc/abis_om2000.c
M src/osmo-bsc/abis_om2000_vty.c
M src/osmo-bsc/abis_rsl.c
M src/osmo-bsc/acc_ramp.c
M src/osmo-bsc/assignment_fsm.c
M src/osmo-bsc/bsc_ctrl_commands.c
M src/osmo-bsc/bsc_init.c
M src/osmo-bsc/bsc_rf_ctrl.c
M src/osmo-bsc/bsc_vty.c
A src/osmo-bsc/bts.c
M src/osmo-bsc/bts_ericsson_rbs2000.c
M src/osmo-bsc/bts_ipaccess_nanobts.c
M src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
M src/osmo-bsc/bts_nokia_site.c
M src/osmo-bsc/bts_siemens_bs11.c
M src/osmo-bsc/bts_sysmobts.c
M src/osmo-bsc/bts_unknown.c
M src/osmo-bsc/cbch_scheduler.c
M src/osmo-bsc/chan_alloc.c
M src/osmo-bsc/codec_pref.c
M src/osmo-bsc/e1_config.c
M src/osmo-bsc/gsm_04_08_rr.c
M src/osmo-bsc/gsm_08_08.c
M src/osmo-bsc/gsm_data.c
M src/osmo-bsc/handover_decision.c
M src/osmo-bsc/handover_decision_2.c
M src/osmo-bsc/handover_fsm.c
M src/osmo-bsc/handover_logic.c
M src/osmo-bsc/handover_vty.c
M src/osmo-bsc/lchan_fsm.c
M src/osmo-bsc/lchan_rtp_fsm.c
M src/osmo-bsc/lchan_select.c
M src/osmo-bsc/meas_feed.c
M src/osmo-bsc/neighbor_ident_vty.c
M src/osmo-bsc/osmo_bsc_bssap.c
M src/osmo-bsc/osmo_bsc_ctrl.c
M src/osmo-bsc/osmo_bsc_filter.c
M src/osmo-bsc/osmo_bsc_grace.c
M src/osmo-bsc/osmo_bsc_main.c
M src/osmo-bsc/osmo_bsc_msc.c
M src/osmo-bsc/osmo_bsc_sigtran.c
M src/osmo-bsc/paging.c
M src/osmo-bsc/pcu_sock.c
M src/osmo-bsc/rest_octets.c
M src/osmo-bsc/smscb.c
M src/osmo-bsc/system_information.c
M src/osmo-bsc/timeslot_fsm.c
M src/utils/Makefile.am
M src/utils/bs11_config.c
M tests/abis/Makefile.am
M tests/abis/abis_test.c
M tests/bsc/Makefile.am
M tests/codec_pref/codec_pref_test.c
M tests/gsm0408/Makefile.am
M tests/gsm0408/gsm0408_test.c
M tests/handover/Makefile.am
M tests/handover/handover_test.c
M tests/nanobts_omlattr/Makefile.am
M tests/nanobts_omlattr/nanobts_omlattr_test.c
66 files changed, 1,471 insertions(+), 1,354 deletions(-)

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



diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 7a296e4..4d2df20 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -10,6 +10,7 @@
 	bsc_subscriber.h \
 	bsc_subscr_conn_fsm.h \
 	bss.h \
+	bts.h \
 	bts_ipaccess_nanobts_omlattr.h \
 	chan_alloc.h \
 	codec_pref.h \
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
new file mode 100644
index 0000000..07d2793
--- /dev/null
+++ b/include/osmocom/bsc/bts.h
@@ -0,0 +1,634 @@
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/bts_features.h>
+
+#include <osmocom/abis/e1_input.h>
+
+#include "osmocom/bsc/gsm_data.h"
+
+enum bts_counter_id {
+	BTS_CTR_CHREQ_TOTAL,
+	BTS_CTR_CHREQ_SUCCESSFUL,
+	BTS_CTR_CHREQ_NO_CHANNEL,
+	BTS_CTR_CHAN_RF_FAIL,
+	BTS_CTR_CHAN_RLL_ERR,
+	BTS_CTR_BTS_OML_FAIL,
+	BTS_CTR_BTS_RSL_FAIL,
+	BTS_CTR_CODEC_AMR_F,
+	BTS_CTR_CODEC_AMR_H,
+	BTS_CTR_CODEC_EFR,
+	BTS_CTR_CODEC_V1_FR,
+	BTS_CTR_CODEC_V1_HR,
+	BTS_CTR_PAGING_ATTEMPTED,
+	BTS_CTR_PAGING_ALREADY,
+	BTS_CTR_PAGING_RESPONDED,
+	BTS_CTR_PAGING_EXPIRED,
+	BTS_CTR_PAGING_NO_ACTIVE_PAGING,
+	BTS_CTR_PAGING_MSC_FLUSH,
+	BTS_CTR_CHAN_ACT_TOTAL,
+	BTS_CTR_CHAN_ACT_NACK,
+	BTS_CTR_RSL_UNKNOWN,
+	BTS_CTR_RSL_IPA_NACK,
+	BTS_CTR_RSL_DELETE_IND,
+	BTS_CTR_MODE_MODIFY_NACK,
+	BTS_CTR_LCHAN_BORKEN_FROM_UNUSED,
+	BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK,
+	BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK,
+	BTS_CTR_LCHAN_BORKEN_FROM_BORKEN,
+	BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN,
+	BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK,
+	BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK,
+	BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK,
+	BTS_CTR_LCHAN_BORKEN_EV_VTY,
+	BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN,
+	BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED,
+	BTS_CTR_TS_BORKEN_FROM_UNUSED,
+	BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT,
+	BTS_CTR_TS_BORKEN_FROM_PDCH,
+	BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT,
+	BTS_CTR_TS_BORKEN_FROM_IN_USE,
+	BTS_CTR_TS_BORKEN_FROM_BORKEN,
+	BTS_CTR_TS_BORKEN_FROM_UNKNOWN,
+	BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK,
+	BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK,
+	BTS_CTR_TS_BORKEN_EV_TEARDOWN,
+	BTS_CTR_ASSIGNMENT_ATTEMPTED,
+	BTS_CTR_ASSIGNMENT_COMPLETED,
+	BTS_CTR_ASSIGNMENT_STOPPED,
+	BTS_CTR_ASSIGNMENT_NO_CHANNEL,
+	BTS_CTR_ASSIGNMENT_TIMEOUT,
+	BTS_CTR_ASSIGNMENT_FAILED,
+	BTS_CTR_ASSIGNMENT_ERROR,
+};
+
+static const struct rate_ctr_desc bts_ctr_description[] = {
+	[BTS_CTR_CHREQ_TOTAL] = 		{"chreq:total", "Received channel requests"},
+	[BTS_CTR_CHREQ_SUCCESSFUL] =  		{"chreq:successful", "Successful channel requests (immediate assign sent)"},
+	[BTS_CTR_CHREQ_NO_CHANNEL] = 		{"chreq:no_channel", "Sent to MS no channel available"},
+	[BTS_CTR_CHAN_RF_FAIL] = 		{"chan:rf_fail", "Received a RF failure indication from BTS"},
+	[BTS_CTR_CHAN_RLL_ERR] = 		{"chan:rll_err", "Received a RLL failure with T200 cause from BTS"},
+	[BTS_CTR_BTS_OML_FAIL] = 		{"oml_fail", "Received a TEI down on a OML link"},
+	[BTS_CTR_BTS_RSL_FAIL] = 		{"rsl_fail", "Received a TEI down on a OML link"},
+	[BTS_CTR_CODEC_AMR_F] =			{"codec:amr_f", "Count the usage of AMR/F codec by channel mode requested"},
+	[BTS_CTR_CODEC_AMR_H] =			{"codec:amr_h", "Count the usage of AMR/H codec by channel mode requested"},
+	[BTS_CTR_CODEC_EFR] = 			{"codec:efr", "Count the usage of EFR codec by channel mode requested"},
+	[BTS_CTR_CODEC_V1_FR] =			{"codec:fr", "Count the usage of FR codec by channel mode requested"},
+	[BTS_CTR_CODEC_V1_HR] =			{"codec:hr", "Count the usage of HR codec by channel mode requested"},
+
+	[BTS_CTR_PAGING_ATTEMPTED] = 		{"paging:attempted", "Paging attempts for a subscriber"},
+	[BTS_CTR_PAGING_ALREADY] = 		{"paging:already", "Paging attempts ignored as subscriber was already being paged"},
+	[BTS_CTR_PAGING_RESPONDED] = 		{"paging:responded", "Paging attempts with successful paging response"},
+	[BTS_CTR_PAGING_EXPIRED] = 		{"paging:expired", "Paging Request expired because of timeout T3113"},
+	[BTS_CTR_PAGING_NO_ACTIVE_PAGING] =	{"paging:no_active_paging", "Paging response without an active paging request (arrived after paging expiration?)"},
+	[BTS_CTR_PAGING_MSC_FLUSH] =		{"paging:msc_flush", "Paging flushed due to MSC Reset BSSMAP message"},
+	[BTS_CTR_CHAN_ACT_TOTAL] =		{"chan_act:total", "Total number of Channel Activations"},
+	[BTS_CTR_CHAN_ACT_NACK] =		{"chan_act:nack", "Number of Channel Activations that the BTS NACKed"},
+	[BTS_CTR_RSL_UNKNOWN] =			{"rsl:unknown", "Number of unknown/unsupported RSL messages received from BTS"},
+	[BTS_CTR_RSL_IPA_NACK] =		{"rsl:ipa_nack", "Number of IPA (RTP/dyn-PDCH) related NACKs received from BTS"},
+	[BTS_CTR_RSL_DELETE_IND] =		{"rsl:delete_ind", "Number of RSL DELETE INDICATION (DL CCCH overload)"},
+	[BTS_CTR_MODE_MODIFY_NACK] =		{"chan:mode_modify_nack", "Number of Channel Mode Modify NACKs received from BTS"},
+
+	/* lchan/TS BORKEN state counters */
+	[BTS_CTR_LCHAN_BORKEN_FROM_UNUSED] =              {"lchan_borken:from_state:unused", "Transitions from lchan UNUSED state to BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK] =      {"lchan_borken:from_state:wait_activ_ack", "Transitions from lchan WAIT_ACTIV_ACK state to BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK] = {"lchan_borken:from_state:wait_rf_release_ack", "Transitions from lchan WAIT_RF_RELEASE_ACK state to BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_FROM_BORKEN] =              {"lchan_borken:from_state:borken", "Transitions from lchan BORKEN state to BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN] =             {"lchan_borken:from_state:unknown", "Transitions from an unknown lchan state to BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK] =        {"lchan_borken:event:chan_activ_ack", "CHAN_ACTIV_ACK received in the lchan BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK] =       {"lchan_borken:event:chan_activ_nack", "CHAN_ACTIV_NACK received in the lchan BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK] =       {"lchan_borken:event:rf_chan_rel_ack", "RF_CHAN_REL_ACK received in the lchan BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_EV_VTY] =                   {"lchan_borken:event:vty", "VTY commands received in the lchan BORKEN state"},
+	[BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN] =              {"lchan_borken:event:teardown", "lchan in a BORKEN state is shutting down (BTS disconnected?)"},
+	[BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED] =   {"ts_borken:from_state:not_initialized", "Transitions from TS NOT_INITIALIZED state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_UNUSED] =            {"ts_borken:from_state:unused", "Transitions from TS UNUSED state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT] =     {"ts_borken:from_state:wait_pdch_act", "Transitions from TS WAIT_PDCH_ACT state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_PDCH] =              {"ts_borken:from_state:pdch", "Transitions from TS PDCH state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT] =   {"ts_borken:from_state:wait_pdch_deact", "Transitions from TS WAIT_PDCH_DEACT state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_IN_USE] =            {"ts_borken:from_state:in_use", "Transitions from TS IN_USE state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_BORKEN] =            {"ts_borken:from_state:borken", "Transitions from TS BORKEN state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_FROM_UNKNOWN] =           {"ts_borken:from_state:unknown", "Transitions from an unknown TS state to BORKEN state"},
+	[BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK] =   {"ts_borken:event:pdch_act_ack_nack", "PDCH_ACT_ACK/NACK received in the TS BORKEN state"},
+	[BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK] = {"ts_borken:event:pdch_deact_ack_nack", "PDCH_DEACT_ACK/NACK received in the TS BORKEN state"},
+	[BTS_CTR_TS_BORKEN_EV_TEARDOWN] =            {"ts_borken:event:teardown", "TS in a BORKEN state is shutting down (BTS disconnected?)"},
+	[BTS_CTR_ASSIGNMENT_ATTEMPTED] =             {"assignment:attempted", "Assignment attempts"},
+	[BTS_CTR_ASSIGNMENT_COMPLETED] =             {"assignment:completed", "Assignment completed"},
+	[BTS_CTR_ASSIGNMENT_STOPPED] =               {"assignment:stopped", "Connection ended during Assignment"},
+	[BTS_CTR_ASSIGNMENT_NO_CHANNEL] =            {"assignment:no_channel", "Failure to allocate lchan for Assignment"},
+	[BTS_CTR_ASSIGNMENT_TIMEOUT] =               {"assignment:timeout", "Assignment timed out"},
+	[BTS_CTR_ASSIGNMENT_FAILED] =                {"assignment:failed", "Received Assignment Failure message"},
+	[BTS_CTR_ASSIGNMENT_ERROR] =                 {"assignment:error", "Assignment failed for other reason"},
+
+};
+
+static const struct rate_ctr_group_desc bts_ctrg_desc = {
+	"bts",
+	"base transceiver station",
+	OSMO_STATS_CLASS_GLOBAL,
+	ARRAY_SIZE(bts_ctr_description),
+	bts_ctr_description,
+};
+
+enum {
+	BTS_STAT_CHAN_LOAD_AVERAGE,
+	BTS_STAT_CHAN_CCCH_SDCCH4_USED,
+	BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL,
+	BTS_STAT_CHAN_TCH_F_USED,
+	BTS_STAT_CHAN_TCH_F_TOTAL,
+	BTS_STAT_CHAN_TCH_H_USED,
+	BTS_STAT_CHAN_TCH_H_TOTAL,
+	BTS_STAT_CHAN_SDCCH8_USED,
+	BTS_STAT_CHAN_SDCCH8_TOTAL,
+	BTS_STAT_CHAN_TCH_F_PDCH_USED,
+	BTS_STAT_CHAN_TCH_F_PDCH_TOTAL,
+	BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED,
+	BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL,
+	BTS_STAT_CHAN_SDCCH8_CBCH_USED,
+	BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL,
+	BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED,
+	BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL,
+	BTS_STAT_T3122,
+	BTS_STAT_RACH_BUSY,
+	BTS_STAT_RACH_ACCESS,
+	BTS_STAT_OML_CONNECTED,
+	BTS_STAT_RSL_CONNECTED,
+	BTS_STAT_LCHAN_BORKEN,
+	BTS_STAT_TS_BORKEN,
+};
+
+enum gsm_bts_type {
+	GSM_BTS_TYPE_UNKNOWN,
+	GSM_BTS_TYPE_BS11,
+	GSM_BTS_TYPE_NANOBTS,
+	GSM_BTS_TYPE_RBS2000,
+	GSM_BTS_TYPE_NOKIA_SITE,
+	GSM_BTS_TYPE_OSMOBTS,
+	_NUM_GSM_BTS_TYPE
+};
+extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1];
+extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1];
+
+enum gsm_bts_type_variant {
+	BTS_UNKNOWN,
+	BTS_OSMO_LITECELL15,
+	BTS_OSMO_OCTPHY,
+	BTS_OSMO_SYSMO,
+	BTS_OSMO_TRX,
+	_NUM_BTS_VARIANT
+};
+
+/* Used by OML layer for BTS Attribute reporting */
+enum bts_attribute {
+	BTS_TYPE_VARIANT,
+	BTS_SUB_MODEL,
+	TRX_PHY_VERSION,
+};
+
+struct vty;
+
+struct gsm_bts_model {
+	struct llist_head list;
+
+	enum gsm_bts_type type;
+	enum gsm_bts_type_variant variant;
+	const char *name;
+
+	bool started;
+	int (*start)(struct gsm_network *net);
+	int (*oml_rcvmsg)(struct msgb *msg);
+	char * (*oml_status)(const struct gsm_bts *bts);
+
+	void (*e1line_bind_ops)(struct e1inp_line *line);
+
+	void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts);
+	void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx);
+	void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts);
+
+	/* Should SI2bis and SI2ter be disabled by default on this BTS model? */
+	bool force_combined_si;
+
+	struct tlv_definition nm_att_tlvdef;
+
+	/* features of a given BTS model set via gsm_bts_model_register() locally */
+	struct bitvec features;
+	uint8_t _features_data[MAX_BTS_FEATURES/8];
+};
+
+/* One BTS */
+struct gsm_bts {
+	/* list header in net->bts_list */
+	struct llist_head list;
+
+	/* Geographical location of the BTS */
+	struct llist_head loc_list;
+
+	/* number of this BTS in network */
+	uint8_t nr;
+	/* human readable name / description */
+	char *description;
+	/* Cell Identity */
+	uint16_t cell_identity;
+	/* location area code of this BTS */
+	uint16_t location_area_code;
+	/* Base Station Identification Code (BSIC), lower 3 bits is BCC,
+	 * which is used as TSC for the CCCH */
+	uint8_t bsic;
+	/* type of BTS */
+	enum gsm_bts_type type;
+	enum gsm_bts_type_variant variant;
+	struct gsm_bts_model *model;
+	enum gsm_band band;
+	char version[MAX_VERSION_LENGTH];
+	char sub_model[MAX_VERSION_LENGTH];
+
+	/* features of a given BTS set/reported via OML */
+	struct bitvec features;
+	uint8_t _features_data[MAX_BTS_FEATURES/8];
+
+	/* Connected PCU version (if any) */
+	char pcu_version[MAX_VERSION_LENGTH];
+
+	/* maximum Tx power that the MS is permitted to use in this cell */
+	int ms_max_power;
+
+	/* how do we talk OML with this TRX? */
+	struct gsm_e1_subslot oml_e1_link;
+	uint8_t oml_tei;
+	struct e1inp_sign_link *oml_link;
+	/* Timer to use for deferred drop of OML link, see \ref ipaccess_drop_oml_deferred */
+	struct osmo_timer_list oml_drop_link_timer;
+	/* when OML link was established */
+	time_t uptime;
+
+	/* Abis network management O&M handle */
+	struct abis_nm_h *nmh;
+
+	struct gsm_abis_mo mo;
+
+	/* number of this BTS on given E1 link */
+	uint8_t bts_nr;
+
+	/* DTX features of this BTS */
+	enum gsm48_dtx_mode dtxu;
+	bool dtxd;
+
+	/* paging state and control */
+	struct gsm_bts_paging_state paging;
+
+	/* CCCH is on C0 */
+	struct gsm_bts_trx *c0;
+
+	struct {
+		struct gsm_abis_mo mo;
+	} site_mgr;
+
+	/* bitmask of all SI that are present/valid in si_buf */
+	uint32_t si_valid;
+	/* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */
+	uint8_t si2q_index; /* distinguish individual SI2quater messages */
+	uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */
+	/* buffers where we put the pre-computed SI */
+	sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
+	/* offsets used while generating SI2quater */
+	size_t e_offset;
+	size_t u_offset;
+	/* 3GPP TS 08.58 §8.5.1 BCCH INFORMATION. Some nanoBTS fail upon
+	 * receival of empty SI disabling unsupported SI. see OS#3707. */
+	bool si_unused_send_empty;
+
+	/* ip.access Unit ID's have Site/BTS/TRX layout */
+	union {
+		struct {
+			uint16_t site_id;
+			uint16_t bts_id;
+			uint32_t flags;
+			uint32_t rsl_ip;
+		} ip_access;
+		struct {
+			struct {
+				struct gsm_abis_mo mo;
+			} cclk;
+			struct {
+				struct gsm_abis_mo mo;
+			} rack;
+			struct gsm_envabtse envabtse[4];
+		} bs11;
+		struct {
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+				struct llist_head conn_groups;
+			} cf;
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+				struct llist_head conn_groups;
+			} is;
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+				struct llist_head conn_groups;
+			} con;
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+			} dp;
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+			} tf;
+			struct {
+				struct om2k_mo om2k_mo;
+				struct gsm_abis_mo mo;
+			} mctr;
+			uint32_t use_superchannel:1;
+			struct {
+				uint16_t limit;
+				uint16_t active;
+			} om2k_version[16];
+		} rbs2000;
+		struct {
+			uint8_t bts_type;
+			unsigned int configured:1,	/* we sent the config data request */
+				skip_reset:1,		/* skip reset at bootstrap */
+				no_loc_rel_cnf:1,	/* don't wait for RSL REL CONF */
+				bts_reset_timer_cnf,	/* timer for BTS RESET */
+				did_reset:1,		/* we received a RESET ACK */
+				wait_reset:2;		/* we are waiting for reset to complete */
+			struct osmo_timer_list reset_timer;
+		} nokia;
+	};
+
+	/* Not entirely sure how ip.access specific this is */
+	struct {
+		enum bts_gprs_mode mode;
+		struct {
+			struct gsm_abis_mo mo;
+			uint16_t nsei;
+			uint8_t timer[7];
+		} nse;
+		struct {
+			struct gsm_abis_mo mo;
+			uint16_t bvci;
+			uint8_t timer[11];
+			struct gprs_rlc_cfg rlc_cfg;
+		} cell;
+		struct gsm_bts_gprs_nsvc nsvc[2];
+		uint8_t rac;
+		uint8_t net_ctrl_ord;
+		bool ctrl_ack_type_use_block;
+		bool egprs_pkt_chan_request;
+	} gprs;
+
+	/* threshold (in percent) when BTS shall send CCCH LOAD IND */
+	int ccch_load_ind_thresh;
+
+	/* RACH NM values */
+	int rach_b_thresh;
+	int rach_ldavg_slots;
+
+	/* transceivers */
+	int num_trx;
+	struct llist_head trx_list;
+
+	/* SI related items */
+	int force_combined_si;
+	bool force_combined_si_set;
+	int bcch_change_mark;
+
+	/* Abis NM queue */
+	struct llist_head abis_queue;
+	int abis_nm_pend;
+
+	struct gsm_network *network;
+
+	/* should the channel allocator allocate channels from high TRX to TRX0,
+	 * rather than starting from TRX0 and go upwards? */
+	int chan_alloc_reverse;
+
+	enum neigh_list_manual_mode neigh_list_manual_mode;
+	/* parameters from which we build SYSTEM INFORMATION */
+	struct {
+		struct gsm48_rach_control rach_control;
+		uint8_t ncc_permitted;
+		struct gsm48_cell_sel_par cell_sel_par;
+		struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */
+		struct gsm48_cell_options cell_options;
+		struct gsm48_control_channel_descr chan_desc;
+		struct bitvec neigh_list;
+		struct bitvec cell_alloc;
+		struct bitvec si5_neigh_list;
+		struct osmo_earfcn_si2q si2quater_neigh_list;
+		size_t uarfcn_length; /* index for uarfcn and scramble lists */
+		struct {
+			/* bitmask large enough for all possible ARFCN's */
+			uint8_t neigh_list[1024/8];
+			uint8_t cell_alloc[1024/8];
+			/* If the user wants a different neighbor list in SI5 than in SI2 */
+			uint8_t si5_neigh_list[1024/8];
+			uint8_t meas_bw_list[MAX_EARFCN_LIST];
+			uint16_t earfcn_list[MAX_EARFCN_LIST];
+			uint16_t uarfcn_list[MAX_EARFCN_LIST];
+			uint16_t scramble_list[MAX_EARFCN_LIST];
+		} data;
+	} si_common;
+	bool early_classmark_allowed;
+	bool early_classmark_allowed_3g;
+	/* for testing only: Have an infinitely long radio link timeout */
+	bool infinite_radio_link_timeout;
+
+	/* do we use static (user-defined) system information messages? (bitmask) */
+	uint32_t si_mode_static;
+
+	/* access control class ramping */
+	struct acc_ramp acc_ramp;
+
+	/* exclude the BTS from the global RF Lock handling */
+	int excl_from_rf_lock;
+
+	/* supported codecs beside FR */
+	struct bts_codec_conf codec;
+
+	/* BTS dependencies bit field */
+	uint32_t depends_on[256/(8*4)];
+
+	/* full and half rate multirate config */
+	struct amr_multirate_conf mr_full;
+	struct amr_multirate_conf mr_half;
+
+	/* PCU socket state */
+	char *pcu_sock_path;
+	struct pcu_sock_state *pcu_state;
+
+	struct rate_ctr_group *bts_ctrs;
+	struct osmo_stat_item_group *bts_statg;
+
+	struct handover_cfg *ho;
+
+	/* A list of struct gsm_bts_ref, indicating neighbors of this BTS.
+	 * When the si_common neigh_list is in automatic mode, it is populated from this list as well as
+	 * gsm_network->neighbor_bss_cells. */
+	struct llist_head local_neighbors;
+
+	/* BTS-specific overrides for timer values from struct gsm_network. */
+	uint8_t T3122;	/* ASSIGNMENT REJECT wait indication */
+	bool T3113_dynamic; /* Calculate T3113 timeout dynamically based on BTS channel config and load */
+
+	/* Periodic channel load measurements are used to maintain T3122. */
+	struct load_counter chan_load_samples[7];
+	int chan_load_samples_idx;
+	uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */
+
+	/* cell broadcast system */
+	struct osmo_timer_list cbch_timer;
+	struct bts_smscb_chan_state cbch_basic;
+	struct bts_smscb_chan_state cbch_extended;
+	struct osmo_timer_list etws_timer;	/* when to stop ETWS PN */
+
+	struct llist_head oml_fail_rep;
+};
+
+#define GSM_BTS_SI2Q(bts, i)   (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
+#define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i))
+#define GSM_BTS_SI(bts, i)     (void *)((bts)->si_buf[i][0])
+
+/* this actually refers to the IPA transport, not the BTS model */
+static inline int is_ipaccess_bts(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_NANOBTS:
+	case GSM_BTS_TYPE_OSMOBTS:
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static inline int is_sysmobts_v2(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_OSMOBTS:
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static inline int is_siemens_bts(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_BS11:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline int is_nokia_bts(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_NOKIA_SITE:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline int is_ericsson_bts(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_RBS2000:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline int is_e1_bts(const struct gsm_bts *bts)
+{
+	switch (bts->type) {
+	case GSM_BTS_TYPE_BS11:
+	case GSM_BTS_TYPE_RBS2000:
+	case GSM_BTS_TYPE_NOKIA_SITE:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
+{
+	static struct osmo_location_area_id lai;
+	lai = (struct osmo_location_area_id){
+		.plmn = bts->network->plmn,
+		.lac = bts->location_area_code,
+	};
+	return &lai;
+}
+
+struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num);
+
+char *gsm_bts_name(const struct gsm_bts *bts);
+
+bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai);
+bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id);
+
+int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor);
+int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor);
+
+/* return the gsm_lchan for the CBCH (if it exists at all) */
+struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts);
+
+int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
+
+struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr);
+
+int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
+
+unsigned long long bts_uptime(const struct gsm_bts *bts);
+
+char *get_model_oml_status(const struct gsm_bts *bts);
+/* reset the state of all MO in the BTS */
+void gsm_bts_mo_reset(struct gsm_bts *bts);
+
+/* dependency handling */
+void bts_depend_mark(struct gsm_bts *bts, int dep);
+void bts_depend_clear(struct gsm_bts *bts, int dep);
+int bts_depend_check(struct gsm_bts *bts);
+int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other);
+
+int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts);
+void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
+
+void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);
+
+int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
+
+int gsm_bts_model_register(struct gsm_bts_model *model);
+struct gsm_bts_model *bts_model_find(enum gsm_bts_type type);
+
+enum gsm_bts_type str2btstype(const char *arg);
+const char *btstype2str(enum gsm_bts_type type);
+
+enum bts_attribute str2btsattr(const char *s);
+const char *btsatttr2str(enum bts_attribute v);
+
+enum gsm_bts_type_variant str2btsvariant(const char *arg);
+const char *btsvariant2str(enum gsm_bts_type_variant v);
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index c357981..a74e6d9 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -39,6 +39,7 @@
 struct mgcp_client;
 struct gsm0808_cell_id;
 struct osmo_mgcpc_ep;
+struct gsm_bts;
 
 /** annotations for msgb ownership */
 #define __uses
@@ -772,69 +773,8 @@
 	struct gsm_bts_trx_ts ts[TRX_NR_TS];
 };
 
-#define GSM_BTS_SI2Q(bts, i)   (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])
-#define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i))
-#define GSM_BTS_SI(bts, i)     (void *)((bts)->si_buf[i][0])
 #define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
 
-enum gsm_bts_type {
-	GSM_BTS_TYPE_UNKNOWN,
-	GSM_BTS_TYPE_BS11,
-	GSM_BTS_TYPE_NANOBTS,
-	GSM_BTS_TYPE_RBS2000,
-	GSM_BTS_TYPE_NOKIA_SITE,
-	GSM_BTS_TYPE_OSMOBTS,
-	_NUM_GSM_BTS_TYPE
-};
-
-enum gsm_bts_type_variant {
-	BTS_UNKNOWN,
-	BTS_OSMO_LITECELL15,
-	BTS_OSMO_OCTPHY,
-	BTS_OSMO_SYSMO,
-	BTS_OSMO_TRX,
-	_NUM_BTS_VARIANT
-};
-
-/* Used by OML layer for BTS Attribute reporting */
-enum bts_attribute {
-	BTS_TYPE_VARIANT,
-	BTS_SUB_MODEL,
-	TRX_PHY_VERSION,
-};
-
-struct vty;
-
-struct gsm_bts_model {
-	struct llist_head list;
-
-	enum gsm_bts_type type;
-	enum gsm_bts_type_variant variant;
-	const char *name;
-
-	bool started;
-	int (*start)(struct gsm_network *net);
-	int (*oml_rcvmsg)(struct msgb *msg);
-	char * (*oml_status)(const struct gsm_bts *bts);
-
-	void (*e1line_bind_ops)(struct e1inp_line *line);
-
-	void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts);
-	void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx);
-	void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts);
-
-	/* Should SI2bis and SI2ter be disabled by default on this BTS model? */
-	bool force_combined_si;
-
-	struct tlv_definition nm_att_tlvdef;
-
-	/* features of a given BTS model set via gsm_bts_model_register() locally */
-	struct bitvec features;
-	uint8_t _features_data[MAX_BTS_FEATURES/8];
-};
-
-
-
 /*
  * This keeps track of the paging status of one BTS. It
  * includes a number of pending requests, a back pointer
@@ -1004,280 +944,6 @@
 	struct msgb *mb;
 };
 
-/* One BTS */
-struct gsm_bts {
-	/* list header in net->bts_list */
-	struct llist_head list;
-
-	/* Geographical location of the BTS */
-	struct llist_head loc_list;
-
-	/* number of this BTS in network */
-	uint8_t nr;
-	/* human readable name / description */
-	char *description;
-	/* Cell Identity */
-	uint16_t cell_identity;
-	/* location area code of this BTS */
-	uint16_t location_area_code;
-	/* Base Station Identification Code (BSIC), lower 3 bits is BCC,
-	 * which is used as TSC for the CCCH */
-	uint8_t bsic;
-	/* type of BTS */
-	enum gsm_bts_type type;
-	enum gsm_bts_type_variant variant;
-	struct gsm_bts_model *model;
-	enum gsm_band band;
-	char version[MAX_VERSION_LENGTH];
-	char sub_model[MAX_VERSION_LENGTH];
-
-	/* features of a given BTS set/reported via OML */
-	struct bitvec features;
-	uint8_t _features_data[MAX_BTS_FEATURES/8];
-
-	/* Connected PCU version (if any) */
-	char pcu_version[MAX_VERSION_LENGTH];
-
-	/* maximum Tx power that the MS is permitted to use in this cell */
-	int ms_max_power;
-
-	/* how do we talk OML with this TRX? */
-	struct gsm_e1_subslot oml_e1_link;
-	uint8_t oml_tei;
-	struct e1inp_sign_link *oml_link;
-	/* Timer to use for deferred drop of OML link, see \ref ipaccess_drop_oml_deferred */
-	struct osmo_timer_list oml_drop_link_timer;
-	/* when OML link was established */
-	time_t uptime;
-
-	/* Abis network management O&M handle */
-	struct abis_nm_h *nmh;
-
-	struct gsm_abis_mo mo;
-
-	/* number of this BTS on given E1 link */
-	uint8_t bts_nr;
-
-	/* DTX features of this BTS */
-	enum gsm48_dtx_mode dtxu;
-	bool dtxd;
-
-	/* paging state and control */
-	struct gsm_bts_paging_state paging;
-
-	/* CCCH is on C0 */
-	struct gsm_bts_trx *c0;
-
-	struct {
-		struct gsm_abis_mo mo;
-	} site_mgr;
-
-	/* bitmask of all SI that are present/valid in si_buf */
-	uint32_t si_valid;
-	/* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */
-	uint8_t si2q_index; /* distinguish individual SI2quater messages */
-	uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */
-	/* buffers where we put the pre-computed SI */
-	sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM];
-	/* offsets used while generating SI2quater */
-	size_t e_offset;
-	size_t u_offset;
-	/* 3GPP TS 08.58 §8.5.1 BCCH INFORMATION. Some nanoBTS fail upon
-	 * receival of empty SI disabling unsupported SI. see OS#3707. */
-	bool si_unused_send_empty;
-
-	/* ip.access Unit ID's have Site/BTS/TRX layout */
-	union {
-		struct {
-			uint16_t site_id;
-			uint16_t bts_id;
-			uint32_t flags;
-			uint32_t rsl_ip;
-		} ip_access;
-		struct {
-			struct {
-				struct gsm_abis_mo mo;
-			} cclk;
-			struct {
-				struct gsm_abis_mo mo;
-			} rack;
-			struct gsm_envabtse envabtse[4];
-		} bs11;
-		struct {
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-				struct llist_head conn_groups;
-			} cf;
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-				struct llist_head conn_groups;
-			} is;
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-				struct llist_head conn_groups;
-			} con;
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-			} dp;
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-			} tf;
-			struct {
-				struct om2k_mo om2k_mo;
-				struct gsm_abis_mo mo;
-			} mctr;
-			uint32_t use_superchannel:1;
-			struct {
-				uint16_t limit;
-				uint16_t active;
-			} om2k_version[16];
-		} rbs2000;
-		struct {
-			uint8_t bts_type;
-			unsigned int configured:1,	/* we sent the config data request */
-				skip_reset:1,		/* skip reset at bootstrap */
-				no_loc_rel_cnf:1,	/* don't wait for RSL REL CONF */
-				bts_reset_timer_cnf,	/* timer for BTS RESET */
-				did_reset:1,		/* we received a RESET ACK */
-				wait_reset:2;		/* we are waiting for reset to complete */
-			struct osmo_timer_list reset_timer;
-		} nokia;
-	};
-
-	/* Not entirely sure how ip.access specific this is */
-	struct {
-		enum bts_gprs_mode mode;
-		struct {
-			struct gsm_abis_mo mo;
-			uint16_t nsei;
-			uint8_t timer[7];
-		} nse;
-		struct {
-			struct gsm_abis_mo mo;
-			uint16_t bvci;
-			uint8_t timer[11];
-			struct gprs_rlc_cfg rlc_cfg;
-		} cell;
-		struct gsm_bts_gprs_nsvc nsvc[2];
-		uint8_t rac;
-		uint8_t net_ctrl_ord;
-		bool ctrl_ack_type_use_block;
-		bool egprs_pkt_chan_request;
-	} gprs;
-
-	/* threshold (in percent) when BTS shall send CCCH LOAD IND */
-	int ccch_load_ind_thresh;
-
-	/* RACH NM values */
-	int rach_b_thresh;
-	int rach_ldavg_slots;
-
-	/* transceivers */
-	int num_trx;
-	struct llist_head trx_list;
-
-	/* SI related items */
-	int force_combined_si;
-	bool force_combined_si_set;
-	int bcch_change_mark;
-
-	/* Abis NM queue */
-	struct llist_head abis_queue;
-	int abis_nm_pend;
-
-	struct gsm_network *network;
-
-	/* should the channel allocator allocate channels from high TRX to TRX0,
-	 * rather than starting from TRX0 and go upwards? */
-	int chan_alloc_reverse;
-
-	enum neigh_list_manual_mode neigh_list_manual_mode;
-	/* parameters from which we build SYSTEM INFORMATION */
-	struct {
-		struct gsm48_rach_control rach_control;
-		uint8_t ncc_permitted;
-		struct gsm48_cell_sel_par cell_sel_par;
-		struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */
-		struct gsm48_cell_options cell_options;
-		struct gsm48_control_channel_descr chan_desc;
-		struct bitvec neigh_list;
-		struct bitvec cell_alloc;
-		struct bitvec si5_neigh_list;
-		struct osmo_earfcn_si2q si2quater_neigh_list;
-		size_t uarfcn_length; /* index for uarfcn and scramble lists */
-		struct {
-			/* bitmask large enough for all possible ARFCN's */
-			uint8_t neigh_list[1024/8];
-			uint8_t cell_alloc[1024/8];
-			/* If the user wants a different neighbor list in SI5 than in SI2 */
-			uint8_t si5_neigh_list[1024/8];
-			uint8_t meas_bw_list[MAX_EARFCN_LIST];
-			uint16_t earfcn_list[MAX_EARFCN_LIST];
-			uint16_t uarfcn_list[MAX_EARFCN_LIST];
-			uint16_t scramble_list[MAX_EARFCN_LIST];
-		} data;
-	} si_common;
-	bool early_classmark_allowed;
-	bool early_classmark_allowed_3g;
-	/* for testing only: Have an infinitely long radio link timeout */
-	bool infinite_radio_link_timeout;
-
-	/* do we use static (user-defined) system information messages? (bitmask) */
-	uint32_t si_mode_static;
-
-	/* access control class ramping */
-	struct acc_ramp acc_ramp;
-
-	/* exclude the BTS from the global RF Lock handling */
-	int excl_from_rf_lock;
-
-	/* supported codecs beside FR */
-	struct bts_codec_conf codec;
-
-	/* BTS dependencies bit field */
-	uint32_t depends_on[256/(8*4)];
-
-	/* full and half rate multirate config */
-	struct amr_multirate_conf mr_full;
-	struct amr_multirate_conf mr_half;
-
-	/* PCU socket state */
-	char *pcu_sock_path;
-	struct pcu_sock_state *pcu_state;
-
-	struct rate_ctr_group *bts_ctrs;
-	struct osmo_stat_item_group *bts_statg;
-
-	struct handover_cfg *ho;
-
-	/* A list of struct gsm_bts_ref, indicating neighbors of this BTS.
-	 * When the si_common neigh_list is in automatic mode, it is populated from this list as well as
-	 * gsm_network->neighbor_bss_cells. */
-	struct llist_head local_neighbors;
-
-	/* BTS-specific overrides for timer values from struct gsm_network. */
-	uint8_t T3122;	/* ASSIGNMENT REJECT wait indication */
-	bool T3113_dynamic; /* Calculate T3113 timeout dynamically based on BTS channel config and load */
-
-	/* Periodic channel load measurements are used to maintain T3122. */
-	struct load_counter chan_load_samples[7];
-	int chan_load_samples_idx;
-	uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */
-
-	/* cell broadcast system */
-	struct osmo_timer_list cbch_timer;
-	struct bts_smscb_chan_state cbch_basic;
-	struct bts_smscb_chan_state cbch_extended;
-	struct osmo_timer_list etws_timer;	/* when to stop ETWS PN */
-
-	struct llist_head oml_fail_rep;
-};
-
 /* One rejected BTS */
 struct gsm_bts_rejected {
 	/* list header in net->bts_rejected */
@@ -1291,28 +957,14 @@
 
 struct gsm_network *gsm_network_init(void *ctx);
 
-struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num);
 struct gsm_bts *gsm_bts_num(const struct gsm_network *net, int num);
-bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai);
-bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id);
 struct gsm_bts *gsm_bts_by_cell_id(const struct gsm_network *net,
 				   const struct gsm0808_cell_id *cell_id,
 				   int match_idx);
-int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor);
-int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor);
 
 struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
 struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
 
-enum gsm_bts_type str2btstype(const char *arg);
-const char *btstype2str(enum gsm_bts_type type);
-
-enum bts_attribute str2btsattr(const char *s);
-const char *btsatttr2str(enum bts_attribute v);
-
-enum gsm_bts_type_variant str2btsvariant(const char *arg);
-const char *btsvariant2str(enum gsm_bts_type_variant v);
-
 extern const struct value_string gsm_chreq_descs[];
 extern const struct value_string gsm_pchant_names[];
 extern const struct value_string gsm_pchant_descs[];
@@ -1323,7 +975,6 @@
 enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
 const char *gsm_lchant_name(enum gsm_chan_t c);
 const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
-char *gsm_bts_name(const struct gsm_bts *bts);
 char *gsm_trx_name(const struct gsm_bts_trx *trx);
 char *gsm_ts_name(const struct gsm_bts_trx_ts *ts);
 char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts);
@@ -1335,6 +986,8 @@
 }
 
 void gsm_abis_mo_reset(struct gsm_abis_mo *mo);
+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);
 
 struct gsm_nm_state *
 gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class,
@@ -1343,9 +996,6 @@
 gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
 	     const struct abis_om_obj_inst *obj_inst);
 
-/* reset the state of all MO in the BTS */
-void gsm_bts_mo_reset(struct gsm_bts *bts);
-
 uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
 			  uint8_t ts_nr, uint8_t lchan_nr);
 uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan);
@@ -1356,16 +1006,7 @@
 			   const struct gsm_lchan *lchan);
 void gsm48_lchan2chan_desc_as_configured(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan);
 
-/* return the gsm_lchan for the CBCH (if it exists at all) */
-struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts);
-
-static inline 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;
-}
+uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts);
 
 struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
 				   int *rc);
@@ -1375,164 +1016,11 @@
 bool ts_is_tch(struct gsm_bts_trx_ts *ts);
 
 
-static inline struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) {
-	if (!conn || !conn->lchan)
-		return NULL;
-	return conn->lchan->ts->trx->bts;
-}
+struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn);
 
 void conn_update_ms_power_class(struct gsm_subscriber_connection *conn, uint8_t power_class);
 void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm);
 
-enum bts_counter_id {
-	BTS_CTR_CHREQ_TOTAL,
-	BTS_CTR_CHREQ_SUCCESSFUL,
-	BTS_CTR_CHREQ_NO_CHANNEL,
-	BTS_CTR_CHAN_RF_FAIL,
-	BTS_CTR_CHAN_RLL_ERR,
-	BTS_CTR_BTS_OML_FAIL,
-	BTS_CTR_BTS_RSL_FAIL,
-	BTS_CTR_CODEC_AMR_F,
-	BTS_CTR_CODEC_AMR_H,
-	BTS_CTR_CODEC_EFR,
-	BTS_CTR_CODEC_V1_FR,
-	BTS_CTR_CODEC_V1_HR,
-	BTS_CTR_PAGING_ATTEMPTED,
-	BTS_CTR_PAGING_ALREADY,
-	BTS_CTR_PAGING_RESPONDED,
-	BTS_CTR_PAGING_EXPIRED,
-	BTS_CTR_PAGING_NO_ACTIVE_PAGING,
-	BTS_CTR_PAGING_MSC_FLUSH,
-	BTS_CTR_CHAN_ACT_TOTAL,
-	BTS_CTR_CHAN_ACT_NACK,
-	BTS_CTR_RSL_UNKNOWN,
-	BTS_CTR_RSL_IPA_NACK,
-	BTS_CTR_RSL_DELETE_IND,
-	BTS_CTR_MODE_MODIFY_NACK,
-	BTS_CTR_LCHAN_BORKEN_FROM_UNUSED,
-	BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK,
-	BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK,
-	BTS_CTR_LCHAN_BORKEN_FROM_BORKEN,
-	BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN,
-	BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK,
-	BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK,
-	BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK,
-	BTS_CTR_LCHAN_BORKEN_EV_VTY,
-	BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN,
-	BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED,
-	BTS_CTR_TS_BORKEN_FROM_UNUSED,
-	BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT,
-	BTS_CTR_TS_BORKEN_FROM_PDCH,
-	BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT,
-	BTS_CTR_TS_BORKEN_FROM_IN_USE,
-	BTS_CTR_TS_BORKEN_FROM_BORKEN,
-	BTS_CTR_TS_BORKEN_FROM_UNKNOWN,
-	BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK,
-	BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK,
-	BTS_CTR_TS_BORKEN_EV_TEARDOWN,
-	BTS_CTR_ASSIGNMENT_ATTEMPTED,
-	BTS_CTR_ASSIGNMENT_COMPLETED,
-	BTS_CTR_ASSIGNMENT_STOPPED,
-	BTS_CTR_ASSIGNMENT_NO_CHANNEL,
-	BTS_CTR_ASSIGNMENT_TIMEOUT,
-	BTS_CTR_ASSIGNMENT_FAILED,
-	BTS_CTR_ASSIGNMENT_ERROR,
-};
-
-static const struct rate_ctr_desc bts_ctr_description[] = {
-	[BTS_CTR_CHREQ_TOTAL] = 		{"chreq:total", "Received channel requests"},
-	[BTS_CTR_CHREQ_SUCCESSFUL] =  		{"chreq:successful", "Successful channel requests (immediate assign sent)"},
-	[BTS_CTR_CHREQ_NO_CHANNEL] = 		{"chreq:no_channel", "Sent to MS no channel available"},
-	[BTS_CTR_CHAN_RF_FAIL] = 		{"chan:rf_fail", "Received a RF failure indication from BTS"},
-	[BTS_CTR_CHAN_RLL_ERR] = 		{"chan:rll_err", "Received a RLL failure with T200 cause from BTS"},
-	[BTS_CTR_BTS_OML_FAIL] = 		{"oml_fail", "Received a TEI down on a OML link"},
-	[BTS_CTR_BTS_RSL_FAIL] = 		{"rsl_fail", "Received a TEI down on a OML link"},
-	[BTS_CTR_CODEC_AMR_F] =			{"codec:amr_f", "Count the usage of AMR/F codec by channel mode requested"},
-	[BTS_CTR_CODEC_AMR_H] =			{"codec:amr_h", "Count the usage of AMR/H codec by channel mode requested"},
-	[BTS_CTR_CODEC_EFR] = 			{"codec:efr", "Count the usage of EFR codec by channel mode requested"},
-	[BTS_CTR_CODEC_V1_FR] =			{"codec:fr", "Count the usage of FR codec by channel mode requested"},
-	[BTS_CTR_CODEC_V1_HR] =			{"codec:hr", "Count the usage of HR codec by channel mode requested"},
-
-	[BTS_CTR_PAGING_ATTEMPTED] = 		{"paging:attempted", "Paging attempts for a subscriber"},
-	[BTS_CTR_PAGING_ALREADY] = 		{"paging:already", "Paging attempts ignored as subscriber was already being paged"},
-	[BTS_CTR_PAGING_RESPONDED] = 		{"paging:responded", "Paging attempts with successful paging response"},
-	[BTS_CTR_PAGING_EXPIRED] = 		{"paging:expired", "Paging Request expired because of timeout T3113"},
-	[BTS_CTR_PAGING_NO_ACTIVE_PAGING] =	{"paging:no_active_paging", "Paging response without an active paging request (arrived after paging expiration?)"},
-	[BTS_CTR_PAGING_MSC_FLUSH] =		{"paging:msc_flush", "Paging flushed due to MSC Reset BSSMAP message"},
-	[BTS_CTR_CHAN_ACT_TOTAL] =		{"chan_act:total", "Total number of Channel Activations"},
-	[BTS_CTR_CHAN_ACT_NACK] =		{"chan_act:nack", "Number of Channel Activations that the BTS NACKed"},
-	[BTS_CTR_RSL_UNKNOWN] =			{"rsl:unknown", "Number of unknown/unsupported RSL messages received from BTS"},
-	[BTS_CTR_RSL_IPA_NACK] =		{"rsl:ipa_nack", "Number of IPA (RTP/dyn-PDCH) related NACKs received from BTS"},
-	[BTS_CTR_RSL_DELETE_IND] =		{"rsl:delete_ind", "Number of RSL DELETE INDICATION (DL CCCH overload)"},
-	[BTS_CTR_MODE_MODIFY_NACK] =		{"chan:mode_modify_nack", "Number of Channel Mode Modify NACKs received from BTS"},
-
-	/* lchan/TS BORKEN state counters */
-	[BTS_CTR_LCHAN_BORKEN_FROM_UNUSED] =              {"lchan_borken:from_state:unused", "Transitions from lchan UNUSED state to BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK] =      {"lchan_borken:from_state:wait_activ_ack", "Transitions from lchan WAIT_ACTIV_ACK state to BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK] = {"lchan_borken:from_state:wait_rf_release_ack", "Transitions from lchan WAIT_RF_RELEASE_ACK state to BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_FROM_BORKEN] =              {"lchan_borken:from_state:borken", "Transitions from lchan BORKEN state to BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN] =             {"lchan_borken:from_state:unknown", "Transitions from an unknown lchan state to BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK] =        {"lchan_borken:event:chan_activ_ack", "CHAN_ACTIV_ACK received in the lchan BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK] =       {"lchan_borken:event:chan_activ_nack", "CHAN_ACTIV_NACK received in the lchan BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK] =       {"lchan_borken:event:rf_chan_rel_ack", "RF_CHAN_REL_ACK received in the lchan BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_EV_VTY] =                   {"lchan_borken:event:vty", "VTY commands received in the lchan BORKEN state"},
-	[BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN] =              {"lchan_borken:event:teardown", "lchan in a BORKEN state is shutting down (BTS disconnected?)"},
-	[BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED] =   {"ts_borken:from_state:not_initialized", "Transitions from TS NOT_INITIALIZED state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_UNUSED] =            {"ts_borken:from_state:unused", "Transitions from TS UNUSED state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT] =     {"ts_borken:from_state:wait_pdch_act", "Transitions from TS WAIT_PDCH_ACT state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_PDCH] =              {"ts_borken:from_state:pdch", "Transitions from TS PDCH state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT] =   {"ts_borken:from_state:wait_pdch_deact", "Transitions from TS WAIT_PDCH_DEACT state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_IN_USE] =            {"ts_borken:from_state:in_use", "Transitions from TS IN_USE state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_BORKEN] =            {"ts_borken:from_state:borken", "Transitions from TS BORKEN state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_FROM_UNKNOWN] =           {"ts_borken:from_state:unknown", "Transitions from an unknown TS state to BORKEN state"},
-	[BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK] =   {"ts_borken:event:pdch_act_ack_nack", "PDCH_ACT_ACK/NACK received in the TS BORKEN state"},
-	[BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK] = {"ts_borken:event:pdch_deact_ack_nack", "PDCH_DEACT_ACK/NACK received in the TS BORKEN state"},
-	[BTS_CTR_TS_BORKEN_EV_TEARDOWN] =            {"ts_borken:event:teardown", "TS in a BORKEN state is shutting down (BTS disconnected?)"},
-	[BTS_CTR_ASSIGNMENT_ATTEMPTED] =             {"assignment:attempted", "Assignment attempts"},
-	[BTS_CTR_ASSIGNMENT_COMPLETED] =             {"assignment:completed", "Assignment completed"},
-	[BTS_CTR_ASSIGNMENT_STOPPED] =               {"assignment:stopped", "Connection ended during Assignment"},
-	[BTS_CTR_ASSIGNMENT_NO_CHANNEL] =            {"assignment:no_channel", "Failure to allocate lchan for Assignment"},
-	[BTS_CTR_ASSIGNMENT_TIMEOUT] =               {"assignment:timeout", "Assignment timed out"},
-	[BTS_CTR_ASSIGNMENT_FAILED] =                {"assignment:failed", "Received Assignment Failure message"},
-	[BTS_CTR_ASSIGNMENT_ERROR] =                 {"assignment:error", "Assignment failed for other reason"},
-
-};
-
-static const struct rate_ctr_group_desc bts_ctrg_desc = {
-	"bts",
-	"base transceiver station",
-	OSMO_STATS_CLASS_GLOBAL,
-	ARRAY_SIZE(bts_ctr_description),
-	bts_ctr_description,
-};
-
-enum {
-	BTS_STAT_CHAN_LOAD_AVERAGE,
-	BTS_STAT_CHAN_CCCH_SDCCH4_USED,
-	BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL,
-	BTS_STAT_CHAN_TCH_F_USED,
-	BTS_STAT_CHAN_TCH_F_TOTAL,
-	BTS_STAT_CHAN_TCH_H_USED,
-	BTS_STAT_CHAN_TCH_H_TOTAL,
-	BTS_STAT_CHAN_SDCCH8_USED,
-	BTS_STAT_CHAN_SDCCH8_TOTAL,
-	BTS_STAT_CHAN_TCH_F_PDCH_USED,
-	BTS_STAT_CHAN_TCH_F_PDCH_TOTAL,
-	BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED,
-	BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL,
-	BTS_STAT_CHAN_SDCCH8_CBCH_USED,
-	BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL,
-	BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_USED,
-	BTS_STAT_CHAN_TCH_F_TCH_H_PDCH_TOTAL,
-	BTS_STAT_T3122,
-	BTS_STAT_RACH_BUSY,
-	BTS_STAT_RACH_ACCESS,
-	BTS_STAT_OML_CONNECTED,
-	BTS_STAT_RSL_CONNECTED,
-	BTS_STAT_LCHAN_BORKEN,
-	BTS_STAT_TS_BORKEN,
-};
-
 enum {
 	BSC_CTR_ASSIGNMENT_ATTEMPTED,
 	BSC_CTR_ASSIGNMENT_COMPLETED,
@@ -1743,20 +1231,8 @@
                 ver : 7;
 };
 
-static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts)
-{
-	static struct osmo_location_area_id lai;
-	lai = (struct osmo_location_area_id){
-		.plmn = bts->network->plmn,
-		.lac = bts->location_area_code,
-	};
-	return &lai;
-}
-
 extern void talloc_ctx_init(void *ctx_root);
 
-int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
-
 enum gsm_bts_type parse_btstype(const char *arg);
 const char *btstype2str(enum gsm_bts_type type);
 struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
@@ -1764,91 +1240,14 @@
 
 extern void *tall_bsc_ctx;
 
-/* this actually refers to the IPA transport, not the BTS model */
-static inline int is_ipaccess_bts(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_NANOBTS:
-	case GSM_BTS_TYPE_OSMOBTS:
-		return 1;
-	default:
-		break;
-	}
-	return 0;
-}
-
-static inline int is_sysmobts_v2(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_OSMOBTS:
-		return 1;
-	default:
-		break;
-	}
-	return 0;
-}
-
-static inline int is_siemens_bts(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_BS11:
-		return 1;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-static inline int is_nokia_bts(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_NOKIA_SITE:
-		return 1;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-static inline int is_ericsson_bts(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_RBS2000:
-		return 1;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-static inline int is_e1_bts(const struct gsm_bts *bts)
-{
-	switch (bts->type) {
-	case GSM_BTS_TYPE_BS11:
-	case GSM_BTS_TYPE_RBS2000:
-	case GSM_BTS_TYPE_NOKIA_SITE:
-		return 1;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
 extern struct gsm_network *bsc_gsmnet;
 
 enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid);
 const char *bts_gprs_mode_name(enum bts_gprs_mode mode);
-int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode);
 
 void gsm48_ra_id_by_bts(struct gsm48_ra_id *buf, struct gsm_bts *bts);
 void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
 
-int gsm_bts_model_register(struct gsm_bts_model *model);
-
 struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network);
 
 struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic);
@@ -1858,42 +1257,23 @@
 		   uint8_t e1_ts, uint8_t e1_ts_ss);
 
 void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason);
-struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr);
 int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx);
 int gsm_bts_set_system_infos(struct gsm_bts *bts);
 
 /* generic E1 line operations for all ISDN-based BTS. */
 extern struct e1inp_line_ops bts_isdn_e1inp_line_ops;
 
-extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1];
-extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1];
-
-char *get_model_oml_status(const struct gsm_bts *bts);
-
-unsigned long long bts_uptime(const struct gsm_bts *bts);
-
 /* control interface handling */
 int bsc_base_ctrl_cmds_install(void);
 
-/* dependency handling */
-void bts_depend_mark(struct gsm_bts *bts, int dep);
-void bts_depend_clear(struct gsm_bts *bts, int dep);
-int bts_depend_check(struct gsm_bts *bts);
-int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other);
-
-int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts);
-void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value);
-
 bool trx_is_usable(const struct gsm_bts_trx *trx);
 bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
 
 int gsm_lchan_type_by_pchan(enum gsm_phys_chan_config pchan);
 enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type);
 
-void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);
 void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data);
-
-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);
+int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan);
 
 bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx);
 
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 at gnumonks.org>
+ * (C) 2020 by sysmocom s.f.m.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/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>
diff --git a/tests/abis/Makefile.am b/tests/abis/Makefile.am
index 60054d9..a01f56a 100644
--- a/tests/abis/Makefile.am
+++ b/tests/abis/Makefile.am
@@ -26,6 +26,7 @@
 
 abis_test_LDADD = \
 	$(top_builddir)/src/osmo-bsc/abis_nm.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(top_builddir)/src/osmo-bsc/net_init.o \
 	$(LIBOSMOCORE_LIBS) \
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index 43934f3..767a9fb 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -26,6 +26,7 @@
 #include <osmocom/gsm/gsm23003.h>
 
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/bsc/abis_nm.h>
 #include <osmocom/bsc/debug.h>
 
diff --git a/tests/bsc/Makefile.am b/tests/bsc/Makefile.am
index b301f9e..7ea002e 100644
--- a/tests/bsc/Makefile.am
+++ b/tests/bsc/Makefile.am
@@ -37,6 +37,7 @@
 	$(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \
 	$(top_builddir)/src/osmo-bsc/osmo_bsc_filter.o \
 	$(top_builddir)/src/osmo-bsc/bsc_subscriber.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(top_builddir)/src/osmo-bsc/handover_cfg.o \
 	$(top_builddir)/src/osmo-bsc/handover_logic.o \
diff --git a/tests/codec_pref/codec_pref_test.c b/tests/codec_pref/codec_pref_test.c
index f163f35..5f4c831 100644
--- a/tests/codec_pref/codec_pref_test.c
+++ b/tests/codec_pref/codec_pref_test.c
@@ -20,6 +20,7 @@
  */
 
 #include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
 #include <osmocom/bsc/osmo_bsc.h>
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/core/application.h>
diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am
index aff7c7d..21b7873 100644
--- a/tests/gsm0408/Makefile.am
+++ b/tests/gsm0408/Makefile.am
@@ -25,6 +25,7 @@
 gsm0408_test_LDADD = \
 	$(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
 	$(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(top_builddir)/src/osmo-bsc/net_init.o \
 	$(top_builddir)/src/osmo-bsc/rest_octets.o \
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 5ff9491..b3e8b11 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -29,6 +29,7 @@
 #include <osmocom/bsc/arfcn_range_encode.h>
 #include <osmocom/bsc/system_information.h>
 #include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/application.h>
 #include <osmocom/core/byteswap.h>
diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am
index 736b444..571b7a9 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -57,6 +57,7 @@
 	$(top_builddir)/src/osmo-bsc/bsc_subscr_conn_fsm.o \
 	$(top_builddir)/src/osmo-bsc/bsc_subscriber.o \
 	$(top_builddir)/src/osmo-bsc/bsc_vty.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \
 	$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
 	$(top_builddir)/src/osmo-bsc/bts_unknown.o \
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index 1a756cd..edc3219 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -47,6 +47,7 @@
 #include <osmocom/bsc/lchan_fsm.h>
 #include <osmocom/bsc/handover_fsm.h>
 #include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
 
 void *ctx;
 
diff --git a/tests/nanobts_omlattr/Makefile.am b/tests/nanobts_omlattr/Makefile.am
index aa7045e..93fdbe8 100644
--- a/tests/nanobts_omlattr/Makefile.am
+++ b/tests/nanobts_omlattr/Makefile.am
@@ -25,6 +25,7 @@
 nanobts_omlattr_test_LDADD = \
 	$(top_builddir)/src/osmo-bsc/abis_nm.o \
 	$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
+	$(top_builddir)/src/osmo-bsc/bts.o \
 	$(top_builddir)/src/osmo-bsc/gsm_data.o \
 	$(LIBOSMOCORE_LIBS) \
 	$(LIBOSMOGSM_LIBS) \
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index a3ba8e0..4180f4a 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -22,6 +22,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/gsm_data.h>
 #include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
+#include <osmocom/bsc/bts.h>
 
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/utils.h>

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

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-Change-Id: I00c15f5285b5c1a0109279b7ab192d5467a04ece
Gerrit-Change-Number: 19287
Gerrit-PatchSet: 4
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Hoernchen <ewild at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis at fe80.eu>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200718/d5c4da67/attachment.htm>


More information about the gerrit-log mailing list