diff -Nur openbsc.old/include/openbsc/gsm_04_08.h openbsc.new/include/openbsc/gsm_04_08.h
--- openbsc.old/include/openbsc/gsm_04_08.h	2009-06-06 11:31:11.000000000 +0200
+++ openbsc.new/include/openbsc/gsm_04_08.h	2009-06-06 12:14:00.000000000 +0200
@@ -508,10 +508,59 @@
 	GSM48_REJECT_MSC_TMP_NOT_REACHABLE	= 16,
 };
 
+enum chreq_type {
+	CHREQ_T_EMERG_CALL,
+	CHREQ_T_CALL_REEST_TCH_F,
+	CHREQ_T_CALL_REEST_TCH_H,
+	CHREQ_T_CALL_REEST_TCH_H_DBL,
+	CHREQ_T_SDCCH,
+	CHREQ_T_TCH_F,
+	CHREQ_T_VOICE_CALL_TCH_H,
+	CHREQ_T_DATA_CALL_TCH_H,
+	CHREQ_T_LOCATION_UPD,
+	CHREQ_T_PAG_R_ANY,
+	CHREQ_T_PAG_R_TCH_F,
+	CHREQ_T_PAG_R_TCH_FH,
+};
+
+/* Chapter 11.3 */
+#define GSM48_T301	180, 0
+#define GSM48_T303	30, 0
+#define GSM48_T305	30, 0
+#define GSM48_T306	30, 0
+#define GSM48_T308	10, 0
+#define GSM48_T310	180, 0
+#define GSM48_T313	30, 0
+#define GSM48_T323	30, 0
+#define GSM48_T331	30, 0
+#define GSM48_T333	30, 0
+#define GSM48_T334	25, 0 /* min 15 */
+#define GSM48_T338	30, 0
+
+/* Chapter 5.1.2.2 */
+#define	GSM_CSTATE_NULL			0
+#define	GSM_CSTATE_INITIATED		1
+#define	GSM_CSTATE_MO_CALL_PROC		3
+#define	GSM_CSTATE_CALL_DELIVERED	4
+#define	GSM_CSTATE_CALL_PRESENT		6
+#define	GSM_CSTATE_CALL_RECEIVED	7
+#define	GSM_CSTATE_CONNECT_REQUEST	8
+#define	GSM_CSTATE_MO_TERM_CALL_CONF	9
+#define	GSM_CSTATE_ACTIVE		10
+#define	GSM_CSTATE_DISCONNECT_REQ	12
+#define	GSM_CSTATE_DISCONNECT_IND	12
+#define	GSM_CSTATE_RELEASE_REQ		19
+#define	GSM_CSTATE_MO_ORIG_MODIFY	26
+#define	GSM_CSTATE_MO_TERM_MODIFY	27
+#define	GSM_CSTATE_CONNECT_IND		28
+
+#define SBIT(a) (1 << a)
+#define ALL_STATES 0xffffffff
 
 struct msgb;
 struct gsm_bts;
 struct gsm_subscriber;
+struct gsm_network;
 
 /* config options controlling the behaviour of the lower leves */
 void gsm0408_allow_everyone(int allow);
@@ -520,7 +569,6 @@
 int gsm0408_rcvmsg(struct msgb *msg);
 void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, 
 		u_int16_t mnc, u_int16_t lac);
-int gsm48_cc_tx_setup(struct gsm_lchan *lchan, struct gsm_subscriber *calling);
 enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
 enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
 
@@ -531,6 +579,10 @@
 
 int gsm48_send_rr_release(struct gsm_lchan *lchan);
 
+int bsc_upqueue(struct gsm_network *net);
+
+int mncc_send(struct gsm_network *net, int msg_type, void *arg);
+
 /* convert a ASCII phone number to call-control BCD */
 int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
 		      int h_len, const char *input);
diff -Nur openbsc.old/include/openbsc/gsm_data.h openbsc.new/include/openbsc/gsm_data.h
--- openbsc.old/include/openbsc/gsm_data.h	2009-06-06 08:52:36.000000000 +0200
+++ openbsc.new/include/openbsc/gsm_data.h	2009-06-06 12:14:00.000000000 +0200
@@ -3,8 +3,37 @@
 
 #include <sys/types.h>
 
+enum gsm_phys_chan_config {
+	GSM_PCHAN_NONE,
+	GSM_PCHAN_CCCH,
+	GSM_PCHAN_CCCH_SDCCH4,
+	GSM_PCHAN_TCH_F,
+	GSM_PCHAN_TCH_H,
+	GSM_PCHAN_SDCCH8_SACCH8C,
+	GSM_PCHAN_UNKNOWN,
+};
+
+enum gsm_chan_t {
+	GSM_LCHAN_NONE,
+	GSM_LCHAN_SDCCH,
+	GSM_LCHAN_TCH_F,
+	GSM_LCHAN_TCH_H,
+	GSM_LCHAN_UNKNOWN,
+};
+
+
+/* Channel Request reason */
+enum gsm_chreq_reason_t {
+	GSM_CHREQ_REASON_EMERG,
+	GSM_CHREQ_REASON_PAG,
+	GSM_CHREQ_REASON_CALL,
+	GSM_CHREQ_REASON_LOCATION_UPD,
+	GSM_CHREQ_REASON_OTHER,
+};
+
 #include <openbsc/timer.h>
 #include <openbsc/gsm_04_08.h>
+#include <openbsc/mncc.h>
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
@@ -58,66 +87,41 @@
 	struct gsm_bts *bts;
 };
 
-enum gsm_call_type {
-	GSM_CT_NONE,
-	GSM_CT_MO,
-	GSM_CT_MT,
-};
-
-enum gsm_call_state {
-	GSM_CSTATE_NULL,
-	GSM_CSTATE_INITIATED,
-	GSM_CSTATE_ACTIVE,
-	GSM_CSTATE_RELEASE_REQ,
-};
-
 struct gsm_lchan;
 struct gsm_subscriber;
+struct gsm_mncc;
 
-/* One end of a call */
-struct gsm_call {
-	enum gsm_call_type type;
-	enum gsm_call_state state;
-	u_int8_t transaction_id;	/* 10.3.2 */
-
-	/* the 'local' channel */
-	struct gsm_lchan *local_lchan;
-	/* the 'remote' channel */
-	struct gsm_lchan *remote_lchan;
+/* One transaction */
+struct gsm_trans {
+	/* Entry in list of all transactions */
+	struct llist_head entry;
 
-	/* the 'remote' subscriber */
-	struct gsm_subscriber *called_subscr;
-};
+	/* Network */
+	struct gsm_network *network;
 
+	/* The current transaction ID */
+	u_int8_t transaction_id;
+	
+	/* The LCHAN that we're part of */
+	struct gsm_lchan *lchan;
 
-enum gsm_phys_chan_config {
-	GSM_PCHAN_NONE,
-	GSM_PCHAN_CCCH,
-	GSM_PCHAN_CCCH_SDCCH4,
-	GSM_PCHAN_TCH_F,
-	GSM_PCHAN_TCH_H,
-	GSM_PCHAN_SDCCH8_SACCH8C,
-	GSM_PCHAN_UNKNOWN,
-};
+	/* To whom we are allocated at the moment */
+	struct gsm_subscriber *subscr;
 
-enum gsm_chan_t {
-	GSM_LCHAN_NONE,
-	GSM_LCHAN_SDCCH,
-	GSM_LCHAN_TCH_F,
-	GSM_LCHAN_TCH_H,
-	GSM_LCHAN_UNKNOWN,
-};
+	/* reference */
+	u_int32_t callref;
 
+	/* current call state */
+	int state;
 
-/* Channel Request reason */
-enum gsm_chreq_reason_t {
-	GSM_CHREQ_REASON_EMERG,
-	GSM_CHREQ_REASON_PAG,
-	GSM_CHREQ_REASON_CALL,
-	GSM_CHREQ_REASON_LOCATION_UPD,
-	GSM_CHREQ_REASON_OTHER,
+	/* current timer and message queue */
+	int Tcurrent;			/* current CC timer */
+	int T308_second;		/* used to send release again */
+        struct timer_list cc_timer;
+	struct gsm_mncc cc_msg;		/* stores setup/disconnect/release message */
 };
 
+
 /* Network Management State */
 struct gsm_nm_state {
 	u_int8_t operational;
@@ -161,12 +165,6 @@
 	/* Timer started to release the channel */
 	struct timer_list release_timer;
 
-	/* local end of a call, if any */
-	struct gsm_call call;
-
-	/* temporary user data, to be removed... and merged into gsm_call */
-	void *user_data;
-
 	/*
 	 * Operations that have a state and might be pending
 	 */
@@ -354,6 +352,11 @@
 	char *name_long;
 	char *name_short;
 
+	/* layer 4 */
+	int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
+	struct llist_head upqueue;
+	struct llist_head trans_list;
+
 	unsigned int num_bts;
 	/* private lists */
 	struct gsm_bts	bts[GSM_MAX_BTS+1];
@@ -371,7 +374,8 @@
 };
 
 struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
-				     u_int16_t country_code, u_int16_t network_code);
+				     u_int16_t country_code, u_int16_t network_code,
+				     int (*mncc_recv)(struct gsm_network *, int, void *));
 
 const char *gsm_pchan_name(enum gsm_phys_chan_config c);
 const char *gsm_lchan_name(enum gsm_chan_t c);
diff -Nur openbsc.old/include/openbsc/gsm_subscriber.h openbsc.new/include/openbsc/gsm_subscriber.h
--- openbsc.old/include/openbsc/gsm_subscriber.h	2009-06-06 08:52:36.000000000 +0200
+++ openbsc.new/include/openbsc/gsm_subscriber.h	2009-06-06 12:54:41.000000000 +0200
@@ -12,6 +12,7 @@
 #define GSM_EXTENSION_LENGTH 128
 
 struct gsm_subscriber {
+	struct gsm_network *net;
 	long long unsigned int id;
 	char imsi[GSM_IMSI_LENGTH];
 	char tmsi[GSM_TMSI_LENGTH];
diff -Nur openbsc.old/include/openbsc/mncc.h openbsc.new/include/openbsc/mncc.h
--- openbsc.old/include/openbsc/mncc.h	1970-01-01 01:00:00.000000000 +0100
+++ openbsc.new/include/openbsc/mncc.h	2009-06-06 12:14:00.000000000 +0200
@@ -0,0 +1,176 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface 
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 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.
+ *
+ */
+
+#ifndef _MNCC_H
+#define _MNCC_H
+
+#include <openbsc/linuxlist.h>
+
+/* One end of a call */
+struct gsm_call {
+	struct llist_head entry;
+
+	/* network handle */
+	void *net;
+
+	/* the 'local' transaction */
+	u_int32_t callref;
+	/* the 'remote' transaction */
+	u_int32_t remote_ref;
+};
+
+#define MNCC_SETUP_REQ		0x0101
+#define MNCC_SETUP_IND		0x0102
+#define MNCC_SETUP_RSP		0x0103
+#define MNCC_SETUP_CNF		0x0104
+#define MNCC_SETUP_COMPL_REQ	0x0105
+#define MNCC_SETUP_COMPL_IND	0x0106
+/* MNCC_REJ_* is perfomed via MNCC_REL_* */
+#define MNCC_CALL_CONF_IND	0x0107
+#define MNCC_CALL_PROC_REQ	0x0108
+#define MNCC_PROGRESS_REQ	0x0109
+#define MNCC_ALERT_REQ		0x010a
+#define MNCC_ALERT_IND		0x010b
+#define MNCC_NOTIFY_REQ		0x010c
+#define MNCC_NOTIFY_IND		0x010d
+#define MNCC_DISC_REQ		0x010e
+#define MNCC_DISC_IND		0x010f
+#define MNCC_REL_REQ		0x0110
+#define MNCC_REL_IND		0x0111
+#define MNCC_REL_CNF		0x0112
+#define MNCC_FACILITY_REQ	0x0113
+#define MNCC_FACILITY_IND	0x0114
+#define MNCC_START_DTMF_IND	0x0115
+#define MNCC_START_DTMF_RSP	0x0116
+#define MNCC_START_DTMF_REJ	0x0117
+#define MNCC_STOP_DTMF_IND	0x0118
+#define MNCC_STOP_DTMF_RSP	0x0119
+#define MNCC_MODIFY_REQ		0x011a
+#define MNCC_MODIFY_IND		0x011b
+#define MNCC_MODIFY_RSP		0x011c
+#define MNCC_MODIFY_CNF		0x011d
+#define MNCC_MODIFY_REJ		0x011e
+#define MNCC_HOLD_IND		0x011f
+#define MNCC_HOLD_CNF		0x0120
+#define MNCC_HOLD_REJ		0x0121
+#define MNCC_RETRIEVE_IND	0x0122
+#define MNCC_RETRIEVE_CNF	0x0123
+#define MNCC_RETRIEVE_REJ	0x0124
+#define MNCC_USERINFO_REQ	0x0125
+#define MNCC_USERINFO_IND	0x0126
+#define MNCC_REJ_REQ		0x0127
+#define MNCC_REJ_IND		0x0128
+
+#define MNCC_BRIDGE		0x0200
+#define MNCC_FRAME_RECV		0x0201
+#define MNCC_FRAME_DROP		0x0202
+#define MNCC_LCHAN_MODIFY	0x0203
+
+#define GSM_TRAU_FRAME		0x0300
+
+#define GSM_MAX_FACILITY	128
+#define GSM_MAX_SSVERSION	128
+#define GSM_MAX_USERUSER	128
+
+
+struct gsm_mncc {
+	/* context based information */
+	u_int32_t	msg_type;
+	u_int32_t	callref;
+	/* data based informations */
+	int		bearer_cap;
+	int		bearer_cap_transfer;
+	int		bearer_cap_mode;
+	int		bearer_cap_coding;
+	int		bearer_cap_radio;
+	int		bearer_cap_speech_ctm;
+	int		bearer_cap_speech_ver[8];
+	int		called;
+	int		called_type;
+	int		called_plan;
+	char		called_number[33];
+	int		calling;
+	int		calling_type;
+	int		calling_plan;
+	int		calling_present;
+	int		calling_screen;
+	char		calling_number[21];
+	int		cause;
+	int		cause_location;
+	int		cause_coding;
+	int		cause_rec;
+	int		cause_rec_val;
+	int		cause_value;
+	int		cause_diag_len;
+	char		cause_diag[32];
+	int		cccap;
+	int		cccap_dtmf;
+	int		cccap_pcp;
+	int		clir_sup;
+	int		clir_inv;
+	int		connected;
+	int		connected_type;
+	int		connected_plan;
+	int		connected_present;
+	int		connected_screen;
+	char		connected_number[21];
+	int		emergency;
+	int		facility;
+	int		facility_len;
+	char		facility_info[GSM_MAX_FACILITY];
+	int		keypad;
+	int		more;
+	int		notify; /* 0..127 */
+	int		progress;
+	int		progress_coding;
+	int		progress_location;
+	int		progress_descr;
+	int		redirecting;
+	int		redirecting_type;
+	int		redirecting_plan;
+	int		redirecting_present;
+	int		redirecting_screen;
+	char		redirecting_number[21];
+	int		useruser;
+	int		useruser_proto;
+	char		useruser_info[GSM_MAX_USERUSER + 1]; /* + termination char */
+	int		signal;
+	int		signal_value;
+	int		ssversion;
+	int		ssversion_len;
+	char		ssversion_info[GSM_MAX_SSVERSION];
+
+	unsigned char	lchan_mode;
+};
+
+struct gsm_trau_frame {
+	u_int32_t	msg_type;
+	u_int32_t	callref;
+	unsigned char	data[0];
+};
+
+char *get_mncc_name(int value);
+int mncc_recv(struct gsm_network *net, int msg_type, void *arg);
+
+#endif
diff -Nur openbsc.old/src/Makefile.am openbsc.new/src/Makefile.am
--- openbsc.old/src/Makefile.am	2009-06-06 08:59:56.000000000 +0200
+++ openbsc.new/src/Makefile.am	2009-06-06 12:14:00.000000000 +0200
@@ -5,7 +5,7 @@
 noinst_LIBRARIES = libbsc.a libvty.a
 noinst_HEADERS = vty/cardshell.h
 
-libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
+libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \
 		gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
 		gsm_04_11.c telnet_interface.c subchan_demux.c vty_interface.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
diff -Nur openbsc.old/src/bs11_config.c openbsc.new/src/bs11_config.c
--- openbsc.old/src/bs11_config.c	2009-05-31 12:59:25.000000000 +0200
+++ openbsc.new/src/bs11_config.c	2009-06-06 12:14:00.000000000 +0200
@@ -775,7 +775,7 @@
 
 	handle_options(argc, argv);
 
-	gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11);
+	gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11, NULL);
 	if (!gsmnet) {
 		fprintf(stderr, "Unable to allocate gsm network\n");
 		exit(1);
diff -Nur openbsc.old/src/bsc_hack.c openbsc.new/src/bsc_hack.c
--- openbsc.old/src/bsc_hack.c	2009-06-06 10:56:53.000000000 +0200
+++ openbsc.new/src/bsc_hack.c	2009-06-06 12:14:00.000000000 +0200
@@ -960,7 +960,7 @@
 	struct gsm_bts *bts;
 
 	/* initialize our data structures */
-	gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC);
+	gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC, mncc_recv);
 	if (!gsmnet)
 		return -ENOMEM;
 
@@ -1149,6 +1149,7 @@
 	signal(SIGABRT, &signal_handler);
 
 	while (1) {
+		bsc_upqueue(gsmnet);
 		bsc_select_main(0);
 	}
 }
diff -Nur openbsc.old/src/chan_alloc.c openbsc.new/src/chan_alloc.c
--- openbsc.old/src/chan_alloc.c	2009-06-06 09:35:17.000000000 +0200
+++ openbsc.new/src/chan_alloc.c	2009-06-06 12:31:22.000000000 +0200
@@ -62,7 +62,7 @@
 	/* FIXME: bounds check */
 };
 
-/* Allocate a logical channel (TS) */
+/* Allocate a physical channel (TS) */
 struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
 				enum gsm_phys_chan_config pchan)
 {
@@ -114,7 +114,7 @@
 	[GSM_PCHAN_CCCH_SDCCH4] = 4,
 	[GSM_PCHAN_TCH_F] = 1,
 	[GSM_PCHAN_TCH_H] = 2,
-	[GSM_PCHAN_SDCCH8_SACCH8C] = 8.
+	[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
 };
 
 static struct gsm_lchan *
diff -Nur openbsc.old/src/gsm_04_08.c openbsc.new/src/gsm_04_08.c
--- openbsc.old/src/gsm_04_08.c	2009-06-06 11:29:30.000000000 +0200
+++ openbsc.new/src/gsm_04_08.c	2009-06-06 13:08:49.000000000 +0200
@@ -113,6 +113,108 @@
 	[GSM48_RR_CAUSE_PROT_ERROR_UNSPC]	= "Protocol error unspecified",
 };
 
+static const char *cc_state_names[] = {
+	"NULL",
+	"INITIATED",
+	"illegal state 2",
+	"MO_CALL_PROC",
+	"CALL_DELIVERED",
+	"illegal state 5",
+	"CALL_PRESENT",
+	"CALL_RECEIVED",
+	"CONNECT_REQUEST",
+	"MO_TERM_CALL_CONF",
+	"ACTIVE",
+	"DISCONNECT_REQ",
+	"DISCONNECT_IND",
+	"illegal state 13",
+	"illegal state 14",
+	"illegal state 15",
+	"illegal state 16",
+	"illegal state 17",
+	"illegal state 18",
+	"RELEASE_REQ",
+	"illegal state 20",
+	"illegal state 21",
+	"illegal state 22",
+	"illegal state 23",
+	"illegal state 24",
+	"illegal state 25",
+	"MO_ORIG_MODIFY",
+	"MO_TERM_MODIFY",
+	"CONNECT_IND",
+	"illegal state 29",
+	"illegal state 30",
+	"illegal state 31",
+};
+
+static const char *cc_msg_names[] = {
+	"unknown 0x00",
+	"ALERTING",
+	"CALL_PROC",
+	"PROGRESS",
+	"ESTAB",
+	"SETUP",
+	"ESTAB_CONF",
+	"CONNECT",
+	"CALL_CONF",
+	"START_CC",
+	"unknown 0x0a",
+	"RECALL",
+	"unknown 0x0c",
+	"unknown 0x0d",
+	"EMERG_SETUP",
+	"CONNECT_ACK",
+	"USER_INFO",
+	"unknown 0x11",
+	"unknown 0x12",
+	"MODIFY_REJECT",
+	"unknown 0x14",
+	"unknown 0x15",
+	"unknown 0x16",
+	"MODIFY",
+	"HOLD",
+	"HOLD_ACK",
+	"HOLD_REJ",
+	"unknown 0x1b",
+	"RETR",
+	"RETR_ACK",
+	"RETR_REJ",
+	"MODIFY_COMPL",
+	"unknown 0x20",
+	"unknown 0x21",
+	"unknown 0x22",
+	"unknown 0x23",
+	"unknown 0x24",
+	"DISCONNECT",
+	"unknown 0x26",
+	"unknown 0x27",
+	"unknown 0x28",
+	"unknown 0x29",
+	"RELEASE_COMPL",
+	"unknown 0x2b",
+	"unknown 0x2c",
+	"RELEASE",
+	"unknown 0x2e",
+	"unknown 0x2f",
+	"unknown 0x30",
+	"STOP_DTMF",
+	"STOP_DTMF_ACK",
+	"unknown 0x33",
+	"STATUS_ENQ",
+	"START_DTMF",
+	"START_DTMF_ACK",
+	"START_DTMF_REJ",
+	"unknown 0x38",
+	"CONG_CTRL",
+	"FACILITY",
+	"unknown 0x3b",
+	"STATUS",
+	"unknown 0x3c",
+	"NOTIFY",
+	"unknown 0x3f",
+};
+
 static char strbuf[64];
 
 static const char *rr_cause_name(u_int8_t cause)
@@ -128,6 +230,7 @@
 static int gsm48_tx_simple(struct gsm_lchan *lchan,
 			   u_int8_t pdisc, u_int8_t msg_type);
 static void schedule_reject(struct gsm_lchan *lchan);
+void free_trans(struct gsm_trans *trans);
 
 struct gsm_lai {
 	u_int16_t mcc;
@@ -148,6 +251,8 @@
 	reject_cause = cause;
 }
 
+static u_int32_t new_callref = 0x80000001;
+
 static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
 				struct gsm_subscriber *subscriber)
 {
@@ -192,6 +297,8 @@
 static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
 					void *handler_data, void *signal_data)
 {
+	struct gsm_trans *trans, *temp;
+
 	if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE)
 		return 0;
 
@@ -199,6 +306,12 @@
 	struct gsm_lchan *lchan = (struct gsm_lchan *)handler_data;
 	release_loc_updating_req(lchan);
 
+	/* Free all transactions that are associated with the released lchan */
+	llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) {
+		if (trans->lchan == lchan)
+			free_trans(trans);
+	}
+
 	return 0;
 }
 
@@ -764,27 +877,18 @@
 
 int gsm48_sendmsg(struct msgb *msg)
 {
-	struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
-
 	if (msg->lchan) {
+		struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
 		msg->trx = msg->lchan->ts->trx;
 
-		if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) {
-			/* Send a 04.08 call control message, add transaction
-			 * ID and TI flag */
-			gh->proto_discr |= msg->lchan->call.transaction_id;
-
-			/* GSM 04.07 Section 11.2.3.1.3 */
-			switch (msg->lchan->call.type) {
-			case GSM_CT_MO:
-				gh->proto_discr |= 0x80;
-				break;
-			case GSM_CT_MT:
-				break;
-			case GSM_CT_NONE:
-				break;
-			}
-		}
+		if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC)
+			DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) Sending '%s' to MS.\n",
+				msg->trx->bts->nr, msg->trx->nr, msg->lchan->ts->nr,
+				gh->proto_discr & 0xf0, cc_msg_names[gh->msg_type & 0x3f]);
+		else
+			DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) Sending 0x%02x to MS.\n",
+				msg->trx->bts->nr, msg->trx->nr, msg->lchan->ts->nr,
+				gh->proto_discr, gh->msg_type);
 	}
 
 	msg->l3h = msg->data;
@@ -792,7 +896,6 @@
 	return rsl_data_request(msg, 0);
 }
 
-
 /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
 int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
 {
@@ -992,7 +1095,7 @@
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm_bts *bts = msg->trx->bts;
 	struct gsm48_loc_upd_req *lu;
-	struct gsm_subscriber *subscr;
+	struct gsm_subscriber *subscr = NULL;
 	struct gsm_lchan *lchan = msg->lchan;
 	u_int8_t mi_type;
 	u_int32_t tmsi;
@@ -1019,6 +1122,7 @@
 
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
+		DEBUGP(DMM, "\n");
 		/* we always want the IMEI, too */
 		rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
 		lchan->loc_operation->waiting_for_imei = 1;
@@ -1027,6 +1131,7 @@
 		subscr = db_create_subscriber(mi_string);
 		break;
 	case GSM_MI_TYPE_TMSI:
+		DEBUGP(DMM, "\n");
 		/* we always want the IMEI, too */
 		rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
 		lchan->loc_operation->waiting_for_imei = 1;
@@ -1049,6 +1154,12 @@
 		break;
 	}
 
+	if (!subscr) {
+		DEBUGP(DRR, "<- Can't find any subscriber for this ID\n");
+		/* FIXME: request id? close channel? */
+		return -EINVAL;
+	}
+
 	lchan->subscr = subscr;
 
 	/*
@@ -1097,6 +1208,13 @@
 	return gsm48_sendmsg(msg);
 }
 
+#if 0
+static u_int8_t to_bcd8(u_int8_t val)
+{
+       return ((val / 10) << 4) | (val % 10);
+}
+#endif
+
 /* Section 9.2.15a */
 int gsm48_tx_mm_info(struct gsm_lchan *lchan)
 {
@@ -1107,6 +1225,11 @@
 	u_int16_t *ptr16;
 	int name_len;
 	int i;
+#if 0
+	time_t cur_t;
+	struct tm* cur_time;
+	int tz15min;
+#endif
 
 	msg->lchan = lchan;
 
@@ -1144,13 +1267,9 @@
 	}
 
 #if 0
-	/* move back to the top */
-	time_t cur_t;
-	struct tm* cur_time;
-	int tz15min;
 	/* Section 10.5.3.9 */
 	cur_t = time(NULL);
-	cur_time = gmtime(cur_t);
+	cur_time = gmtime(&cur_t);
 	ptr8 = msgb_put(msg, 8);
 	ptr8[0] = GSM48_IE_NET_TIME_TZ;
 	ptr8[1] = to_bcd8(cur_time->tm_year % 100);
@@ -1161,9 +1280,9 @@
 	ptr8[6] = to_bcd8(cur_time->tm_sec);
 	/* 02.42: coded as BCD encoded signed value in units of 15 minutes */
 	tz15min = (cur_time->tm_gmtoff)/(60*15);
-	ptr8[6] = to_bcd8(tz15min);
+	ptr8[7] = to_bcd8(tz15min);
 	if (tz15min < 0)
-		ptr8[6] |= 0x80;
+		ptr8[7] |= 0x80;
 #endif
 
 	return gsm48_sendmsg(msg);
@@ -1268,7 +1387,7 @@
 				(struct gsm48_imsi_detach_ind *) gh->data;
 	u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK;
 	char mi_string[MI_SIZE];
-	struct gsm_subscriber *subscr;
+	struct gsm_subscriber *subscr = NULL;
 
 	mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
 	DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
@@ -1316,7 +1435,7 @@
 static int gsm0408_rcv_mm(struct msgb *msg)
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
-	int rc;
+	int rc = 0;
 
 	switch (gh->msg_type & 0xbf) {
 	case GSM48_MT_MM_LOC_UPD_REQUEST:
@@ -1364,7 +1483,7 @@
 	u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv;
 	u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
 	char mi_string[MI_SIZE];
-	struct gsm_subscriber *subscr;
+	struct gsm_subscriber *subscr = NULL;
 	struct gsm_bts *bts;
 	struct paging_signal_data sig_data;
 	int rc = 0;
@@ -1497,16 +1616,25 @@
  * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY
  * it for voice */
 
-static int gsm48_cc_tx_status(struct gsm_lchan *lchan)
+static void new_cc_state(struct gsm_trans *trans, int state)
+{
+	if (state > 31 || state < 0)
+		return;
+
+	DEBUGP(DCC, "new state %s -> %s\n",
+		cc_state_names[trans->state], cc_state_names[state]);
+
+	trans->state = state;
+}
+
+static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	u_int8_t *cause, *call_state;
 
-	gh->proto_discr = GSM48_PDISC_CC;
-
-	msg->lchan = lchan;
-
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_STATUS;
 
 	cause = msgb_put(msg, 3);
@@ -1534,133 +1662,150 @@
 	return gsm48_sendmsg(msg);
 }
 
-/* call-back from paging the B-end of the connection */
-static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
-			      struct msgb *msg, void *_lchan, void *param)
+static void gsm48_stop_cc_timer(struct gsm_trans *trans)
 {
-	struct gsm_lchan *lchan = _lchan;
-	struct gsm_call *remote_call = param;
-	struct gsm_call *call = &lchan->call;
-	int rc = 0;
-
-	if (hooknum != GSM_HOOK_RR_PAGING)
-		return -EINVAL;
-
-	switch (event) {
-	case GSM_PAGING_SUCCEEDED:
-		DEBUGP(DCC, "paging succeeded!\n");
-		remote_call->remote_lchan = lchan;
-		call->remote_lchan = remote_call->local_lchan;
-		/* send SETUP request to called party */
-		rc = gsm48_cc_tx_setup(lchan, call->remote_lchan->subscr);
-		if (is_ipaccess_bts(lchan->ts->trx->bts))
-			rsl_ipacc_bind(lchan);
-		break;
-	case GSM_PAGING_EXPIRED:
-		DEBUGP(DCC, "paging expired!\n");
-		/* notify caller that we cannot reach called party */
-		/* FIXME: correct cause, etc */
-		rc = gsm48_tx_simple(remote_call->local_lchan, GSM48_PDISC_CC,
-				     GSM48_MT_CC_RELEASE_COMPL);
-		break;
+	if (bsc_timer_pending(&trans->cc_timer)) {
+		DEBUGP(DCC, "stopping pending timer T%x\n", trans->Tcurrent);
+		bsc_del_timer(&trans->cc_timer);
+		trans->Tcurrent = 0;
 	}
-	return rc;
 }
 
-static int gsm48_cc_rx_status_enq(struct msgb *msg)
+static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, int msg_type, struct gsm_mncc *mncc)
 {
-	DEBUGP(DCC, "-> STATUS ENQ\n");
-	return gsm48_cc_tx_status(msg->lchan);
+	struct msgb *msg;
+
+	if (trans)
+		if (trans->lchan)
+			DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) Sending '%s' to MNCC.\n",
+				trans->lchan->ts->trx->bts->nr, trans->lchan->ts->trx->nr,
+				trans->lchan->ts->nr, trans->transaction_id,
+				(trans->subscr)?(trans->subscr->extension):"-",
+				get_mncc_name(msg_type));
+		else
+			DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) Sending '%s' to MNCC.\n",
+				(trans->subscr)?(trans->subscr->extension):"-",
+				get_mncc_name(msg_type));
+	else
+		DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) Sending '%s' to MNCC.\n",
+			get_mncc_name(msg_type));
+
+	mncc->msg_type = msg_type;
+	
+	msg = msgb_alloc(sizeof(struct gsm_mncc));
+	if (!msg)
+		return -ENOMEM;
+	memcpy(msg->data, mncc, sizeof(struct gsm_mncc));
+	msgb_enqueue(&net->upqueue, msg);
+
+	return 0;
 }
 
-static int gsm48_cc_rx_setup(struct msgb *msg)
+void free_trans(struct gsm_trans *trans)
 {
-	struct gsm_call *call = &msg->lchan->call;
-	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;
 
-	if (call->state == GSM_CSTATE_NULL ||
-	    call->state == GSM_CSTATE_RELEASE_REQ)
-		use_lchan(msg->lchan);
-
-	call->type = GSM_CT_MO;
-	call->state = GSM_CSTATE_INITIATED;
-	call->local_lchan = msg->lchan;
-	call->transaction_id = gh->proto_discr & 0xf0;
+	gsm48_stop_cc_timer(trans);
 
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
-	if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
-		goto err;
+	/* send release to L4, if callref still exists */
+	if (trans->callref) {
+		struct gsm_mncc rel;
+		
+		memset(&rel, 0, sizeof(struct gsm_mncc));
+		rel.callref = trans->callref;
+		rel.cause = 1;
+		rel.cause_location = 1;
+		rel.cause_value = 47; /* Ressource unavailable */
+		mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+		if (trans->state != GSM_CSTATE_NULL)
+			new_cc_state(trans, GSM_CSTATE_NULL);
+	}
+
+	if (!trans->lchan && trans->subscr && trans->subscr->net) {
+		/* Stop paging on all bts' */
+		bts = NULL;
+		do {
+			bts = gsm_bts_by_lac(trans->subscr->net, trans->subscr->lac, bts);
+			if (!bts)
+				break;
+			/* Stop paging */
+			paging_request_stop(bts, trans->subscr, NULL);
+		} while (1);
+	}
 
-	/* Parse the number that was dialed and lookup subscriber */
-	decode_bcd_number(called_number, sizeof(called_number),
-				     TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1, 1);
-
-	DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id,
-		called_number);
-
-	called_subscr = subscr_get_by_extension(called_number);
-	if (!called_subscr) {
-		DEBUGP(DCC, "could not find subscriber, RELEASE\n");
-		put_lchan(msg->lchan);
-		return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				GSM48_MT_CC_RELEASE_COMPL);
-	}
-	if (called_subscr == msg->lchan->subscr) {
-		DEBUGP(DCC, "subscriber calling himself ?!?\n");
-		put_lchan(msg->lchan);
-		subscr_put(called_subscr);
-		return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				GSM48_MT_CC_RELEASE_COMPL);
+	if (trans->lchan) {
+		trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
+		put_lchan(trans->lchan);
 	}
 
-	subscr_get(msg->lchan->subscr);
-	call->called_subscr = called_subscr;
+	if (trans->subscr)
+		subscr_put(trans->subscr);
 
-	/* Start paging subscriber on all BTS with her location */
-	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);
+	if (trans->state != GSM_CSTATE_NULL)
+		new_cc_state(trans, GSM_CSTATE_NULL);
 
-	/* send a CALL PROCEEDING message to the MO */
-	ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_CALL_PROC);
+	llist_del(&trans->entry);
 
-	if (is_ipaccess_bts(msg->trx->bts))
-		rsl_ipacc_bind(msg->lchan);
+	free(trans);
+}
 
-	/* change TCH/F mode to voice */ 
-	return gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
 
-err:
-	/* FIXME: send some kind of RELEASE */
-	return 0;
-}
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
 
-static int gsm48_cc_rx_alerting(struct msgb *msg)
+/* call-back from paging the B-end of the connection */
+static int setup_trig_pag_evt(unsigned int hooknum,
+		unsigned int event, struct msgb *msg, void *_lchan, void *param)
 {
-	struct gsm_call *call = &msg->lchan->call;
+	struct gsm_lchan *lchan = _lchan;
+	struct gsm_subscriber *subscr = param;
+	struct gsm_trans *transt, *tmp;
+	struct gsm_network *net;
+	struct gsm_mncc rel;
 
-	DEBUGP(DCC, "A -> ALERTING\n");
+	if (hooknum != GSM_HOOK_RR_PAGING)
+		return -EINVAL;
 
-	/* forward ALERTING to other party */
-	if (!call->remote_lchan)
-		return -EIO;
+	if (!subscr)
+		return -EINVAL;
+	net = subscr->net;
+	if (!net) {
+		DEBUGP(DCC, "Error Network not set!\n");
+		return -EINVAL;
+	}
 
-	DEBUGP(DCC, "B <- ALERTING\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_ALERTING);
+	/* check all tranactions (without lchan) for subscriber */
+	llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) {
+		if (transt->subscr == subscr && !transt->lchan) {
+			switch (event) {
+			case GSM_PAGING_SUCCEEDED:
+				if (!lchan) // paranoid
+					break;
+				DEBUGP(DCC, "Paging subscr %s succeeded!\n", subscr->extension);
+				/* Assign lchan */
+				if (!transt->lchan) {
+					transt->lchan = lchan;
+					use_lchan(lchan);
+				}
+				/* send SETUP request to called party */
+				gsm48_cc_tx_setup(transt, &transt->cc_msg);
+				if (is_ipaccess_bts(lchan->ts->trx->bts))
+					rsl_ipacc_bind(lchan);
+				break;
+			case GSM_PAGING_EXPIRED:
+				DEBUGP(DCC, "Paging subscr %s expired!\n", subscr->extension);
+				memset(&rel, 0, sizeof(struct gsm_mncc));
+				rel.callref = transt->callref;
+				rel.cause = 1;
+				rel.cause_location = 1;
+				rel.cause_value = 27; /* Temporarily out of order */
+				mncc_recvmsg(transt->network, transt, MNCC_REL_IND, &rel);
+				transt->callref = 0;
+				free_trans(transt);
+				break;
+			}
+		}
+	}
+	return 0;
 }
 
 /* map two ipaccess RTP streams onto each other */
@@ -1701,249 +1846,1926 @@
 	return 0;
 }
 
-static int gsm48_cc_rx_connect(struct msgb *msg)
+static struct gsm_trans *get_trans_ref(struct gsm_network *net, u_int32_t callref)
 {
-	struct gsm_call *call = &msg->lchan->call;
-	int rc;
+	struct gsm_trans *trans;
+	llist_for_each_entry(trans, &net->trans_list, entry) {
+		if (trans->callref == callref)
+			return trans;
+	}
+	return NULL;
+}
 
-	DEBUGP(DCC, "A -> CONNECT\n");
-	
-	rc = tch_map(msg->lchan, call->remote_lchan);
-	if (rc)
+/* bridge channels of two transactions */
+static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
+{
+	struct gsm_trans *trans1 = get_trans_ref(net, refs[0]);
+	struct gsm_trans *trans2 = get_trans_ref(net, refs[1]);
+
+	if (!trans1 || !trans2)
 		return -EIO;
-		
-	if (!call->remote_lchan)
+
+	if (!trans1->lchan || !trans2->lchan)
 		return -EIO;
 
-	DEBUGP(DCC, "A <- CONNECT ACK\n");
-	/* MT+MO: need to respond with CONNECT_ACK and pass on */
-	rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-			     GSM48_MT_CC_CONNECT_ACK);
+	/* through-connect channel */
+	return tch_map(trans1->lchan, trans2->lchan);
+}
+
+/* enable receive of channels to upqueue */
+static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+{
+	struct gsm_trans *trans;
 
+	/* Find callref */
+	trans = get_trans_ref(net, data->callref);
+	if (!trans)
+		return -EIO;
+	if (!trans->lchan)
+		return 0;
 
-	/* forward CONNECT to other party */
-	DEBUGP(DCC, "B <- CONNECT\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_CONNECT);
+	// todo IPACCESS
+	if (enable)
+		return trau_recv_lchan(trans->lchan, data->callref);
+	return trau_mux_unmap(NULL, data->callref);
 }
 
-static int gsm48_cc_rx_disconnect(struct msgb *msg)
+/* send a frame to channel */
+static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
 {
-	struct gsm_call *call = &msg->lchan->call;
-	int rc;
+	struct gsm_trans *trans;
 
+	/* Find callref */
+	trans = get_trans_ref(net, frame->callref);
+	if (!trans)
+		return -EIO;
+	if (!trans->lchan)
+		return 0;
+	if (trans->lchan->type != GSM_LCHAN_TCH_F && trans->lchan->type != GSM_LCHAN_TCH_H)
+		return 0;
 
-	/* Section 5.4.3.2 */
-	DEBUGP(DCC, "A -> DISCONNECT (state->RELEASE_REQ)\n");
-	call->state = GSM_CSTATE_RELEASE_REQ;
-	/* FIXME: clear the network connection */
-	DEBUGP(DCC, "A <- RELEASE\n");
-	rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-			     GSM48_MT_CC_RELEASE);
+	// todo IPACCESS
+	return trau_send_lchan(trans->lchan, (struct decoded_trau_frame *)frame->data);
+}
 
-	/*
-	 * FIXME: This looks wrong! We should have a single
-	 * place to do MMCC-REL-CNF/-REQ/-IND and then switch
-	 * to the NULL state and relase the call
-	 */
-	subscr_put_channel(msg->lchan);
 
-	/* forward DISCONNECT to other party */
-	if (!call->remote_lchan)
-		return -EIO;
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+	DEBUGP(DCC, "-> STATUS ENQ\n");
+	return gsm48_cc_tx_status(trans, msg);
+}
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
+
+static void gsm48_cc_timeout(void *arg)
+{
+	struct gsm_trans *trans = arg;
+	int disconnect = 0, release = 0;
+	int mo_cause = 102, mo_location = 0;
+	int l4_cause = 31, l4_location = 1;
+	struct gsm_mncc mo_rel, l4_rel;
+
+	memset(&mo_rel, 0, sizeof(struct gsm_mncc));
+	mo_rel.callref = trans->callref;
+	memset(&l4_rel, 0, sizeof(struct gsm_mncc));
+	l4_rel.callref = trans->callref;
+
+	switch(trans->Tcurrent) {
+	case 0x303:
+		release = 1;
+		l4_cause = 18;
+		break;
+	case 0x310:
+		disconnect = 1;
+		l4_cause = 18;
+		break;
+	case 0x313:
+		disconnect = 1;
+		/* unknown, did not find it in the specs */
+		break;
+	case 0x301:
+		disconnect = 1;
+		l4_cause = 19;
+		break;
+	case 0x308:
+		if (!trans->T308_second) {
+			/* restart T308 a second time */
+			gsm48_cc_tx_release(trans, &trans->cc_msg);
+			trans->T308_second = 1;
+			break; /* stay in release state */
+		}
+		free_trans(trans);
+		return;
+//		release = 1;
+//		l4_cause = 14;
+//		break;
+	case 0x306:
+		release = 1;
+		mo_cause = trans->cc_msg.cause_value;
+		mo_location = trans->cc_msg.cause_location;
+		break;
+	case 0x323:
+		disconnect = 1;
+		break;
+	default:
+		release = 1;
+	}
+
+	if (release && trans->callref) {
+		/* process release towards layer 4 */
+		l4_rel.cause = 1;
+		l4_rel.cause_value = l4_cause;
+		l4_rel.cause_location = l4_location;
+		mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &l4_rel);
+		trans->callref = 0;
+	}
+
+	if (disconnect && trans->callref) {
+		/* process disconnect towards layer 4 */
+		l4_rel.cause = 1;
+		l4_rel.cause_value = l4_cause;
+		l4_rel.cause_location = l4_location;
+		mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &l4_rel);
+	}
+
+	/* process disconnect towards mobile station */
+	if (disconnect || release) {
+		mo_rel.cause = 1;
+		mo_rel.cause_value = mo_cause;
+		mo_rel.cause_location = mo_location;
+		mo_rel.cause_diag[0] = ((trans->Tcurrent & 0xf00) >> 8) + '0';
+		mo_rel.cause_diag[1] = ((trans->Tcurrent & 0x0f0) >> 4) + '0';
+		mo_rel.cause_diag[2] = (trans->Tcurrent & 0x00f) + '0';
+		mo_rel.cause_diag_len = 3;
+
+		if (disconnect)
+			gsm48_cc_tx_disconnect(trans, &mo_rel);
+		if (release)
+			gsm48_cc_tx_release(trans, &mo_rel);
+	}
 
-	DEBUGP(DCC, "B <- DISCONNECT\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_DISCONNECT);
 }
 
-static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 };
+static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, int sec, int micro)
+{
+	DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec);
+	trans->cc_timer.cb = gsm48_cc_timeout;
+	trans->cc_timer.data = trans;
+	bsc_schedule_timer(&trans->cc_timer, sec, micro);
+	trans->Tcurrent = current;
+}
+
+static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	u_int8_t msg_type = gh->msg_type & 0xbf;
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc setup;
+
+	memset(&setup, 0, sizeof(struct gsm_mncc));
+	setup.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* emergency setup is identified by msg_type */
+	if (msg_type == GSM48_MT_CC_EMERG_SETUP)
+		setup.emergency = 1;
+
+	/* use subscriber as calling party number */
+	if (trans->subscr) {
+		setup.calling = 1;
+		strncpy(setup.calling_number, trans->subscr->extension, sizeof(setup.calling_number)-1);
+	}
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP))
+		setup.bearer_cap = decode_bearer_cap(
+			&setup.bearer_cap_transfer,
+			&setup.bearer_cap_mode,
+			&setup.bearer_cap_coding,
+			&setup.bearer_cap_radio,
+			&setup.bearer_cap_speech_ctm,
+			setup.bearer_cap_speech_ver,
+			TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		setup.facility = decode_facility(
+			setup.facility_info,
+			sizeof(setup.facility_info),
+			&setup.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* called party bcd number */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
+		setup.called = decode_called(
+			&setup.called_type,
+			&setup.called_plan,
+			setup.called_number,
+			sizeof(setup.called_number),
+			TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		setup.useruser = decode_useruser(
+			&setup.useruser_proto,
+			setup.useruser_info,
+			sizeof(setup.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		setup.ssversion = decode_ssversion(
+			setup.ssversion_info,
+			sizeof(setup.ssversion_info),
+			&setup.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+	/* CLIR suppression */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP))
+		setup.clir_sup = 1;
+	/* CLIR invocation */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC))
+		setup.clir_inv = 1;
+	/* cc cap */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP))
+		setup.cccap = decode_cccap(
+			&setup.cccap_dtmf,
+			&setup.cccap_pcp,
+			TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
+
+	if (is_ipaccess_bts(msg->trx->bts))
+		rsl_ipacc_bind(msg->lchan);
+
+	new_cc_state(trans, GSM_CSTATE_INITIATED);
+
+	/* indicate setup to MNCC */
+	mncc_recvmsg(trans->network, trans, MNCC_SETUP_IND, &setup);
+
+	return 0;
+}
 
-int gsm48_cc_tx_setup(struct gsm_lchan *lchan, 
-		      struct gsm_subscriber *calling_subscr)
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
-	struct gsm_call *call = &lchan->call;
-	u_int8_t bcd_lv[19];
+	struct gsm_mncc rel, *setup = arg;
+	struct gsm_trans *transt;
+	u_int16_t trans_id_mask = 0;
+	int rc, i;
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	call->type = GSM_CT_MT;
-
-	call->local_lchan = msg->lchan = lchan;
-	use_lchan(lchan);
+	/* transaction id must not be assigned */
+	if (trans->transaction_id != 0xff) { /* unasssigned */
+		DEBUGP(DCC, "TX Setup with assigned transaction. This is not allowed!\n");
+		memset(&rel, 0, sizeof(struct gsm_mncc));
+		rel.callref = trans->callref;
+		rel.cause = 1;
+		rel.cause_location = 1;
+		rel.cause_value = 47; /* Temporarily out of order */
+		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+		trans->callref = 0;
+		free_trans(trans);
+		return rc;
+	}
+	
+	/* Get free transaction_id */
+	llist_for_each_entry(transt, &trans->network->trans_list, entry) {
+		/* Transaction of our lchan? */
+		if (transt->lchan == trans->lchan && transt->transaction_id != 0xff)
+			trans_id_mask |= (1 << (transt->transaction_id >> 4));
+	}
+	/* Assign free transaction ID */
+	if ((trans_id_mask & 0x007f) == 0x7f) {
+		/* no free transaction ID */
+		memset(&rel, 0, sizeof(struct gsm_mncc));
+		rel.callref = trans->callref;
+		rel.cause = 1;
+		rel.cause_location = 1;
+		rel.cause_value = 47; /* Temporarily out of order */
+		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+		trans->callref = 0;
+		free_trans(trans);
+		return rc;
+	}
+	for (i = 0; i < 7; i++) {
+		if ((trans_id_mask & (1 << i)) == 0) {
+			trans->transaction_id = i << 4; /* flag = 0 */
+			break;
+		}
+	}
 
-	gh->proto_discr = GSM48_PDISC_CC;
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_SETUP;
 
-	msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE);
-	if (calling_subscr) {
-		bcd_lv[1] = 0xb9;
-		encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1,
-				  calling_subscr->extension);
-		msgb_tlv_put(msg, GSM48_IE_CALLING_BCD,
-			     bcd_lv[0], bcd_lv+1);
-	}
-	if (lchan->subscr) {
-		bcd_lv[1] = 0xb9;
-		encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1,
-				  lchan->subscr->extension);
-		msgb_tlv_put(msg, GSM48_IE_CALLED_BCD,
-			     bcd_lv[0], bcd_lv+1);
-	}
+	gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
 
-	DEBUGP(DCC, "B <- SETUP\n");
+	/* bearer capability */
+	if (setup->bearer_cap)
+		encode_bearer_cap(0, msg,
+			setup->bearer_cap_transfer,
+			setup->bearer_cap_mode,
+			setup->bearer_cap_coding,
+			setup->bearer_cap_radio,
+			setup->bearer_cap_speech_ctm,
+			setup->bearer_cap_speech_ver);
+	/* facility */
+	if (setup->facility)
+		encode_facility(0, msg,
+			setup->facility_info,
+			setup->facility_len);
+	/* progress */
+	if (setup->progress)
+		encode_progress(0, msg,
+			setup->progress_coding,
+			setup->progress_location,
+			setup->progress_descr);
+	/* calling party BCD number */
+	if (setup->calling)
+		encode_calling(msg,
+			setup->calling_type,
+			setup->calling_plan,
+			setup->calling_present,
+			setup->calling_screen,
+			setup->calling_number);
+	/* called party BCD number */
+	if (setup->called)
+		encode_called(msg,
+			setup->called_type,
+			setup->called_plan,
+			setup->called_number);
+	/* user-user */
+	if (setup->useruser)
+		encode_useruser(0, msg,
+			setup->useruser_proto,
+			setup->useruser_info);
+	/* redirecting party BCD number */
+	if (setup->redirecting)
+		encode_redirecting(msg,
+			setup->redirecting_type,
+			setup->redirecting_plan,
+			setup->redirecting_present,
+			setup->redirecting_screen,
+			setup->redirecting_number);
+	/* signal */
+	if (setup->signal)
+		encode_signal(msg,
+			setup->signal_value);
+	
+	new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
 
 	return gsm48_sendmsg(msg);
 }
 
-static int gsm0408_rcv_cc(struct msgb *msg)
+static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
-	u_int8_t msg_type = gh->msg_type & 0xbf;
-	struct gsm_call *call = &msg->lchan->call;
-	int rc = 0;
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc call_conf;
 
-	switch (msg_type) {
-	case GSM48_MT_CC_CALL_CONF:
-		/* Response to SETUP */
-		DEBUGP(DCC, "-> CALL CONFIRM\n");
-		/* we now need to MODIFY the channel */
-		rc = gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
-		break;
-	case GSM48_MT_CC_RELEASE_COMPL:
-		/* Answer from MS to RELEASE */
-		DEBUGP(DCC, "-> RELEASE COMPLETE (state->NULL)\n");
-		call->state = GSM_CSTATE_NULL;
-		break;
-	case GSM48_MT_CC_ALERTING:
-		rc = gsm48_cc_rx_alerting(msg);
-		break;
-	case GSM48_MT_CC_CONNECT:
-		rc = gsm48_cc_rx_connect(msg);
-		break;
-	case GSM48_MT_CC_CONNECT_ACK:
-		/* MO: Answer to CONNECT */
-		call->state = GSM_CSTATE_ACTIVE;
-		DEBUGP(DCC, "-> CONNECT_ACK (state->ACTIVE)\n");
-		break;
-	case GSM48_MT_CC_RELEASE:
-		DEBUGP(DCC, "-> RELEASE\n");
-		DEBUGP(DCC, "<- RELEASE_COMPLETE\n");
-		/* need to respond with RELEASE_COMPLETE */
-		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				     GSM48_MT_CC_RELEASE_COMPL);
-		subscr_put_channel(msg->lchan);
-                call->state = GSM_CSTATE_NULL;
-		break;
-	case GSM48_MT_CC_STATUS_ENQ:
-		rc = gsm48_cc_rx_status_enq(msg);
-		break;
-	case GSM48_MT_CC_DISCONNECT:
-		rc = gsm48_cc_rx_disconnect(msg);
-		break;
-	case GSM48_MT_CC_SETUP:
-		/* MO: wants to establish a call */
-		rc = gsm48_cc_rx_setup(msg);
-		break;
-	case GSM48_MT_CC_EMERG_SETUP:
-		DEBUGP(DCC, "-> EMERGENCY SETUP\n");
-		/* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
-		break;
-	case GSM48_MT_CC_HOLD:
-		DEBUGP(DCC, "-> HOLD (rejecting)\n");
-		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				     GSM48_MT_CC_HOLD_REJ);
-		break;
-	case GSM48_MT_CC_RETR:
-		DEBUGP(DCC, "-> RETR (rejecting)\n");
-		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-				     GSM48_MT_CC_RETR_REJ);
-		break;
-	default:
-		fprintf(stderr, "Unimplemented GSM 04.08 CC msg type 0x%02x\n",
-			msg_type);
-		break;
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x310, GSM48_T310);
+
+	memset(&call_conf, 0, sizeof(struct gsm_mncc));
+	call_conf.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+#if 0
+	/* repeat */
+	if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
+		call_conf.repeat = 1;
+	if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ))
+		call_conf.repeat = 2;
+#endif
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP))
+		call_conf.bearer_cap = decode_bearer_cap(
+			&call_conf.bearer_cap_transfer,
+			&call_conf.bearer_cap_mode,
+			&call_conf.bearer_cap_coding,
+			&call_conf.bearer_cap_radio,
+			&call_conf.bearer_cap_speech_ctm,
+			call_conf.bearer_cap_speech_ver,
+			TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE))
+		call_conf.cause = decode_cause(
+			&call_conf.cause_location,
+			&call_conf.cause_coding,
+			&call_conf.cause_rec,
+			&call_conf.cause_rec_val,
+			&call_conf.cause_value,
+			call_conf.cause_diag,
+			&call_conf.cause_diag_len,
+			TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	/* cc cap */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP))
+		call_conf.cccap = decode_cccap(
+			&call_conf.cccap_dtmf,
+			&call_conf.cccap_pcp,
+			TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
+
+	new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_CALL_CONF_IND, &call_conf);
+}
+
+static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *proceeding = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_CALL_PROC;
+
+	new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+
+	/* bearer capability */
+	if (proceeding->bearer_cap)
+		encode_bearer_cap(0, msg,
+			proceeding->bearer_cap_transfer,
+			proceeding->bearer_cap_mode,
+			proceeding->bearer_cap_coding,
+			proceeding->bearer_cap_radio,
+			proceeding->bearer_cap_speech_ctm,
+			proceeding->bearer_cap_speech_ver);
+	/* facility */
+	if (proceeding->facility)
+		encode_facility(0, msg,
+			proceeding->facility_info,
+			proceeding->facility_len);
+	/* progress */
+	if (proceeding->progress) {
+		encode_progress(0, msg,
+			proceeding->progress_coding,
+			proceeding->progress_location,
+			proceeding->progress_descr);
 	}
 
-	return rc;
+	return gsm48_sendmsg(msg);
 }
 
-/* here we pass in a msgb from the RSL->RLL.  We expect the l3 pointer to be set */
-int gsm0408_rcvmsg(struct msgb *msg)
+static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
-	u_int8_t pdisc = gh->proto_discr & 0x0f;
-	int rc = 0;
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc alerting;
 	
-	switch (pdisc) {
-	case GSM48_PDISC_CC:
-		rc = gsm0408_rcv_cc(msg);
-		break;
-	case GSM48_PDISC_MM:
-		rc = gsm0408_rcv_mm(msg);
-		break;
-	case GSM48_PDISC_RR:
-		rc = gsm0408_rcv_rr(msg);
-		break;
-	case GSM48_PDISC_SMS:
-		rc = gsm0411_rcv_sms(msg);
-		break;
-	case GSM48_PDISC_MM_GPRS:
-	case GSM48_PDISC_SM_GPRS:
-		fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n",
-			pdisc);
-		break;
-	default:
-		fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n",
-			pdisc);
-		break;
-	}
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x301, GSM48_T301);
 
-	return rc;
+	memset(&alerting, 0, sizeof(struct gsm_mncc));
+	alerting.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		alerting.facility = decode_facility(
+			alerting.facility_info,
+			sizeof(alerting.facility_info),
+			&alerting.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+
+	/* progress */
+	if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND))
+		alerting.progress = decode_progress(
+			&alerting.progress_coding,
+			&alerting.progress_location,
+			&alerting.progress_descr,
+			TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		alerting.ssversion = decode_ssversion(
+			alerting.ssversion_info,
+			sizeof(alerting.ssversion_info),
+			&alerting.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_ALERT_IND, &alerting);
 }
 
-enum chreq_type {
-	CHREQ_T_EMERG_CALL,
-	CHREQ_T_CALL_REEST_TCH_F,
-	CHREQ_T_CALL_REEST_TCH_H,
-	CHREQ_T_CALL_REEST_TCH_H_DBL,
-	CHREQ_T_SDCCH,
-	CHREQ_T_TCH_F,
-	CHREQ_T_VOICE_CALL_TCH_H,
-	CHREQ_T_DATA_CALL_TCH_H,
-	CHREQ_T_LOCATION_UPD,
-	CHREQ_T_PAG_R_ANY,
-	CHREQ_T_PAG_R_TCH_F,
-	CHREQ_T_PAG_R_TCH_FH,
-};
+static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *alerting = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-/* Section 9.1.8 / Table 9.9 */
-struct chreq {
-	u_int8_t val;
-	u_int8_t mask;
-	enum chreq_type type;
-};
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_ALERTING;
+
+	/* facility */
+	if (alerting->facility)
+		encode_facility(0, msg,
+			alerting->facility_info,
+			alerting->facility_len);
+	/* progress */
+	if (alerting->progress)
+		encode_progress(0, msg,
+			alerting->progress_coding,
+			alerting->progress_location,
+			alerting->progress_descr);
+	/* user-user */
+	if (alerting->useruser)
+		encode_useruser(0, msg,
+			alerting->useruser_proto,
+			alerting->useruser_info);
 
-/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
-static const struct chreq chreq_type_neci1[] = {
-	{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
-	{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
-	{ 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
-	{ 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
-	{ 0xe0, 0xe0, CHREQ_T_SDCCH },
-	{ 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
-	{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
-	{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
-	{ 0x10, 0xf0, CHREQ_T_SDCCH },
-	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
-	{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
-	{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
-};
+	new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+	
+	return gsm48_sendmsg(msg);
+}
 
-/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
-static const struct chreq chreq_type_neci0[] = {
+static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *progress = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_PROGRESS;
+
+	/* progress */
+	encode_progress(1, msg,
+		progress->progress_coding,
+		progress->progress_location,
+		progress->progress_descr);
+	/* user-user */
+	if (progress->useruser)
+		encode_useruser(0, msg,
+			progress->useruser_proto,
+			progress->useruser_info);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *connect = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_CONNECT;
+
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x313, GSM48_T313);
+
+	/* facility */
+	if (connect->facility)
+		encode_facility(0, msg,
+			connect->facility_info,
+			connect->facility_len);
+	/* progress */
+	if (connect->progress)
+		encode_progress(0, msg,
+			connect->progress_coding,
+			connect->progress_location,
+			connect->progress_descr);
+	/* connected number */
+	if (connect->connected)
+		encode_connected(msg,
+			connect->connected_type,
+			connect->connected_plan,
+			connect->connected_present,
+			connect->connected_screen,
+			connect->connected_number);
+	/* user-user */
+	if (connect->useruser)
+		encode_useruser(0, msg,
+			connect->useruser_proto,
+			connect->useruser_info);
+
+	new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc connect;
+
+	gsm48_stop_cc_timer(trans);
+
+	memset(&connect, 0, sizeof(struct gsm_mncc));
+	connect.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* use subscriber as connected party number */
+	if (trans->subscr) {
+		connect.connected = 1;
+		strncpy(connect.connected_number, trans->subscr->extension, sizeof(connect.connected_number)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		connect.facility = decode_facility(
+			connect.facility_info,
+			sizeof(connect.facility_info),
+			&connect.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		connect.useruser = decode_useruser(
+			&connect.useruser_proto,
+			connect.useruser_info,
+			sizeof(connect.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		connect.ssversion = decode_ssversion(
+			connect.ssversion_info,
+			sizeof(connect.ssversion_info),
+			&connect.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_SETUP_CNF, &connect);
+}
+
+
+static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm_mncc connect_ack;
+
+	gsm48_stop_cc_timer(trans);
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+	
+	memset(&connect_ack, 0, sizeof(struct gsm_mncc));
+	connect_ack.callref = trans->callref;
+	return mncc_recvmsg(trans->network, trans, MNCC_SETUP_COMPL_IND, &connect_ack);
+}
+
+static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_CONNECT_ACK;
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc disc;
+
+	gsm48_stop_cc_timer(trans);
+
+	new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
+
+	memset(&disc, 0, sizeof(struct gsm_mncc));
+	disc.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0);
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE))
+		disc.cause = decode_cause(
+			&disc.cause_location,
+			&disc.cause_coding,
+			&disc.cause_rec,
+			&disc.cause_rec_val,
+			&disc.cause_value,
+			disc.cause_diag,
+			&disc.cause_diag_len,
+			TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		disc.facility = decode_facility(
+			disc.facility_info,
+			sizeof(disc.facility_info),
+			&disc.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		disc.useruser = decode_useruser(
+			&disc.useruser_proto,
+			disc.useruser_info,
+			sizeof(disc.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		disc.ssversion = decode_ssversion(
+			disc.ssversion_info,
+			sizeof(disc.ssversion_info),
+			&disc.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &disc);
+
+}
+
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *disc = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_DISCONNECT;
+
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x306, GSM48_T306);
+
+	/* cause */
+	if (disc->cause)
+		encode_cause(1, msg,
+			disc->cause_location,
+			disc->cause_coding,
+			disc->cause_rec,
+			disc->cause_rec_val,
+			disc->cause_value,
+			disc->cause_diag,
+			disc->cause_diag_len);
+	else
+		encode_cause(1, msg,
+			1,
+			0,
+			0,
+			0,
+			31,
+			NULL,
+			0);
+
+	/* facility */
+	if (disc->facility)
+		encode_facility(0, msg,
+			disc->facility_info,
+			disc->facility_len);
+	/* progress */
+	if (disc->progress)
+		encode_progress(0, msg,
+			disc->progress_coding,
+			disc->progress_location,
+			disc->progress_descr);
+	/* user-user */
+	if (disc->useruser)
+		encode_useruser(0, msg,
+			disc->useruser_proto,
+			disc->useruser_info);
+
+	/* store disconnect cause for T306 expiry */
+	memcpy(&trans->cc_msg, disc, sizeof(struct gsm_mncc));
+
+	new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc rel;
+	int rc;
+
+	gsm48_stop_cc_timer(trans);
+
+	memset(&rel, 0, sizeof(struct gsm_mncc));
+	rel.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE))
+		rel.cause = decode_cause(
+			&rel.cause_location,
+			&rel.cause_coding,
+			&rel.cause_rec,
+			&rel.cause_rec_val,
+			&rel.cause_value,
+			rel.cause_diag,
+			&rel.cause_diag_len,
+			TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		rel.facility = decode_facility(
+			rel.facility_info,
+			sizeof(rel.facility_info),
+			&rel.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		rel.useruser = decode_useruser(
+			&rel.useruser_proto,
+			rel.useruser_info,
+			sizeof(rel.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		rel.ssversion = decode_ssversion(
+			rel.ssversion_info,
+			sizeof(rel.ssversion_info),
+			&rel.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	if (trans->state == GSM_CSTATE_RELEASE_REQ) {
+		/* release collision 5.4.5 */
+		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_CNF, &rel);
+	} else {
+		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC | trans->transaction_id,
+			     GSM48_MT_CC_RELEASE_COMPL);
+		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+	}
+
+	new_cc_state(trans, GSM_CSTATE_NULL);
+
+	trans->callref = 0;
+	free_trans(trans);
+
+	return rc;
+}
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *rel = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_RELEASE;
+
+	trans->callref = 0;
+	
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x308, GSM48_T308);
+
+	/* cause */
+	if (rel->cause)
+		encode_cause(0, msg,
+			rel->cause_location,
+			rel->cause_coding,
+			rel->cause_rec,
+			rel->cause_rec_val,
+			rel->cause_value,
+			rel->cause_diag,
+			rel->cause_diag_len);
+	/* facility */
+	if (rel->facility)
+		encode_facility(0, msg,
+			rel->facility_info,
+			rel->facility_len);
+	/* user-user */
+	if (rel->useruser)
+		encode_useruser(0, msg,
+			rel->useruser_proto,
+			rel->useruser_info);
+
+	trans->T308_second = 0;
+	memcpy(&trans->cc_msg, rel, sizeof(struct gsm_mncc));
+
+	if (trans->state != GSM_CSTATE_RELEASE_REQ)
+		new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc rel;
+	int rc = 0;
+
+	gsm48_stop_cc_timer(trans);
+
+	memset(&rel, 0, sizeof(struct gsm_mncc));
+	rel.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE))
+		rel.cause = decode_cause(
+			&rel.cause_location,
+			&rel.cause_coding,
+			&rel.cause_rec,
+			&rel.cause_rec_val,
+			&rel.cause_value,
+			rel.cause_diag,
+			&rel.cause_diag_len,
+			TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		rel.facility = decode_facility(
+			rel.facility_info,
+			sizeof(rel.facility_info),
+			&rel.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		rel.useruser = decode_useruser(
+			&rel.useruser_proto,
+			rel.useruser_info,
+			sizeof(rel.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		rel.ssversion = decode_ssversion(
+			rel.ssversion_info,
+			sizeof(rel.ssversion_info),
+			&rel.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	switch (trans->state) {
+		case GSM_CSTATE_CALL_PRESENT:
+			if (trans->callref)
+				rc = mncc_recvmsg(trans->network, trans, MNCC_REJ_IND, &rel);
+			break;
+			break;
+		case GSM_CSTATE_RELEASE_REQ:
+			if (trans->callref)
+				rc = mncc_recvmsg(trans->network, trans, MNCC_REL_CNF, &rel);
+			break;
+		default:
+			if (trans->callref) 
+				rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+	}
+
+	trans->callref = 0;
+	free_trans(trans);
+
+	return rc;
+}
+
+static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *rel = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
+
+	trans->callref = 0;
+	
+	gsm48_stop_cc_timer(trans);
+
+	/* cause */
+	if (rel->cause)
+		encode_cause(0, msg,
+			rel->cause_location,
+			rel->cause_coding,
+			rel->cause_rec,
+				rel->cause_rec_val,
+			rel->cause_value,
+			rel->cause_diag,
+			rel->cause_diag_len);
+	/* facility */
+	if (rel->facility)
+		encode_facility(0, msg,
+			rel->facility_info,
+			rel->facility_len);
+	/* user-user */
+	if (rel->useruser)
+		encode_useruser(0, msg,
+			rel->useruser_proto,
+			rel->useruser_info);
+
+	free_trans(trans);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc fac;
+
+	memset(&fac, 0, sizeof(struct gsm_mncc));
+	fac.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0);
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY))
+		fac.facility = decode_facility(
+			fac.facility_info,
+			sizeof(fac.facility_info),
+			&fac.facility_len,
+			TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS))
+		fac.ssversion = decode_ssversion(
+			fac.ssversion_info,
+			sizeof(fac.ssversion_info),
+			&fac.ssversion_len,
+			TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_FACILITY_IND, &fac);
+}
+
+static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *fac = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_FACILITY;
+
+	/* facility */
+	encode_facility(1, msg,
+		fac->facility_info,
+		fac->facility_len);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm_mncc hold;
+
+	memset(&hold, 0, sizeof(struct gsm_mncc));
+	hold.callref = trans->callref;
+	return mncc_recvmsg(trans->network, trans, MNCC_HOLD_IND, &hold);
+}
+
+static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_HOLD_ACK;
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *hold_rej = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_HOLD_REJ;
+
+	/* cause */
+	if (hold_rej->cause)
+		encode_cause(1, msg,
+			hold_rej->cause_location,
+			hold_rej->cause_coding,
+			hold_rej->cause_rec,
+			hold_rej->cause_rec_val,
+			hold_rej->cause_value,
+			hold_rej->cause_diag,
+			hold_rej->cause_diag_len);
+	else
+		encode_cause(1, msg,
+			1,
+			0,
+			0,
+			0,
+			31,
+			NULL,
+			0);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm_mncc retrieve;
+
+	memset(&retrieve, 0, sizeof(struct gsm_mncc));
+	retrieve.callref = trans->callref;
+	return mncc_recvmsg(trans->network, trans, MNCC_RETRIEVE_IND, &retrieve);
+}
+
+static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_RETR_ACK;
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *retrieve_rej = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_RETR_REJ;
+
+	/* cause */
+	if (retrieve_rej->cause)
+		encode_cause(1, msg,
+			retrieve_rej->cause_location,
+			retrieve_rej->cause_coding,
+			retrieve_rej->cause_rec,
+			retrieve_rej->cause_rec_val,
+			retrieve_rej->cause_value,
+			retrieve_rej->cause_diag,
+			retrieve_rej->cause_diag_len);
+	else
+		encode_cause(1, msg,
+			1,
+			0,
+			0,
+			0,
+			31,
+			NULL,
+			0);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc dtmf;
+
+	memset(&dtmf, 0, sizeof(struct gsm_mncc));
+	dtmf.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	/* keypad facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY))
+		decode_keypad(
+			&dtmf.keypad,
+			TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_START_DTMF_IND, &dtmf);
+}
+
+static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *dtmf = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_START_DTMF_ACK;
+
+	/* keypad */
+	if (dtmf->cause)
+		encode_keypad(msg,
+			dtmf->keypad);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *dtmf = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_START_DTMF_REJ;
+
+	/* cause */
+	if (dtmf->cause)
+		encode_cause(1, msg,
+			dtmf->cause_location,
+			dtmf->cause_coding,
+			dtmf->cause_rec,
+			dtmf->cause_rec_val,
+			dtmf->cause_value,
+			dtmf->cause_diag,
+			dtmf->cause_diag_len);
+	else
+		encode_cause(1, msg,
+			1,
+			0,
+			0,
+			0,
+			31,
+			NULL,
+			0);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK;
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm_mncc dtmf;
+
+	memset(&dtmf, 0, sizeof(struct gsm_mncc));
+	dtmf.callref = trans->callref;
+
+	return mncc_recvmsg(trans->network, trans, MNCC_STOP_DTMF_IND, &dtmf);
+}
+
+static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc modify;
+
+	memset(&modify, 0, sizeof(struct gsm_mncc));
+	modify.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP))
+		modify.bearer_cap = decode_bearer_cap(
+			&modify.bearer_cap_transfer,
+			&modify.bearer_cap_mode,
+			&modify.bearer_cap_coding,
+			&modify.bearer_cap_radio,
+			&modify.bearer_cap_speech_ctm,
+			modify.bearer_cap_speech_ver,
+			TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+
+	new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_IND, &modify);
+}
+
+static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *modify = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_MODIFY;
+
+	gsm48_start_cc_timer(trans, 0x323, GSM48_T323);
+
+	/* bearer capability */
+	encode_bearer_cap(1, msg,
+		modify->bearer_cap_transfer,
+		modify->bearer_cap_mode,
+		modify->bearer_cap_coding,
+		modify->bearer_cap_radio,
+		modify->bearer_cap_speech_ctm,
+		modify->bearer_cap_speech_ver);
+
+	new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc modify;
+
+	gsm48_stop_cc_timer(trans);
+
+	memset(&modify, 0, sizeof(struct gsm_mncc));
+	modify.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP))
+		modify.bearer_cap = decode_bearer_cap(
+			&modify.bearer_cap_transfer,
+			&modify.bearer_cap_mode,
+			&modify.bearer_cap_coding,
+			&modify.bearer_cap_radio,
+			&modify.bearer_cap_speech_ctm,
+			modify.bearer_cap_speech_ver,
+			TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_CNF, &modify);
+}
+
+static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *modify = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
+
+	/* bearer capability */
+	encode_bearer_cap(1, msg,
+		modify->bearer_cap_transfer,
+		modify->bearer_cap_mode,
+		modify->bearer_cap_coding,
+		modify->bearer_cap_radio,
+		modify->bearer_cap_speech_ctm,
+		modify->bearer_cap_speech_ver);
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc modify;
+
+	gsm48_stop_cc_timer(trans);
+
+	memset(&modify, 0, sizeof(struct gsm_mncc));
+	modify.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP))
+		modify.bearer_cap = decode_bearer_cap(
+			&modify.bearer_cap_transfer,
+			&modify.bearer_cap_mode,
+			&modify.bearer_cap_coding,
+			&modify.bearer_cap_radio,
+			&modify.bearer_cap_speech_ctm,
+			modify.bearer_cap_speech_ver,
+			TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE))
+		modify.cause = decode_cause(
+			&modify.cause_location,
+			&modify.cause_coding,
+			&modify.cause_rec,
+			&modify.cause_rec_val,
+			&modify.cause_value,
+			modify.cause_diag,
+			&modify.cause_diag_len,
+			TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_REJ, &modify);
+}
+
+static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *modify = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
+
+	/* bearer capability */
+	encode_bearer_cap(1, msg,
+		modify->bearer_cap_transfer,
+		modify->bearer_cap_mode,
+		modify->bearer_cap_coding,
+		modify->bearer_cap_radio,
+		modify->bearer_cap_speech_ctm,
+		modify->bearer_cap_speech_ver);
+	/* cause */
+	encode_cause(1, msg,
+		modify->cause_location,
+		modify->cause_coding,
+		modify->cause_rec,
+		modify->cause_rec_val,
+		modify->cause_value,
+		modify->cause_diag,
+		modify->cause_diag_len);
+
+	new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *notify = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_NOTIFY;
+
+	/* notify */
+	encode_notify(msg,
+			notify->notify);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+//	struct tlv_parsed tp;
+	struct gsm_mncc notify;
+
+	memset(&notify, 0, sizeof(struct gsm_mncc));
+	notify.callref = trans->callref;
+//	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len);
+	if (payload_len >= 1)
+		decode_notify(
+			&notify.notify,
+			gh->data);
+
+	return mncc_recvmsg(trans->network, trans, MNCC_NOTIFY_IND, &notify);
+}
+
+static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *user = arg;
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_USER_INFO;
+
+	/* user-user */
+	if (user->useruser)
+		encode_useruser(1, msg,
+			user->useruser_proto,
+			user->useruser_info);
+	/* more data */
+	if (user->more)
+		encode_more(msg);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+	struct tlv_parsed tp;
+	struct gsm_mncc user;
+
+	memset(&user, 0, sizeof(struct gsm_mncc));
+	user.callref = trans->callref;
+	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0);
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER))
+		user.useruser = decode_useruser(
+			&user.useruser_proto,
+			user.useruser_info,
+			sizeof(user.useruser_info),
+			TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	/* more data */
+	if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA))
+		user.more = 1;
+
+	return mncc_recvmsg(trans->network, trans, MNCC_USERINFO_IND, &user);
+}
+
+static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
+{
+	struct gsm_mncc *mode = arg;
+
+	return gsm48_tx_chan_mode_modify(trans->lchan, mode->lchan_mode);
+}
+
+static struct downstate {
+	u_int32_t	states;
+	int		type;
+	int		(*rout) (struct gsm_trans *trans, void *arg);
+} downstatelist[] = {
+	/* mobile originating call establishment */
+	{SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */
+	 MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc},
+	{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */
+	 MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
+	{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */
+	 MNCC_SETUP_RSP, gsm48_cc_tx_connect},
+	{SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */
+	 MNCC_PROGRESS_REQ, gsm48_cc_tx_progress},
+	/* mobile terminating call establishment */
+	{SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */
+	 MNCC_SETUP_REQ, gsm48_cc_tx_setup},
+	{SBIT(GSM_CSTATE_CONNECT_REQUEST),
+	 MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack},
+	 /* signalling during call */
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
+	 MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
+	{ALL_STATES,
+	 MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack},
+	{ALL_STATES,
+	 MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej},
+	{ALL_STATES,
+	 MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_MODIFY_REQ, gsm48_cc_tx_modify},
+	{SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+	 MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete},
+	{SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+	 MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
+	/* clearing */
+	{SBIT(GSM_CSTATE_INITIATED),
+	 MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */
+	 MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
+	 MNCC_REL_REQ, gsm48_cc_tx_release},
+	/* special */
+	{ALL_STATES,
+	 MNCC_LCHAN_MODIFY, gsm48_lchan_modify},
+};
+
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct downstate))
+
+int mncc_send(struct gsm_network *net, int msg_type, void *arg)
+{
+	int i, j, k, l, rc = 0;
+	struct gsm_trans *trans = NULL, *transt;
+	struct gsm_subscriber *subscr;
+	struct gsm_lchan *lchan = NULL, *lchant;
+	struct gsm_bts *bts = NULL;
+	struct gsm_bts_trx *trx;
+	struct gsm_bts_trx_ts *ts;
+	struct gsm_mncc *data = arg, rel;
+
+	/* handle special messages */
+	switch(msg_type) {
+	case MNCC_BRIDGE:
+		return tch_bridge(net, arg);
+	case MNCC_FRAME_DROP:
+		return tch_recv(net, arg, 0);
+	case MNCC_FRAME_RECV:
+		return tch_recv(net, arg, 1);
+	case GSM_TRAU_FRAME:
+		return tch_frame(net, arg);
+	}
+
+	memset(&rel, 0, sizeof(struct gsm_mncc));
+	rel.callref = data->callref;
+
+	/* Find callref */
+	trans = get_trans_ref(net, data->callref);
+
+	/* Callref unknown */
+	if (!trans) {
+		if (msg_type != MNCC_SETUP_REQ || !data->called_number[0]) {
+			DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) Received '%s' from MNCC with "
+					"unknown callref %d\n", data->called_number,
+					get_mncc_name(msg_type), data->callref);
+			rel.cause = 1;
+			rel.cause_location = 1;
+			rel.cause_value = 81; /* Invalid call reference */
+			return mncc_recvmsg(net, NULL, MNCC_REL_IND, &rel);
+		}
+		/* New transaction due to setup, find subscriber */
+		subscr = subscr_get_by_extension(data->called_number);
+		/* If subscriber is not found */
+		if (!subscr) {
+			DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) Received '%s' from MNCC with "
+					"unknown subscriber %s\n", data->called_number,
+					get_mncc_name(msg_type), data->called_number);
+			rel.cause = 1;
+			rel.cause_location = 1;
+			rel.cause_value = 1; /* Unknown subscriber */
+			return mncc_recvmsg(net, NULL, MNCC_REL_IND, &rel);
+		}
+		/* If subscriber is not "attached" */
+		if (!subscr->lac) {
+			DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) Received '%s' from MNCC with "
+					"detached subscriber %s\n", data->called_number,
+					get_mncc_name(msg_type), data->called_number);
+			subscr_put(subscr);
+			rel.cause = 1;
+			rel.cause_location = 1;
+			rel.cause_value = 27; /* Temporarily out of order */
+			return mncc_recvmsg(net, NULL, MNCC_REL_IND, &rel);
+		}
+		/* Create transaction */
+		if (!(trans = calloc(1, sizeof(struct gsm_trans)))) {
+			DEBUGP(DCC, "No memory for trans.\n");
+			subscr_put(subscr);
+			rel.cause = 1;
+			rel.cause_location = 1;
+			rel.cause_value = 47; /* Ressource unavailable */
+			mncc_recvmsg(net, NULL, MNCC_REL_IND, &rel);
+			return -ENOMEM;
+		}
+		trans->callref = data->callref;
+		trans->network = net;
+		trans->transaction_id = 0xff; /* unassigned */
+		llist_add_tail(&trans->entry, &net->trans_list);
+		/* Assign subscriber to transaction */
+		trans->subscr = subscr;
+		/* Find lchan */
+		for (i = 0; i < net->num_bts; i++) {
+			bts = &net->bts[i];
+			for (j = 0; j < bts->num_trx; j++) {
+				trx = &bts->trx[j];
+				for (k = 0; k < TRX_NR_TS; k++) {
+					ts = &trx->ts[k];
+					for (l = 0; l < TS_MAX_LCHAN; l++) {
+						lchant = &ts->lchan[l];
+						if (lchant->subscr == subscr) {
+							lchan = lchant;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		/* If subscriber has no lchan */
+		if (!lchan) {
+			/* find transaction with this subscriber already paging */
+			llist_for_each_entry(transt, &net->trans_list, entry) {
+				/* Transaction of our lchan? */
+				if (transt != trans && transt->subscr == subscr) {
+					DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) Received '%s' from MNCC with "
+						"unallocated channel, paging already started.\n", bts->nr, data->called_number,
+						get_mncc_name(msg_type));
+					return 0;
+				}
+			}
+			/* store setup informations until paging was successfull */
+			memcpy(&trans->cc_msg, data, sizeof(struct gsm_mncc));
+			/* start paging subscriber on all BTS with her location */
+			subscr->net = net;
+			bts = NULL;
+			do {
+				bts = gsm_bts_by_lac(net, subscr->lac, bts);
+				if (!bts)
+					break;
+				DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) Received '%s' from MNCC with "
+					"unallocated channel, paging.\n", bts->nr, data->called_number,
+					get_mncc_name(msg_type));
+				/* Trigger paging */
+				paging_request(bts, subscr, RSL_CHANNEED_TCH_F,
+					setup_trig_pag_evt, subscr);
+			} while (1);
+			return 0;
+		}
+		/* Assign lchan */
+		trans->lchan = lchan;
+		use_lchan(lchan);
+	}
+	lchan = trans->lchan;
+
+	/* if paging did not respond yet */
+	if (!lchan) {
+		DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) Received '%s' from MNCC in "
+				"paging state\n", 
+				(trans->subscr)?(trans->subscr->extension):"-",
+				get_mncc_name(msg_type));
+		rel.cause = 1;
+		rel.cause_location = 1;
+		rel.cause_value = 16; /* Normal call clearing */
+		if (msg_type == MNCC_REL_REQ)
+			rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
+		else
+			rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
+		trans->callref = 0;
+		free_trans(trans);
+		return rc;
+	}
+
+	DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) Received '%s' from MNCC in "
+			"state %d (%s)\n", lchan->ts->trx->bts->nr,
+			lchan->ts->trx->nr, lchan->ts->nr, trans->transaction_id,
+			(lchan->subscr)?(lchan->subscr->extension):"-",
+			get_mncc_name(msg_type), trans->state,
+			cc_state_names[trans->state]);
+
+	/* Find function for current state and message */
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((msg_type == downstatelist[i].type)
+		 && ((1 << trans->state) & downstatelist[i].states))
+			break;
+	if (i == DOWNSLLEN) {
+		DEBUGP(DCC, "Message unhandled at this state.\n");
+		return 0;
+	}
+
+	rc = downstatelist[i].rout(trans, arg);
+
+	return rc;
+}
+
+
+static struct datastate {
+	u_int32_t	states;
+	int		type;
+	int		(*rout) (struct gsm_trans *trans, struct msgb *msg);
+} datastatelist[] = {
+	/* mobile originating call establishment */
+	{SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
+	 GSM48_MT_CC_SETUP, gsm48_cc_rx_setup},
+	{SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
+	 GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup},
+	{SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */
+	 GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack},
+	/* mobile terminating call establishment */
+	{SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */
+	 GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf},
+	{SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */
+	 GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
+	{SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */  
+	 GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
+	 /* signalling during call */
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL),
+	 GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify},
+	{ALL_STATES,
+	 GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf},
+	{ALL_STATES,
+	 GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf},
+	{ALL_STATES,
+	 GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 GSM48_MT_CC_HOLD, gsm48_cc_rx_hold},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify},
+	{SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+	 GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete},
+	{SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+	 GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject},
+	{SBIT(GSM_CSTATE_ACTIVE),
+	 GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo},
+	/* clearing */
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
+	 GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect},
+	{ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */
+	 GSM48_MT_CC_RELEASE, gsm48_cc_rx_release},
+	{ALL_STATES, /* 5.4.3.4 */
+	 GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl},
+};
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct datastate))
+
+static int gsm0408_rcv_cc(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	u_int8_t msg_type = gh->msg_type & 0xbf;
+	u_int8_t transaction_id = (gh->proto_discr & 0xf0) ^ 0x80; /* flip */
+	struct gsm_lchan *lchan = msg->lchan;
+	struct gsm_trans *trans = NULL, *transt;
+	struct gsm_network *net = lchan->ts->trx->bts->network;
+	int i, rc = 0;
+
+	if (msg_type & 0x80) {
+		DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type);
+		return -EINVAL;
+	}
+	
+	/* Find transaction */
+	llist_for_each_entry(transt, &net->trans_list, entry) {
+		/* Transaction of our lchan? */
+		if (transt->lchan == lchan
+			&& transt->transaction_id == transaction_id) {
+			trans = transt;
+		}
+	}
+	
+	DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) Received '%s' from MS in "
+			"state %d (%s)\n", lchan->ts->trx->bts->nr,
+			lchan->ts->trx->nr, lchan->ts->nr, transaction_id,
+			(lchan->subscr)?(lchan->subscr->extension):"-",
+			cc_msg_names[msg_type], trans?(trans->state):0,
+			cc_state_names[trans?(trans->state):0]);
+
+	/* Create transaction */
+	if (!trans) {
+		DEBUGP(DCC, "Unknown transaction ID %02x, creating new trans.\n", transaction_id);
+		/* Create transaction */
+		if (!(trans = calloc(1, sizeof(struct gsm_trans)))) {
+			DEBUGP(DCC, "No memory for trans.\n");
+			rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC | transaction_id,
+				     GSM48_MT_CC_RELEASE_COMPL);
+			return -ENOMEM;
+		}
+		llist_add_tail(&trans->entry, &net->trans_list);
+		/* Assign transaction */
+		trans->callref = new_callref++;
+		trans->network = net;
+		trans->transaction_id = transaction_id;
+		trans->lchan = lchan;
+		use_lchan(lchan);
+		if (lchan->subscr) {
+			trans->subscr = lchan->subscr;
+			subscr_get(trans->subscr);
+		}
+	}
+
+	/* find function for current state and message */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((msg_type == datastatelist[i].type)
+		 && ((1 << trans->state) & datastatelist[i].states))
+			break;
+	if (i == DATASLLEN) {
+		DEBUGP(DCC, "Message unhandled at this state.\n");
+		return 0;
+	}
+
+	rc = datastatelist[i].rout(trans, msg);
+
+	return rc;
+}
+
+/* here we pass in a msgb from the RSL->RLL.  We expect the l3 pointer to be set */
+int gsm0408_rcvmsg(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	u_int8_t pdisc = gh->proto_discr & 0x0f;
+	int rc = 0;
+	
+	switch (pdisc) {
+	case GSM48_PDISC_CC:
+		rc = gsm0408_rcv_cc(msg);
+		break;
+	case GSM48_PDISC_MM:
+		rc = gsm0408_rcv_mm(msg);
+		break;
+	case GSM48_PDISC_RR:
+		rc = gsm0408_rcv_rr(msg);
+		break;
+	case GSM48_PDISC_SMS:
+		rc = gsm0411_rcv_sms(msg);
+		break;
+	case GSM48_PDISC_MM_GPRS:
+	case GSM48_PDISC_SM_GPRS:
+		fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n",
+			pdisc);
+		break;
+	default:
+		fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n",
+			pdisc);
+		break;
+	}
+
+	return rc;
+}
+
+/* Section 9.1.8 / Table 9.9 */
+struct chreq {
+	u_int8_t val;
+	u_int8_t mask;
+	enum chreq_type type;
+};
+
+/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
+static const struct chreq chreq_type_neci1[] = {
+	{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
+	{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
+	{ 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
+	{ 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
+	{ 0xe0, 0xe0, CHREQ_T_SDCCH },
+	{ 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
+	{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
+	{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
+	{ 0x10, 0xf0, CHREQ_T_SDCCH },
+	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+	{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
+	{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+};
+
+/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
+static const struct chreq chreq_type_neci0[] = {
 	{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
 	{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H },
 	{ 0xe0, 0xe0, CHREQ_T_TCH_F },
@@ -2011,3 +3833,23 @@
 	fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
 	return GSM_CHREQ_REASON_OTHER;
 }
+
+/* dequeue messages to layer 4 */
+int bsc_upqueue(struct gsm_network *net)
+{
+	struct gsm_mncc *mncc;
+	struct msgb *msg;
+	int work = 0;
+
+	if (net)
+		while ((msg = msgb_dequeue(&net->upqueue))) {
+			mncc = (struct gsm_mncc *)msg->data;
+			if (net->mncc_recv)
+				net->mncc_recv(net, mncc->msg_type, mncc);
+			work = 1; /* work done */
+		}
+
+	return work;
+}
+
+
diff -Nur openbsc.old/src/gsm_data.c openbsc.new/src/gsm_data.c
--- openbsc.old/src/gsm_data.c	2009-05-31 12:59:25.000000000 +0200
+++ openbsc.new/src/gsm_data.c	2009-06-06 12:14:00.000000000 +0200
@@ -84,7 +84,8 @@
 }
 
 struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
-				     u_int16_t country_code, u_int16_t network_code)
+				     u_int16_t country_code, u_int16_t network_code,
+				     int (*mncc_recv)(struct gsm_network *, int, void *))
 {
 	int i;
 	struct gsm_network *net;
@@ -101,6 +102,11 @@
 	net->network_code = network_code;
 	net->num_bts = num_bts;
 
+	INIT_LLIST_HEAD(&net->trans_list);
+	INIT_LLIST_HEAD(&net->upqueue);
+
+	net->mncc_recv = mncc_recv;
+
 	for (i = 0; i < num_bts; i++) {
 		struct gsm_bts *bts = &net->bts[i];
 		int j;
@@ -118,7 +124,7 @@
 			trx->bts = bts;
 			trx->nr = j;
 
-			for (k = 0; k < 8; k++) {
+			for (k = 0; k < TRX_NR_TS; k++) {
 				struct gsm_bts_trx_ts *ts = &trx->ts[k];
 				int l;
 				
diff -Nur openbsc.old/src/ipaccess-config.c openbsc.new/src/ipaccess-config.c
--- openbsc.old/src/ipaccess-config.c	2009-05-23 17:32:17.000000000 +0200
+++ openbsc.new/src/ipaccess-config.c	2009-06-06 12:14:00.000000000 +0200
@@ -170,7 +170,7 @@
 		exit(2);
 	}
 
-	gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1);
+	gsmnet = gsm_network_init(1, GSM_BTS_TYPE_NANOBTS_900, 1, 1, NULL);
 	if (!gsmnet)
 		exit(1);
 
diff -Nur openbsc.old/src/mncc.c openbsc.new/src/mncc.c
--- openbsc.old/src/mncc.c	1970-01-01 01:00:00.000000000 +0100
+++ openbsc.new/src/mncc.c	2009-06-06 12:14:00.000000000 +0200
@@ -0,0 +1,354 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/debug.h>
+#include <openbsc/mncc.h>
+
+static struct mncc_names {
+	char *name;
+	int value;
+} mncc_names[] = {
+	{"MNCC_SETUP_REQ",	0x0101},
+	{"MNCC_SETUP_IND",	0x0102},
+	{"MNCC_SETUP_RSP",	0x0103},
+	{"MNCC_SETUP_CNF",	0x0104},
+	{"MNCC_SETUP_COMPL_REQ",0x0105},
+	{"MNCC_SETUP_COMPL_IND",0x0106},
+	{"MNCC_CALL_CONF_IND",	0x0107},
+	{"MNCC_CALL_PROC_REQ",	0x0108},
+	{"MNCC_PROGRESS_REQ",	0x0109},
+	{"MNCC_ALERT_REQ",	0x010a},
+	{"MNCC_ALERT_IND",	0x010b},
+	{"MNCC_NOTIFY_REQ",	0x010c},
+	{"MNCC_NOTIFY_IND",	0x010d},
+	{"MNCC_DISC_REQ",	0x010e},
+	{"MNCC_DISC_IND",	0x010f},
+	{"MNCC_REL_REQ",	0x0110},
+	{"MNCC_REL_IND",	0x0111},
+	{"MNCC_REL_CNF",	0x0112},
+	{"MNCC_FACILITY_REQ",	0x0113},
+	{"MNCC_FACILITY_IND",	0x0114},
+	{"MNCC_START_DTMF_IND",	0x0115},
+	{"MNCC_START_DTMF_RSP",	0x0116},
+	{"MNCC_START_DTMF_REJ",	0x0117},
+	{"MNCC_STOP_DTMF_IND",	0x0118},
+	{"MNCC_STOP_DTMF_RSP",	0x0119},
+	{"MNCC_MODIFY_REQ",	0x011a},
+	{"MNCC_MODIFY_IND",	0x011b},
+	{"MNCC_MODIFY_RSP",	0x011c},
+	{"MNCC_MODIFY_CNF",	0x011d},
+	{"MNCC_MODIFY_REJ",	0x011e},
+	{"MNCC_HOLD_IND",	0x011f},
+	{"MNCC_HOLD_CNF",	0x0120},
+	{"MNCC_HOLD_REJ",	0x0121},
+	{"MNCC_RETRIEVE_IND",	0x0122},
+	{"MNCC_RETRIEVE_CNF",	0x0123},
+	{"MNCC_RETRIEVE_REJ",	0x0124},
+	{"MNCC_USERINFO_REQ",	0x0125},
+	{"MNCC_USERINFO_IND",	0x0126},
+	{"MNCC_REJ_REQ",	0x0127},
+	{"MNCC_REJ_IND",	0x0128},
+
+	{"MNCC_BRIDGE",		0x0200},
+	{"MNCC_FRAME_RECV",	0x0201},
+	{"MNCC_FRAME_DROP",	0x0202},
+	{"MNCC_LCHAN_MODIFY",	0x0203},
+
+	{"GSM_TRAU_FRAME",	0x0300},
+
+	{NULL, 0}
+};
+
+static LLIST_HEAD(call_list);
+
+static u_int32_t new_callref = 0x00000001;
+
+char *get_mncc_name(int value)
+{
+	int i;
+
+	for (i = 0; mncc_names[i].name; i++) {
+		if (mncc_names[i].value == value)
+			return mncc_names[i].name;
+	}
+
+	return "MNCC_Unknown";
+}
+
+static void free_call(struct gsm_call *call)
+{
+	llist_del(&call->entry);
+	DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
+	free(call);
+}
+
+
+struct gsm_call *get_call_ref(u_int32_t callref)
+{
+	struct gsm_call *callt;
+
+	llist_for_each_entry(callt, &call_list, entry) {
+		if (callt->callref == callref) {
+			return callt;
+		}
+	}
+	return NULL;
+}
+
+/* on incomming call, look up database and send setup to remote subscr. */
+static int mncc_setup_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *setup)
+{
+	struct gsm_mncc mncc;
+	struct gsm_call *remote;
+
+	/* already have remote call */
+	if (call->remote_ref) {
+		return 0;
+	}
+	
+	/* create remote call */
+	if (!(remote = calloc(1, sizeof(struct gsm_call)))) {
+		memset(&mncc, 0, sizeof(struct gsm_mncc));
+		mncc.callref = call->callref;
+		mncc.cause = 1;
+		mncc.cause_location = 1;
+		mncc.cause_value = 47;
+		mncc_send(call->net, MNCC_REJ_REQ, &mncc);
+		free_call(call);
+		return 0;
+	}
+	llist_add_tail(&remote->entry, &call_list);
+	remote->net = call->net;
+	remote->callref = new_callref++;
+	DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", call->callref, remote->callref);
+
+	/* link remote call */
+	call->remote_ref = remote->callref;
+	remote->remote_ref = call->callref;
+
+	/* modify mode */
+	memset(&mncc, 0, sizeof(struct gsm_mncc));
+	mncc.callref = call->callref;
+	mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
+	DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
+	mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
+
+	/* send call proceeding */
+	memset(&mncc, 0, sizeof(struct gsm_mncc));
+	mncc.callref = call->callref;
+	DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
+	mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
+
+	/* send setup to remote */
+//	setup->signal = 1;
+//	setup->signal_value = GSM48_SIGNAL_DIALTONE;
+	setup->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
+	return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
+}
+
+static int mncc_alert_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *alert)
+{
+	struct gsm_call *remote;
+
+	/* send alerting to remote */
+	if (!(remote = get_call_ref(call->remote_ref)))
+		return 0;
+	alert->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
+	return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
+}
+
+static int mncc_notify_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *notify)
+{
+	struct gsm_call *remote;
+
+	/* send notify to remote */
+	if (!(remote = get_call_ref(call->remote_ref)))
+		return 0;
+	notify->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
+	return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
+}
+
+static int mncc_setup_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *connect)
+{
+	struct gsm_mncc connect_ack;
+	struct gsm_call *remote;
+	u_int32_t refs[2];
+
+	/* acknowledge connect */
+	memset(&connect_ack, 0, sizeof(struct gsm_mncc));
+	connect_ack.callref = call->callref;
+	DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
+	mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
+
+	/* send connect message to remote */
+	if (!(remote = get_call_ref(call->remote_ref)))
+		return 0;
+	connect->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
+	mncc_send(remote->net, MNCC_SETUP_RSP, connect);
+
+	/* bridge tch */
+	refs[0] = call->callref;
+	refs[1] = call->remote_ref;
+	DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
+	return mncc_send(call->net, MNCC_BRIDGE, refs);
+}
+
+static int mncc_disc_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *disc)
+{
+	struct gsm_call *remote;
+
+	/* send release */
+	DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", call->callref, disc->cause_value);
+	mncc_send(call->net, MNCC_REL_REQ, disc);
+
+	/* send disc to remote */
+	if (!(remote = get_call_ref(call->remote_ref))) {
+		return 0;
+	}
+	disc->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", remote->callref, disc->cause_value);
+	return mncc_send(remote->net, MNCC_DISC_REQ, disc);
+}
+
+static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
+{
+	struct gsm_call *remote;
+
+	/* send release to remote */
+	if (!(remote = get_call_ref(call->remote_ref))) {
+		free_call(call);
+		return 0;
+	}
+	rel->callref = remote->callref;
+	DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", call->callref, rel->cause_value);
+	mncc_send(remote->net, MNCC_REL_REQ, rel);
+
+	free_call(call);
+
+	return 0;
+}
+
+static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
+{
+	free_call(call);
+	return 0;
+}
+
+int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
+{
+	struct gsm_mncc *data = arg;
+	int callref;
+	struct gsm_call *call = NULL, *callt;
+	int rc = 0;
+
+	/* Special messages */
+	switch(msg_type) {
+	}
+	
+	/* find callref */
+	callref = data->callref;
+	llist_for_each_entry(callt, &call_list, entry) {
+		if (callt->callref == callref) {
+			call = callt;
+			break;
+		}
+	}
+
+	/* create callref, if setup is received */
+	if (!call) {
+		if (msg_type != MNCC_SETUP_IND)
+			return 0; /* drop */
+		/* create call */
+		if (!(call = calloc(1, sizeof(struct gsm_call)))) {
+			struct gsm_mncc rel;
+			
+			memset(&rel, 0, sizeof(struct gsm_mncc));
+			rel.callref = callref;
+			rel.cause = 1;
+			rel.cause_location = 1;
+			rel.cause_value = 47;
+			mncc_send(net, MNCC_REL_REQ, &rel);
+			return 0;
+		}
+		llist_add_tail(&call->entry, &call_list);
+		call->net = net;
+		call->callref = callref;
+		DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
+	}
+
+	DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, get_mncc_name(msg_type));
+
+	switch(msg_type) {
+	case MNCC_SETUP_IND:
+		rc = mncc_setup_ind(call, msg_type, arg);
+		break;
+	case MNCC_SETUP_CNF:
+		rc = mncc_setup_cnf(call, msg_type, arg);
+		break;
+	case MNCC_SETUP_COMPL_IND:
+		break;
+	case MNCC_CALL_CONF_IND:
+		/* we now need to MODIFY the channel */
+		data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
+		mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
+		break;
+	case MNCC_ALERT_IND:
+		rc = mncc_alert_ind(call, msg_type, arg);
+		break;
+	case MNCC_NOTIFY_IND:
+		rc = mncc_notify_ind(call, msg_type, arg);
+		break;
+	case MNCC_DISC_IND:
+		rc = mncc_disc_ind(call, msg_type, arg);
+		break;
+	case MNCC_REL_IND:
+	case MNCC_REJ_IND:
+		rc = mncc_rel_ind(call, msg_type, arg);
+		break;
+	case MNCC_REL_CNF:
+		rc = mncc_rel_cnf(call, msg_type, arg);
+		break;
+	case MNCC_FACILITY_IND:
+		break;
+	case MNCC_START_DTMF_IND:
+		break;
+	case MNCC_STOP_DTMF_IND:
+		break;
+	case MNCC_MODIFY_IND:
+		data->cause = 1;
+		data->cause_location = 1;
+		data->cause_value = 79;
+		DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", call->callref, data->cause_value);
+		rc = mncc_send(net, MNCC_MODIFY_REJ, data);
+		break;
+	case MNCC_MODIFY_CNF:
+		break;
+	case MNCC_HOLD_IND:
+		data->cause = 1;
+		data->cause_location = 1;
+		data->cause_value = 79;
+		DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", call->callref, data->cause_value);
+		rc = mncc_send(net, MNCC_HOLD_REJ, data);
+		break;
+	case MNCC_RETRIEVE_IND:
+		data->cause = 1;
+		data->cause_location = 1;
+		data->cause_value = 79;
+		DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", call->callref, data->cause_value);
+		rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
+		break;
+	default:
+		DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
+		break;
+	}
+
+	return rc;
+}
+
diff -Nur openbsc.old/src/paging.c openbsc.new/src/paging.c
--- openbsc.old/src/paging.c	2009-06-06 10:58:30.000000000 +0200
+++ openbsc.new/src/paging.c	2009-06-06 13:15:11.000000000 +0200
@@ -221,6 +221,7 @@
 		return;
 	}
 
+	DEBUGP(DPAG, "Start paging on bts %d.\n", bts->nr);
 	req = (struct gsm_paging_request *)malloc(sizeof(*req));
 	memset(req, 0, sizeof(*req));
 	req->subscr = subscr_get(subscr);
@@ -247,9 +248,12 @@
 	llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
 				 entry) {
 		if (req->subscr == subscr) {
-			if (lchan && req->cbfn)
+			if (lchan && req->cbfn) {
+				DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
 				req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
 					  NULL, lchan, req->cbfn_param);
+			} else
+				DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr);
 			paging_remove_request(&bts->paging, req);
 			break;
 		}
diff -Nur openbsc.old/src/trau_mux.c openbsc.new/src/trau_mux.c
--- openbsc.old/src/trau_mux.c	2009-05-31 12:59:25.000000000 +0200
+++ openbsc.new/src/trau_mux.c	2009-06-06 12:01:57.000000000 +0200
@@ -145,6 +145,9 @@
 	u_int8_t trau_bits_out[TRAU_FRAME_BITS];
 	struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
 	struct subch_mux *mx;
+	struct upqueue_entry *ue;
+	struct msgb *msg;
+	struct gsm_trau_frame *frame;
 	int rc;
 
 	/* decode TRAU, change it to downlink, re-encode */
@@ -152,8 +155,23 @@
 	if (rc)
 		return rc;
 
-	if (!dst_e1_ss)
-		return -EINVAL;
+	if (!dst_e1_ss) {
+		/* frame shall be sent to upqueue */
+		if (!(ue = lookup_trau_upqueue(src_e1_ss)))
+			return -EINVAL;
+		if (!ue->callref)
+			return -EINVAL;
+		msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf));
+		if (!msg)
+			return -ENOMEM;
+		frame = (struct gsm_trau_frame *)msg->data;
+		frame->msg_type = GSM_TRAU_FRAME;
+		frame->callref = ue->callref;
+		memcpy(frame->data, &tf, sizeof(tf));
+		msgb_enqueue(&ue->net->upqueue, msg);
+
+		return 0;
+	}
 
 	mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
 	if (!mx)
diff -Nur openbsc.old/src/vty_interface.c openbsc.new/src/vty_interface.c
--- openbsc.old/src/vty_interface.c	2009-05-31 12:59:25.000000000 +0200
+++ openbsc.new/src/vty_interface.c	2009-06-06 12:14:00.000000000 +0200
@@ -326,6 +326,8 @@
 		vty_out(vty, "  No Subscriber%s", VTY_NEWLINE);
 }
 
+#if 0
+TODO: callref and remote callref of call must be resolved to get gsm_trans object
 static void call_dump_vty(struct vty *vty, struct gsm_call *call)
 {
 	vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
@@ -349,6 +351,7 @@
 	} else
 		vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
 }
+#endif
 
 DEFUN(show_lchan,
       show_lchan_cmd,
