Merge commit 'origin/master'
diff --git a/openbsc/configure.in b/openbsc/configure.in
index b886e7a..94fb15f 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -45,4 +45,5 @@
     tests/sms/Makefile
     tests/gsm0408/Makefile
     tests/db/Makefile
+    tests/channel/Makefile
     Makefile)
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e85adf8..1fb80a2 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -25,6 +25,7 @@
 enum gsm_paging_event {
 	GSM_PAGING_SUCCEEDED,
 	GSM_PAGING_EXPIRED,
+	GSM_PAGING_OOM,
 };
 
 struct msgb;
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 1ca79e2..780d8ed 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -31,6 +31,10 @@
 	u_int8_t classmark2[3];
 	u_int8_t classmark3_len;
 	u_int8_t classmark3[14];
+
+	/* pending requests */
+	int in_callback;
+	struct llist_head requests;
 };
 
 enum gsm_subscriber_field {
@@ -51,6 +55,9 @@
 struct gsm_subscriber *subscr_get_by_extension(const char *ext);
 int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
 void subscr_put_channel(struct gsm_lchan *lchan);
+void subscr_get_channel(struct gsm_subscriber *subscr,
+                        struct gsm_network *network, int type,
+		        gsm_cbfn *cbfn, void *param);
 
 /* internal */
 struct gsm_subscriber *subscr_alloc(void);
diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h
index de512d1..2f17e24 100644
--- a/openbsc/include/openbsc/paging.h
+++ b/openbsc/include/openbsc/paging.h
@@ -33,7 +33,7 @@
 void paging_init(struct gsm_bts *bts);
 
 /* schedule paging request */
-void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+void paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
 		    int type, gsm_cbfn *cbfn, void *data);
 
 /* stop paging requests */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 4a583f6..c065f90 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -54,6 +54,7 @@
 enum signal_nm {
 	S_NM_SW_ACTIV_REP,	/* GSM 12.21 software activated report */
 	S_NM_FAIL_REP,		/* GSM 12.21 failure event report */
+	S_NM_NACK,		/* GSM 12.21 various NM_MT_*_NACK happened */
 };
 
 /* SS_LCHAN signals */
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 74dba23..83e6bbb 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -893,6 +893,9 @@
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			DEBUGPC(DNM, "\n");
+
+		dispatch_signal(SS_NM, S_NM_NACK, (void*) ((long)mt));
+		return 0;
 	}
 #if 0
 	/* check if last message is to be acked */
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 7aa8b9a..3888407 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -454,6 +454,18 @@
 	return 0;
 }
 
+/* Callback function for NACK on the OML NM */
+static int oml_msg_nack(int mt)
+{
+	if (mt == NM_MT_SET_BTS_ATTR_NACK) {
+		fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
+				"Was the bts type and frequency properly specified?\n");
+		exit(-1);
+	}
+
+	return 0;
+}
+
 /* Callback function to be called every time we receive a signal from NM */
 static int nm_sig_cb(unsigned int subsys, unsigned int signal,
 		     void *handler_data, void *signal_data)
@@ -461,6 +473,8 @@
 	switch (signal) {
 	case S_NM_SW_ACTIV_REP:
 		return sw_activ_rep(signal_data);
+	case S_NM_NACK:
+		return oml_msg_nack((int)signal_data);
 	default:
 		break;
 	}
@@ -960,6 +974,26 @@
 {
 	struct gsm_bts *bts;
 
+	switch(BTS_TYPE) {
+	case GSM_BTS_TYPE_NANOBTS_1800:
+		if (ARFCN < 512 || ARFCN > 885) {
+			fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
+			return -EINVAL;
+		}
+		break;
+	case GSM_BTS_TYPE_BS11:
+	case GSM_BTS_TYPE_NANOBTS_900:
+		/* Assume we have a P-GSM900 here */
+		if (ARFCN < 1 || ARFCN > 124) {
+			fprintf(stderr, "GSM900 channel must be between 1-124.\n");
+			return -EINVAL;
+		}
+		break;
+	case GSM_BTS_TYPE_UNKNOWN:
+		fprintf(stderr, "Unknown BTS. Please use the --bts-type switch\n");
+		return -EINVAL;
+	}
+
 	/* initialize our data structures */
 	gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC);
 	if (!gsmnet)
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index c3c7c75..034bd97 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -140,6 +140,7 @@
 	int ret;
 	time_t cur_time;
 	struct tm *tm;
+	int mi_head = (direction==PCAP_INPUT) ? MISDN_HEADER_LEN : 0;
 
 	struct fake_linux_lapd_header header = {
 		.pkttype	= 4,
@@ -163,13 +164,15 @@
 		.ts_usec    = 0,
 		.incl_len   = msg->len + sizeof(struct fake_linux_lapd_header)
 				+ sizeof(struct lapd_header)
-				- MISDN_HEADER_LEN,
+				- mi_head,
 		.orig_len   = msg->len + sizeof(struct fake_linux_lapd_header)
 				+ sizeof(struct lapd_header)
-				- MISDN_HEADER_LEN,
+				- mi_head,
 	};
 
 
+        printf("Packet of: %d\n", direction);
+
 	cur_time = time(NULL);
 	tm = localtime(&cur_time);
 	payload_header.ts_sec = mktime(tm);
@@ -177,8 +180,8 @@
 	ret = write(pcap_fd, &payload_header, sizeof(payload_header));
 	ret = write(pcap_fd, &header, sizeof(header));
 	ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
-	ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
-			msg->len - MISDN_HEADER_LEN);
+	ret = write(pcap_fd, msg->data + mi_head,
+			msg->len - mi_head);
 }
 
 static const char *sign_types[] = {
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 052991c..90b88df 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1022,11 +1022,6 @@
 	DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
 		mi_type, mi_string);
 
-	/*
-	 * Rogue messages could trick us but so is life
-	 */
-	put_lchan(lchan);
-
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
 		if (!lchan->subscr)
@@ -1116,7 +1111,6 @@
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
 		/* we always want the IMEI, too */
-		use_lchan(lchan);
 		rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
 		lchan->loc_operation->waiting_for_imei = 1;
 
@@ -1125,7 +1119,6 @@
 		break;
 	case GSM_MI_TYPE_TMSI:
 		/* we always want the IMEI, too */
-		use_lchan(lchan);
 		rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
 		lchan->loc_operation->waiting_for_imei = 1;
 
@@ -1133,7 +1126,6 @@
 		subscr = subscr_get_by_tmsi(mi_string);
 		if (!subscr) {
 			/* send IDENTITY REQUEST message to get IMSI */
-			use_lchan(lchan);
 			rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
 			lchan->loc_operation->waiting_for_imsi = 1;
 		}
@@ -1460,7 +1452,6 @@
 	u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
 	char mi_string[MI_SIZE];
 	struct gsm_subscriber *subscr;
-	struct gsm_bts *bts;
 	struct paging_signal_data sig_data;
 	int rc = 0;
 
@@ -1508,18 +1499,6 @@
 	/* Stop paging on the bts we received the paging response */
 	paging_request_stop(msg->trx->bts, subscr, msg->lchan);
 
-	/* Stop paging on all other bts' */
-	bts = NULL;
-	do {
-		bts = gsm_bts_by_lac(msg->trx->bts->network, subscr->lac, bts);
-		if (!bts)
-			break;
-		if (bts == msg->trx->bts)
-			continue;
-		/* Stop paging */
-		paging_request_stop(bts, subscr, NULL);
-	} while (1);
-
 	/* FIXME: somehow signal the completion of the PAGING to
 	 * the entity that requested the paging */
 
@@ -1754,7 +1733,6 @@
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
 	struct gsm_subscriber *called_subscr;
-	struct gsm_bts *bts;
 	char called_number[(43-2)*2 + 1] = "\0";
 	struct tlv_parsed tp;
 	int ret;
@@ -1798,16 +1776,8 @@
 	call->called_subscr = called_subscr;
 
 	/* Start paging subscriber on all BTS in LAC of subscriber */
-	bts = NULL;
-	do {
-		bts = gsm_bts_by_lac(msg->trx->bts->network,
-				     msg->lchan->subscr->lac, bts);
-		if (!bts)
-			break;
-		/* Trigger paging */
-		paging_request(bts, called_subscr, RSL_CHANNEED_TCH_F,
-				setup_trig_pag_evt, call);
-	} while (1);
+	subscr_get_channel(called_subscr, msg->trx->bts->network, RSL_CHANNEED_TCH_F,
+		       setup_trig_pag_evt, call);
 
 	/* send a CALL PROCEEDING message to the MO */
 	ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index 3f608ec..3062a6b 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -25,14 +25,77 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 
 #include <openbsc/gsm_subscriber.h>
+#include <openbsc/paging.h>
 #include <openbsc/debug.h>
+#include <openbsc/paging.h>
 #include <openbsc/db.h>
 
-
 LLIST_HEAD(active_subscribers);
 
+/*
+ * Struct for pending channel requests. This is managed in the
+ * llist_head requests of each subscriber. The reference counting
+ * should work in such a way that a subscriber with a pending request
+ * remains in memory.
+ */
+struct subscr_request {
+	struct llist_head entry;
+
+	/* back reference */
+	struct gsm_subscriber *subscr;
+
+	/* the requested channel type */
+	int channel_type;
+
+	/* the bts we have decided to use */
+	struct gsm_network *network;
+
+	/* the callback data */
+	gsm_cbfn *cbfn;
+	void *param;
+};
+
+/*
+ * We got the channel assigned and can now hand this channel
+ * over to one of our callbacks.
+ */
+static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
+			     struct msgb *msg, void *data, void *param)
+{
+	struct subscr_request *request;
+	struct gsm_subscriber *subscr = (struct gsm_subscriber *)param;
+
+	assert(!llist_empty(&subscr->requests));
+
+	/*
+	 * FIXME: What to do with paging requests coming during
+	 * this callback? We must be sure to not start paging when
+	 * we have an active connection to a subscriber and to make
+	 * the subscr_put_channel work as required...
+	 */
+	request = (struct subscr_request *)subscr->requests.next;
+	llist_del(&request->entry);
+	subscr->in_callback = 1;
+	request->cbfn(hooknum, event, msg, data, request->param);
+	subscr->in_callback = 0;
+
+	free(request);
+	return 0;
+}
+
+static void subscr_send_paging_request(struct gsm_subscriber *subscr)
+{
+	struct subscr_request *request;
+	assert(!llist_empty(&subscr->requests));
+
+	request = (struct subscr_request *)subscr->requests.next;
+	paging_request(request->network, subscr, request->channel_type,
+		       subscr_paging_cb, subscr);
+}
+
 struct gsm_subscriber *subscr_alloc(void)
 {
 	struct gsm_subscriber *s;
@@ -45,6 +108,8 @@
 	llist_add_tail(&s->entry, &active_subscribers);
 	s->use_count = 1;
 
+	INIT_LLIST_HEAD(&s->requests);
+
 	return s;
 }
 
@@ -131,6 +196,42 @@
 	return NULL;
 }
 
+void subscr_get_channel(struct gsm_subscriber *subscr,
+			struct gsm_network *network, int type,
+			gsm_cbfn *cbfn, void *param)
+{
+	struct subscr_request *request;
+
+	request = (struct subscr_request *)malloc(sizeof(*request));
+	if (!request) {
+		if (cbfn)
+			cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM,
+				NULL, NULL, param);
+		return;
+	}
+
+	memset(request, 0, sizeof(*request));
+	request->network = network;
+	request->subscr = subscr;
+	request->channel_type = type;
+	request->cbfn = cbfn;
+	request->param = param;
+
+	/*
+	 * FIXME: We might be able to assign more than one
+	 * channel, e.g. voice and SMS submit at the same
+	 * time.
+	 */
+	if (!subscr->in_callback && llist_empty(&subscr->requests)) {
+		/* add to the list, send a request */
+		llist_add_tail(&request->entry, &subscr->requests);
+		subscr_send_paging_request(subscr);
+	} else {
+		/* this will be picked up later, from subscr_put_channel */
+		llist_add_tail(&request->entry, &subscr->requests);
+	}
+}
+
 void subscr_put_channel(struct gsm_lchan *lchan)
 {
 	/*
@@ -139,5 +240,21 @@
 	 * of the lchan after having asked the next requestee to handle
 	 * the channel.
 	 */
+	/*
+	 * FIXME: is the lchan is of a different type we could still
+	 * issue an immediate assignment for another channel and then
+	 * close this one.
+	 */
+	/*
+	 * Currently we will drop the last ref of the lchan which
+	 * will result in a channel release on RSL and we will start
+	 * the paging. This should work most of the time as the MS
+	 * will listen to the paging requests before we timeout
+	 */
+
 	put_lchan(lchan);
+
+	if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
+		subscr_send_paging_request(lchan->subscr);
 }
+
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index f3bdf69..8f15e16 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -210,8 +210,8 @@
 	paging_remove_request(&req->bts->paging, req);
 }
 
-void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
-		    int type, gsm_cbfn *cbfn, void *data)
+static void _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+			    int type, gsm_cbfn *cbfn, void *data)
 {
 	struct gsm_bts_paging_state *bts_entry = &bts->paging;
 	struct gsm_paging_request *req;
@@ -237,9 +237,25 @@
 		bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
 }
 
+void paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
+		    int type, gsm_cbfn *cbfn, void *data)
+{
+	struct gsm_bts *bts = NULL;
+
+	do {
+		bts = gsm_bts_by_lac(network, subscr->lac, bts);
+		if (!bts)
+			break;
+
+		/* Trigger paging */
+		_paging_request(bts, subscr, RSL_CHANNEED_TCH_F, cbfn, data);
+	} while (1);
+}
+
+
 /* we consciously ignore the type of the request here */
-void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
-			 struct gsm_lchan *lchan)
+static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+				 struct gsm_lchan *lchan)
 {
 	struct gsm_bts_paging_state *bts_entry = &bts->paging;
 	struct gsm_paging_request *req, *req2;
@@ -256,6 +272,31 @@
 	}
 }
 
+/* Stop paging on all other bts' */
+void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr,
+			 struct gsm_lchan *lchan)
+{
+	struct gsm_bts *bts = NULL;
+
+	_paging_request_stop(_bts, subscr, lchan);
+
+	do {
+		/*
+		 * FIXME: Don't use the lac of the subscriber...
+		 * as it might have magically changed the lac.. use the
+		 * location area of the _bts as reconfiguration of the
+		 * network is probably happening less often.
+		 */
+		bts = gsm_bts_by_lac(_bts->network, subscr->lac, bts);
+		if (!bts)
+			break;
+
+		/* Stop paging */
+                if (bts != _bts)
+			_paging_request_stop(bts, subscr, NULL);
+	} while (1);
+}
+
 void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots)
 {
 	bts->paging.available_slots = free_slots;
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 2b72c9c..2d4e81c 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = debug timer sms gsm0408 db
+SUBDIRS = debug timer sms gsm0408 db channel
diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am
new file mode 100644
index 0000000..60defe0
--- /dev/null
+++ b/openbsc/tests/channel/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3
+
+noinst_PROGRAMS = channel_test
+
+channel_test_SOURCES = channel_test.c \
+	$(top_srcdir)/src/db.c \
+	$(top_srcdir)/src/gsm_subscriber.c \
+	$(top_srcdir)/src/debug.c \
+	$(top_srcdir)/src/timer.c \
+	$(top_srcdir)/src/select.c \
+	$(top_srcdir)/src/gsm_data.c
+channel_test_LDADD = -ldl -ldbi
+
diff --git a/openbsc/tests/channel/channel_test.c b/openbsc/tests/channel/channel_test.c
new file mode 100644
index 0000000..1787e35
--- /dev/null
+++ b/openbsc/tests/channel/channel_test.c
@@ -0,0 +1,72 @@
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+
+#include <assert.h>
+
+#include <openbsc/select.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/abis_rsl.h>
+
+/* our handler */
+static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param)
+{
+	assert(hook == 101);
+	assert(event == 200);
+	assert(msg == (void*)0x1323L);
+	assert(data == (void*)0x4242L);
+	assert(param == (void*)0x2342L);
+	printf("Reached, didn't crash, test passed\n");
+	return 0;
+}
+
+/* mock object for testing, directly invoke the cb... maybe later through the timer */
+void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data)
+{
+	cbfn(101, 200, (void*)0x1323L, (void*)0x4242L, data);
+}
+
+
+int main(int argc, char** argv)
+{
+	struct gsm_network network;
+
+	printf("Testing the gsm_subscriber chan logic\n");
+
+	/* Create a dummy network */
+	network.bts[0].location_area_code = 23;
+	network.bts[0].network = &network;
+
+	/* Create a dummy subscriber */
+	struct gsm_subscriber *subscr = subscr_alloc();
+	subscr->lac = 23;
+
+	/* Ask for a channel... */
+	subscr_get_channel(subscr, &network, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L);
+
+	while (1) {
+		bsc_select_main(0);
+	}
+}
+
+void nm_state_event() {}
+void input_event() {}
+