reworked MNCC codebase

This is Harald's reworked MNCC base, slowly heading towards integration
into master.  The key changes are:
* provide much more structure to the data in gsm_mncc
* encode_* and decode_* functions now take a structure rather than tons
  of individual arguments (whose order nobody can remember)
* make sure we don't have copies of the same code everywhere by introducing
  mncc_set_cause() and mncc_release_ind()
* save horizontal screen space if possible
* make sure we break lines > 80 characters
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 03922ac..d83d67b 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -540,10 +540,59 @@
 void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
 			  int len);
 
+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);
@@ -552,7 +601,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);
 
@@ -563,6 +611,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 --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 1fb80a2..cc92a9e 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -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]))
 
@@ -59,66 +88,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 */
+/* One transaction */
+struct gsm_trans {
+	/* Entry in list of all transactions */
+	struct llist_head entry;
 
-	/* the 'local' channel */
-	struct gsm_lchan *local_lchan;
-	/* the 'remote' channel */
-	struct gsm_lchan *remote_lchan;
+	/* Network */
+	struct gsm_network *network;
 
-	/* the 'remote' subscriber */
-	struct gsm_subscriber *called_subscr;
+	/* The current transaction ID */
+	u_int8_t transaction_id;
+	
+	/* The LCHAN that we're part of */
+	struct gsm_lchan *lchan;
+
+	/* To whom we are allocated at the moment */
+	struct gsm_subscriber *subscr;
+
+	/* reference */
+	u_int32_t callref;
+
+	/* current call state */
+	int state;
+
+	/* 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 */
 };
 
 
-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,
-};
-
 /* Network Management State */
 struct gsm_nm_state {
 	u_int8_t operational;
@@ -162,12 +166,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
 	 */
@@ -355,6 +353,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];
@@ -372,7 +375,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 --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 780d8ed..dc4f6d7 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -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 --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
new file mode 100644
index 0000000..c04a81f
--- /dev/null
+++ b/openbsc/include/openbsc/mncc.h
@@ -0,0 +1,210 @@
+/* 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
+
+#define	MNCC_F_BEARER_CAP	0x0001
+#define MNCC_F_CALLED		0x0002
+#define MNCC_F_CALLING		0x0004
+#define MNCC_F_REDIRECTING	0x0008
+#define MNCC_F_CONNECTED	0x0010
+#define MNCC_F_CAUSE		0x0020
+#define MNCC_F_USERUSER		0x0040
+#define MNCC_F_PROGRESS		0x0080
+#define MNCC_F_EMERGENCY	0x0100
+#define MNCC_F_FACILITY		0x0200
+#define MNCC_F_SSVERSION	0x0400
+#define MNCC_F_CCCAP		0x0800
+#define MNCC_F_KEYPAD		0x1000
+#define MNCC_F_SIGNAL		0x2000
+
+struct gsm_mncc_bearer_cap {
+	int		transfer;
+	int 		mode;
+	int		coding;
+	int		radio;
+	int		speech_ctm;
+	int		speech_ver[8];
+};
+
+struct gsm_mncc_number {
+	int 		type;
+	int 		plan;
+	int		present;
+	int		screen;
+	char		number[33];
+};
+
+struct gsm_mncc_cause {
+	int		location;
+	int		coding;
+	int		rec;
+	int		rec_val;
+	int		value;
+	int		diag_len;
+	char		diag[32];
+};
+
+struct gsm_mncc_useruser {
+	int		proto;
+	char		info[GSM_MAX_USERUSER + 1]; /* + termination char */
+};
+
+struct gsm_mncc_progress {
+	int		coding;
+	int		location;
+	int 		descr;
+};
+
+struct gsm_mncc_facility {
+	int		len;
+	char		info[GSM_MAX_FACILITY];
+};
+
+struct gsm_mncc_ssversion {
+	int		len;
+	char		info[GSM_MAX_SSVERSION];
+};
+
+struct gsm_mncc_cccap {
+	int		dtmf;
+	int		pcp;
+};
+
+
+struct gsm_mncc {
+	/* context based information */
+	u_int32_t	msg_type;
+	u_int32_t	callref;
+
+	/* which fields are present */
+	u_int32_t	fields;
+
+	/* data derived informations (MNCC_F_ based) */
+	struct gsm_mncc_bearer_cap	bearer_cap;
+	struct gsm_mncc_number		called;
+	struct gsm_mncc_number		calling;
+	struct gsm_mncc_number		redirecting;
+	struct gsm_mncc_number		connected;
+	struct gsm_mncc_cause		cause;
+	struct gsm_mncc_progress	progress;
+	struct gsm_mncc_useruser	useruser;
+	struct gsm_mncc_facility	facility;
+	struct gsm_mncc_cccap		cccap;
+	struct gsm_mncc_ssversion	ssversion;
+	struct	{
+		int		sup;
+		int		inv;
+	} clir;
+	int		signal;
+
+	/* data derived information, not MNCC_F based */
+	int		keypad;
+	int		more;
+	int		notify; /* 0..127 */
+	int		emergency;
+
+	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);
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
+
+#endif
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index fbbbfdc..f4b8e55 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -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 \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 656feb4..386b865 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -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 --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 3888407..e41b0e3 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -995,7 +995,7 @@
 	}
 
 	/* 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;
 
@@ -1188,6 +1188,7 @@
 	signal(SIGABRT, &signal_handler);
 
 	while (1) {
+		bsc_upqueue(gsmnet);
 		bsc_select_main(0);
 	}
 }
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 77a4f57..96632bc 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -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 --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 90b88df..3aaa0f5 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -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)
@@ -186,6 +288,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;
@@ -206,6 +309,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)
 {
@@ -265,6 +370,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;
 
@@ -275,6 +382,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;
 }
 
@@ -411,8 +524,7 @@
 }
 
 /* decode 'bearer capability' */
-static int decode_bearer_cap(int *transfer, int *mode, int *coding,
-			     int *radio, int *speech_ctm, int *speech_ver,
+static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
 			     const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
@@ -421,13 +533,13 @@
 	if (in_len < 1)
 		return -EINVAL;
 
-	speech_ver[0] = -1; /* end of list, of maximum 7 values */
+	bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
 
 	/* octet 3 */
-	*transfer = lv[1] & 0x07;
-	*mode = (lv[1] & 0x08) >> 3;
-	*coding = (lv[1] & 0x10) >> 4;
-	*radio = (lv[1] & 0x60) >> 5;
+	bcap->transfer = lv[1] & 0x07;
+	bcap->mode = (lv[1] & 0x08) >> 3;
+	bcap->coding = (lv[1] & 0x10) >> 4;
+	bcap->radio = (lv[1] & 0x60) >> 5;
 
 	i = 1;
 	s = 0;
@@ -435,10 +547,10 @@
 		i++; /* octet 3a etc */
 		if (in_len < i)
 			return 0;
-		speech_ver[s++] = lv[i] & 0x0f;
-		speech_ver[s] = -1; /* end of list */
+		bcap->speech_ver[s++] = lv[i] & 0x0f;
+		bcap->speech_ver[s] = -1; /* end of list */
 		if (i == 2) /* octet 3a */
-			*speech_ctm = (lv[i] & 0x20) >> 5;
+			bcap->speech_ctm = (lv[i] & 0x20) >> 5;
 		if (s == 7) /* maximum speech versions + end of list */
 			return 0;
 	}
@@ -447,24 +559,23 @@
 }
 
 /* encode 'bearer capability' */
-static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer,
-			     int mode, int coding, int radio, int speech_ctm,
-			     int *speech_ver)
+static int encode_bearer_cap(struct msgb *msg, int lv_only,
+			     const struct gsm_mncc_bearer_cap *bcap)
 {
 	u_int8_t lv[32 + 1];
 	int i, s;
 
-	lv[1] = transfer;
-	lv[1] |= mode << 3;
-	lv[1] |= coding << 4;
-	lv[1] |= radio << 5;
+	lv[1] = bcap->transfer;
+	lv[1] |= bcap->mode << 3;
+	lv[1] |= bcap->coding << 4;
+	lv[1] |= bcap->radio << 5;
 
 	i = 1;
-	for (s = 0; speech_ver[s] >= 0; s++) {
+	for (s = 0; bcap->speech_ver[s] >= 0; s++) {
 		i++; /* octet 3a etc */
-		lv[i] = speech_ver[s];
+		lv[i] = bcap->speech_ver[s];
 		if (i == 2) /* octet 3a */
-			lv[i] |= speech_ctm << 5;
+			lv[i] |= bcap->speech_ctm << 5;
 	}
 	lv[i] |= 0x80; /* last IE of octet 3 etc */
 
@@ -478,7 +589,7 @@
 }
 
 /* decode 'call control cap' */
-static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv)
+static int decode_cccap(struct gsm_mncc_cccap *ccap, const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
 
@@ -486,15 +597,15 @@
 		return -EINVAL;
 
 	/* octet 3 */
-	*dtmf = lv[1] & 0x01;
-	*pcp = (lv[1] & 0x02) >> 1;
+	ccap->dtmf = lv[1] & 0x01;
+	ccap->pcp = (lv[1] & 0x02) >> 1;
 	
 	return 0;
 }
 
 /* decode 'called party BCD number' */
-static int decode_called(int *type, int *plan, char *number,
-			 int number_len, const u_int8_t *lv)
+static int decode_called(struct gsm_mncc_number *called,
+			 const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
 
@@ -502,27 +613,28 @@
 		return -EINVAL;
 
 	/* octet 3 */
-	*plan = lv[1] & 0x0f;
-	*type = (lv[1] & 0x70) >> 4;
+	called->plan = lv[1] & 0x0f;
+	called->type = (lv[1] & 0x70) >> 4;
 
 	/* octet 4..N */
-	decode_bcd_number(number, number_len, lv, 1);
+	decode_bcd_number(called->number, sizeof(called->number), lv, 1);
 	
 	return 0;
 }
 
 /* encode 'called party BCD number' */
-static int encode_called(struct msgb *msg, int type, int plan, char *number)
+static int encode_called(struct msgb *msg,
+			 const struct gsm_mncc_number *called)
 {
 	u_int8_t lv[18];
 	int ret;
 
 	/* octet 3 */
-	lv[1] = plan;
-	lv[1] |= type << 4;
+	lv[1] = called->plan;
+	lv[1] |= called->type << 4;
 
 	/* octet 4..N, octet 2 */
-	ret = encode_bcd_number(lv, sizeof(lv), 1, number);
+	ret = encode_bcd_number(lv, sizeof(lv), 1, called->number);
 	if (ret < 0)
 		return ret;
 
@@ -532,28 +644,28 @@
 }
 
 /* encode callerid of various IEs */
-static int encode_callerid(struct msgb *msg, int ie, int type, int plan,
-			   int present, int screen, char *number)
+static int encode_callerid(struct msgb *msg, int ie, 
+			   const struct gsm_mncc_number *callerid)
 {
 	u_int8_t lv[13];
 	int h_len = 1;
 	int ret;
 
 	/* octet 3 */
-	lv[1] = plan;
-	lv[1] |= type << 4;
+	lv[1] = callerid->plan;
+	lv[1] |= callerid->type << 4;
 
-	if (present || screen) {
+	if (callerid->present || callerid->screen) {
 		/* octet 3a */
-		lv[2] = screen;
-		lv[2] |= present << 5;
+		lv[2] = callerid->screen;
+		lv[2] |= callerid->present << 5;
 		lv[2] |= 0x80;
 		h_len++;
 	} else
 		lv[1] |= 0x80;
 
 	/* octet 4..N, octet 2 */
-	ret = encode_bcd_number(lv, sizeof(lv), h_len, number);
+	ret = encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
 	if (ret < 0)
 		return ret;
 
@@ -563,8 +675,7 @@
 }
 
 /* decode 'cause' */
-static int decode_cause(int *location, int *coding, int *rec, int *rec_val,
-			int *value, u_char *diag, u_int *diag_len,
+static int decode_cause(struct gsm_mncc_cause *cause,
 			const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
@@ -573,25 +684,25 @@
 	if (in_len < 2)
 		return -EINVAL;
 
-	*diag_len = 0;
+	cause->diag_len = 0;
 
 	/* octet 3 */
-	*location = lv[1] & 0x0f;
-	*coding = (lv[1] & 0x60) >> 5;
+	cause->location = lv[1] & 0x0f;
+	cause->coding = (lv[1] & 0x60) >> 5;
 	
 	i = 1;
 	if (!(lv[i] & 0x80)) {
 		i++; /* octet 3a */
 		if (in_len < i+1)
 			return 0;
-		*rec = 1;
-		*rec_val = lv[i] & 0x7f;
+		cause->rec = 1;
+		cause->rec_val = lv[i] & 0x7f;
 		
 	}
 	i++;
 
 	/* octet 4 */
-	*value = lv[i] & 0x7f;
+	cause->value = lv[i] & 0x7f;
 	i++;
 
 	if (in_len < i) /* no diag */
@@ -601,42 +712,41 @@
 		return 0;
 
 	/* octet 5-N */
-	memcpy(diag, lv + i, in_len - (i-1));
-	*diag_len = in_len - (i-1);
+	memcpy(cause->diag, lv + i, in_len - (i-1));
+	cause->diag_len = in_len - (i-1);
 
 	return 0;
 }
 
 /* encode 'cause' */
-static int encode_cause(int lv_only, struct msgb *msg, int location,
-			int coding, int rec, int rec_val, int value,
-			u_char *diag, u_int diag_len)
+static int encode_cause(struct msgb *msg, int lv_only,
+			const struct gsm_mncc_cause *cause)
 {
 	u_int8_t lv[32+4];
 	int i;
 
-	if (diag_len > 32)
+	if (cause->diag_len > 32)
 		return -EINVAL;
 
 	/* octet 3 */
-	lv[1] = location;
-	lv[1] |= coding << 5;
+	lv[1] = cause->location;
+	lv[1] |= cause->coding << 5;
 
 	i = 1;
-	if (rec) {
+	if (cause->rec) {
 		i++; /* octet 3a */
-		lv[i] = rec_val;
+		lv[i] = cause->rec_val;
 	}
 	lv[i] |= 0x80; /* end of octet 3 */
 
 	/* octet 4 */
 	i++;
-	lv[i] = 0x80 | value;
+	lv[i] = 0x80 | cause->value;
 
 	/* octet 5-N */
-	if (diag_len) {
-		memcpy(lv + i, diag, diag_len);
-		i += diag_len;
+	if (cause->diag_len) {
+		memcpy(lv + i, cause->diag, cause->diag_len);
+		i += cause->diag_len;
 	}
 
 	lv[0] = i;
@@ -649,31 +759,28 @@
 }
 
 /* encode 'calling number' */
-static int encode_calling(struct msgb *msg, int type, int plan, int present,
-			  int screen, char *number)
+static int encode_calling(struct msgb *msg, 
+			  const struct gsm_mncc_number *calling)
 {
-	return encode_callerid(msg, GSM48_IE_CALLING_BCD, type, plan,
-				present, screen, number);
+	return encode_callerid(msg, GSM48_IE_CALLING_BCD, calling);
 }
 
 /* encode 'connected number' */
-static int encode_connected(struct msgb *msg, int type, int plan, int present,
-			    int screen, char *number)
+static int encode_connected(struct msgb *msg, 
+			    const struct gsm_mncc_number *connected)
 {
-	return encode_callerid(msg, GSM48_IE_CONN_BCD, type, plan,
-				present, screen, number);
+	return encode_callerid(msg, GSM48_IE_CONN_BCD, connected);
 }
 
 /* encode 'redirecting number' */
-static int encode_redirecting(struct msgb *msg, int type, int plan, int present,
-			      int screen, char *number)
+static int encode_redirecting(struct msgb *msg,
+			      const struct gsm_mncc_number *redirecting)
 {
-	return encode_callerid(msg, GSM48_IE_REDIR_BCD, type, plan,
-				present, screen, number);
+	return encode_callerid(msg, GSM48_IE_REDIR_BCD, redirecting);
 }
 
 /* decode 'facility' */
-static int decode_facility(char *info, int info_len, int *len,
+static int decode_facility(struct gsm_mncc_facility *facility,
 			   const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
@@ -681,25 +788,26 @@
 	if (in_len < 1)
 		return -EINVAL;
 
-	if (in_len > info_len)
+	if (in_len > sizeof(facility->info))
 		return -EINVAL;
 
-	memcpy(info, lv+1, in_len);
-	*len = in_len;
+	memcpy(facility->info, lv+1, in_len);
+	facility->len = in_len;
 
 	return 0;
 }
 
 /* encode 'facility' */
-static int encode_facility(int lv_only, struct msgb *msg, char *info, int len)
+static int encode_facility(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_facility *facility)
 {
 	u_int8_t lv[GSM_MAX_FACILITY + 1];
 
-	if (len < 1 || len > GSM_MAX_FACILITY)
+	if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
 		return -EINVAL;
 
-	memcpy(lv+1, info, len);
-	lv[0] = len;
+	memcpy(lv+1, facility->info, facility->len);
+	lv[0] = facility->len;
 	if (lv_only)
 		msgb_lv_put(msg, lv[0], lv+1);
 	else
@@ -754,7 +862,7 @@
 }
 
 /* decode 'progress' */
-static int decode_progress(int *coding, int *location, int *descr,
+static int decode_progress(struct gsm_mncc_progress *progress,
 			   const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
@@ -762,22 +870,22 @@
 	if (in_len < 2)
 		return -EINVAL;
 
-	*coding = (lv[1] & 0x60) >> 5;
-	*location = lv[1] & 0x0f;
-	*descr = lv[2] & 0x7f;
+	progress->coding = (lv[1] & 0x60) >> 5;
+	progress->location = lv[1] & 0x0f;
+	progress->descr = lv[2] & 0x7f;
 	
 	return 0;
 }
 
 /* encode 'progress' */
-static int encode_progress(int lv_only, struct msgb *msg, int coding,
-			   int location, int descr)
+static int encode_progress(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_progress *p)
 {
 	u_int8_t lv[3];
 
 	lv[0] = 2;
-	lv[1] = 0x80 | ((coding & 0x3) << 5) | (location & 0xf);
-	lv[2] = 0x80 | (descr & 0x7f);
+	lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
+	lv[2] = 0x80 | (p->descr & 0x7f);
 	if (lv_only)
 		msgb_lv_put(msg, lv[0], lv+1);
 	else
@@ -787,16 +895,18 @@
 }
 
 /* decode 'user-user' */
-static int decode_useruser(int *proto, char *info, int info_len,
+static int decode_useruser(struct gsm_mncc_useruser *uu,
 			   const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
+	char *info = uu->info;
+	int info_len = sizeof(uu->info);
 	int i;
 
 	if (in_len < 1)
 		return -EINVAL;
 
-	*proto = lv[1];
+	uu->proto = lv[1];
 
 	for (i = 2; i <= in_len; i++) {
 		info_len--;
@@ -811,16 +921,17 @@
 }
 
 /* encode 'useruser' */
-static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info)
+static int encode_useruser(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_useruser *uu)
 {
 	u_int8_t lv[GSM_MAX_USERUSER + 2];
 
-	if (strlen(info) > GSM_MAX_USERUSER)
+	if (strlen(uu->info) > GSM_MAX_USERUSER)
 		return -EINVAL;
 
-	lv[0] = 1 + strlen(info);
-	lv[1] = proto;
-	memcpy(lv + 2, info, strlen(info));
+	lv[0] = 1 + strlen(uu->info);
+	lv[1] = uu->proto;
+	memcpy(lv + 2, uu->info, strlen(uu->info));
 	if (lv_only)
 		msgb_lv_put(msg, lv[0], lv+1);
 	else
@@ -830,16 +941,16 @@
 }
 
 /* decode 'ss version' */
-static int decode_ssversion(char *info, int info_len, int *len,
+static int decode_ssversion(struct gsm_mncc_ssversion *ssv,
 			    const u_int8_t *lv)
 {
 	u_int8_t in_len = lv[0];
 
-	if (in_len < 1 || in_len < info_len)
+	if (in_len < 1 || in_len < sizeof(ssv->info))
 		return -EINVAL;
 
-	memcpy(info, lv + 1, in_len);
-	*len = in_len;
+	memcpy(ssv->info, lv + 1, in_len);
+	ssv->len = in_len;
 
 	return 0;
 }
@@ -862,27 +973,21 @@
 
 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;
@@ -890,7 +995,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)
 {
@@ -1080,7 +1184,7 @@
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	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;
 	char mi_string[MI_SIZE];
@@ -1110,6 +1214,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;
@@ -1118,6 +1223,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;
@@ -1140,6 +1246,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;
 
 	/*
@@ -1179,6 +1291,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)
 {
@@ -1189,6 +1308,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;
 
@@ -1226,13 +1350,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);
@@ -1243,9 +1363,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);
@@ -1353,7 +1473,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): ",
@@ -1403,7 +1523,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:
@@ -1451,7 +1571,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 paging_signal_data sig_data;
 	int rc = 0;
 
@@ -1651,16 +1771,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);
@@ -1688,127 +1817,159 @@
 	return gsm48_sendmsg(msg);
 }
 
+static void gsm48_stop_cc_timer(struct gsm_trans *trans)
+{
+	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;
+	}
+}
+ 
+static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
+			int msg_type, struct gsm_mncc *mncc)
+{
+	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;
+}
+
+int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
+		     u_int32_t callref, int location, int value)
+{
+	struct gsm_mncc rel;
+
+	memset(&rel, 0, sizeof(&rel));
+	rel.callref = callref;
+	mncc_set_cause(&rel, 1, 1); /* Unknown subscriber */
+	return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
+}
+
+void free_trans(struct gsm_trans *trans)
+{
+	struct gsm_bts *bts;
+
+	gsm48_stop_cc_timer(trans);
+
+	/* send release to L4, if callref still exists */
+	if (trans->callref) {
+		/* Ressource unavailable */
+		mncc_release_ind(trans->network, trans, trans->callref, 1, 47);
+		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);
+	}
+
+	if (trans->lchan) {
+		trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
+		put_lchan(trans->lchan);
+	}
+
+	if (trans->subscr)
+		subscr_put(trans->subscr);
+
+	if (trans->state != GSM_CSTATE_NULL)
+		new_cc_state(trans, GSM_CSTATE_NULL);
+
+	llist_del(&trans->entry);
+
+	free(trans);
+}
+
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
+ 
 /* 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_lchan *lchan = _lchan;
-	struct gsm_call *remote_call = param;
-	struct gsm_call *call = &lchan->call;
-	int rc = 0;
-
+	struct gsm_subscriber *subscr = param;
+	struct gsm_trans *transt, *tmp;
+	struct gsm_network *net;
+  
 	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;
-	}
-	return rc;
-}
-
-static int gsm48_cc_rx_status_enq(struct msgb *msg)
-{
-	DEBUGP(DCC, "-> STATUS ENQ\n");
-	return gsm48_cc_tx_status(msg->lchan);
-}
-
-static int gsm48_cc_rx_setup(struct msgb *msg)
-{
-	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;
-	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;
-
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
-	if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
-		goto err;
-
-	/* 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 (!subscr)
+		return -EINVAL;
+	net = subscr->net;
+	if (!net) {
+		DEBUGP(DCC, "Error Network not set!\n");
+		return -EINVAL;
 	}
 
-	subscr_get(msg->lchan->subscr);
-	call->called_subscr = called_subscr;
-
-	/* Start paging subscriber on all BTS in LAC of subscriber */
-	subscr_get_channel(called_subscr, msg->trx->bts->network, RSL_CHANNEED_TCH_F,
-		       setup_trig_pag_evt, call);
-
-	/* send a CALL PROCEEDING message to the MO */
-	ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_CALL_PROC);
-
-	if (is_ipaccess_bts(msg->trx->bts))
-		rsl_ipacc_bind(msg->lchan);
-
-	/* 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 */
+	/* check all tranactions (without lchan) for subscriber */
+	llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) {
+		if (transt->subscr != subscr || transt->lchan)
+			continue;
+		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);
+			/* Temporarily out of order */
+			mncc_release_ind(transt->network, transt, transt->callref,
+					 1, 27);
+			transt->callref = 0;
+			free_trans(transt);
+			break;
+		}
+	}
 	return 0;
 }
 
-static int gsm48_cc_rx_alerting(struct msgb *msg)
-{
-	struct gsm_call *call = &msg->lchan->call;
-
-	DEBUGP(DCC, "A -> ALERTING\n");
-
-	/* forward ALERTING to other party */
-	if (!call->remote_lchan)
-		return -EIO;
-
-	DEBUGP(DCC, "B <- ALERTING\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_ALERTING);
-}
-
 /* map two ipaccess RTP streams onto each other */
 static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
 {
@@ -1847,171 +2008,1616 @@
 	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;
-
-	DEBUGP(DCC, "A -> CONNECT\n");
-	
-	rc = tch_map(msg->lchan, call->remote_lchan);
-	if (rc)
-		return -EIO;
-		
-	if (!call->remote_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);
-
-
-	/* forward CONNECT to other party */
-	DEBUGP(DCC, "B <- CONNECT\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_CONNECT);
+	struct gsm_trans *trans;
+	llist_for_each_entry(trans, &net->trans_list, entry) {
+		if (trans->callref == callref)
+			return trans;
+	}
+	return NULL;
 }
 
-static int gsm48_cc_rx_disconnect(struct msgb *msg)
+/* bridge channels of two transactions */
+static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
 {
-	struct gsm_call *call = &msg->lchan->call;
-	int rc;
+	struct gsm_trans *trans1 = get_trans_ref(net, refs[0]);
+	struct gsm_trans *trans2 = get_trans_ref(net, refs[1]);
 
-
-	/* 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);
-
-	/*
-	 * 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)
+	if (!trans1 || !trans2)
 		return -EIO;
 
-	DEBUGP(DCC, "B <- DISCONNECT\n");
-	return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
-			       GSM48_MT_CC_DISCONNECT);
+	if (!trans1->lchan || !trans2->lchan)
+		return -EIO;
+
+	/* through-connect channel */
+	return tch_map(trans1->lchan, trans2->lchan);
 }
 
-static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 };
+/* enable receive of channels to upqueue */
+static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+{
+	struct gsm_trans *trans;
 
-int gsm48_cc_tx_setup(struct gsm_lchan *lchan, 
-		      struct gsm_subscriber *calling_subscr)
+	/* Find callref */
+	trans = get_trans_ref(net, data->callref);
+	if (!trans)
+		return -EIO;
+	if (!trans->lchan)
+		return 0;
+
+	// todo IPACCESS
+	if (enable)
+		return trau_recv_lchan(trans->lchan, data->callref);
+	return trau_mux_unmap(NULL, data->callref);
+}
+
+/* send a frame to channel */
+static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
+{
+	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;
+
+	// todo IPACCESS
+	return trau_send_lchan(trans->lchan, 
+				(struct decoded_trau_frame *)frame->data);
+}
+
+
+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 */
+		mncc_release_ind(trans->network, trans, trans->callref,
+				 l4_location, l4_cause);
+		trans->callref = 0;
+	}
+
+	if (disconnect && trans->callref) {
+		/* process disconnect towards layer 4 */
+		mncc_set_cause(&l4_rel, l4_location, l4_cause);
+		mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &l4_rel);
+	}
+
+	/* process disconnect towards mobile station */
+	if (disconnect || release) {
+		mncc_set_cause(&mo_rel, mo_location, mo_cause);
+		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);
+	}
+
+}
+
+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.fields |= MNCC_F_CALLING;
+		strncpy(setup.calling.number, trans->subscr->extension,
+			sizeof(setup.calling.number)-1);
+	}
+	/* bearer capability */
+	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+		setup.fields |= MNCC_F_BEARER_CAP;
+		decode_bearer_cap(&setup.bearer_cap,
+				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+		setup.fields |= MNCC_F_FACILITY;
+		decode_facility(&setup.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* called party bcd number */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
+		setup.fields |= MNCC_F_CALLED;
+		decode_called(&setup.called,
+			      TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+	}
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+		setup.fields |= MNCC_F_USERUSER;
+		decode_useruser(&setup.useruser,
+				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		setup.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&setup.ssversion,
+				 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.fields |= MNCC_F_CCCAP;
+		decode_cccap(&setup.cccap,
+			     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;
+}
+
+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 *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;
+	/* transaction id must not be assigned */
+	if (trans->transaction_id != 0xff) { /* unasssigned */
+		DEBUGP(DCC, "TX Setup with assigned transaction. "
+			"This is not allowed!\n");
+		/* Temporarily out of order */
+		rc = mncc_release_ind(trans->network, trans, trans->callref,
+				      1, 47);
+		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 */
+		rc = mncc_release_ind(trans->network, trans, trans->callref,
+				      1, 47);
+		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;
+		}
+	}
 
-	call->local_lchan = msg->lchan = lchan;
-	use_lchan(lchan);
-
-	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->fields & MNCC_F_BEARER_CAP)
+		encode_bearer_cap(msg, 0, &setup->bearer_cap);
+	/* facility */
+	if (setup->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &setup->facility);
+	/* progress */
+	if (setup->fields & MNCC_F_PROGRESS)
+		encode_progress(msg, 0, &setup->progress);
+	/* calling party BCD number */
+	if (setup->fields & MNCC_F_CALLING)
+		encode_calling(msg, &setup->calling);
+	/* called party BCD number */
+	if (setup->fields & MNCC_F_CALLED)
+		encode_called(msg, &setup->called);
+	/* user-user */
+	if (setup->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &setup->useruser);
+	/* redirecting party BCD number */
+	if (setup->fields & MNCC_F_REDIRECTING)
+		encode_redirecting(msg, &setup->redirecting);
+	/* signal */
+	if (setup->fields & MNCC_F_SIGNAL)
+		encode_signal(msg, setup->signal);
+	
+	new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
 
 	return gsm48_sendmsg(msg);
 }
 
+static int gsm48_cc_rx_call_conf(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 call_conf;
+
+	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.fields |= MNCC_F_BEARER_CAP;
+		decode_bearer_cap(&call_conf.bearer_cap,
+				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	}
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+		call_conf.fields |= MNCC_F_CAUSE;
+		decode_cause(&call_conf.cause,
+			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	}
+	/* cc cap */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
+		call_conf.fields |= MNCC_F_CCCAP;
+		decode_cccap(&call_conf.cccap,
+			     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->fields & MNCC_F_BEARER_CAP)
+		encode_bearer_cap(msg, 0, &proceeding->bearer_cap);
+	/* facility */
+	if (proceeding->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &proceeding->facility);
+	/* progress */
+	if (proceeding->fields & MNCC_F_PROGRESS)
+		encode_progress(msg, 0, &proceeding->progress);
+
+	return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_alerting(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 alerting;
+	
+	gsm48_stop_cc_timer(trans);
+	gsm48_start_cc_timer(trans, 0x301, GSM48_T301);
+
+	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.fields |= MNCC_F_FACILITY;
+		decode_facility(&alerting.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+
+	/* progress */
+	if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+		alerting.fields |= MNCC_F_PROGRESS;
+		decode_progress(&alerting.progress,
+				TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		alerting.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&alerting.ssversion,
+				 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);
+}
+
+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));
+
+	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+	msg->lchan = trans->lchan;
+	gh->msg_type = GSM48_MT_CC_ALERTING;
+
+	/* facility */
+	if (alerting->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &alerting->facility);
+	/* progress */
+	if (alerting->fields & MNCC_F_PROGRESS)
+		encode_progress(msg, 0, &alerting->progress);
+	/* user-user */
+	if (alerting->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &alerting->useruser);
+
+	new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+	
+	return gsm48_sendmsg(msg);
+}
+
+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(msg, 1, &progress->progress);
+	/* user-user */
+	if (progress->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &progress->useruser);
+
+	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->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &connect->facility);
+	/* progress */
+	if (connect->fields & MNCC_F_PROGRESS)
+		encode_progress(msg, 0, &connect->progress);
+	/* connected number */
+	if (connect->fields & MNCC_F_CONNECTED)
+		encode_connected(msg, &connect->connected);
+	/* user-user */
+	if (connect->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &connect->useruser);
+
+	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.fields |= MNCC_F_CONNECTED;
+		strncpy(connect.connected.number, trans->subscr->extension,
+			sizeof(connect.connected.number)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+		connect.fields |= MNCC_F_FACILITY;
+		decode_facility(&connect.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+		connect.fields |= MNCC_F_USERUSER;
+		decode_useruser(&connect.useruser,
+				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		connect.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&connect.ssversion,
+				 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.fields |= MNCC_F_CAUSE;
+		decode_cause(&disc.cause,
+			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+		disc.fields |= MNCC_F_FACILITY;
+		decode_facility(&disc.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+		disc.fields |= MNCC_F_USERUSER;
+		decode_useruser(&disc.useruser,
+				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		disc.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&disc.ssversion,
+				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+	}
+
+	return mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &disc);
+
+}
+
+static struct gsm_mncc_cause default_cause = { 1, 0, 0, 0, 31, 0, { 0 } };
+
+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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 1, &disc->cause);
+	else
+		encode_cause(msg, 1, &default_cause);
+
+	/* facility */
+	if (disc->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &disc->facility);
+	/* progress */
+	if (disc->fields & MNCC_F_PROGRESS)
+		encode_progress(msg, 0, &disc->progress);
+	/* user-user */
+	if (disc->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &disc->useruser);
+
+	/* 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.fields |= MNCC_F_CAUSE;
+		decode_cause(&rel.cause,
+			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+		rel.fields |= MNCC_F_FACILITY;
+		decode_facility(&rel.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+		rel.fields |= MNCC_F_USERUSER;
+		decode_useruser(&rel.useruser,
+				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		rel.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&rel.ssversion,
+				 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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 0, &rel->cause);
+	/* facility */
+	if (rel->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &rel->facility);
+	/* user-user */
+	if (rel->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &rel->useruser);
+
+	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.fields |= MNCC_F_CAUSE;
+		decode_cause(&rel.cause,
+			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+	}
+	/* facility */
+	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+		rel.fields |= MNCC_F_FACILITY;
+		decode_facility(&rel.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* user-user */
+	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+		rel.fields |= MNCC_F_USERUSER;
+		decode_useruser(&rel.useruser,
+				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		rel.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&rel.ssversion,
+				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+	}
+
+	if (trans->callref) {
+		switch (trans->state) {
+		case GSM_CSTATE_CALL_PRESENT:
+			rc = mncc_recvmsg(trans->network, trans,
+					  MNCC_REJ_IND, &rel);
+			break;
+		case GSM_CSTATE_RELEASE_REQ:
+			rc = mncc_recvmsg(trans->network, trans,
+					  MNCC_REL_CNF, &rel);
+			break;
+		default:
+			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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 0, &rel->cause);
+	/* facility */
+	if (rel->fields & MNCC_F_FACILITY)
+		encode_facility(msg, 0, &rel->facility);
+	/* user-user */
+	if (rel->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 0, &rel->useruser);
+
+	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.fields |= MNCC_F_FACILITY;
+		decode_facility(&fac.facility,
+				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+	}
+	/* ss-version */
+	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+		fac.fields |= MNCC_F_SSVERSION;
+		decode_ssversion(&fac.ssversion,
+				 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(msg, 1, &fac->facility);
+
+	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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 1, &hold_rej->cause);
+	else
+		encode_cause(msg, 1, &default_cause);
+
+	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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 1, &retrieve_rej->cause);
+	else
+		encode_cause(msg, 1, &default_cause);
+
+	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)) {
+		dtmf.fields |= MNCC_F_KEYPAD;
+		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->fields & MNCC_F_KEYPAD)
+		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->fields & MNCC_F_CAUSE)
+		encode_cause(msg, 1, &dtmf->cause);
+	else
+		encode_cause(msg, 1, &default_cause);
+
+	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.fields |= MNCC_F_BEARER_CAP;
+		decode_bearer_cap(&modify.bearer_cap,
+				  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(msg, 1, &modify->bearer_cap);
+
+	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.fields |= MNCC_F_BEARER_CAP;
+		decode_bearer_cap(&modify.bearer_cap,
+				  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(msg, 1, &modify->bearer_cap);
+
+	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.fields |= GSM48_IE_BEARER_CAP;
+		decode_bearer_cap(&modify.bearer_cap,
+				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+	}
+	/* cause */
+	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+		modify.fields |= MNCC_F_CAUSE;
+		decode_cause(&modify.cause,
+			     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(msg, 1, &modify->bearer_cap);
+	/* cause */
+	encode_cause(msg, 1, &modify->cause);
+
+	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->fields & MNCC_F_USERUSER)
+		encode_useruser(msg, 1, &user->useruser);
+	/* 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.fields |= MNCC_F_USERUSER;
+		decode_useruser(&user.useruser,
+				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);
+			/* Invalid call reference */
+			return mncc_release_ind(net, NULL, data->callref, 1, 81);
+		}
+		/* 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);
+			/* Unknown subscriber */
+			return mncc_release_ind(net, NULL, data->callref, 1, 1);
+		}
+		/* 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);
+			/* Temporarily out of order */
+			return mncc_release_ind(net, NULL, data->callref, 1, 27);
+		}
+		/* Create transaction */
+		if (!(trans = calloc(1, sizeof(struct gsm_trans)))) {
+			DEBUGP(DCC, "No memory for trans.\n");
+			subscr_put(subscr);
+			/* Ressource unavailable */
+			mncc_release_ind(net, NULL, data->callref, 1, 47);
+			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)
+					continue;
+				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));
+		mncc_set_cause(&rel, 1, 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;
-	struct gsm_call *call = &msg->lchan->call;
-	int rc = 0;
+	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;
 
-	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;
+	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;
 }
@@ -2050,21 +3656,6 @@
 	return rc;
 }
 
-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,
-};
-
 /* Section 9.1.8 / Table 9.9 */
 struct chreq {
 	u_int8_t val;
@@ -2157,3 +3748,21 @@
 	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 --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index a78425f..9ab5895 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -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 --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index b74e46e..cc8a6c9 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -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 --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
new file mode 100644
index 0000000..8f93b3f
--- /dev/null
+++ b/openbsc/src/mncc.c
@@ -0,0 +1,382 @@
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdio.h>
+#include <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;
+}
+
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
+{
+	data->fields |= MNCC_F_CAUSE;
+	data->cause.location = loc;
+	data->cause.value = val;
+}
+
+/* on incoming 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_set_cause(&mncc, 1, 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->fields |= MNCC_F_SIGNAL;
+//	setup->signal = 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;
+			mncc_set_cause(&rel, 1, 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:
+		mncc_set_cause(data, 1, 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:
+		mncc_set_cause(data, 1, 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:
+		mncc_set_cause(data, 1, 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 --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 8f15e16..53e5146 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -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);
@@ -263,9 +264,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 --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 96f8589..196d15f 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -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 --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 44531dd..6597e44 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -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,