Merge branch 'master' of git.osmocom.org:openbsc
diff --git a/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg b/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg
new file mode 100644
index 0000000..a7d980f
--- /dev/null
+++ b/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg
@@ -0,0 +1,118 @@
+!
+! OpenBSC configuration saved from vty
+!   !
+password foo
+!
+line vty
+ no login
+!
+e1_input
+ e1_line 0 driver misdn
+network
+ network country code 1
+ mobile network code 1
+ short name OpenBSC
+ long name OpenBSC
+ auth policy accept-all
+ timer t3101 10
+ timer t3113 60
+ bts 0
+  type nokia_site
+  band GSM1800
+  cell_identity 1
+  location_area_code 1
+  base_station_id_code 63
+  training_sequence_code 7
+  
+  oml e1 line 0 timeslot 1 sub-slot full
+  oml e1 tei 1
+  
+  trx 0
+   arfcn 866
+   max_power_red 24
+   rsl e1 line 0 timeslot 2 sub-slot full
+   rsl e1 tei 1
+    timeslot 0
+     phys_chan_config CCCH+SDCCH4
+     e1 line 0 timeslot 6 sub-slot full
+    timeslot 1
+     phys_chan_config SDCCH8
+     e1 line 0 timeslot 6 sub-slot 1
+    timeslot 2
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 6 sub-slot 2
+    timeslot 3
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 6 sub-slot 3
+    timeslot 4
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 7 sub-slot 0
+    timeslot 5
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 7 sub-slot 1
+    timeslot 6
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 7 sub-slot 2
+    timeslot 7
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 7 sub-slot 3
+
+  trx 1
+   arfcn 870
+   max_power_red 24
+   rsl e1 line 0 timeslot 3 sub-slot full
+   rsl e1 tei 2
+    timeslot 0
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 8 sub-slot 0
+    timeslot 1
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 8 sub-slot 1
+    timeslot 2
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 8 sub-slot 2
+    timeslot 3
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 8 sub-slot 3
+    timeslot 4
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 9 sub-slot 0
+    timeslot 5
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 9 sub-slot 1
+    timeslot 6
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 9 sub-slot 2
+    timeslot 7
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 9 sub-slot 3
+
+  trx 2
+   arfcn 874
+   max_power_red 24
+   rsl e1 line 0 timeslot 4 sub-slot full
+   rsl e1 tei 3
+    timeslot 0
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 10 sub-slot 0
+    timeslot 1
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 10 sub-slot 1
+    timeslot 2
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 10 sub-slot 2
+    timeslot 3
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 10 sub-slot 3
+    timeslot 4
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 11 sub-slot 0
+    timeslot 5
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 11 sub-slot 1
+    timeslot 6
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 11 sub-slot 2
+    timeslot 7
+     phys_chan_config TCH/F
+     e1 line 0 timeslot 11 sub-slot 3
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 3b4df90..bf1df5e 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -90,5 +90,12 @@
 int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number,
 		       uint8_t cb_command, const uint8_t *data, int len);
 
+/* some Nokia specific stuff */
+int rsl_nokia_si_begin(struct gsm_bts_trx *trx);
+int rsl_nokia_si_end(struct gsm_bts_trx *trx);
+
+/* required for Nokia BTS power control */
+int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction);
+
 #endif /* RSL_MT_H */
 
diff --git a/openbsc/include/openbsc/bss.h b/openbsc/include/openbsc/bss.h
index 05495dd..2317bd2 100644
--- a/openbsc/include/openbsc/bss.h
+++ b/openbsc/include/openbsc/bss.h
@@ -14,4 +14,5 @@
 extern int bts_model_rbs2k_init(void);
 extern int bts_model_nanobts_init(void);
 extern int bts_model_hslfemto_init(void);
+extern int bts_model_nokia_site_init(void);
 #endif
diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h
index 0d79c7d..f201c4f 100644
--- a/openbsc/include/openbsc/e1_input.h
+++ b/openbsc/include/openbsc/e1_input.h
@@ -8,6 +8,7 @@
 #include <openbsc/gsm_data.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/select.h>
+#include <osmocom/core/rate_ctr.h>
 #include <openbsc/subchan_demux.h>
 
 #define NUM_E1_TS   32
@@ -19,6 +20,14 @@
 };
 const char *e1inp_signtype_name(enum e1inp_sign_type tp);
 
+enum e1inp_ctr {
+	E1I_CTR_HDLC_ABORT,
+	E1I_CTR_HDLC_BADFCS,
+	E1I_CTR_HDLC_OVERR,
+	E1I_CTR_ALARM,
+	E1I_CTR_REMOVED,
+};
+
 struct e1inp_ts;
 
 struct e1inp_sign_link {
@@ -107,6 +116,7 @@
 	struct llist_head list;
 	unsigned int num;
 	const char *name;
+	struct rate_ctr_group *rate_ctr;
 
 	/* array of timestlots */
 	struct e1inp_ts ts[NUM_E1_TS];
diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index 011e0aa..89375cf 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -182,6 +182,7 @@
 	struct osmo_timer_list T3101;
 	struct osmo_timer_list T3111;
 	struct osmo_timer_list error_timer;
+	struct osmo_timer_list act_timer;
 
 	/* table of neighbor cell measurements */
 	struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
@@ -325,6 +326,7 @@
 	GSM_BTS_TYPE_NANOBTS,
 	GSM_BTS_TYPE_RBS2000,
 	GSM_BTS_TYPE_HSL_FEMTO,
+	GSM_BTS_TYPE_NOKIA_SITE,
 };
 
 struct vty;
@@ -487,6 +489,13 @@
 		struct {
 			unsigned long serno;
 		} hsl;
+		struct {
+			uint8_t bts_type;
+			int configured:1,
+			    do_reset:1,
+			    wait_reset:1;
+			struct osmo_timer_list reset_timer;
+		} nokia;
 	};
 
 	/* Not entirely sure how ip.access specific this is */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 2991cfa..71e1dee 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -145,6 +145,7 @@
 	S_INP_NONE,
 	S_INP_TEI_UP,
 	S_INP_TEI_DN,
+	S_INP_TEI_UNKNOWN,
 	S_INP_LINE_INIT,
 	S_INP_LINE_ALARM,
 	S_INP_LINE_NOALARM,
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 098e4c2..c9fe517 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -867,7 +867,7 @@
 }
 
 static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
-				  uint16_t pdp_status)
+				  uint8_t *pdp_status)
 {
 	struct sgsn_pdp_ctx *pdp, *pdp2;
 	/* 24.008 4.7.5.1.3: If the PDP context status information element is
@@ -878,11 +878,20 @@
 	 * being in state PDP-INACTIVE. */
 
 	llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
-		if (!(pdp_status & (1 << pdp->nsapi))) {
-			LOGP(DMM, LOGL_NOTICE, "Dropping PDP context for NSAPI=%u "
-				"due to PDP CTX STATUS IE= 0x%04x\n",
-				pdp->nsapi, pdp_status);
-			sgsn_delete_pdp_ctx(pdp);
+		if (pdp->nsapi < 8) {
+			if (!(pdp_status[0] & (1 << pdp->nsapi))) {
+				LOGP(DMM, LOGL_NOTICE, "Dropping PDP context for NSAPI=%u "
+					"due to PDP CTX STATUS IE= 0x%02x%02x\n",
+					pdp->nsapi, pdp_status[1], pdp_status[0]);
+				sgsn_delete_pdp_ctx(pdp);
+			}
+		} else {
+			if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) {
+				LOGP(DMM, LOGL_NOTICE, "Dropping PDP context for NSAPI=%u "
+					"due to PDP CTX STATUS IE= 0x%02x%02x\n",
+					pdp->nsapi, pdp_status[1], pdp_status[0]);
+				sgsn_delete_pdp_ctx(pdp);
+			}
 		}
 	}
 }
@@ -975,8 +984,7 @@
 	/* Look at PDP Context Status IE and see if MS's view of
 	 * activated/deactivated NSAPIs agrees with our view */
 	if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) {
-		uint16_t pdp_status = ntohs(*(uint16_t *)
-				TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS));
+		uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
 		process_ms_ctx_status(mmctx, pdp_status);
 	}
 
diff --git a/openbsc/src/libabis/e1_input.c b/openbsc/src/libabis/e1_input.c
index 97dcd33..a6661b7 100644
--- a/openbsc/src/libabis/e1_input.c
+++ b/openbsc/src/libabis/e1_input.c
@@ -39,17 +39,19 @@
 #endif
 
 #include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
 #include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/rate_ctr.h>
+
 #include <openbsc/debug.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/e1_input.h>
 #include <openbsc/abis_nm.h>
 #include <openbsc/abis_rsl.h>
-#include <osmocom/core/linuxlist.h>
 #include <openbsc/subchan_demux.h>
 #include <openbsc/trau_frame.h>
 #include <openbsc/trau_mux.h>
-#include <osmocom/core/talloc.h>
 #include <openbsc/signal.h>
 #include <openbsc/misdn.h>
 
@@ -65,6 +67,31 @@
 
 static void *tall_sigl_ctx;
 
+static const struct rate_ctr_desc e1inp_ctr_d[] = {
+	[E1I_CTR_HDLC_ABORT]  = {
+		"hdlc.abort", 	"HDLC abort"
+	},
+	[E1I_CTR_HDLC_BADFCS] = {
+		"hdlc.bad_fcs",	"HLDC Bad FCS"
+	},
+	[E1I_CTR_HDLC_OVERR]  = {
+		"hdlc.overrun",	"HDLC Overrun"
+	},
+	[E1I_CTR_ALARM] = {
+		"alarm", 	"Alarm"
+	},
+	[E1I_CTR_REMOVED] = {
+		"removed", 	"Line removed"
+	},
+};
+
+static const struct rate_ctr_group_desc e1inp_ctr_g_d = {
+	.group_name_prefix = "e1inp",
+	.group_description = "E1 Input subsystem",
+	.num_ctr = ARRAY_SIZE(e1inp_ctr_d),
+	.ctr_desc = e1inp_ctr_d,
+};
+
 /*
  * pcap writing of the misdn load
  * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
@@ -360,8 +387,10 @@
 		return NULL;
 
 	line->driver = driver;
-
 	line->num = e1_nr;
+
+	line->rate_ctr = rate_ctr_group_alloc(line, &e1inp_ctr_g_d, line->num);
+
 	for (i = 0; i < NUM_E1_TS; i++) {
 		line->ts[i].num = i+1;
 		line->ts[i].line = line;
@@ -570,6 +599,7 @@
 	if (!link)
 		return -EINVAL;
 
+	isd.line = ts->line;
 	isd.link_type = link->type;
 	isd.trx = link->trx;
 	isd.tei = tei;
diff --git a/openbsc/src/libabis/e1_input_vty.c b/openbsc/src/libabis/e1_input_vty.c
index b211e81..eb34aa1 100644
--- a/openbsc/src/libabis/e1_input_vty.c
+++ b/openbsc/src/libabis/e1_input_vty.c
@@ -20,30 +20,36 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm_utils.h>
+
 #include <osmocom/vty/command.h>
 #include <osmocom/vty/buffer.h>
 #include <osmocom/vty/vty.h>
 #include <osmocom/vty/logging.h>
+#include <osmocom/vty/misc.h>
 #include <osmocom/vty/telnet_interface.h>
 
-#include <osmocom/core/linuxlist.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/e1_input.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/talloc.h>
 #include <openbsc/vty.h>
 #include <openbsc/debug.h>
 
 #include "../../bscconfig.h"
 
+/* CONFIG */
+
 #define E1_DRIVER_NAMES		"(misdn|dahdi)"
 #define E1_DRIVER_HELP		"mISDN supported E1 Card\n" \
 				"DAHDI supported E1/T1/J1 Card\n"
 
+#define E1_LINE_HELP		"Configure E1/T1/J1 Line\n" "Line Number\n"
+
 DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd,
 	"e1_line <0-255> driver " E1_DRIVER_NAMES,
-	"Configure E1/T1/J1 Line\n" "Line Number\n" "Set driver for this line\n"
+	E1_LINE_HELP "Set driver for this line\n"
 	E1_DRIVER_HELP)
 {
 	struct e1inp_line *line;
@@ -63,6 +69,27 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_e1line_name, cfg_e1_line_name_cmd,
+	"e1_line <0-255> name .LINE",
+	E1_LINE_HELP "Set name for this line\n" "Human readable name\n")
+{
+	struct e1inp_line *line;
+	int e1_nr = atoi(argv[0]);
+
+	line = e1inp_line_get(e1_nr);
+	if (!line) {
+		vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	if (line->name) {
+		talloc_free((void *)line->name);
+		line->name = NULL;
+	}
+	line->name = talloc_strdup(line, argv[1]);
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_e1inp, cfg_e1inp_cmd,
 	"e1_input",
 	"Configure E1/T1/J1 TDM input\n")
@@ -84,10 +111,138 @@
 	llist_for_each_entry(line, &e1inp_line_list, list) {
 		vty_out(vty, " e1_line %u driver %s%s", line->num,
 			line->driver->name, VTY_NEWLINE);
+		if (line->name)
+			vty_out(vty, " e1_line %u name %s%s", line->num,
+				line->name, VTY_NEWLINE);
 	}
 	return CMD_SUCCESS;
 }
 
+/* SHOW */
+
+static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
+{
+	vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
+}
+
+DEFUN(show_e1drv,
+      show_e1drv_cmd,
+      "show e1_driver",
+	SHOW_STR "Display information about available E1 drivers\n")
+{
+	struct e1inp_driver *drv;
+
+	llist_for_each_entry(drv, &e1inp_driver_list, list)
+		e1drv_dump_vty(vty, drv);
+
+	return CMD_SUCCESS;
+}
+
+static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line,
+			    int stats)
+{
+	vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s",
+		line->num, line->name ? line->name : "",
+		line->driver->name, VTY_NEWLINE);
+	if (stats)
+		vty_out_rate_ctr_group(vty, " ", line->rate_ctr);
+}
+
+DEFUN(show_e1line,
+      show_e1line_cmd,
+      "show e1_line [line_nr] [stats]",
+	SHOW_STR "Display information about a E1 line\n"
+	"E1 Line Number\n")
+{
+	struct e1inp_line *line;
+	int stats = 0;
+
+	if (argc >= 1 && strcmp(argv[0], "stats")) {
+		int num = atoi(argv[0]);
+		if (argc >= 2)
+			stats = 1;
+		llist_for_each_entry(line, &e1inp_line_list, list) {
+			if (line->num == num) {
+				e1line_dump_vty(vty, line, stats);
+				return CMD_SUCCESS;
+			}
+		}
+		return CMD_WARNING;
+	}
+
+	if (argc >= 1 && !strcmp(argv[0], "stats"))
+		stats = 1;
+
+	llist_for_each_entry(line, &e1inp_line_list, list)
+		e1line_dump_vty(vty, line, stats);
+
+	return CMD_SUCCESS;
+}
+
+static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
+{
+	if (ts->type == E1INP_TS_TYPE_NONE)
+		return;
+	vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s",
+		ts->num, ts->line->num, e1inp_tstype_name(ts->type),
+		VTY_NEWLINE);
+}
+
+DEFUN(show_e1ts,
+      show_e1ts_cmd,
+      "show e1_timeslot [line_nr] [ts_nr]",
+	SHOW_STR "Display information about a E1 timeslot\n"
+	"E1 Line Number\n" "E1 Timeslot Number\n")
+{
+	struct e1inp_line *line = NULL;
+	struct e1inp_ts *ts;
+	int ts_nr;
+
+	if (argc == 0) {
+		llist_for_each_entry(line, &e1inp_line_list, list) {
+			for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+				ts = &line->ts[ts_nr];
+				e1ts_dump_vty(vty, ts);
+			}
+		}
+		return CMD_SUCCESS;
+	}
+	if (argc >= 1) {
+		int num = atoi(argv[0]);
+		struct e1inp_line *l;
+		llist_for_each_entry(l, &e1inp_line_list, list) {
+			if (l->num == num) {
+				line = l;
+				break;
+			}
+		}
+		if (!line) {
+			vty_out(vty, "E1 line %s is invalid%s",
+				argv[0], VTY_NEWLINE);
+			return CMD_WARNING;
+		}
+	}
+	if (argc >= 2) {
+		ts_nr = atoi(argv[1]);
+		if (ts_nr >= NUM_E1_TS) {
+			vty_out(vty, "E1 timeslot %s is invalid%s",
+				argv[1], VTY_NEWLINE);
+			return CMD_WARNING;
+		}
+		ts = &line->ts[ts_nr];
+		e1ts_dump_vty(vty, ts);
+		return CMD_SUCCESS;
+	} else {
+		for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+			ts = &line->ts[ts_nr];
+			e1ts_dump_vty(vty, ts);
+		}
+		return CMD_SUCCESS;
+	}
+	return CMD_SUCCESS;
+}
+
+
 struct cmd_node e1inp_node = {
 	E1INP_NODE,
 	"%s(e1_input)#",
@@ -99,6 +254,11 @@
 	install_element(CONFIG_NODE, &cfg_e1inp_cmd);
 	install_node(&e1inp_node, e1inp_config_write);
 	install_element(E1INP_NODE, &cfg_e1_line_driver_cmd);
+	install_element(E1INP_NODE, &cfg_e1_line_name_cmd);
+
+	install_element_ve(&show_e1drv_cmd);
+	install_element_ve(&show_e1line_cmd);
+	install_element_ve(&show_e1ts_cmd);
 
 	return 0;
 }
diff --git a/openbsc/src/libabis/input/dahdi.c b/openbsc/src/libabis/input/dahdi.c
index 6f89837..6802a1e 100644
--- a/openbsc/src/libabis/input/dahdi.c
+++ b/openbsc/src/libabis/input/dahdi.c
@@ -40,6 +40,8 @@
 
 #include <osmocom/core/select.h>
 #include <osmocom/core/msgb.h>
+#include <osmocom/core/rate_ctr.h>
+
 #include <openbsc/debug.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/abis_nm.h>
@@ -68,6 +70,7 @@
 static void handle_dahdi_exception(struct e1inp_ts *ts)
 {
 	int rc, evt;
+	struct e1inp_line *line = ts->line;
 	struct input_signal_data isd;
 
 	rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt);
@@ -84,11 +87,24 @@
 	case DAHDI_EVENT_ALARM:
 		/* we should notify the code that the line is gone */
 		osmo_signal_dispatch(SS_INPUT, S_INP_LINE_ALARM, &isd);
+		rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_ALARM]);
 		break;
 	case DAHDI_EVENT_NOALARM:
 		/* alarm has gone, we should re-start the SABM requests */
 		osmo_signal_dispatch(SS_INPUT, S_INP_LINE_NOALARM, &isd);
 		break;
+	case DAHDI_EVENT_ABORT:
+		rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_ABORT]);
+		break;
+	case DAHDI_EVENT_OVERRUN:
+		rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_OVERR]);
+		break;
+	case DAHDI_EVENT_BADFCS:
+		rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_BADFCS]);
+		break;
+	case DAHDI_EVENT_REMOVED:
+		rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_REMOVED]);
+		break;
 	}
 }
 
@@ -100,7 +116,7 @@
 	struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1");
 	lapd_mph_type prim;
 	unsigned int sapi, tei;
-	int ilen, ret;
+	int ilen, ret, error = 0;
 	uint8_t *idata;
 
 	if (!msg)
@@ -122,9 +138,21 @@
 
 	DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei);
 
-	idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim);
-	if (!idata && prim == 0)
-		return -EIO;
+	idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim, &error);
+	if (!idata) {
+		switch(error) {
+		case LAPD_ERR_UNKNOWN_TEI:
+			/* We don't know about this TEI, probably the BSC
+			 * lost local states (it crashed or it was stopped),
+			 * notify the driver to see if it can do anything to
+			 * recover the existing signalling links with the BTS.
+			 */
+			e1inp_event(e1i_ts, S_INP_TEI_UNKNOWN, tei, sapi);
+			return -EIO;
+		}
+		if (prim == 0)
+			return -EIO;
+	}
 
 	msgb_pull(msg, 2);
 
@@ -421,11 +449,17 @@
 		char openstr[128];
 		struct e1inp_ts *e1i_ts = &line->ts[idx];
 		struct osmo_fd *bfd = &e1i_ts->driver.dahdi.fd;
+		int dev_nr;
+
+		/* DAHDI device names/numbers just keep incrementing
+		 * even over multiple boards.  So TS1 of the second
+		 * board will be 32 */
+		dev_nr = line->num * (NUM_E1_TS-1) + ts;
 
 		bfd->data = line;
 		bfd->priv_nr = ts;
 		bfd->cb = dahdi_fd_cb;
-		snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", ts);
+		snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", dev_nr);
 
 		switch (e1i_ts->type) {
 		case E1INP_TS_TYPE_NONE:
diff --git a/openbsc/src/libabis/input/lapd.c b/openbsc/src/libabis/input/lapd.c
index d0fab18..2934b58 100644
--- a/openbsc/src/libabis/input/lapd.c
+++ b/openbsc/src/libabis/input/lapd.c
@@ -31,7 +31,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <assert.h>
 #include <errno.h>
 
 #include "lapd.h"
@@ -190,7 +189,7 @@
 
 static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
 {
-	DEBUGP(DMI, "state change on TEI %d: %s -> %s\n", teip->tei,
+	LOGP(DMI, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n", teip->tei,
 		   lapd_tei_states[teip->state], lapd_tei_states[newstate]);
 	teip->state = newstate;
 };
@@ -234,7 +233,7 @@
 {
 	struct lapd_sap *sap = talloc_zero(teip, struct lapd_sap);
 
-	LOGP(DMI, LOGL_INFO, "Allocating SAP for SAPI=%u / TEI=%u\n",
+	LOGP(DMI, LOGL_INFO, "LAPD Allocating SAP for SAPI=%u / TEI=%u\n",
 		sapi, teip->tei);
 
 	sap->sapi = sapi;
@@ -254,8 +253,9 @@
 	if (!sap)
 		return;
 
-	DEBUGP(DMI, "state change on TEI %u / SAPI %u: %s -> %s\n", teip->tei,
-		sapi, lapd_sap_states[sap->state], lapd_sap_states[newstate]);
+	LOGP(DMI, LOGL_INFO, "LAPD state change on TEI %u / SAPI %u: "
+		"%s -> %s\n", teip->tei, sapi,
+		lapd_sap_states[sap->state], lapd_sap_states[newstate]);
 	switch (sap->state) {
 	case SAP_STATE_SABM_RETRANS:
 		if (newstate != SAP_STATE_SABM_RETRANS)
@@ -281,11 +281,12 @@
 	uint8_t resp[8];
 	struct lapd_tei *teip;
 
-	DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
+	DEBUGP(DMI, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n",
+		entity, ref, mt, action, e);
 
 	switch (mt) {
 	case 0x01:	/* IDENTITY REQUEST */
-		DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action);
+		DEBUGP(DMI, "LAPD TEIMGR: identity request for TEI %u\n", action);
 
 		teip = teip_from_tei(li, action);
 		if (!teip) {
@@ -302,15 +303,16 @@
 			lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
 		break;
 	default:
-		LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n",
+		LOGP(DMI, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x action %x\n",
 		     mt, action);
 		break;
 	};
 };
 
 /* General input function for any data received for this LAPD instance */
-uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
-		      int *ilen, lapd_mph_type *prim)
+uint8_t *
+lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
+	     int *ilen, lapd_mph_type *prim, int *error)
 {
 	uint8_t sapi, cr, tei, command;
 	int pf, ns, nr;
@@ -325,13 +327,15 @@
 	*prim = 0;
 
 	if (len < 2) {
-		DEBUGP(DMI, "len %d < 2\n", len);
+		LOGP(DMI, LOGL_ERROR, "LAPD receive len %d < 2, ignoring\n", len);
+		*error = LAPD_ERR_BAD_LEN;
 		return NULL;
 	};
 
 	if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
-		DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
-			   data[1]);
+		LOGP(DMI, LOGL_ERROR, "LAPD address field %x/%x not well formed\n",
+			data[0], data[1]);
+		*error = LAPD_ERR_BAD_ADDR;
 		return NULL;
 	};
 
@@ -342,7 +346,8 @@
 	//DEBUGP(DMI, "  address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
 
 	if (len < 3) {
-		DEBUGP(DMI, "len %d < 3\n", len);
+		LOGP(DMI, LOGL_ERROR, "LAPD receive len %d < 3, ignoring\n", len);
+		*error = LAPD_ERR_BAD_LEN;
 		return NULL;
 	};
 
@@ -353,14 +358,22 @@
 	nr = -1;
 	if ((data[2] & 1) == 0) {
 		typ = LAPD_TYPE_I;
-		assert(len >= 4);
+		if (len < 4) {
+			LOGP(DMI, LOGL_ERROR, "LAPD I frame, len %d < 4\n", len);
+			*error = LAPD_ERR_BAD_LEN;
+			return NULL;
+		}
 		ns = data[2] >> 1;
 		nr = data[3] >> 1;
 		pf = data[3] & 1;
 		cmd = LAPD_CMD_I;
 	} else if ((data[2] & 3) == 1) {
 		typ = LAPD_TYPE_S;
-		assert(len >= 4);
+		if (len < 4) {
+			LOGP(DMI, LOGL_ERROR, "LAPD S frame, len %d < 4\n", len);
+			*error = LAPD_ERR_BAD_LEN;
+			return NULL;
+		}
 		nr = data[3] >> 1;
 		pf = data[3] & 1;
 		switch (data[2]) {
@@ -374,7 +387,8 @@
 			cmd = LAPD_CMD_REJ;
 			break;
 		default:
-			LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
+			LOGP(DMI, LOGL_ERROR, "LAPD unknown S cmd %x\n", data[2]);
+			*error = LAPD_ERR_UNKNOWN_S_CMD;
 			return NULL;
 		};
 	} else if ((data[2] & 3) == 3) {
@@ -405,8 +419,9 @@
 			break;
 
 		default:
-			LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
+			LOGP(DMI, LOGL_ERROR, "LAPD unknown U cmd %x "
 			     "(pf %x data %x)\n", val, pf, data[2]);
+			*error = LAPD_ERR_UNKNOWN_U_CMD;
 			return NULL;
 		};
 	};
@@ -421,13 +436,14 @@
 
 	teip = teip_from_tei(li, tei);
 	if (!teip) {
-		LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
+		LOGP(DMI, LOGL_NOTICE, "LAPD Unknown TEI %u\n", tei);
+		*error = LAPD_ERR_UNKNOWN_TEI;
 		return NULL;
 	}
 
 	sap = lapd_sap_find(teip, sapi);
 	if (!sap) {
-		LOGP(DMI, LOGL_INFO, "No SAP for TEI=%u / SAPI=%u, "
+		LOGP(DMI, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, "
 			"allocating\n", tei, sapi);
 		sap = lapd_sap_alloc(teip, sapi);
 	}
@@ -440,12 +456,15 @@
 	switch (cmd) {
 	case LAPD_CMD_I:
 		if (ns != sap->vr) {
-			DEBUGP(DMI, "ns %d != vr %d\n", ns, sap->vr);
+			DEBUGP(DMI, "LAPD ns %d != vr %d\n", ns, sap->vr);
 			if (ns == ((sap->vr - 1) & 0x7f)) {
-				DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
+				LOGP(DMI, LOGL_NOTICE, "LAPD double frame, "
+					"ignoring\n");
 				cmd = 0;	// ignore
 			} else {
-				assert(0);
+				LOGP(DMI, LOGL_ERROR, "LAPD Out of order "
+					"ns %d != vr %d, ignoring\n", ns, sap->vr);
+				return NULL;
 			};
 		} else {
 			//printf("IN SEQUENCE\n");
@@ -560,7 +579,7 @@
 		 */
 
 		/* interrogating us, send rr */
-		DEBUGP(DMI, "Sending RR response\n");
+		DEBUGP(DMI, "LAPD Sending RR response\n");
 		resp[l++] = data[0];
 		resp[l++] = (tei << 1) | 1;
 		resp[l++] = 0x01;	// rr
@@ -577,6 +596,7 @@
 		return contents;
 	}
 
+	*error = LAPD_ERR_BAD_CMD;
 	return NULL;
 };
 
@@ -587,7 +607,7 @@
 	if (!msg)
 		return -ENOMEM;
 
-	DEBUGP(DMI, "Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi);
+	LOGP(DMI, LOGL_INFO, "LAPD Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi);
 
 	msgb_put_u8(msg, (sapi << 2) | (li->network_side ? 2 : 0));
 	msgb_put_u8(msg, (tei << 1) | 1);
@@ -662,15 +682,15 @@
 	struct lapd_sap *sap;
 
 	if (!teip) {
-		LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing "
-		     "TEI %u\n", tei);
+		LOGP(DMI, LOGL_ERROR, "LAPD Cannot transmit on "
+		     "non-existing TEI %u\n", tei);
 		return;
 	}
 
 	sap = lapd_sap_find(teip, sapi);
 	if (!sap) {
-		LOGP(DMI, LOGL_INFO, "Tx on unknown SAPI=%u in TEI=%u, "
-			"allocating\n", sapi, tei);
+		LOGP(DMI, LOGL_INFO, "LAPD Tx on unknown SAPI=%u "
+		     "in TEI=%u, allocating\n", sapi, tei);
 		sap = lapd_sap_alloc(teip, sapi);
 	}
 
diff --git a/openbsc/src/libabis/input/lapd.h b/openbsc/src/libabis/input/lapd.h
index fb980d1..dd22028 100644
--- a/openbsc/src/libabis/input/lapd.h
+++ b/openbsc/src/libabis/input/lapd.h
@@ -26,8 +26,20 @@
 	struct llist_head tei_list;	/* list of TEI in this LAPD instance */
 };
 
-extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data, unsigned int len,
-			     int *ilen, lapd_mph_type *prim);
+enum lapd_recv_errors {
+	LAPD_ERR_NONE = 0,
+	LAPD_ERR_BAD_LEN,
+	LAPD_ERR_BAD_ADDR,
+	LAPD_ERR_UNKNOWN_S_CMD,
+	LAPD_ERR_UNKNOWN_U_CMD,
+	LAPD_ERR_UNKNOWN_TEI,
+	LAPD_ERR_BAD_CMD,
+	__LAPD_ERR_MAX
+};
+
+extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data,
+			     unsigned int len, int *ilen, lapd_mph_type *prim,
+			     int *error);
 
 extern void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
 			  uint8_t *data, unsigned int len);
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..cb2c9bc 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -178,6 +178,29 @@
 		LOGPC(DRSL, lvl, "%02x ", cause_v[i]);
 }
 
+static void lchan_act_tmr_cb(void *data)
+{
+	struct gsm_lchan *lchan = data;
+
+	LOGP(DRSL, LOGL_NOTICE, "%s Timeout during activation!\n",
+		gsm_lchan_name(lchan));
+
+	rsl_lchan_set_state(lchan, LCHAN_S_NONE);
+	lchan_free(lchan);
+}
+
+static void lchan_deact_tmr_cb(void *data)
+{
+	struct gsm_lchan *lchan = data;
+
+	LOGP(DRSL, LOGL_NOTICE, "%s Timeout during deactivation!\n",
+		gsm_lchan_name(lchan));
+
+	rsl_lchan_set_state(lchan, LCHAN_S_NONE);
+	lchan_free(lchan);
+}
+
+
 /* Send a BCCH_INFO message as per Chapter 8.5.1 */
 int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type,
 		  const uint8_t *data, int len)
@@ -609,6 +632,11 @@
 				   msg->trx->bts->network->T3111 + 2, 0);
 	}
 
+	/* Start another timer or assume the BTS sends a ACK/NACK? */
+	lchan->act_timer.cb = lchan_deact_tmr_cb;
+	lchan->act_timer.data = lchan;
+	osmo_timer_schedule(&lchan->act_timer, 4, 0);
+
 	rc =  abis_rsl_sendmsg(msg);
 
 	/* BTS will respond by RF CHAN REL ACK */
@@ -626,6 +654,8 @@
 
 	DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan));
 
+	osmo_timer_del(&lchan->act_timer);
+
 	if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR)
 		LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
 			gsm_lchan_name(lchan),
@@ -791,6 +821,8 @@
 	if (rslh->ie_chan != RSL_IE_CHAN_NR)
 		return -EINVAL;
 
+	osmo_timer_del(&msg->lchan->act_timer);
+
 	if (msg->lchan->state != LCHAN_S_ACT_REQ)
 		LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
 			gsm_lchan_name(msg->lchan),
@@ -815,7 +847,9 @@
 	struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
 	struct tlv_parsed tp;
 
-	LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK",
+	osmo_timer_del(&msg->lchan->act_timer);
+
+	LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ",
 		gsm_lchan_name(msg->lchan));
 
 	/* BTS has rejected channel activation ?!? */
@@ -829,6 +863,9 @@
 				TLVP_LEN(&tp, RSL_IE_CAUSE));
 		if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
 			rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
+		else
+			rsl_rf_chan_release(msg->lchan, 1);
+
 	} else
 		rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
 
@@ -1108,6 +1145,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);
@@ -1251,7 +1294,11 @@
 	lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
 	lchan->tch_mode = GSM48_CMODE_SIGN;
 
-	/* FIXME: Start another timer or assume the BTS sends a ACK/NACK? */
+	/* Start another timer or assume the BTS sends a ACK/NACK? */
+	lchan->act_timer.cb = lchan_act_tmr_cb;
+	lchan->act_timer.data = lchan;
+	osmo_timer_schedule(&lchan->act_timer, 4, 0);
+
 	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
 
 	DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
@@ -1892,3 +1939,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_api.c b/openbsc/src/libbsc/bsc_api.c
index 70d6413..78fee88 100644
--- a/openbsc/src/libbsc/bsc_api.c
+++ b/openbsc/src/libbsc/bsc_api.c
@@ -137,7 +137,11 @@
 	LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn);
 
 	/* normal release on the secondary channel */
-	lchan_release(conn->secondary_lchan, 0, 1);
+	if (conn->secondary_lchan) {
+		lchan_release(conn->secondary_lchan, 0, 1);
+	} else {
+		LOGP(DMSC, LOGL_NOTICE, "Secondary lchan is NULL, not releasing\n");
+	}
 	conn->secondary_lchan = NULL;
 
 	/* inform them about the failure */
@@ -367,7 +371,8 @@
 	if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN)
 		rsl_ipacc_crcx(conn->lchan);
 
-	api->assign_compl(conn, gh->data[0],
+	if (api->assign_compl)
+		api->assign_compl(conn, gh->data[0],
 			  lchan_to_chosen_channel(conn->lchan),
 			  conn->lchan->encr.alg_id,
 			  chan_mode_to_speech(conn->lchan));
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/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 26ed7d9..8a5c7fd 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -942,7 +942,7 @@
 				for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN;
 				     lchan_nr++) {
 					lchan = &ts->lchan[lchan_nr];
-					if (lchan->type == GSM_LCHAN_NONE)
+					if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE))
 						continue;
 					dump_cb(vty, lchan);
 				}
@@ -975,119 +975,6 @@
 	return lchan_summary(vty, argc, argv, lchan_dump_short_vty);
 }
 
-static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
-{
-	vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
-}
-
-DEFUN(show_e1drv,
-      show_e1drv_cmd,
-      "show e1_driver",
-	SHOW_STR "Display information about available E1 drivers\n")
-{
-	struct e1inp_driver *drv;
-
-	llist_for_each_entry(drv, &e1inp_driver_list, list)
-		e1drv_dump_vty(vty, drv);
-
-	return CMD_SUCCESS;
-}
-
-static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line)
-{
-	vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s",
-		line->num, line->name ? line->name : "",
-		line->driver->name, VTY_NEWLINE);
-}
-
-DEFUN(show_e1line,
-      show_e1line_cmd,
-      "show e1_line [line_nr]",
-	SHOW_STR "Display information about a E1 line\n"
-	"E1 Line Number\n")
-{
-	struct e1inp_line *line;
-
-	if (argc >= 1) {
-		int num = atoi(argv[0]);
-		llist_for_each_entry(line, &e1inp_line_list, list) {
-			if (line->num == num) {
-				e1line_dump_vty(vty, line);
-				return CMD_SUCCESS;
-			}
-		}
-		return CMD_WARNING;
-	}	
-	
-	llist_for_each_entry(line, &e1inp_line_list, list)
-		e1line_dump_vty(vty, line);
-
-	return CMD_SUCCESS;
-}
-
-static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
-{
-	if (ts->type == E1INP_TS_TYPE_NONE)
-		return;
-	vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s",
-		ts->num, ts->line->num, e1inp_tstype_name(ts->type),
-		VTY_NEWLINE);
-}
-
-DEFUN(show_e1ts,
-      show_e1ts_cmd,
-      "show e1_timeslot [line_nr] [ts_nr]",
-	SHOW_STR "Display information about a E1 timeslot\n"
-	"E1 Line Number\n" "E1 Timeslot Number\n")
-{
-	struct e1inp_line *line = NULL;
-	struct e1inp_ts *ts;
-	int ts_nr;
-
-	if (argc == 0) {
-		llist_for_each_entry(line, &e1inp_line_list, list) {
-			for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
-				ts = &line->ts[ts_nr];
-				e1ts_dump_vty(vty, ts);
-			}
-		}
-		return CMD_SUCCESS;
-	}
-	if (argc >= 1) {
-		int num = atoi(argv[0]);
-		struct e1inp_line *l;
-		llist_for_each_entry(l, &e1inp_line_list, list) {
-			if (l->num == num) {
-				line = l;
-				break;
-			}
-		}
-		if (!line) {
-			vty_out(vty, "E1 line %s is invalid%s",
-				argv[0], VTY_NEWLINE);
-			return CMD_WARNING;
-		}
-	}	
-	if (argc >= 2) {
-		ts_nr = atoi(argv[1]);
-		if (ts_nr >= NUM_E1_TS) {
-			vty_out(vty, "E1 timeslot %s is invalid%s",
-				argv[1], VTY_NEWLINE);
-			return CMD_WARNING;
-		}
-		ts = &line->ts[ts_nr];
-		e1ts_dump_vty(vty, ts);
-		return CMD_SUCCESS;
-	} else {
-		for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
-			ts = &line->ts[ts_nr];
-			e1ts_dump_vty(vty, ts);
-		}
-		return CMD_SUCCESS;
-	}
-	return CMD_SUCCESS;
-}
-
 static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag)
 {
 	vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE);
@@ -2678,10 +2565,6 @@
 	install_element_ve(&show_lchan_summary_cmd);
 	install_element_ve(&logging_fltr_imsi_cmd);
 
-	install_element_ve(&show_e1drv_cmd);
-	install_element_ve(&show_e1line_cmd);
-	install_element_ve(&show_e1ts_cmd);
-
 	install_element_ve(&show_paging_cmd);
 
 	logging_vty_add_cmds(cat);
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..b5bc2fe
--- /dev/null
+++ b/openbsc/src/libbsc/bts_nokia_site.c
@@ -0,0 +1,1727 @@
+/* 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 ? */
+
+#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 (bts->nokia.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
+*/
+
+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;
+	case S_INP_TEI_UNKNOWN:
+		/* We are receiving LAPD frames with one TEI that we do not
+		 * seem to know, likely that we (the BSC) stopped working
+		 * and lost our local states. However, the BTS is already
+		 * configured, we try to take over the RSL links. */
+		start_sabm_in_line(isd->line, 1, SAPI_RSL);
+		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 const struct value_string nokia_msgt_name[] = {
+	{ 0x80, "NOKIA_BTS_CONF_DATA" },
+	{ 0x81, "NOKIA_BTS_ACK" },
+	{ 0x82, "NOKIA_BTS_OMU_STARTED" },
+	{ 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" },
+	{ 0x84, "NOKIA_BTS_MF_REQ" },
+	{ 0x85, "NOKIA_BTS_AF_REQ" },
+	{ 0x86, "NOKIA_BTS_RESET_REQ" },
+	{ 0x87, "NOKIA_reserved" },
+	{ 0x88, "NOKIA_BTS_CONF_REQ" },
+	{ 0x89, "NOKIA_BTS_TEST_REQ" },
+	{ 0x8A, "NOKIA_BTS_TEST_REPORT" },
+	{ 0x8B, "NOKIA_reserved" },
+	{ 0x8C, "NOKIA_reserved" },
+	{ 0x8D, "NOKIA_reserved" },
+	{ 0x8E, "NOKIA_BTS_CONF_COMPL" },
+	{ 0x8F, "NOKIA_reserved" },
+	{ 0x90, "NOKIA_BTS_STM_TEST_REQ" },
+	{ 0x91, "NOKIA_BTS_STM_TEST_REPORT" },
+	{ 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" },
+	{ 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" },
+	{ 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" },
+	{ 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" },
+	{ 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" },
+	{ 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" },
+	{ 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" },
+	{ 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" },
+	{ 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" },
+	{ 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" },
+	{ 0x9C, "NOKIA_BTS_HW_REQ" },
+	{ 0x9D, "NOKIA_BTS_HW_REPORT" },
+	{ 0x9E, "NOKIA_BTS_RTE_TEST_REQ" },
+	{ 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" },
+	{ 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" },
+	{ 0xA1, "NOKIA_BTS_CLOCK_REQ" },
+	{ 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" },
+	{ 0xA3, "NOKIA_AC_INTERRUPTED" },
+	{ 0xA4, "NOKIA_BTS_NEW_TRE_INFO" },
+	{ 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" },
+	{ 0xA6, "NOKIA_BTS_TRE_POLL_LIST" },
+	{ 0xA7, "NOKIA_AC_CIRCUIT_REQ" },
+	{ 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" },
+	{ 0xA9, "NOKIA_BTS_GSM_TIME_REQ" },
+	{ 0xAA, "NOKIA_BTS_GSM_TIME" },
+	{ 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" },
+	{ 0xAC, "NOKIA_BTS_STATE_CHANGED" },
+	{ 0xAD, "NOKIA_BTS_SW_SAVE_REQ" },
+	{ 0xAE, "NOKIA_BTS_ALARM" },
+	{ 0xAF, "NOKIA_BTS_CHA_ADM_STATE" },
+	{ 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" },
+	{ 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" },
+	{ 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" },
+	{ 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" },
+	{ 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" },
+	{ 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" },
+	{ 0xB6, "NOKIA_BTS_LCS_COMMAND" },
+	{ 0xB7, "NOKIA_BTS_LCS_ANSWER" },
+	{ 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" },
+	{ 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" },
+	{ 0, NULL }
+};
+
+static const char *get_msg_type_name_string(uint8_t msg_type)
+{
+	return get_value_string(nokia_msgt_name, msg_type);
+}
+
+static const struct value_string nokia_element_name[] = {
+	{ 0x01, "Ny1" },
+	{ 0x02, "T3105_F" },
+	{ 0x03, "Interference band limits" },
+	{ 0x04, "Interference report timer in secs" },
+	{ 0x05, "Channel configuration per TS" },
+	{ 0x06, "BSIC" },
+	{ 0x07, "RACH report timer in secs" },
+	{ 0x08, "Hardware database status" },
+	{ 0x09, "BTS RX level" },
+	{ 0x0A, "ARFN" },
+	{ 0x0B, "STM antenna attenuation" },
+	{ 0x0C, "Cell allocation bitmap" },
+	{ 0x0D, "Radio definition per TS" },
+	{ 0x0E, "Frame number" },
+	{ 0x0F, "Antenna diversity" },
+	{ 0x10, "T3105_D" },
+	{ 0x11, "File format" },
+	{ 0x12, "Last File" },
+	{ 0x13, "BTS type" },
+	{ 0x14, "Erasure mode" },
+	{ 0x15, "Hopping mode" },
+	{ 0x16, "Floating TRX" },
+	{ 0x17, "Power supplies" },
+	{ 0x18, "Reset type" },
+	{ 0x19, "Averaging period" },
+	{ 0x1A, "RBER2" },
+	{ 0x1B, "LAC" },
+	{ 0x1C, "CI" },
+	{ 0x1D, "Failure parameters" },
+	{ 0x1E, "(RF max power reduction)" },
+	{ 0x1F, "Measured RX_SENS" },
+	{ 0x20, "Extended cell radius" },
+	{ 0x21, "reserved" },
+	{ 0x22, "Success-Failure" },
+	{ 0x23, "Ack-Nack" },
+	{ 0x24, "OMU test results" },
+	{ 0x25, "File identity" },
+	{ 0x26, "Generation and version code" },
+	{ 0x27, "SW description" },
+	{ 0x28, "BCCH LEV" },
+	{ 0x29, "Test type" },
+	{ 0x2A, "Subscriber number" },
+	{ 0x2B, "reserved" },
+	{ 0x2C, "HSN" },
+	{ 0x2D, "reserved" },
+	{ 0x2E, "MS RXLEV" },
+	{ 0x2F, "MS TXLEV" },
+	{ 0x30, "RXQUAL" },
+	{ 0x31, "RX SENS" },
+	{ 0x32, "Alarm block" },
+	{ 0x33, "Neighbouring BCCH levels" },
+	{ 0x34, "STM report type" },
+	{ 0x35, "MA" },
+	{ 0x36, "MAIO" },
+	{ 0x37, "H_FLAG" },
+	{ 0x38, "TCH_ARFN" },
+	{ 0x39, "Clock output" },
+	{ 0x3A, "Transmitted power" },
+	{ 0x3B, "Clock sync" },
+	{ 0x3C, "TMS protocol discriminator" },
+	{ 0x3D, "TMS protocol data" },
+	{ 0x3E, "FER" },
+	{ 0x3F, "SWR result" },
+	{ 0x40, "Object identity" },
+	{ 0x41, "STM RX Antenna Test" },
+	{ 0x42, "reserved" },
+	{ 0x43, "reserved" },
+	{ 0x44, "Object current state" },
+	{ 0x45, "reserved" },
+	{ 0x46, "FU channel configuration" },
+	{ 0x47, "reserved" },
+	{ 0x48, "ARFN of a CU" },
+	{ 0x49, "FU radio definition" },
+	{ 0x4A, "reserved" },
+	{ 0x4B, "Severity" },
+	{ 0x4C, "Diversity selection" },
+	{ 0x4D, "RX antenna test" },
+	{ 0x4E, "RX antenna supervision period" },
+	{ 0x4F, "RX antenna state" },
+	{ 0x50, "Sector configuration" },
+	{ 0x51, "Additional info" },
+	{ 0x52, "SWR parameters" },
+	{ 0x53, "HW inquiry mode" },
+	{ 0x54, "reserved" },
+	{ 0x55, "Availability status" },
+	{ 0x56, "reserved" },
+	{ 0x57, "EAC inputs" },
+	{ 0x58, "EAC outputs" },
+	{ 0x59, "reserved" },
+	{ 0x5A, "Position" },
+	{ 0x5B, "HW unit identity" },
+	{ 0x5C, "RF test signal attenuation" },
+	{ 0x5D, "Operational state" },
+	{ 0x5E, "Logical object identity" },
+	{ 0x5F, "reserved" },
+	{ 0x60, "BS_TXPWR_OM" },
+	{ 0x61, "Loop_Duration" },
+	{ 0x62, "LNA_Path_Selection" },
+	{ 0x63, "Serial number" },
+	{ 0x64, "HW version" },
+	{ 0x65, "Obj. identity and obj. state" },
+	{ 0x66, "reserved" },
+	{ 0x67, "EAC input definition" },
+	{ 0x68, "EAC id and text" },
+	{ 0x69, "HW unit status" },
+	{ 0x6A, "SW release version" },
+	{ 0x6B, "FW version" },
+	{ 0x6C, "Bit_Error_Ratio" },
+	{ 0x6D, "RXLEV_with_Attenuation" },
+	{ 0x6E, "RXLEV_without_Attenuation" },
+	{ 0x6F, "reserved" },
+	{ 0x70, "CU_Results" },
+	{ 0x71, "reserved" },
+	{ 0x72, "LNA_Path_Results" },
+	{ 0x73, "RTE Results" },
+	{ 0x74, "Real Time" },
+	{ 0x75, "RX diversity selection" },
+	{ 0x76, "EAC input config" },
+	{ 0x77, "Feature support" },
+	{ 0x78, "File version" },
+	{ 0x79, "Outputs" },
+	{ 0x7A, "FU parameters" },
+	{ 0x7B, "Diagnostic info" },
+	{ 0x7C, "FU BSIC" },
+	{ 0x7D, "TRX Configuration" },
+	{ 0x7E, "Download status" },
+	{ 0x7F, "RX difference limit" },
+	{ 0x80, "TRX HW capability" },
+	{ 0x81, "Common HW config" },
+	{ 0x82, "Autoconfiguration pool size" },
+	{ 0x83, "TRE diagnostic info" },
+	{ 0x84, "TRE object identity" },
+	{ 0x85, "New TRE Info" },
+	{ 0x86, "Acknowledgement period" },
+	{ 0x87, "Synchronization mode" },
+	{ 0x88, "reserved" },
+	{ 0x89, "Block Control Data" },
+	{ 0x8A, "SW load mode" },
+	{ 0x8B, "Recommended recovery action" },
+	{ 0x8C, "BSC BCF id" },
+	{ 0x8D, "Q1 baud rate" },
+	{ 0x8E, "Allocation status" },
+	{ 0x8F, "Functional entity number" },
+	{ 0x90, "Transmission delay" },
+	{ 0x91, "Loop Duration ms" },
+	{ 0x92, "Logical channel" },
+	{ 0x93, "Q1 address" },
+	{ 0x94, "Alarm detail" },
+	{ 0x95, "Cabinet type" },
+	{ 0x96, "HW unit existence" },
+	{ 0x97, "RF power parameters" },
+	{ 0x98, "Message scenario" },
+	{ 0x99, "HW unit max amount" },
+	{ 0x9A, "Master TRX" },
+	{ 0x9B, "Transparent data" },
+	{ 0x9C, "BSC topology info" },
+	{ 0x9D, "Air i/f modulation" },
+	{ 0x9E, "LCS Q1 command data" },
+	{ 0x9F, "Frame number offset" },
+	{ 0xA0, "Abis TSL" },
+	{ 0xA1, "Dynamic pool info" },
+	{ 0xA2, "LCS LLP data" },
+	{ 0xA3, "LCS Q1 answer data" },
+	{ 0xA4, "DFCA FU Radio Definition" },
+	{ 0xA5, "Antenna hopping" },
+	{ 0xA6, "Field record sequence number" },
+	{ 0xA7, "Timeslot offslot" },
+	{ 0xA8, "EPCR capability" },
+	{ 0xA9, "Connectsite optional element" },
+	{ 0xAA, "TSC" },
+	{ 0xAB, "Special TX Power Setting" },
+	{ 0xAC, "Optional sync settings" },
+	{ 0xFA, "Abis If parameters" },
+	{ 0, NULL }
+};
+
+static const char *get_element_name_string(uint16_t element)
+{
+	return get_value_string(nokia_element_name, element);
+}
+
+static const struct value_string nokia_bts_types[] = {
+	{ 0x0a, 	"MetroSite GSM 900" },
+	{ 0x0b,		"MetroSite GSM 1800" },
+	{ 0x0c,		"MetroSite GSM 1900 (PCS)" },
+	{ 0x0d,		"MetroSite GSM 900 & 1800" },
+	{ 0x0e,		"InSite GSM 900" },
+	{ 0x0f,		"InSite GSM 1800" },
+	{ 0x10,		"InSite GSM 1900" },
+	{ 0x11,		"UltraSite GSM 900" },
+	{ 0x12,		"UltraSite GSM 1800" },
+	{ 0x13,		"UltraSite GSM/US-TDMA 1900" },
+	{ 0x14,		"UltraSite GSM 900 & 1800" },
+	{ 0x16,		"UltraSite GSM/US-TDMA 850" },
+	{ 0x18,		"MetroSite GSM/US-TDMA 850" },
+	{ 0x19,		"UltraSite GSM 800/1900" },
+	{ 0, 		NULL }
+};
+
+static const char *get_bts_type_string(uint8_t type)
+{
+	return get_value_string(nokia_bts_types, type);
+}
+
+static const struct value_string nokia_severity[] = {
+	{ 0,	"indeterminate" },
+	{ 1,	"critical" },
+	{ 2,	"major" },
+	{ 3,	"minor" },
+	{ 4,	"warning" },
+	{ 0,	NULL }
+};
+
+static const char *get_severity_string(uint8_t severity)
+{
+	return get_value_string(nokia_severity, severity);
+}
+
+/* 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 (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;
+
+	bts->nokia.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);
+	uint8_t info[256];
+	uint8_t ack = 0xFF;
+	uint8_t severity = 0xFF;
+	int str_len;
+	int len_data;
+
+	if (bts->nokia.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->nokia.bts_type,
+				 sizeof(uint8_t)) == sizeof(uint8_t))
+			LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n",
+			     bts->nokia.bts_type,
+			     get_bts_type_string(bts->nokia.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->nokia.bts_type);
+		bts->nokia.configured = 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 (bts->nokia.do_reset != 0) {
+			bts->nokia.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.
+			 */
+			bts->nokia.wait_reset = 1;
+
+			bts->nokia.reset_timer.cb = &reset_timer_cb;
+			bts->nokia.reset_timer.data = bts;
+			osmo_timer_schedule(&bts->nokia.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 (bts->nokia.configured != 0) {
+			/* start TRX  (RSL link) */
+
+			struct gsm_e1_subslot *e1_link = &mb->trx->rsl_e1_link;
+			struct e1inp_line *line;
+
+			bts->nokia.configured = 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/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c
index c0bdc1b..6f4fe20 100644
--- a/openbsc/src/libbsc/chan_alloc.c
+++ b/openbsc/src/libbsc/chan_alloc.c
@@ -299,6 +299,10 @@
 	sig.type = lchan->type;
 	lchan->type = GSM_LCHAN_NONE;
 
+	if (lchan->state != LCHAN_S_NONE) {
+		LOGP(DRLL, LOGL_NOTICE, "Freeing lchan with state %s - setting to NONE\n", gsm_lchans_name(lchan->state));
+		lchan->state = LCHAN_S_NONE;
+	}
 
 	if (lchan->conn) {
 		struct lchan_signal_data sig;
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;
diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c
index 6b15bd0..c70ffae 100644
--- a/openbsc/src/libcommon/gsm_data.c
+++ b/openbsc/src/libcommon/gsm_data.c
@@ -193,6 +193,7 @@
 	{ GSM_BTS_TYPE_NANOBTS,	"nanobts" },
 	{ GSM_BTS_TYPE_RBS2000,	"rbs2000" },
 	{ GSM_BTS_TYPE_HSL_FEMTO, "hsl_femto" },
+	{ GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" },
 	{ 0,			NULL }
 };
 
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index a6ed3e8..bd452ed 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -1602,7 +1602,7 @@
 		remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr);
 
 	if (bts->type != remote_bts->type) {
-		DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
+		LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n");
 		return -EINVAL;
 	}
 
@@ -1634,10 +1634,11 @@
 		break;
 	case GSM_BTS_TYPE_BS11:
 	case GSM_BTS_TYPE_RBS2000:
+	case GSM_BTS_TYPE_NOKIA_SITE:
 		trau_mux_map_lchan(lchan, remote_lchan);
 		break;
 	default:
-		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
 		return -EINVAL;
 	}
 
@@ -1680,7 +1681,7 @@
 	switch (bts->type) {
 	case GSM_BTS_TYPE_NANOBTS:
 		if (ipacc_rtp_direct) {
-			DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+			LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n");
 			return -EINVAL;
 		}
 		/* in case, we don't have a RTP socket yet, we note this
@@ -1704,12 +1705,13 @@
 		break;
 	case GSM_BTS_TYPE_BS11:
 	case GSM_BTS_TYPE_RBS2000:
+	case GSM_BTS_TYPE_NOKIA_SITE:
 		if (enable)
 			return trau_recv_lchan(lchan, callref);
 		return trau_mux_unmap(NULL, callref);
 		break;
 	default:
-		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
 		return -EINVAL;
 	}
 
@@ -2989,9 +2991,10 @@
 			return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg);
 		case GSM_BTS_TYPE_BS11:
 		case GSM_BTS_TYPE_RBS2000:
+		case GSM_BTS_TYPE_NOKIA_SITE:
 			return trau_send_frame(trans->conn->lchan, arg);
 		default:
-			DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+			LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
 		}
 		return -EINVAL;
 	}
diff --git a/openbsc/src/libmsc/mncc_sock.c b/openbsc/src/libmsc/mncc_sock.c
index 5ef9922..d8caf07 100644
--- a/openbsc/src/libmsc/mncc_sock.c
+++ b/openbsc/src/libmsc/mncc_sock.c
@@ -165,6 +165,13 @@
 
 		bfd->when &= ~BSC_FD_WRITE;
 
+		/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
+		if (!msgb_length(msg)) {
+			LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO "
+				"bytes!\n", mncc_prim->msg_type);
+			goto dontsend;
+		}
+
 		/* try to send it over the socket */
 		rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
 		if (rc == 0)
@@ -176,6 +183,8 @@
 			}
 			goto close;
 		}
+
+dontsend:
 		/* _after_ we send it, we can deueue */
 		msg2 = msgb_dequeue(&net->upqueue);
 		assert(msg == msg2);
diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c
index b2c630c..ab27879 100644
--- a/openbsc/src/libtrau/trau_mux.c
+++ b/openbsc/src/libtrau/trau_mux.c
@@ -214,6 +214,7 @@
 		}
 		frame->msg_type = GSM_TCHF_FRAME;
 		frame->callref = ue->callref;
+		msgb_put(msg, sizeof(struct gsm_data_frame) + 33);
 		trau_tx_to_mncc(ue->net, msg);
 
 		return 0;