[BSC] Move the BTS-type specific code from bcs_init.c to bts_*.c

bsc_init.c was a big mess even only for two supported BTS models,
so before adding more BTS types, this needs a cleanup.

All the BTS specific code from bsc_init.c has now moved into
bts_{siemens_bs11,ipaccess_nanobts}.c

This has required that input_event() and nm_state_event() get both
converted to proper libosmocore signals instead of referencing external
symbols.
diff --git a/openbsc/src/bts_ipaccess_nanobts.c b/openbsc/src/bts_ipaccess_nanobts.c
index 40f6108..6c7b2bd 100644
--- a/openbsc/src/bts_ipaccess_nanobts.c
+++ b/openbsc/src/bts_ipaccess_nanobts.c
@@ -20,9 +20,12 @@
  */
 
 #include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <osmocore/tlv.h>
 
 #include <openbsc/gsm_data.h>
-#include <osmocore/tlv.h>
+#include <openbsc/signal.h>
 #include <openbsc/abis_nm.h>
 
 static struct gsm_bts_model model_nanobts = {
@@ -76,6 +79,358 @@
 	},
 };
 
+static unsigned char nanobts_attr_bts[] = {
+	NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73,
+	/* interference avg. period in numbers of SACCH multifr */
+	NM_ATT_INTAVE_PARAM, 0x06,
+	/* conn fail based on SACCH error rate */
+	NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10,
+	NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8,
+	NM_ATT_MAX_TA, 0x3f,
+	NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */
+	NM_ATT_CCCH_L_T, 10, /* percent */
+	NM_ATT_CCCH_L_I_P, 1, /* seconds */
+	NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */
+	NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */
+	NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */
+	NM_ATT_NY1, 10, /* 10 retransmissions of physical config */
+	NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
+	NM_ATT_BSIC, HARDCODED_BSIC,
+	NM_ATT_IPACC_CGI, 0, 7,  0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00,
+};
+
+static unsigned char nanobts_attr_radio[] = {
+	NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */
+	NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
+};
+
+static unsigned char nanobts_attr_nse[] = {
+	NM_ATT_IPACC_NSEI, 0, 2,  0x03, 0x9d, /* NSEI 925 */
+	NM_ATT_IPACC_NS_CFG, 0, 7,  3,  /* (un)blocking timer (Tns-block) */
+				    3,  /* (un)blocking retries */
+				    3,  /* reset timer (Tns-reset) */
+				    3,  /* reset retries */
+				    30,  /* test timer (Tns-test) */
+				    3,  /* alive timer (Tns-alive) */
+				    10, /* alive retrires */
+	NM_ATT_IPACC_BSSGP_CFG, 0, 11,
+				    3,  /* blockimg timer (T1) */
+				    3,  /* blocking retries */
+				    3,  /* unblocking retries */
+				    3,  /* reset timer */
+				    3,  /* reset retries */
+				    10, /* suspend timer (T3) in 100ms */
+				    3,  /* suspend retries */
+				    10, /* resume timer (T4) in 100ms */
+				    3,  /* resume retries */
+				    10, /* capability update timer (T5) */
+				    3,  /* capability update retries */
+};
+
+static unsigned char nanobts_attr_cell[] = {
+	NM_ATT_IPACC_RAC, 0, 1,  1, /* routing area code */
+	NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2,
+		5,	/* repeat time (50ms) */
+		3,	/* repeat count */
+	NM_ATT_IPACC_BVCI, 0, 2,  0x03, 0x9d, /* BVCI 925 */
+	NM_ATT_IPACC_RLC_CFG, 0, 9,
+		20, 	/* T3142 */
+		5, 	/* T3169 */
+		5,	/* T3191 */
+		200,	/* T3193 */
+		5,	/* T3195 */
+		10,	/* N3101 */
+		4,	/* N3103 */
+		8,	/* N3105 */
+		15,	/* RLC CV countdown */
+	NM_ATT_IPACC_CODING_SCHEMES, 0, 2,  0x0f, 0x00,	/* CS1..CS4 */
+	NM_ATT_IPACC_RLC_CFG_2, 0, 5,
+		0x00, 250,	/* T downlink TBF extension (0..500) */
+		0x00, 250,	/* T uplink TBF extension (0..500) */
+		2,	/* CS2 */
+#if 0
+	/* EDGE model only, breaks older models.
+	 * Should inquire the BTS capabilities */
+	NM_ATT_IPACC_RLC_CFG_3, 0, 1,
+		2,	/* MCS2 */
+#endif
+};
+
+static unsigned char nanobts_attr_nsvc0[] = {
+	NM_ATT_IPACC_NSVCI, 0, 2,  0x03, 0x9d, /* 925 */
+	NM_ATT_IPACC_NS_LINK_CFG, 0, 8,
+		0x59, 0xd8, /* remote udp port (23000) */
+		192, 168, 100, 11, /* remote ip address */
+		0x59, 0xd8, /* local udp port (23000) */
+};
+
+static void patch_16(uint8_t *data, const uint16_t val)
+{
+	memcpy(data, &val, sizeof(val));
+}
+
+static void patch_32(uint8_t *data, const uint32_t val)
+{
+	memcpy(data, &val, sizeof(val));
+}
+
+/*
+ * Patch the various SYSTEM INFORMATION tables to update
+ * the LAI
+ */
+static void patch_nm_tables(struct gsm_bts *bts)
+{
+	u_int8_t arfcn_low = bts->c0->arfcn & 0xff;
+	u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
+
+	/* patch ARFCN into BTS Attributes */
+	nanobts_attr_bts[42] &= 0xf0;
+	nanobts_attr_bts[42] |= arfcn_high;
+	nanobts_attr_bts[43] = arfcn_low;
+
+	/* patch the RACH attributes */
+	if (bts->rach_b_thresh != -1) {
+		nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff;
+	}
+
+	if (bts->rach_ldavg_slots != -1) {
+		u_int8_t avg_high = bts->rach_ldavg_slots & 0xff;
+		u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f;
+
+		nanobts_attr_bts[35] = avg_high;
+		nanobts_attr_bts[36] = avg_low;
+	}
+
+	/* patch BSIC */
+	nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic;
+
+	/* patch CGI */
+	abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts);
+
+	/* patch the power reduction */
+	nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
+
+	/* patch NSEI */
+	nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8;
+	nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff;
+	memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer,
+		ARRAY_SIZE(bts->gprs.nse.timer));
+	memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer,
+		ARRAY_SIZE(bts->gprs.cell.timer));
+
+	/* patch NSVCI */
+	nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8;
+	nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff;
+
+	/* patch IP address as SGSN IP */
+	patch_16(nanobts_attr_nsvc0 + 8, 
+			htons(bts->gprs.nsvc[0].remote_port));
+	patch_32(nanobts_attr_nsvc0 + 10,
+			htonl(bts->gprs.nsvc[0].remote_ip));
+	patch_16(nanobts_attr_nsvc0 + 14,
+			htons(bts->gprs.nsvc[0].local_port));
+
+	/* patch BVCI */
+	nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8;
+	nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff;
+	/* patch RAC */
+	nanobts_attr_cell[3] = bts->gprs.rac;
+
+	if (bts->gprs.mode == BTS_GPRS_EGPRS) {
+		/* patch EGPRS coding schemes MCS 1..9 */
+		nanobts_attr_cell[29] = 0x8f;
+		nanobts_attr_cell[30] = 0xff;
+	}
+}
+
+
+/* Callback function to be called whenever we get a GSM 12.21 state change event */
+static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd)
+{
+	u_int8_t obj_class = nsd->obj_class;
+	void *obj = nsd->obj;
+	struct gsm_nm_state *new_state = nsd->new_state;
+
+	struct gsm_bts *bts;
+	struct gsm_bts_trx *trx;
+	struct gsm_bts_trx_ts *ts;
+	struct gsm_bts_gprs_nsvc *nsvc;
+
+	/* This event-driven BTS setup is currently only required on nanoBTS */
+
+	/* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create
+	 * endless loop */
+	if (evt != S_NM_STATECHG_OPER)
+		return 0;
+
+	switch (obj_class) {
+	case NM_OC_SITE_MANAGER:
+		bts = container_of(obj, struct gsm_bts, site_mgr);
+		if ((new_state->operational == NM_OPSTATE_ENABLED &&
+		     new_state->availability == NM_AVSTATE_OK) ||
+		    (new_state->operational == NM_OPSTATE_DISABLED &&
+		     new_state->availability == NM_AVSTATE_OFF_LINE))
+			abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
+		break;
+	case NM_OC_BTS:
+		bts = obj;
+		if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
+			patch_nm_tables(bts);
+			abis_nm_set_bts_attr(bts, nanobts_attr_bts,
+					     sizeof(nanobts_attr_bts));
+			abis_nm_chg_adm_state(bts, obj_class,
+					      bts->bts_nr, 0xff, 0xff,
+					      NM_STATE_UNLOCKED);
+			abis_nm_opstart(bts, obj_class,
+					bts->bts_nr, 0xff, 0xff);
+		}
+		break;
+	case NM_OC_CHANNEL:
+		ts = obj;
+		trx = ts->trx;
+		if (new_state->operational == NM_OPSTATE_DISABLED &&
+		    new_state->availability == NM_AVSTATE_DEPENDENCY) {
+			patch_nm_tables(trx->bts);
+			enum abis_nm_chan_comb ccomb =
+						abis_nm_chcomb4pchan(ts->pchan);
+			abis_nm_set_channel_attr(ts, ccomb);
+			abis_nm_chg_adm_state(trx->bts, obj_class,
+					      trx->bts->bts_nr, trx->nr, ts->nr,
+					      NM_STATE_UNLOCKED);
+			abis_nm_opstart(trx->bts, obj_class,
+					trx->bts->bts_nr, trx->nr, ts->nr);
+		}
+		break;
+	case NM_OC_RADIO_CARRIER:
+		trx = obj;
+		if (new_state->operational == NM_OPSTATE_DISABLED &&
+		    new_state->availability == NM_AVSTATE_OK)
+			abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
+					trx->nr, 0xff);
+		break;
+	case NM_OC_GPRS_NSE:
+		bts = container_of(obj, struct gsm_bts, gprs.nse);
+		if (bts->gprs.mode == BTS_GPRS_NONE)
+			break;
+		if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  0xff, 0xff, nanobts_attr_nse,
+						  sizeof(nanobts_attr_nse));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					0xff, 0xff);
+		}
+		break;
+	case NM_OC_GPRS_CELL:
+		bts = container_of(obj, struct gsm_bts, gprs.cell);
+		if (bts->gprs.mode == BTS_GPRS_NONE)
+			break;
+		if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  0, 0xff, nanobts_attr_cell,
+						  sizeof(nanobts_attr_cell));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					0, 0xff);
+			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
+					      0, 0xff, NM_STATE_UNLOCKED);
+			abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr,
+					      0xff, 0xff, NM_STATE_UNLOCKED);
+		}
+		break;
+	case NM_OC_GPRS_NSVC:
+		nsvc = obj;
+		bts = nsvc->bts;
+		if (bts->gprs.mode == BTS_GPRS_NONE)
+			break;
+		/* We skip NSVC1 since we only use NSVC0 */
+		if (nsvc->id == 1)
+			break;
+		if (new_state->availability == NM_AVSTATE_OFF_LINE) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  nsvc->id, 0xff,
+						  nanobts_attr_nsvc0,
+						  sizeof(nanobts_attr_nsvc0));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					nsvc->id, 0xff);
+			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
+					      nsvc->id, 0xff,
+					      NM_STATE_UNLOCKED);
+		}
+	default:
+		break;
+	}
+	return 0;
+}
+
+/* Callback function to be called every time we receive a 12.21 SW activated report */
+static int sw_activ_rep(struct msgb *mb)
+{
+	struct abis_om_fom_hdr *foh = msgb_l3(mb);
+	struct gsm_bts *bts = mb->trx->bts;
+	struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
+
+	if (!trx)
+		return -EINVAL;
+
+	if (trx->bts->type != GSM_BTS_TYPE_NANOBTS)
+		return 0;
+
+	switch (foh->obj_class) {
+	case NM_OC_BASEB_TRANSC:
+		abis_nm_chg_adm_state(trx->bts, foh->obj_class,
+				      trx->bts->bts_nr, trx->nr, 0xff,
+				      NM_STATE_UNLOCKED);
+		abis_nm_opstart(trx->bts, foh->obj_class,
+				trx->bts->bts_nr, trx->nr, 0xff);
+		/* TRX software is active, tell it to initiate RSL Link */
+		abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
+		break;
+	case NM_OC_RADIO_CARRIER: {
+		/*
+		 * Locking the radio carrier will make it go
+		 * offline again and we would come here. The
+		 * framework should determine that there was
+		 * no change and avoid recursion.
+		 *
+		 * This code is here to make sure that on start
+		 * a TRX remains locked.
+		 */
+		int rc_state = trx->nm_state.administrative;
+		/* Patch ARFCN into radio attribute */
+		nanobts_attr_radio[5] &= 0xf0;
+		nanobts_attr_radio[5] |= trx->arfcn >> 8;
+		nanobts_attr_radio[6] = trx->arfcn & 0xff;
+		abis_nm_set_radio_attr(trx, nanobts_attr_radio,
+				       sizeof(nanobts_attr_radio));
+		abis_nm_chg_adm_state(trx->bts, foh->obj_class,
+				      trx->bts->bts_nr, trx->nr, 0xff,
+				      rc_state);
+		abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
+				trx->nr, 0xff);
+		break;
+		}
+	}
+	return 0;
+}
+
+/* Callback function to be called every time we receive a signal from NM */
+static int nm_sig_cb(unsigned int subsys, unsigned int signal,
+		     void *handler_data, void *signal_data)
+{
+	if (subsys != SS_NM)
+		return 0;
+
+	switch (signal) {
+	case S_NM_SW_ACTIV_REP:
+		return sw_activ_rep(signal_data);
+	case S_NM_STATECHG_OPER:
+	case S_NM_STATECHG_ADM:
+		return nm_statechg_event(signal, signal_data);
+	default:
+		break;
+	}
+	return 0;
+}
+
 int bts_model_nanobts_init(void)
 {
 	model_nanobts.features.data = &model_nanobts._features_data[0];
@@ -84,5 +439,7 @@
 	gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_GPRS);
 	gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_EGPRS);
 
+	register_signal_handler(SS_NM, nm_sig_cb, NULL);
+
 	return gsm_bts_model_register(&model_nanobts);
 }