Initial version of Support for Nokia *Site BTS

This includes the MetroSite, but also other Nokia BTS models.
diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am
index 3af4a2a..a8e0520 100644
--- a/openbsc/src/libbsc/Makefile.am
+++ b/openbsc/src/libbsc/Makefile.am
@@ -11,6 +11,7 @@
 			bts_ericsson_rbs2000.c \
 			bts_ipaccess_nanobts.c \
 			bts_siemens_bs11.c \
+			bts_nokia_site.c \
 			bts_hsl_femtocell.c \
 			bts_unknown.c \
 			chan_alloc.c \
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index d74907b..f20b4f2 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -1108,6 +1108,12 @@
 		LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n",
 		     gsm_trx_name(msg->trx));
 		break;
+	case 0x42: /* Nokia specific: SI End ACK */
+		LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n");
+		break;
+	case 0x43: /* Nokia specific: SI End NACK */
+		LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n");
+		break;
 	default:
 		LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message "
 			"type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type);
@@ -1892,3 +1898,50 @@
 
 	return abis_rsl_sendmsg(cb_cmd);
 }
+
+int rsl_nokia_si_begin(struct gsm_bts_trx *trx)
+{
+	struct abis_rsl_common_hdr *ch;
+	struct msgb *msg = rsl_msgb_alloc();
+
+	ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+	ch->msg_discr = ABIS_RSL_MDISC_TRX;
+	ch->msg_type = 0x40; /* Nokia SI Begin */
+
+	msg->trx = trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
+int rsl_nokia_si_end(struct gsm_bts_trx *trx)
+{
+	struct abis_rsl_common_hdr *ch;
+	struct msgb *msg = rsl_msgb_alloc();
+
+	ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+	ch->msg_discr = ABIS_RSL_MDISC_TRX;
+	ch->msg_type = 0x41;  /* Nokia SI End */
+
+	msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */
+
+	msg->trx = trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
+int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction)
+{
+	struct abis_rsl_common_hdr *ch;
+	struct msgb *msg = rsl_msgb_alloc();
+
+	ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+	ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN;
+	ch->msg_type = RSL_MT_BS_POWER_CONTROL;
+
+	msgb_tv_put(msg, RSL_IE_CHAN_NR, channel);
+	msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */
+
+	msg->trx = trx;
+
+	return abis_rsl_sendmsg(msg);
+}
diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c
index 02a3adf..1d885db 100644
--- a/openbsc/src/libbsc/bsc_init.c
+++ b/openbsc/src/libbsc/bsc_init.c
@@ -249,8 +249,19 @@
 		trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code,
 		bsc_gsmnet->network_code, trx->bts->location_area_code,
 		trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc);
+
+	if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
+		rsl_nokia_si_begin(trx);
+	}
+
 	set_system_infos(trx);
 
+	if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
+		/* channel unspecific, power reduction in 2 dB steps */
+		rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2);
+		rsl_nokia_si_end(trx);
+	}
+
 	for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
 		generate_ma_for_ts(&trx->ts[i]);
 }
@@ -268,6 +279,24 @@
 
 	switch (signal) {
 	case S_INP_TEI_UP:
+		if (isd->link_type == E1INP_SIGN_OML) {
+			/* TODO: this is required for the Nokia BTS, hopping is configured
+			   during OML, other MA is not set.  */
+			struct gsm_bts_trx *cur_trx;
+			/* was static in system_information.c */
+			extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts);
+			uint8_t ca[20];
+			/* has to be called before generate_ma_for_ts to
+			  set bts->si_common.cell_alloc */
+			generate_cell_chan_list(ca, trx->bts);
+
+			llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) {
+				int i;
+
+				for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++)
+					generate_ma_for_ts(&cur_trx->ts[i]);
+			}
+		}
 		if (isd->link_type == E1INP_SIGN_RSL)
 			bootstrap_rsl(trx);
 		break;
diff --git a/openbsc/src/libbsc/bts_init.c b/openbsc/src/libbsc/bts_init.c
index 87bdde0..693eca8 100644
--- a/openbsc/src/libbsc/bts_init.c
+++ b/openbsc/src/libbsc/bts_init.c
@@ -24,6 +24,7 @@
 	bts_model_rbs2k_init();
 	bts_model_nanobts_init();
 	bts_model_hslfemto_init();
+	bts_model_nokia_site_init();
 	/* Your new BTS here. */
 	return 0;
 }
diff --git a/openbsc/src/libbsc/bts_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c
new file mode 100644
index 0000000..d30881b
--- /dev/null
+++ b/openbsc/src/libbsc/bts_nokia_site.c
@@ -0,0 +1,1949 @@
+/* Nokia XXXsite family specific code */
+
+/* (C) 2011 by Dieter Spaar <spaar@mirider.augusta.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/>.
+ *
+ */
+
+/*
+  TODO: Attention: There are some static variables used for states during
+  configuration. Those variables have to be moved to a BTS specific context,
+  otherwise there will most certainly be problems if more than one Nokia BTS
+  is used.
+*/
+
+#include <time.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/signal.h>
+
+#include <osmocom/core/timer.h>
+
+#include "../libabis/input/lapd.h"
+
+/* TODO: put in a separate file ? */
+
+/* TODO: move statics to BTS context */
+static int do_reset = 1;
+/*static*/ int wait_reset = 0;
+struct osmo_timer_list reset_timer; /* timer to re-start after reset */
+
+#define RESET_INTERVAL      0, 3000000 /* 3 seconds */
+
+extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg);
+/* was static in system_information.c */
+extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts);
+
+static void abis_nm_queue_send_next(struct gsm_bts *bts);
+static void reset_timer_cb(void *_bts);
+static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref);
+static int dump_elements(uint8_t *data, int len);
+
+static void bootstrap_om_bts(struct gsm_bts *bts)
+{
+    LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
+        
+    if(do_reset)
+        abis_nm_reset(bts, 1);
+}
+
+static void bootstrap_om_trx(struct gsm_bts_trx *trx)
+{
+    LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n",
+         trx->bts->nr, trx->nr);
+}
+
+static int shutdown_om(struct gsm_bts *bts)
+{
+    /* TODO !? */
+    return 0;
+}
+
+#define SAPI_OML    62
+#define SAPI_RSL    0
+
+/* 
+
+  Tell LAPD to start start the SAP (send SABM requests) for all signalling
+  timeslots in this line 
+  
+  Attention: this has to be adapted for MISDN  
+*/
+ 
+static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi)
+{
+    struct e1inp_sign_link *link;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
+        struct e1inp_ts *ts = &line->ts[i];
+
+        if (ts->type != E1INP_TS_TYPE_SIGN)
+            continue;
+
+        llist_for_each_entry(link, &ts->sign.sign_links, list) {
+            if(sapi != -1 && link->sapi != sapi)
+                continue;
+            
+#if 0 /* debugging */   
+            printf("sap start/stop (%d): %d tei=%d sapi=%d\n", start, i + 1, link->tei, link->sapi);
+#endif
+            
+            if (start)
+                lapd_sap_start(ts->driver.dahdi.lapd, link->tei, link->sapi);
+            else
+                lapd_sap_stop(ts->driver.dahdi.lapd, link->tei, link->sapi);
+        }
+    }
+}
+
+/* Callback function to be called every time we receive a signal from INPUT */
+static int gbl_sig_cb(unsigned int subsys, unsigned int signal,
+              void *handler_data, void *signal_data)
+{
+    struct gsm_bts *bts;
+
+    if (subsys != SS_GLOBAL)
+        return 0;
+
+    switch (signal) {
+    case S_GLOBAL_BTS_CLOSE_OM:
+        bts = signal_data;
+        if (bts->type == GSM_BTS_TYPE_NOKIA_SITE)
+            shutdown_om(signal_data);
+        break;
+    }
+
+    return 0;
+}
+
+/* Callback function to be called every time we receive a signal from INPUT */
+static int inp_sig_cb(unsigned int subsys, unsigned int signal,
+              void *handler_data, void *signal_data)
+{
+    struct input_signal_data *isd = signal_data;
+
+    if (subsys != SS_INPUT)
+        return 0;
+
+    switch (signal) {
+    case S_INP_LINE_INIT:
+        start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */
+        break;      
+    case S_INP_TEI_DN:
+        break;      
+    case S_INP_TEI_UP:
+        switch (isd->link_type) {
+        case E1INP_SIGN_OML:
+            if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE)
+                break;
+                
+            if (isd->tei == isd->trx->bts->oml_tei)
+                bootstrap_om_bts(isd->trx->bts);
+            else
+                bootstrap_om_trx(isd->trx);
+            break;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+static void nm_statechg_evt(unsigned int signal,
+                struct nm_statechg_signal_data *nsd)
+{
+    if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE)
+        return;
+}
+
+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_STATECHG_OPER:
+    case S_NM_STATECHG_ADM:
+        nm_statechg_evt(signal, signal_data);
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+/* TODO: put in a separate file ? */
+
+static char *get_msg_type_name_string(uint8_t msg_type)
+{
+    switch(msg_type) {
+    case 0x80:
+        return "NOKIA_BTS_CONF_DATA";
+    case 0x81:
+        return "NOKIA_BTS_ACK";
+    case 0x82:
+        return "NOKIA_BTS_OMU_STARTED";
+    case 0x83:
+        return "NOKIA_BTS_START_DOWNLOAD_REQ";
+    case 0x84:
+        return "NOKIA_BTS_MF_REQ";
+    case 0x85:
+        return "NOKIA_BTS_AF_REQ";
+    case 0x86:
+        return "NOKIA_BTS_RESET_REQ";
+    case 0x87:
+        return "NOKIA_reserved";
+    case 0x88:
+        return "NOKIA_BTS_CONF_REQ";
+    case 0x89:
+        return "NOKIA_BTS_TEST_REQ";
+    case 0x8A:
+        return "NOKIA_BTS_TEST_REPORT";
+    case 0x8B:
+        return "NOKIA_reserved";
+    case 0x8C:
+        return "NOKIA_reserved";
+    case 0x8D:
+        return "NOKIA_reserved";
+    case 0x8E:
+        return "NOKIA_BTS_CONF_COMPL";
+    case 0x8F:
+        return "NOKIA_reserved";
+    case 0x90:
+        return "NOKIA_BTS_STM_TEST_REQ";
+    case 0x91:
+        return "NOKIA_BTS_STM_TEST_REPORT";
+    case 0x92:
+        return "NOKIA_BTS_TRANSMISSION_COMMAND";
+    case 0x93:
+        return "NOKIA_BTS_TRANSMISSION_ANSWER";
+    case 0x94:
+        return "NOKIA_BTS_HW_DB_UPLOAD_REQ";
+    case 0x95:
+        return "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ";
+    case 0x96:
+        return "NOKIA_BTS_HW_DB_SAVE_REQ";
+    case 0x97:
+        return "NOKIA_BTS_FLASH_ERASURE_REQ";
+    case 0x98:
+        return "NOKIA_BTS_HW_DB_DOWNLOAD_REQ";
+    case 0x99:
+        return "NOKIA_BTS_PWR_SUPPLY_CONTROL";
+    case 0x9A:
+        return "NOKIA_BTS_ATTRIBUTE_REQ";
+    case 0x9B:
+        return "NOKIA_BTS_ATTRIBUTE_REPORT";
+    case 0x9C:
+        return "NOKIA_BTS_HW_REQ";
+    case 0x9D:
+        return "NOKIA_BTS_HW_REPORT";
+    case 0x9E:
+        return "NOKIA_BTS_RTE_TEST_REQ";
+    case 0x9F:
+        return "NOKIA_BTS_RTE_TEST_REPORT";
+    case 0xA0:
+        return "NOKIA_BTS_HW_DB_VERIFICATION_REQ";
+    case 0xA1:
+        return "NOKIA_BTS_CLOCK_REQ";
+    case 0xA2:
+        return "NOKIA_AC_CIRCUIT_REQ_NACK";
+    case 0xA3:
+        return "NOKIA_AC_INTERRUPTED";
+    case 0xA4:
+        return "NOKIA_BTS_NEW_TRE_INFO";
+    case 0xA5:
+        return "NOKIA_AC_BSC_CIRCUITS_ALLOCATED";
+    case 0xA6:
+        return "NOKIA_BTS_TRE_POLL_LIST";
+    case 0xA7:
+        return "NOKIA_AC_CIRCUIT_REQ";
+    case 0xA8:
+        return "NOKIA_BTS_BLOCK_CTRL_REQ";
+    case 0xA9:
+        return "NOKIA_BTS_GSM_TIME_REQ";
+    case 0xAA:
+        return "NOKIA_BTS_GSM_TIME";
+    case 0xAB:
+        return "NOKIA_BTS_OUTPUT_CONTROL";
+    case 0xAC:
+        return "NOKIA_BTS_STATE_CHANGED";
+    case 0xAD:
+        return "NOKIA_BTS_SW_SAVE_REQ";
+    case 0xAE:
+        return "NOKIA_BTS_ALARM";
+    case 0xAF:
+        return "NOKIA_BTS_CHA_ADM_STATE";
+    case 0xB0:
+        return "NOKIA_AC_POOL_SIZE_REPORT";
+    case 0xB1:
+        return "NOKIA_AC_POOL_SIZE_INQUIRY";
+    case 0xB2:
+        return "NOKIA_BTS_COMMISS_TEST_COMPLETED";
+    case 0xB3:
+        return "NOKIA_BTS_COMMISS_TEST_REQ";
+    case 0xB4:
+        return "NOKIA_BTS_TRANSP_BTS_TO_BSC";
+    case 0xB5:
+        return "NOKIA_BTS_TRANSP_BSC_TO_BTS";
+    case 0xB6:
+        return "NOKIA_BTS_LCS_COMMAND";
+    case 0xB7:
+        return "NOKIA_BTS_LCS_ANSWER";
+    case 0xB8:
+        return "NOKIA_BTS_LMU_FN_OFFSET_COMMAND";
+    case 0xB9:
+        return "NOKIA_BTS_LMU_FN_OFFSET_ANSWER";
+    default:
+        return "unknown";   
+    }
+}
+
+static char *get_element_name_string(uint16_t element)
+{
+    switch(element) {
+    case 0x01:
+        return "Ny1";
+    case 0x02:
+        return "T3105_F";
+    case 0x03:
+        return "Interference band limits";
+    case 0x04:
+        return "Interference report timer in secs";
+    case 0x05:
+        return "Channel configuration per TS";
+    case 0x06:
+        return "BSIC";
+    case 0x07:
+        return "RACH report timer in secs";
+    case 0x08:
+        return "Hardware database status";
+    case 0x09:
+        return "BTS RX level";
+    case 0x0A:
+        return "ARFN";
+    case 0x0B:
+        return "STM antenna attenuation";
+    case 0x0C:
+        return "Cell allocation bitmap";
+    case 0x0D:
+        return "Radio definition per TS";
+    case 0x0E:
+        return "Frame number";
+    case 0x0F:
+        return "Antenna diversity";
+    case 0x10:
+        return "T3105_D";
+    case 0x11:
+        return "File format";
+    case 0x12:
+        return "Last File";
+    case 0x13:
+        return "BTS type";
+    case 0x14:
+        return "Erasure mode";
+    case 0x15:
+        return "Hopping mode";
+    case 0x16:
+        return "Floating TRX";
+    case 0x17:
+        return "Power supplies";
+    case 0x18:
+        return "Reset type";
+    case 0x19:
+        return "Averaging period";
+    case 0x1A:
+        return "RBER2";
+    case 0x1B:
+        return "LAC";
+    case 0x1C:
+        return "CI";
+    case 0x1D:
+        return "Failure parameters";
+    case 0x1E:
+        return "(RF max power reduction)";
+    case 0x1F:
+        return "Measured RX_SENS";
+    case 0x20:
+        return "Extended cell radius";
+    case 0x21:
+        return "reserved";
+    case 0x22:
+        return "Success-Failure";
+    case 0x23:
+        return "Ack-Nack";
+    case 0x24:
+        return "OMU test results";
+    case 0x25:
+        return "File identity";
+    case 0x26:
+        return "Generation and version code";
+    case 0x27:
+        return "SW description";
+    case 0x28:
+        return "BCCH LEV";
+    case 0x29:
+        return "Test type";
+    case 0x2A:
+        return "Subscriber number";
+    case 0x2B:
+        return "reserved";
+    case 0x2C:
+        return "HSN";
+    case 0x2D:
+        return "reserved";
+    case 0x2E:
+        return "MS RXLEV";
+    case 0x2F:
+        return "MS TXLEV";
+    case 0x30:
+        return "RXQUAL";
+    case 0x31:
+        return "RX SENS";
+    case 0x32:
+        return "Alarm block";
+    case 0x33:
+        return "Neighbouring BCCH levels";
+    case 0x34:
+        return "STM report type";
+    case 0x35:
+        return "MA";
+    case 0x36:
+        return "MAIO";
+    case 0x37:
+        return "H_FLAG";
+    case 0x38:
+        return "TCH_ARFN";
+    case 0x39:
+        return "Clock output";
+    case 0x3A:
+        return "Transmitted power";
+    case 0x3B:
+        return "Clock sync";
+    case 0x3C:
+        return "TMS protocol discriminator";
+    case 0x3D:
+        return "TMS protocol data";
+    case 0x3E:
+        return "FER";
+    case 0x3F:
+        return "SWR result";
+    case 0x40:
+        return "Object identity";
+    case 0x41:
+        return "STM RX Antenna Test";
+    case 0x42:
+        return "reserved";
+    case 0x43:
+        return "reserved";
+    case 0x44:
+        return "Object current state";
+    case 0x45:
+        return "reserved";
+    case 0x46:
+        return "FU channel configuration";
+    case 0x47:
+        return "reserved";
+    case 0x48:
+        return "ARFN of a CU";
+    case 0x49:
+        return "FU radio definition";
+    case 0x4A:
+        return "reserved";
+    case 0x4B:
+        return "Severity";
+    case 0x4C:
+        return "Diversity selection";
+    case 0x4D:
+        return "RX antenna test";
+    case 0x4E:
+        return "RX antenna supervision period";
+    case 0x4F:
+        return "RX antenna state";
+    case 0x50:
+        return "Sector configuration";
+    case 0x51:
+        return "Additional info";
+    case 0x52:
+        return "SWR parameters";
+    case 0x53:
+        return "HW inquiry mode";
+    case 0x54:
+        return "reserved";
+    case 0x55:
+        return "Availability status";
+    case 0x56:
+        return "reserved";
+    case 0x57:
+        return "EAC inputs";
+    case 0x58:
+        return "EAC outputs";
+    case 0x59:
+        return "reserved";
+    case 0x5A:
+        return "Position";
+    case 0x5B:
+        return "HW unit identity";
+    case 0x5C:
+        return "RF test signal attenuation";
+    case 0x5D:
+        return "Operational state";
+    case 0x5E:
+        return "Logical object identity";
+    case 0x5F:
+        return "reserved";
+    case 0x60:
+        return "BS_TXPWR_OM";
+    case 0x61:
+        return "Loop_Duration";
+    case 0x62:
+        return "LNA_Path_Selection";
+    case 0x63:
+        return "Serial number";
+    case 0x64:
+        return "HW version";
+    case 0x65:
+        return "Obj. identity and obj. state";
+    case 0x66:
+        return "reserved";
+    case 0x67:
+        return "EAC input definition";
+    case 0x68:
+        return "EAC id and text";
+    case 0x69:
+        return "HW unit status";
+    case 0x6A:
+        return "SW release version";
+    case 0x6B:
+        return "FW version";
+    case 0x6C:
+        return "Bit_Error_Ratio";
+    case 0x6D:
+        return "RXLEV_with_Attenuation";
+    case 0x6E:
+        return "RXLEV_without_Attenuation";
+    case 0x6F:
+        return "reserved";
+    case 0x70:
+        return "CU_Results";
+    case 0x71:
+        return "reserved";
+    case 0x72:
+        return "LNA_Path_Results";
+    case 0x73:
+        return "RTE Results";
+    case 0x74:
+        return "Real Time";
+    case 0x75:
+        return "RX diversity selection";
+    case 0x76:
+        return "EAC input config";
+    case 0x77:
+        return "Feature support";
+    case 0x78:
+        return "File version";
+    case 0x79:
+        return "Outputs";
+    case 0x7A:
+        return "FU parameters";
+    case 0x7B:
+        return "Diagnostic info";
+    case 0x7C:
+        return "FU BSIC";
+    case 0x7D:
+        return "TRX Configuration";
+    case 0x7E:
+        return "Download status";
+    case 0x7F:
+        return "RX difference limit";
+    case 0x80:
+        return "TRX HW capability";
+    case 0x81:
+        return "Common HW config";
+    case 0x82:
+        return "Autoconfiguration pool size";
+    case 0x83:
+        return "TRE diagnostic info";
+    case 0x84:
+        return "TRE object identity";
+    case 0x85:
+        return "New TRE Info";
+    case 0x86:
+        return "Acknowledgement period";
+    case 0x87:
+        return "Synchronization mode";
+    case 0x88:
+        return "reserved";
+    case 0x89:
+        return "Block Control Data";
+    case 0x8A:
+        return "SW load mode";
+    case 0x8B:
+        return "Recommended recovery action";
+    case 0x8C:
+        return "BSC BCF id";
+    case 0x8D:
+        return "Q1 baud rate";
+    case 0x8E:
+        return "Allocation status";
+    case 0x8F:
+        return "Functional entity number";
+    case 0x90:
+        return "Transmission delay";
+    case 0x91:
+        return "Loop Duration ms";
+    case 0x92:
+        return "Logical channel";
+    case 0x93:
+        return "Q1 address";
+    case 0x94:
+        return "Alarm detail";
+    case 0x95:
+        return "Cabinet type";
+    case 0x96:
+        return "HW unit existence";
+    case 0x97:
+        return "RF power parameters";
+    case 0x98:
+        return "Message scenario";
+    case 0x99:
+        return "HW unit max amount";
+    case 0x9A:
+        return "Master TRX";
+    case 0x9B:
+        return "Transparent data";
+    case 0x9C:
+        return "BSC topology info";
+    case 0x9D:
+        return "Air i/f modulation";
+    case 0x9E:
+        return "LCS Q1 command data";
+    case 0x9F:
+        return "Frame number offset";
+    case 0xA0:
+        return "Abis TSL";
+    case 0xA1:
+        return "Dynamic pool info";
+    case 0xA2:
+        return "LCS LLP data";
+    case 0xA3:
+        return "LCS Q1 answer data";
+    case 0xA4:
+        return "DFCA FU Radio Definition";
+    case 0xA5:
+        return "Antenna hopping";
+    case 0xA6:
+        return "Field record sequence number";
+    case 0xA7:
+        return "Timeslot offslot";
+    case 0xA8:
+        return "EPCR capability";
+    case 0xA9:
+        return "Connectsite optional element";
+    case 0xAA:
+        return "TSC";
+    case 0xAB:
+        return "Special TX Power Setting";
+    case 0xAC:
+        return "Optional sync settings";
+    case 0xFA:
+        return "Abis If parameters";
+    default:
+        return "unknown";   
+    }
+}
+
+static char *get_bts_type_string(uint8_t type)
+{
+    switch(type) {
+    case 0x0A: 
+        return "MetroSite GSM 900";
+    case 0x0B: 
+        return "MetroSite GSM 1800";
+    case 0x0C:
+        return "MetroSite GSM 1900 (PCS)";
+    case 0x0D:
+        return "MetroSite GSM 900 & 1800";
+    case 0x0E:
+        return "InSite GSM 900";
+    case 0x0F:
+        return "InSite GSM 1800";
+    case 0x10:
+        return "InSite GSM 1900";
+    case 0x11:
+        return "UltraSite GSM 900";
+    case 0x12:
+        return "UltraSite GSM 1800";
+    case 0x13:
+        return "UltraSite GSM/US-TDMA 1900";
+    case 0x14:
+        return "UltraSite GSM 900 & 1800";
+    case 0x16:
+        return "UltraSite GSM/US-TDMA 850";
+    case 0x18:
+        return "MetroSite GSM/US-TDMA 850";
+    case 0x19:
+        return "UltraSite GSM 800/1900";
+    default:
+        return "unknown";   
+    }
+}
+
+static char *get_severity_string(uint8_t severity)
+{
+    switch(severity) {
+    case 0:
+        return "indeterminate";
+    case 1:
+        return "critical";
+    case 2:
+        return "major";
+    case 3:
+        return "minor";
+    case 4:
+        return "warning";
+    default:
+        return "unknown";   
+    }
+}
+
+/* TODO: put in a separate file ? */
+
+/* some message IDs */
+
+#define NOKIA_MSG_CONF_DATA             128
+#define NOKIA_MSG_ACK                   129
+#define NOKIA_MSG_OMU_STARTED           130
+#define NOKIA_MSG_START_DOWNLOAD_REQ    131
+#define NOKIA_MSG_MF_REQ                132
+#define NOKIA_MSG_RESET_REQ             134
+#define NOKIA_MSG_CONF_REQ              136
+#define NOKIA_MSG_CONF_COMPLETE         142
+#define NOKIA_MSG_BLOCK_CTRL_REQ        168
+#define NOKIA_MSG_STATE_CHANGED         172
+#define NOKIA_MSG_ALARM                 174
+
+/* some element IDs */
+
+#define NOKIA_EI_BTS_TYPE       0x13
+#define NOKIA_EI_ACK            0x23
+#define NOKIA_EI_ADD_INFO       0x51
+#define NOKIA_EI_SEVERITY       0x4B
+#define NOKIA_EI_ALARM_DETAIL   0x94
+
+#define OM_ALLOC_SIZE       1024
+#define OM_HEADROOM_SIZE    128
+
+static uint8_t fu_config_template[] =
+{
+  0x7F, 0x7A, 0x39, 
+  /* ID = 0x7A (FU parameters) ## constructed ## */
+  /* length = 57 */
+  /* [3] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [6] */
+     0x00, 0x07, 0x01, 0xFF, 
+
+     0x41, 0x02, 
+     /* ID = 0x01 (Ny1) */
+     /* length = 2 */
+     /* [12] */
+     0x00, 0x05, 
+
+     0x42, 0x02, 
+     /* ID = 0x02 (T3105_F) */
+     /* length = 2 */
+     /* [16] */
+     0x00, 0x28, 
+
+     0x50, 0x02, 
+     /* ID = 0x10 (T3105_D) */
+     /* length = 2 */
+     /* [20] */
+     0x00, 0x28, 
+
+     0x43, 0x05, 
+     /* ID = 0x03 (Interference band limits) */
+     /* length = 5 */
+     /* [24] */
+     0x0F, 0x1B, 0x27, 0x33, 0x3F, 
+
+     0x44, 0x02, 
+     /* ID = 0x04 (Interference report timer in secs) */
+     /* length = 2 */
+     /* [31] */
+     0x00, 0x10, 
+
+     0x47, 0x01, 
+     /* ID = 0x07 (RACH report timer in secs) */
+     /* length = 1 */
+     /* [35] */
+     0x1E, 
+
+     0x4C, 0x10, 
+     /* ID = 0x0C (Cell allocation bitmap) ####### */
+     /* length = 16 */
+     /* [38] */
+     0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+
+     0x59, 0x01, 
+     /* ID = 0x19 (Averaging period) */
+     /* length = 1 */
+     /* [56] */
+     0x01, 
+
+     0x5E, 0x01, 
+     /* ID = 0x1E ((RF max power reduction)) */
+     /* length = 1 */
+     /* [59] */
+     0x00, 
+
+   
+  0x7F, 0x46, 0x11, 
+  /* ID = 0x46 (FU channel configuration) ## constructed ## */
+  /* length = 17 */
+  /* [63] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [66] */
+     0x00, 0x07, 0x01, 0xFF, 
+
+     0x45, 0x08, 
+     /* ID = 0x05 (Channel configuration per TS) */
+     /* length = 8 */
+     /* [72] */
+     0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 
+
+   
+  0x7F, 0x65, 0x0B, 
+  /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */
+  /* length = 11 */
+  /* [83] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [86] */
+     0x00, 0x04, 0x01, 0xFF, 
+
+     0x5F, 0x44, 0x01, 
+     /* ID = 0x44 (Object current state) */
+     /* length = 1 */
+     /* [93] */
+     0x03, 
+
+   
+  0x7F, 0x7C, 0x0A, 
+  /* ID = 0x7C (FU BSIC) ## constructed ## */
+  /* length = 10 */
+  /* [97] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [100] */
+     0x00, 0x07, 0x01, 0xFF, 
+
+     0x46, 0x01, 
+     /* ID = 0x06 (BSIC) */
+     /* length = 1 */
+     /* [106] */
+     0x00, 
+
+   
+  0x7F, 0x48, 0x0B, 
+  /* ID = 0x48 (ARFN of a CU) ## constructed ## */
+  /* length = 11 */
+  /* [110] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [113] */
+     0x00, 0x08, 0x01, 0xFF, 
+
+     0x4A, 0x02, 
+     /* ID = 0x0A (ARFN) ####### */
+     /* length = 2 */
+     /* [119] */
+     0x03, 0x62, 
+
+   
+  0x7F, 0x49, 0x59, 
+  /* ID = 0x49 (FU radio definition) ## constructed ## */
+  /* length = 89 */
+  /* [124] */
+
+     0x5F, 0x40, 0x04, 
+     /* ID = 0x40 (Object identity) */
+     /* length = 4 */
+     /* [127] */
+     0x00, 0x07, 0x01, 0xFF, 
+
+     0x4D, 0x50, 
+     /* ID = 0x0D (Radio definition per TS) ####### */
+     /* length = 80 */
+     /* [133] */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */
+     0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+     0x03, 0x62, 
+};
+
+/* TODO: put in a separate file ? */
+
+/*
+  build the configuration for each TRX  
+*/
+
+static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, uint8_t *fu_config, int *hopping)
+{
+    int i;
+    
+    *hopping = 0;
+    
+    memcpy(fu_config, fu_config_template, sizeof(fu_config_template));
+    
+    /* set ID */
+    
+    fu_config[  6 + 2] = id;
+    fu_config[ 66 + 2] = id;
+    fu_config[ 86 + 2] = id;
+    fu_config[100 + 2] = id;
+    fu_config[113 + 2] = id;
+    fu_config[127 + 2] = id;
+    
+    /* set ARFCN */
+    
+    uint16_t arfcn = trx->arfcn;
+    
+    fu_config[119] = arfcn >> 8;
+    fu_config[119 + 1] = arfcn & 0xFF;
+    
+    for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+        struct gsm_bts_trx_ts *ts = &trx->ts[i];
+
+        if (ts->hopping.enabled) {
+            /* reverse order */
+            int j;
+            for(j = 0; j < ts->hopping.ma_len; j++)
+                fu_config[133 + (i * 10) + (7 - j)] = ts->hopping.ma_data[j];
+            fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn;
+            fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio;
+            *hopping = 1;
+        } else {
+            fu_config[133 + 8 + (i * 10)] = arfcn >> 8;
+            fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF;
+        }
+    }
+    
+    /* set BSIC */
+    
+    /* 
+       Attention: all TRX except the first one seem to get the TSC
+       from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION,
+       GSM 04.08 CHANNEL DESCRIPTION). 
+       There was a bug in rsl_chan_activate_lchan() setting this parameter.       
+    */
+    
+    uint8_t bsic = trx->bts->bsic;
+            
+    fu_config[106] = bsic;
+            
+    /* set CA */
+    
+    if(generate_cell_chan_list(&fu_config[38], trx->bts) != 0) {    
+        fprintf(stderr, "generate_cell_chan_list failed\n");
+        return 0;
+    }
+                                    
+    /* set channel configuration */
+    
+    for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+        struct gsm_bts_trx_ts *ts = &trx->ts[i];
+        uint8_t chan_config;                
+        
+        /*
+            0 = FCCH + SCH + BCCH + CCCH
+            1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4
+            2 = BCCH + CCCH (This combination is not used in any BTS)
+            3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH
+            4 = SDCCH/8 + SACCH/8
+            5 = SDCCH/8 with SDCCH2 used as CBCH
+            6 = TCH/F + FACCH/F + SACCH/F
+            7 = E-RACH (Talk family)
+            9 = Dual rate (capability for TCH/F and TCH/H)
+            10 = reserved for BTS internal use
+            11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (This channel configuration type can be used in GPRS release 2).
+            0xFF = spare TS 
+        */
+        
+        if(ts->pchan == GSM_PCHAN_NONE)
+            chan_config = 0xFF;
+        else if(ts->pchan == GSM_PCHAN_CCCH)
+            chan_config = 0;
+        else if(ts->pchan == GSM_PCHAN_CCCH_SDCCH4)
+            chan_config = 1;
+        else if(ts->pchan == GSM_PCHAN_TCH_F)
+            chan_config = 6; /* 9 should work too */
+        else if(ts->pchan == GSM_PCHAN_TCH_H)
+            chan_config = 9;
+        else if(ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C)
+            chan_config = 4;
+        else if(ts->pchan == GSM_PCHAN_PDCH)
+            chan_config = 11;
+        else {      
+            fprintf(stderr, "unsupported channel config %d for timeslot %d\n", ts->pchan, i);
+            return 0;
+        }
+        
+        fu_config[72 + i] = chan_config; 
+    }   
+    return sizeof(fu_config_template);
+}
+
+/* TODO: put in a separate file ? */
+
+static uint8_t bts_config_1[] =
+{
+  0x4E, 0x02, 
+  /* ID = 0x0E (Frame number) */
+  /* length = 2 */
+  /* [2] */
+  0xFF, 0xFF, 
+
+  0x5F, 0x4E, 0x02, 
+  /* ID = 0x4E (RX antenna supervision period) */
+  /* length = 2 */
+  /* [7] */
+  0xFF, 0xFF, 
+
+  0x5F, 0x50, 0x02, 
+  /* ID = 0x50 (Sector configuration) */
+  /* length = 2 */
+  /* [12] */
+  0x01, 0x01, 
+};
+
+static uint8_t bts_config_2[] =
+{
+  0x55, 0x02, 
+  /* ID = 0x15 (Hopping mode) */
+  /* length = 2 */
+  /* [2] */
+  0x01, 0x00, 
+
+  0x5F, 0x75, 0x02, 
+  /* ID = 0x75 (RX diversity selection) */
+  /* length = 2 */
+  /* [7] */
+  0x01, 0x01, 
+};
+
+static uint8_t bts_config_3[] =
+{  
+  0x5F, 0x20, 0x02, 
+  /* ID = 0x20 (Extended cell radius) */
+  /* length = 2 */
+  /* [3] */
+  0x01, 0x00, 
+};
+
+static uint8_t bts_config_4[] =
+{    
+  0x5F, 0x74, 0x09, 
+  /* ID = 0x74 (Real Time) */
+  /* length = 9 */
+  /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */
+  0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, 
+  0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [15] */
+  0x01, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [21] */
+  0x02, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [27] */
+  0x03, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [33] */
+  0x04, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [39] */
+  0x05, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [45] */
+  0x06, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [51] */
+  0x07, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [57] */
+  0x08, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [63] */
+  0x09, 0x01, 0x00, 
+
+  0x5F, 0x76, 0x03, 
+  /* ID = 0x76 (EAC input config) */
+  /* length = 3 */
+  /* [69] */
+  0x0A, 0x01, 0x00, 
+};
+
+static uint8_t bts_config_insite[] =
+{
+  0x4E, 0x02, 
+  /* ID = 0x0E (Frame number) */
+  /* length = 2 */
+  /* [2] */
+  0xFF, 0xFF, 
+
+  0x5F, 0x4E, 0x02, 
+  /* ID = 0x4E (RX antenna supervision period) */
+  /* length = 2 */
+  /* [7] */
+  0xFF, 0xFF, 
+
+  0x5F, 0x50, 0x02, 
+  /* ID = 0x50 (Sector configuration) */
+  /* length = 2 */
+  /* [12] */
+  0x01, 0x01, 
+
+  0x55, 0x02, 
+  /* ID = 0x15 (Hopping mode) */
+  /* length = 2 */
+  /* [16] */
+  0x01, 0x00, 
+
+  0x5F, 0x20, 0x02, 
+  /* ID = 0x20 (Extended cell radius) */
+  /* length = 2 */
+  /* [21] */
+  0x01, 0x00, 
+
+  0x5F, 0x74, 0x09, 
+  /* ID = 0x74 (Real Time) */
+  /* length = 9 */
+  /* [26] */
+  0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, 
+  0x00, 
+};
+
+void set_real_time(uint8_t *real_time)
+{
+    time_t t;
+    struct tm *tm;
+
+    t = time(NULL);
+    tm = localtime(&t);
+
+    /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */
+  
+    real_time[0] = (1900 + tm->tm_year) >> 8;
+    real_time[1] = (1900 + tm->tm_year) & 0xFF;
+    real_time[2] = tm->tm_mon + 1;
+    real_time[3] = tm->tm_mday;
+    real_time[4] = tm->tm_hour;
+    real_time[5] = tm->tm_min;
+    real_time[6] = tm->tm_sec;
+    real_time[7] = 0; 
+    real_time[8] = 0;
+}
+
+/* TODO: put in a separate file ? */
+
+/*
+
+  build the configuration data
+  
+*/
+
+static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t *fu_config, int need_hopping)
+{
+    /* is it an InSite BTS ? */
+    if(bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */
+        if(n_trx != 1) {
+            fprintf(stderr, "InSite has only one TRX\n");       
+            return 0;
+        }
+        if(need_hopping != 0) {
+            fprintf(stderr, "InSite does not support hopping\n");
+            return 0;
+        }
+        memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite));
+        set_real_time(&fu_config[26]);
+        return sizeof(bts_config_insite);
+    }
+                
+    int len = 0;
+    int i;
+    
+    memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1));    
+    
+    /* set sector configuration */
+    fu_config[len + 12 - 1] = 1 + n_trx; /* len */
+    for(i = 0; i < n_trx; i++)
+        fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF);
+    
+    len += (sizeof(bts_config_1) + (n_trx - 1));
+
+    memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2));
+    /* set hopping mode (Baseband and RF hopping work for the MetroSite) */
+    if(need_hopping)
+        fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */
+    len += sizeof(bts_config_2);
+    
+    /* set extended cell radius for each TRX */ 
+    for(i = 0; i < n_trx; i++) {
+        memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3));
+        fu_config[len + 3] = ((i + 1) & 0xFF);
+        len += sizeof(bts_config_3);
+    }
+    
+    memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4));
+    set_real_time(&fu_config[len + 3]);
+    len += sizeof(bts_config_4);
+    
+    return len;
+}
+
+/* TODO: put in a separate file ? */
+
+static struct msgb *nm_msgb_alloc(void)
+{
+    return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE,
+                   "OML");
+}
+
+/* TODO: put in a separate file ? */
+
+struct abis_om_nokia_hdr {
+    uint8_t  msg_type;
+    uint8_t  spare;
+    uint16_t reference;
+    uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr))
+
+static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t *data, int len_data)
+{
+    struct abis_om_hdr *oh;
+    struct abis_om_nokia_hdr *noh;
+    struct msgb *msg = nm_msgb_alloc();
+    
+    oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_data);
+        
+    oh->mdisc = ABIS_OM_MDISC_FOM;
+    oh->placement = ABIS_OM_PLACEMENT_ONLY;
+    oh->sequence = 0;
+    oh->length = sizeof(struct abis_om_nokia_hdr) + len_data;
+
+    noh = (struct abis_om_nokia_hdr *)oh->data; 
+    
+    noh->msg_type = msg_type;
+    noh->spare = 0;
+    noh->reference = htons(ref);
+    memcpy(noh->data, data, len_data);
+    
+    DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type));
+
+    return abis_nm_sendmsg(bts, msg);
+}
+
+/* TODO: put in a separate file ? */
+
+static uint8_t download_req[] = {
+  0x5F, 0x25, 0x0B, 
+  /* ID = 0x25 (File identity) */
+  /* length = 11 */
+  /* [3] */
+  0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 
+  0x2A, 0x2A, 0x2A, 
+
+  0x5F, 0x78, 0x03, 
+  /* ID = 0x78 (File version) */
+  /* length = 3 */
+  /* [17] */
+  0x2A, 0x2A, 0x2A, 
+
+  0x5F, 0x81, 0x0A, 0x01, 
+  /* ID = 0x8A (SW load mode) */
+  /* length = 1 */
+  /* [24] */
+  0x01, 
+
+  0x5F, 0x81, 0x06, 0x01, 
+  /* ID = 0x86 (Acknowledgement period) */
+  /* length = 1 */
+  /* [29] */
+  0x01, 
+};
+
+static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref)
+{
+    uint8_t *data = download_req;
+    int len_data = sizeof(download_req);
+    
+    return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, len_data);
+}
+
+/* TODO: put in a separate file ? */
+
+static uint8_t ack[] = {
+  0x5F, 0x23, 0x01, 
+  /* ID = 0x23 (Ack-Nack) */
+  /* length = 1 */
+  /* [3] */
+  0x01, 
+};
+
+static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref)
+{
+    uint8_t *data = ack;
+    int len_data = sizeof(ack);
+    
+    return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data);
+}
+
+/* TODO: put in a separate file ? */
+
+static uint8_t reset[] = {
+  0x5F, 0x40, 0x04, 
+  /* ID = 0x40 (Object identity) */
+  /* length = 4 */
+  /* [3] */
+  0x00, 0x01, 0xFF, 0xFF, 
+};
+
+static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref)
+{
+    uint8_t *data = reset;
+    int len_data = sizeof(reset);
+    
+    return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data);
+}
+
+/* TODO: put in a separate file ? */
+
+static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t *data, int len)
+{
+    int len_remain, len_to_send, max_send;
+    int seq = 0;
+    int ret;
+        
+    len_remain = len;
+    
+    while(len_remain) { 
+        struct abis_om_hdr *oh;
+        struct abis_om_nokia_hdr *noh;
+        struct msgb *msg = nm_msgb_alloc();
+        
+        if(seq == 0)
+            max_send = 256 - sizeof(struct abis_om_nokia_hdr);
+        else
+            max_send = 256;
+            
+        if(len_remain > max_send) {
+            len_to_send = max_send;
+            
+            if(seq == 0) {
+                /* first segment */
+                oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_to_send);
+        
+                oh->mdisc = ABIS_OM_MDISC_FOM;
+                oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */
+                oh->sequence = seq;
+                oh->length = 0; /* 256 bytes */
+            
+                noh = (struct abis_om_nokia_hdr *)oh->data; 
+    
+                noh->msg_type = msg_type;
+                noh->spare = 0;
+                noh->reference = htons(ref);
+                memcpy(noh->data, data, len_to_send);
+            } else {
+                /* segment in between */
+                oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(struct abis_om_hdr) + len_to_send);
+        
+                oh->mdisc = ABIS_OM_MDISC_FOM;
+                oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */
+                oh->sequence = seq;
+                oh->length = 0; /* 256 bytes */
+            
+                memcpy(oh->data, data, len_to_send);
+            }
+        }
+        else {
+        
+            len_to_send = len_remain;
+        
+            /* check if message fits in a single segment */
+            
+            if(seq == 0)
+                return abis_nm_send(bts, msg_type, ref, data, len_to_send);
+                    
+            /* last segment */
+            
+            oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(struct abis_om_hdr) + len_to_send);
+        
+            oh->mdisc = ABIS_OM_MDISC_FOM;
+            oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */
+            oh->sequence = seq;
+            oh->length = len_to_send;
+            
+            memcpy(oh->data, data, len_to_send);
+        }
+    
+        DEBUGPC(DNM, "Sending multi-segment %d\n", seq);
+
+        ret = abis_nm_sendmsg(bts, msg);
+        if(ret < 0)
+            return ret;
+        
+        abis_nm_queue_send_next(bts);
+            
+        /* next segment */  
+        len_remain -= len_to_send;
+        data += len_to_send;
+        seq++;
+    }
+    return ret;
+}
+
+/* TODO: put in a separate file ? */
+
+static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type)
+{
+    struct gsm_bts_trx *trx;        
+    uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */
+    int len = 0;
+    int idx = 0;
+    int ret;
+    int hopping = 0;
+    int need_hopping = 0;
+        
+    memset(config, 0, sizeof(config));
+        
+    llist_for_each_entry(trx, &bts->trx_list, list)
+    {
+#if 0 /* debugging */   
+        printf("TRX\n");
+        printf("  arfcn: %d\n", trx->arfcn);
+        printf("  bsic: %d\n", trx->bts->bsic);
+        uint8_t ca[20];
+        memset(ca, 0xFF, sizeof(ca));
+        ret = generate_cell_chan_list(ca, trx->bts);
+        printf("  ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca)));                                    
+        int i;
+        for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+            struct gsm_bts_trx_ts *ts = &trx->ts[i];
+                
+            printf("  pchan %d: %d\n", i, ts->pchan);
+        }           
+#endif          
+        ret = make_fu_config(trx, idx + 1, config + len, &hopping);
+        need_hopping |= hopping;
+        len += ret;
+            
+        idx++;
+    }
+        
+    ret = make_bts_config(bts_type, idx, config + len, need_hopping);
+    len += ret;
+        
+#if 0 /* debugging */   
+    dump_elements(config, len); 
+#endif
+    
+    return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, len);
+}
+
+#define GET_NEXT_BYTE if(idx >= len) return 0; \
+                        ub = data[idx++]; 
+
+static int find_element(uint8_t *data, int len, uint16_t id, uint8_t *value, int max_value)
+{   
+    uint8_t ub;
+    int idx = 0;
+    int found = 0;
+    int constructed;
+    uint16_t id_value;
+        
+    for(;;) {   
+    
+        GET_NEXT_BYTE;
+        
+        /* encoding bit, construced means that other elements are contained */
+        constructed = ((ub & 0x20) ? 1 : 0);
+        
+        if((ub & 0x1F) == 0x1F) {
+            /* fixed pattern, ID follows */
+            GET_NEXT_BYTE; /* ID */
+            id_value = ub & 0x7F;
+            if(ub & 0x80) { 
+                /* extension bit */
+                GET_NEXT_BYTE; /* ID low part */
+                id_value = (id_value << 7) | (ub & 0x7F);
+            }
+            if(id_value == id)
+                found = 1;
+        }
+        else {
+            id_value = (ub & 0x3F);
+            if(id_value == id)
+                found = 1;
+        }
+        
+        GET_NEXT_BYTE; /* length */
+        
+        if(found) {
+            /* get data */
+            uint8_t n = ub;
+            uint8_t i;
+            for(i = 0; i < n; i++) {
+                GET_NEXT_BYTE;
+                if(max_value <= 0)
+                    return -1; /* buffer too small */
+                *value = ub;
+                value++;
+                max_value--;
+            }
+            return n; /* length */
+        }
+        else {
+            /* skip data */
+            uint8_t n = ub;
+            uint8_t i;
+            for(i = 0; i < n; i++) {
+                GET_NEXT_BYTE;
+            }
+        }
+    }
+    return 0; /* not found */
+}
+
+static int dump_elements(uint8_t *data, int len)
+{   
+    uint8_t ub;
+    int idx = 0;
+    int constructed;
+    uint16_t id_value;
+    static char indent[100] = ""; /* TODO: move static to BTS context */
+        
+    for(;;) {   
+    
+        GET_NEXT_BYTE;
+        
+        /* encoding bit, construced means that other elements are contained */
+        constructed = ((ub & 0x20) ? 1 : 0);
+        
+        if((ub & 0x1F) == 0x1F) {
+            /* fixed pattern, ID follows */
+            GET_NEXT_BYTE; /* ID */
+            id_value = ub & 0x7F;
+            if(ub & 0x80) { 
+                /* extension bit */
+                GET_NEXT_BYTE; /* ID low part */
+                id_value = (id_value << 7) | (ub & 0x7F);
+            }
+            
+        }
+        else {
+            id_value = (ub & 0x3F);
+        }
+        
+        GET_NEXT_BYTE; /* length */
+        
+        printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, get_element_name_string(id_value), constructed ? "** constructed **" : "");
+        printf("%s  length = %d\n", indent, ub);        
+        printf("%s  %s\n", indent, osmo_hexdump(data + idx, ub));
+
+        if(constructed) {
+            int indent_len = strlen(indent);
+            strcat(indent, "   ");          
+            
+            dump_elements(data + idx, ub);          
+            
+            indent[indent_len] = 0;
+        }
+        /* skip data */
+        uint8_t n = ub;
+        uint8_t i;
+        for(i = 0; i < n; i++) {
+            GET_NEXT_BYTE;
+        }
+    }
+    return 0;
+}
+
+/* TODO: put in a separate file ? */
+
+/* taken from abis_nm.c */
+
+static void abis_nm_queue_send_next(struct gsm_bts *bts)
+{
+    int wait = 0;
+    struct msgb *msg;
+    /* the queue is empty */
+    while (!llist_empty(&bts->abis_queue)) {
+        msg = msgb_dequeue(&bts->abis_queue);
+        wait = OBSC_NM_W_ACK_CB(msg);
+        _abis_nm_sendmsg(msg, 0);
+
+        if (wait)
+            break;
+    }
+
+    bts->abis_nm_pend = wait;
+}
+
+/* TODO: put in a separate file ? */
+
+/* timer for restarting OML after BTS reset */
+
+static void reset_timer_cb(void *_bts)
+{
+    struct gsm_bts *bts = _bts;
+    struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
+    struct e1inp_line *line;
+    
+    wait_reset = 0;
+    
+    /* OML link */
+    line = e1inp_line_get(e1_link->e1_nr);
+    if (!line) {
+        LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to "
+             "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
+        return;
+    }
+    
+    start_sabm_in_line(line, 0, -1); /* stop all first */
+    start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */
+}
+
+/* TODO: put in a separate file ? */
+
+/*
+  This is how the configuration is done:
+  
+  - start OML link
+  
+  - reset BTS
+  
+  - receive ACK, wait some time and restart OML link
+  
+  - receive OMU STARTED message, send START DOWNLOAD REQ
+  
+  - receive CNF REQ message, send CONF DATA
+  
+  - receive ACK, start RSL link(s)
+  
+  ACK some other messages received from the BTS.
+  
+  Probably its also possible to configure the BTS without a reset, this 
+  has not been tested yet.
+*/
+
+static int abis_nm_rcvmsg_fom(struct msgb *mb)
+{
+    struct gsm_bts *bts = mb->trx->bts;
+    struct abis_om_hdr *oh = msgb_l2(mb);
+    struct abis_om_nokia_hdr *noh = msgb_l3(mb);
+    uint8_t mt = noh->msg_type;
+    int ret = 0;
+    uint16_t ref = ntohs(noh->reference);
+    /* TODO: move statics to BTS context */
+    static int conf = 0;
+    static uint8_t bts_type = 0xFF;
+    uint8_t info[256];
+    uint8_t ack = 0xFF;
+    uint8_t severity = 0xFF;
+    int str_len;
+    int len_data;
+
+    if(wait_reset) {
+        LOGP(DNM, LOGL_INFO, "Ignore message while waiting for reset\n");
+        return ret;
+    }
+
+    if(oh->length < sizeof(struct abis_om_nokia_hdr)) { 
+        LOGP(DNM, LOGL_ERROR, "Message too short\n");
+        return -EINVAL;
+    }
+    
+    len_data = oh->length - sizeof(struct abis_om_nokia_hdr);
+    LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt));
+#if 0 /* debugging */   
+    dump_elements(noh->data, len_data); 
+#endif
+    
+    switch (mt) {
+    case NOKIA_MSG_OMU_STARTED:
+        if(find_element(noh->data, len_data, NOKIA_EI_BTS_TYPE, &bts_type, sizeof(uint8_t)) == sizeof(uint8_t))
+            LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", bts_type, get_bts_type_string(bts_type));
+        else
+            LOGP(DNM, LOGL_ERROR, "BTS type not found\n");
+        /* send START_DOWNLOAD_REQ */
+        abis_nm_download_req(bts, ref);
+        break;
+    case NOKIA_MSG_MF_REQ:
+        break;
+    case NOKIA_MSG_CONF_REQ:
+        /* send ACK */
+        abis_nm_ack(bts, ref);
+        abis_nm_queue_send_next(bts);
+        /* send CONF_DATA */
+        abis_nm_send_config(bts, bts_type);
+        conf = 1;
+        break;
+    case NOKIA_MSG_ACK:
+        if(find_element(noh->data, len_data, NOKIA_EI_ACK, &ack, sizeof(uint8_t)) == sizeof(uint8_t)) {
+            LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack);
+            if(ack != 1) {
+                LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", ack);
+                /* TODO: properly handle failures (NACK) */                     
+            }
+        }
+        else
+            LOGP(DNM, LOGL_ERROR, "ACK not found\n");
+
+        /* TODO: the assumption for the following is that no NACK was received */
+            
+        /* ACK for reset message ? */
+        if(do_reset != 0) {
+            do_reset = 0;
+            
+            /* 
+              TODO: For the InSite processing the received data is 
+              blocked in the driver during reset.
+              Otherwise the LAPD module might assert because the InSite
+              sends garbage on the E1 line during reset.
+              This is done by looking at "wait_reset" in the driver
+              (function handle_ts1_read()) and ignoring the received data.
+              It seems to be necessary for the MetroSite too.
+            */
+            wait_reset = 1;
+            
+            reset_timer.cb = &reset_timer_cb;
+            reset_timer.data = bts;
+            osmo_timer_schedule(&reset_timer, RESET_INTERVAL);
+            
+            struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
+            struct e1inp_line *line;
+            /* OML link */
+            line = e1inp_line_get(e1_link->e1_nr);
+            if (!line) {
+                LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to "
+                     "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
+                return -ENOMEM;
+            }
+    
+            start_sabm_in_line(line, 0, -1); /* stop all first */           
+        }
+        
+        /* ACK for CONF DATA message ? */
+        if(conf != 0) {         
+            /* start TRX  (RSL link) */
+            
+            struct gsm_e1_subslot *e1_link = &mb->trx->rsl_e1_link;
+            struct e1inp_line *line;
+            
+            conf = 0;
+            
+            /* RSL Link */
+            line = e1inp_line_get(e1_link->e1_nr);
+            if (!line) {
+                LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring "
+                     "to non-existing E1 line %u\n", mb->trx->bts->nr,
+                     mb->trx->nr, e1_link->e1_nr);
+                return -ENOMEM;
+            }            
+            /* start TRX */         
+            start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */
+        }
+        break;
+    case NOKIA_MSG_STATE_CHANGED:
+        /* send ACK */
+        abis_nm_ack(bts, ref);
+        break;
+    case NOKIA_MSG_CONF_COMPLETE:
+        /* send ACK */
+        abis_nm_ack(bts, ref);
+        break;
+    case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */
+        /* send ACK (do we have to send an ACK ?) */
+        abis_nm_ack(bts, ref);
+        break;
+    case NOKIA_MSG_ALARM:
+        find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, sizeof(severity));
+        /* TODO: there might be alarms with both elements set */
+        str_len = find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, sizeof(info));
+        if(str_len > 0) {
+            info[str_len] = 0;
+            LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", get_severity_string(severity), severity, info);
+        } else { /* nothing found, try details */       
+            str_len = find_element(noh->data, len_data, NOKIA_EI_ALARM_DETAIL, info, sizeof(info));
+            if(str_len > 0) {
+                uint16_t code;
+                info[str_len] = 0;              
+                code = (info[0] << 8) + info[1];
+                LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d), code 0x%X : %s\n", get_severity_string(severity), severity, code, info + 2);
+            }
+        }
+        /* send ACK */
+        abis_nm_ack(bts, ref);
+        break;
+    }
+
+    abis_nm_queue_send_next(bts);
+    
+    return ret;
+}
+
+/* TODO: put in a separate file ? */
+
+int abis_nokia_rcvmsg(struct msgb *msg)
+{
+    struct abis_om_hdr *oh = msgb_l2(msg);
+    int rc = 0;
+
+    /* Various consistency checks */
+    if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
+        LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
+            oh->placement);
+        if (oh->placement != ABIS_OM_PLACEMENT_FIRST)
+            return -EINVAL;
+    }
+    if (oh->sequence != 0) {
+        LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
+            oh->sequence);
+        return -EINVAL;
+    }
+    msg->l3h = (unsigned char *)oh + sizeof(*oh);
+
+    switch (oh->mdisc) {
+    case ABIS_OM_MDISC_FOM:
+        LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n");
+        rc = abis_nm_rcvmsg_fom(msg);
+        break;
+    case ABIS_OM_MDISC_MANUF:
+        LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n");
+        break;
+    case ABIS_OM_MDISC_MMI:
+    case ABIS_OM_MDISC_TRAU:
+        LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
+            oh->mdisc);
+        break;
+    default:
+        LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
+            oh->mdisc);
+        return -EINVAL;
+    }
+
+    msgb_free(msg);
+    return rc;
+}
+
+static int bts_model_nokia_site_start(struct gsm_network *net);
+
+static struct gsm_bts_model model_nokia_site = {
+    .type = GSM_BTS_TYPE_NOKIA_SITE,
+    .name = "nokia_site",
+    .start = bts_model_nokia_site_start,
+    .oml_rcvmsg = &abis_nokia_rcvmsg
+};
+
+static struct gsm_network *my_net;
+
+static int bts_model_nokia_site_start(struct gsm_network *net)
+{
+    model_nokia_site.features.data = &model_nokia_site._features_data[0];
+    model_nokia_site.features.data_len = sizeof(model_nokia_site._features_data);
+
+    gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING);
+    gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD);
+
+    osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL);
+    osmo_signal_register_handler(SS_GLOBAL, gbl_sig_cb, NULL);
+    osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
+
+    my_net = net;
+    
+    return 0;
+}
+
+int bts_model_nokia_site_init(void)
+{
+    return gsm_bts_model_register(&model_nokia_site);
+}
+
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 353b3dd..5814c76 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -154,7 +154,7 @@
 }
 
 /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-static int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts)
+/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts)
 {
 	struct gsm_bts_trx *trx;
 	struct bitvec *bv = &bts->si_common.cell_alloc;