Merge branch 'zecke/e1_speed'
diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h
index 5c9cbd0..76e5f6e 100644
--- a/openbsc/include/openbsc/abis_nm.h
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -175,4 +175,7 @@
 
 int abis_nm_vty_init(void);
 
+void abis_nm_clear_queue(struct gsm_bts *bts);
+
+
 #endif /* _NM_H */
diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h
index 1a3d9d6..aa4aa39 100644
--- a/openbsc/include/openbsc/e1_input.h
+++ b/openbsc/include/openbsc/e1_input.h
@@ -66,6 +66,8 @@
 		struct {
 			/* list of all signalling links on this TS */
 			struct llist_head sign_links;
+			/* delay for the queue */
+			int delay;
 			/* timer when to dequeue next frame */
 			struct timer_list tx_timer;
 		} sign;
@@ -93,6 +95,7 @@
 	struct llist_head list;
 	const char *name;
 	int (*want_write)(struct e1inp_ts *ts);
+	int default_delay;
 };	
 
 struct e1inp_line {
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 364eaf4..b73c4d1 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -91,6 +91,8 @@
 	BTS_GPRS_EGPRS = 2,
 };
 
+#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3]
+
 /* the data structure stored in msgb->cb for openbsc apps */
 struct openbsc_msgb_cb {
 	unsigned char *bssgph;
@@ -592,6 +594,10 @@
 	/* transceivers */
 	int num_trx;
 	struct llist_head trx_list;
+
+	/* Abis NM queue */
+	struct llist_head abis_queue;
+	int abis_nm_pend;
 };
 
 /* Some statistics of our network */
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 9f55d35..8bcc96b 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -410,11 +410,31 @@
 }
 
 /* Send a OML NM Message from BSC to BTS */
-int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
+static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg)
 {
 	msg->trx = bts->c0;
 
-	return _abis_nm_sendmsg(msg);
+	/* queue OML messages */
+	if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) {
+		bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg);
+		return _abis_nm_sendmsg(msg);
+	} else {
+		msgb_enqueue(&bts->abis_queue, msg);
+		return 0;
+	}
+
+}
+
+int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
+{
+	OBSC_NM_W_ACK_CB(msg) = 1;
+	return abis_nm_queue_msg(bts, msg);
+}
+
+static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg)
+{
+	OBSC_NM_W_ACK_CB(msg) = 0;
+	return abis_nm_queue_msg(bts, msg);
 }
 
 static int abis_nm_rcvmsg_sw(struct msgb *mb);
@@ -951,12 +971,30 @@
 	return 0;
 }
 
+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);
+
+		if (wait)
+			break;
+	}
+
+	bts->abis_nm_pend = wait;
+}
+
 /* Receive a OML NM Message from BTS */
 static int abis_nm_rcvmsg_fom(struct msgb *mb)
 {
 	struct abis_om_hdr *oh = msgb_l2(mb);
 	struct abis_om_fom_hdr *foh = msgb_l3(mb);
 	u_int8_t mt = foh->msg_type;
+	int ret = 0;
 
 	/* check for unsolicited message */
 	if (is_report(mt))
@@ -983,6 +1021,7 @@
 		nack_data.msg = mb;
 		nack_data.mt = mt;
 		dispatch_signal(SS_NM, S_NM_NACK, &nack_data);
+		abis_nm_queue_send_next(mb->trx->bts);
 		return 0;
 	}
 #if 0
@@ -1004,13 +1043,13 @@
 
 	switch (mt) {
 	case NM_MT_CHG_ADM_STATE_ACK:
-		return abis_nm_rx_chg_adm_state_ack(mb);
+		ret = abis_nm_rx_chg_adm_state_ack(mb);
 		break;
 	case NM_MT_SW_ACT_REQ:
-		return abis_nm_rx_sw_act_req(mb);
+		ret = abis_nm_rx_sw_act_req(mb);
 		break;
 	case NM_MT_BS11_LMT_SESSION:
-		return abis_nm_rx_lmt_event(mb);
+		ret = abis_nm_rx_lmt_event(mb);
 		break;
 	case NM_MT_CONN_MDROP_LINK_ACK:
 		DEBUGP(DNM, "CONN MDROP LINK ACK\n");
@@ -1023,7 +1062,8 @@
 		break;
 	}
 
-	return 0;
+	abis_nm_queue_send_next(mb->trx->bts);
+	return ret;
 }
 
 static int abis_nm_rx_ipacc(struct msgb *mb);
@@ -1036,6 +1076,7 @@
 	switch (bts_type) {
 	case GSM_BTS_TYPE_NANOBTS:
 		rc = abis_nm_rx_ipacc(mb);
+		abis_nm_queue_send_next(mb->trx->bts);
 		break;
 	default:
 		LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
@@ -1280,7 +1321,7 @@
 			sw->obj_instance[0], sw->obj_instance[1],
 			sw->obj_instance[2]);
 
-	return abis_nm_sendmsg(sw->bts, msg);
+	return abis_nm_sendmsg_direct(sw->bts, msg);
 }
 
 /* 6.2.4 / 8.3.4 Load Data End */
@@ -1473,6 +1514,7 @@
 					 sw->cb_data, NULL);
 			rc = sw_fill_window(sw);
 			sw->state = SW_STATE_WAIT_SEGACK;
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		case NM_MT_LOAD_INIT_NACK:
 			if (sw->forced) {
@@ -1493,6 +1535,7 @@
 						 sw->cb_data, NULL);
 				sw->state = SW_STATE_ERROR;
 			}
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		}
 		break;
@@ -1513,6 +1556,7 @@
 				sw->state = SW_STATE_WAIT_ENDACK;
 				rc = sw_load_end(sw);
 			}
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		case NM_MT_LOAD_ABORT:
 			if (sw->cbfn)
@@ -1534,6 +1578,7 @@
 					 NM_MT_LOAD_END_ACK, mb,
 					 sw->cb_data, NULL);
 			rc = 0;
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		case NM_MT_LOAD_END_NACK:
 			if (sw->forced) {
@@ -1553,6 +1598,7 @@
 						 NM_MT_LOAD_END_NACK, mb,
 						 sw->cb_data, NULL);
 			}
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		}
 	case SW_STATE_WAIT_ACTACK:
@@ -1566,6 +1612,7 @@
 				sw->cbfn(GSM_HOOK_NM_SWLOAD,
 					 NM_MT_ACTIVATE_SW_ACK, mb,
 					 sw->cb_data, NULL);
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		case NM_MT_ACTIVATE_SW_NACK:
 			DEBUGP(DNM, "Activate Software NACK\n");
@@ -1575,6 +1622,7 @@
 				sw->cbfn(GSM_HOOK_NM_SWLOAD,
 					 NM_MT_ACTIVATE_SW_NACK, mb,
 					 sw->cb_data, NULL);
+			abis_nm_queue_send_next(mb->trx->bts);
 			break;
 		}
 	case SW_STATE_NONE:
@@ -2032,7 +2080,7 @@
 	if (nack)
 		msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
 
-	return abis_nm_sendmsg(bts, msg);
+	return abis_nm_sendmsg_direct(bts, msg);
 }
 
 int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg)
@@ -3045,3 +3093,15 @@
 
 	return 0;
 }
+
+void abis_nm_clear_queue(struct gsm_bts *bts)
+{
+	struct msgb *msg;
+
+	while (!llist_empty(&bts->abis_queue)) {
+		msg = msgb_dequeue(&bts->abis_queue);
+		msgb_free(msg);
+	}
+
+	bts->abis_nm_pend = 0;
+}
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index 4f7e29c..875da16 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -33,6 +33,7 @@
 #include <openbsc/signal.h>
 #include <openbsc/chan_alloc.h>
 #include <osmocore/talloc.h>
+#include <openbsc/ipaccess.h>
 
 /* global pointer to the gsm network data structure */
 extern struct gsm_network *bsc_gsmnet;
@@ -471,8 +472,6 @@
 						  sizeof(nanobts_attr_nse));
 			abis_nm_opstart(bts, obj_class, bts->bts_nr,
 					0xff, 0xff);
-			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
-					      0xff, 0xff, NM_STATE_UNLOCKED);
 		}
 		break;
 	case NM_OC_GPRS_CELL:
@@ -487,6 +486,8 @@
 					0, 0xff);
 			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
 					      0, 0xff, NM_STATE_UNLOCKED);
+			abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr,
+					      0xff, 0xff, NM_STATE_UNLOCKED);
 		}
 		break;
 	case NM_OC_GPRS_NSVC:
@@ -565,10 +566,20 @@
 /* Callback function for NACK on the OML NM */
 static int oml_msg_nack(struct nm_nack_signal_data *nack)
 {
+	int i;
+
 	if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) {
+
 		LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
 				"Was the bts type and frequency properly specified?\n");
 		exit(-1);
+	} else {
+		LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n");
+		for (i = 0; i < bsc_gsmnet->num_bts; ++i) {
+			struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, i);
+			if (is_ipaccess_bts(bts))
+				ipaccess_drop_oml(bts);
+		}
 	}
 
 	return 0;
@@ -1070,6 +1081,8 @@
 		trx->nm_state.availability = 0;
 		trx->bb_transc.nm_state.operational = 0;
 		trx->bb_transc.nm_state.availability = 0;
+
+		abis_nm_clear_queue(trx->bts);
 		break;
 	default:
 		break;
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index 9b205b7..f7d69c5 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -303,6 +303,10 @@
 
 	switch (type) {
 	case E1INP_TS_TYPE_SIGN:
+		if (line->driver)
+			ts->sign.delay = line->driver->default_delay;
+		else
+			ts->sign.delay = 100000;
 		INIT_LLIST_HEAD(&ts->sign.sign_links);
 		break;
 	case E1INP_TS_TYPE_TRAU:
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index f388306..1d07cbf 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -241,6 +241,8 @@
 	bts->rach_b_thresh = -1;
 	bts->rach_ldavg_slots = -1;
 	bts->paging.free_chans_need = -1;
+	INIT_LLIST_HEAD(&bts->abis_queue);
+
 	llist_add_tail(&bts->list, &net->bts_list);
 
 	return bts;
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 18268f6..8a2f827 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -282,6 +282,7 @@
 			trx->rsl_link = e1inp_sign_link_create(e1i_ts,
 							E1INP_SIGN_RSL, trx,
 							trx->rsl_tei, 0);
+			trx->rsl_link->ts->sign.delay = 0;
 
 			/* get rid of our old temporary bfd */
 			memcpy(newbfd, bfd, sizeof(*newbfd));
@@ -589,7 +590,7 @@
 	e1i_ts->sign.tx_timer.data = e1i_ts;
 
 	/* Reducing this might break the nanoBTS 900 init. */
-	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000);
+	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
 
 	return ret;
 }
@@ -622,6 +623,7 @@
 struct e1inp_driver ipaccess_driver = {
 	.name = "ip.access",
 	.want_write = ts_want_write,
+	.default_delay = 0,
 };
 
 /* callback of the OML listening filedescriptor */
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
index fa8aca0..b36bdf8 100644
--- a/openbsc/src/input/misdn.c
+++ b/openbsc/src/input/misdn.c
@@ -247,7 +247,7 @@
 	/* set tx delay timer for next event */
 	e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
 	e1i_ts->sign.tx_timer.data = e1i_ts;
-	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
+	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
 
 	return ret;
 }
@@ -387,6 +387,7 @@
 struct e1inp_driver misdn_driver = {
 	.name = "mISDNuser",
 	.want_write = ts_want_write,
+	.default_delay = 50000,
 };
 
 static int mi_e1_setup(struct e1inp_line *line, int release_l2)
diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
index 081239d..98d8bcc 100644
--- a/openbsc/src/ipaccess/ipaccess-config.c
+++ b/openbsc/src/ipaccess/ipaccess-config.c
@@ -865,6 +865,8 @@
 		exit(1);
 	}
 	
+	bts->oml_link->ts->sign.delay = 10;
+	bts->c0->rsl_link->ts->sign.delay = 10;
 	while (1) {
 		rc = bsc_select_main(0);
 		if (rc < 0)