Merge branch 'on-waves/sccp'
diff --git a/libosmocore/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am
index fb4f089..1c3a33f 100644
--- a/libosmocore/include/osmocore/Makefile.am
+++ b/libosmocore/include/osmocore/Makefile.am
@@ -1,7 +1,7 @@
 osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
 		   tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
 		   gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
-		   gsm48_ie.h
+		   gsm48_ie.h logging.h
 
 if ENABLE_TALLOC
 osmocore_HEADERS += talloc.h
diff --git a/libosmocore/include/osmocore/logging.h b/libosmocore/include/osmocore/logging.h
new file mode 100644
index 0000000..93f18a0
--- /dev/null
+++ b/libosmocore/include/osmocore/logging.h
@@ -0,0 +1,130 @@
+#ifndef _OSMOCORE_LOGGING_H
+#define _OSMOCORE_LOGGING_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocore/linuxlist.h>
+
+#define LOG_MAX_CATEGORY	32
+#define LOG_MAX_CTX		8
+#define LOG_MAX_FILTERS	8
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
+#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
+#else
+#define DEBUGP(xss, fmt, args...)
+#define DEBUGPC(ss, fmt, args...)
+#endif
+
+#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+char *hexdump(const unsigned char *buf, int len);
+void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
+
+/* new logging interface */
+#define LOGP(ss, level, fmt, args...) \
+	logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
+#define LOGPC(ss, level, fmt, args...) \
+	logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
+
+/* different levels */
+#define LOGL_DEBUG	1	/* debugging information */
+#define LOGL_INFO	3
+#define LOGL_NOTICE	5	/* abnormal/unexpected condition */
+#define LOGL_ERROR	7	/* error condition, requires user action */
+#define LOGL_FATAL	8	/* fatal, program aborted */
+
+#define LOG_FILTER_ALL	0x0001
+
+struct log_category {
+	uint8_t loglevel;
+	uint8_t enabled;
+};
+
+struct log_info_cat {
+	const char *name;
+	const char *color;
+	const char *description;
+	uint8_t loglevel;
+	uint8_t enabled;
+};
+
+/* log context information, passed to filter */
+struct log_context {
+	void *ctx[LOG_MAX_CTX+1];
+};
+
+struct log_target;
+
+typedef int log_filter(const struct log_context *ctx,
+		       struct log_target *target);
+
+struct log_info {
+	/* filter callback function */
+	log_filter *filter_fn;
+
+	/* per-category information */
+	const struct log_info_cat *cat;
+	unsigned int num_cat;
+};
+
+struct log_target {
+        struct llist_head entry;
+
+	int filter_map;
+	void *filter_data[LOG_MAX_FILTERS+1];
+
+	struct log_category categories[LOG_MAX_CATEGORY+1];
+	uint8_t loglevel;
+	int use_color:1;
+	int print_timestamp:1;
+
+	union {
+		struct {
+			FILE *out;
+		} tgt_stdout;
+
+		struct {
+			int priority;
+		} tgt_syslog;
+
+		struct {
+			void *vty;
+		} tgt_vty;
+	};
+
+        void (*output) (struct log_target *target, const char *string);
+};
+
+/* use the above macros */
+void logp2(unsigned int subsys, unsigned int level, char *file,
+	   int line, int cont, const char *format, ...)
+				__attribute__ ((format (printf, 6, 7)));
+void log_init(const struct log_info *cat);
+
+/* context management */
+void log_reset_context(void);
+int log_set_context(uint8_t ctx, void *value);
+
+/* filter on the targets */
+void log_set_all_filter(struct log_target *target, int);
+
+void log_set_use_color(struct log_target *target, int);
+void log_set_print_timestamp(struct log_target *target, int);
+void log_set_log_level(struct log_target *target, int log_level);
+void log_parse_category_mask(struct log_target *target, const char* mask);
+int log_parse_level(const char *lvl);
+int log_parse_category(const char *category);
+void log_set_category_filter(struct log_target *target, int category,
+			       int enable, int level);
+
+/* management of the targets */
+struct log_target *log_target_create(void);
+struct log_target *log_target_create_stderr(void);
+void log_add_target(struct log_target *target);
+void log_del_target(struct log_target *target);
+
+#endif /* _OSMOCORE_LOGGING_H */
diff --git a/libosmocore/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h
index c84000c..64d4159 100644
--- a/libosmocore/include/osmocore/write_queue.h
+++ b/libosmocore/include/osmocore/write_queue.h
@@ -38,6 +38,7 @@
 };
 
 void write_queue_init(struct write_queue *queue, int max_length);
+void write_queue_clear(struct write_queue *queue);
 int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
 int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
 
diff --git a/libosmocore/src/Makefile.am b/libosmocore/src/Makefile.am
index f0effa2..1697807 100644
--- a/libosmocore/src/Makefile.am
+++ b/libosmocore/src/Makefile.am
@@ -9,7 +9,8 @@
 
 libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
 			 tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
-			 write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c
+			 write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
+			 logging.c
 
 if ENABLE_TALLOC
 libosmocore_la_SOURCES += talloc.c
diff --git a/libosmocore/src/logging.c b/libosmocore/src/logging.c
new file mode 100644
index 0000000..508ccfd
--- /dev/null
+++ b/libosmocore/src/logging.c
@@ -0,0 +1,345 @@
+/* Debugging/Logging support code */
+
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <errno.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/utils.h>
+#include <osmocore/logging.h>
+
+static const struct log_info *log_info;
+
+static struct log_context log_context;
+static void *tall_log_ctx = NULL;
+static LLIST_HEAD(target_list);
+
+static const struct value_string loglevel_strs[] = {
+	{ 0,		"EVERYTHING" },
+	{ LOGL_DEBUG,	"DEBUG" },
+	{ LOGL_INFO,	"INFO" },
+	{ LOGL_NOTICE,	"NOTICE" },
+	{ LOGL_ERROR,	"ERROR" },
+	{ LOGL_FATAL,	"FATAL" },
+	{ 0, NULL },
+};
+
+int log_parse_level(const char *lvl)
+{
+	return get_string_value(loglevel_strs, lvl);
+}
+
+int log_parse_category(const char *category)
+{
+	int i;
+
+	for (i = 0; i < log_info->num_cat; ++i) {
+		if (!strcasecmp(log_info->cat[i].name+1, category))
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Parse the category mask.
+ * The format can be this: category1:category2:category3
+ * or category1,2:category2,3:...
+ */
+void log_parse_category_mask(struct log_target* target, const char *_mask)
+{
+	int i = 0;
+	char *mask = strdup(_mask);
+	char *category_token = NULL;
+
+	/* Disable everything to enable it afterwards */
+	for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
+		target->categories[i].enabled = 0;
+
+	category_token = strtok(mask, ":");
+	do {
+		for (i = 0; i < log_info->num_cat; ++i) {
+			char* colon = strstr(category_token, ",");
+			int length = strlen(category_token);
+
+			if (colon)
+			    length = colon - category_token;
+
+			if (strncasecmp(log_info->cat[i].name, category_token,
+					length) == 0) {
+				int level = 0;
+
+				if (colon)
+					level = atoi(colon+1);
+
+				target->categories[i].enabled = 1;
+				target->categories[i].loglevel = level;
+			}
+		}
+	} while ((category_token = strtok(NULL, ":")));
+
+	free(mask);
+}
+
+static const char* color(int subsys)
+{
+	if (subsys < log_info->num_cat)
+		return log_info->cat[subsys].color;
+
+	return NULL;
+}
+
+static void _output(struct log_target *target, unsigned int subsys,
+		    char *file, int line, int cont, const char *format,
+		    va_list ap)
+{
+	char col[30];
+	char sub[30];
+	char tim[30];
+	char buf[4096];
+	char final[4096];
+
+	/* prepare the data */
+	col[0] = '\0';
+	sub[0] = '\0';
+	tim[0] = '\0';
+	buf[0] = '\0';
+
+	/* are we using color */
+	if (target->use_color) {
+		const char *c = color(subsys);
+		if (c) {
+			snprintf(col, sizeof(col), "%s", color(subsys));
+			col[sizeof(col)-1] = '\0';
+		}
+	}
+	vsnprintf(buf, sizeof(buf), format, ap);
+	buf[sizeof(buf)-1] = '\0';
+
+	if (!cont) {
+		if (target->print_timestamp) {
+			char *timestr;
+			time_t tm;
+			tm = time(NULL);
+			timestr = ctime(&tm);
+			timestr[strlen(timestr)-1] = '\0';
+			snprintf(tim, sizeof(tim), "%s ", timestr);
+			tim[sizeof(tim)-1] = '\0';
+		}
+		snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
+		sub[sizeof(sub)-1] = '\0';
+	}
+
+	snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
+	final[sizeof(final)-1] = '\0';
+	target->output(target, final);
+}
+
+
+static void _logp(unsigned int subsys, int level, char *file, int line,
+		  int cont, const char *format, va_list ap)
+{
+	struct log_target *tar;
+
+	llist_for_each_entry(tar, &target_list, entry) {
+		struct log_category *category;
+		int output = 0;
+
+		category = &tar->categories[subsys];
+		/* subsystem is not supposed to be logged */
+		if (!category->enabled)
+			continue;
+
+		/* Check the global log level */
+		if (tar->loglevel != 0 && level < tar->loglevel)
+			continue;
+
+		/* Check the category log level */
+		if (tar->loglevel == 0 && category->loglevel != 0 &&
+		    level < category->loglevel)
+			continue;
+
+		/* Apply filters here... if that becomes messy we will
+		 * need to put filters in a list and each filter will
+		 * say stop, continue, output */
+		if ((tar->filter_map & LOG_FILTER_ALL) != 0)
+			output = 1;
+		else if (log_info->filter_fn)
+			output = log_info->filter_fn(&log_context,
+						       tar);
+
+		if (output) {
+			/* FIXME: copying the va_list is an ugly
+			 * workaround against a bug hidden somewhere in
+			 * _output.  If we do not copy here, the first
+			 * call to _output() will corrupt the va_list
+			 * contents, and any further _output() calls
+			 * with the same va_list will segfault */
+			va_list bp;
+			va_copy(bp, ap);
+			_output(tar, subsys, file, line, cont, format, bp);
+			va_end(bp);
+		}
+	}
+}
+
+void logp(unsigned int subsys, char *file, int line, int cont,
+	  const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	_logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
+	va_end(ap);
+}
+
+void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	_logp(subsys, level, file, line, cont, format, ap);
+	va_end(ap);
+}
+
+static char hexd_buff[4096];
+
+char *hexdump(const unsigned char *buf, int len)
+{
+	int i;
+	char *cur = hexd_buff;
+
+	hexd_buff[0] = 0;
+	for (i = 0; i < len; i++) {
+		int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
+		int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
+		if (rc <= 0)
+			break;
+		cur += rc;
+	}
+	hexd_buff[sizeof(hexd_buff)-1] = 0;
+	return hexd_buff;
+}
+
+void log_add_target(struct log_target *target)
+{
+	llist_add_tail(&target->entry, &target_list);
+}
+
+void log_del_target(struct log_target *target)
+{
+	llist_del(&target->entry);
+}
+
+void log_reset_context(void)
+{
+	memset(&log_context, 0, sizeof(log_context));
+}
+
+int log_set_context(uint8_t ctx_nr, void *value)
+{
+	if (ctx_nr > LOG_MAX_CTX)
+		return -EINVAL;
+
+	log_context.ctx[ctx_nr] = value;
+
+	return 0;
+}
+
+void log_set_all_filter(struct log_target *target, int all)
+{
+	if (all)
+		target->filter_map |= LOG_FILTER_ALL;
+	else
+		target->filter_map &= ~LOG_FILTER_ALL;
+}
+
+void log_set_use_color(struct log_target *target, int use_color)
+{
+	target->use_color = use_color;
+}
+
+void log_set_print_timestamp(struct log_target *target, int print_timestamp)
+{
+	target->print_timestamp = print_timestamp;
+}
+
+void log_set_log_level(struct log_target *target, int log_level)
+{
+	target->loglevel = log_level;
+}
+
+void log_set_category_filter(struct log_target *target, int category,
+			       int enable, int level)
+{
+	if (category >= log_info->num_cat)
+		return;
+	target->categories[category].enabled = !!enable;
+	target->categories[category].loglevel = level;
+}
+
+static void _stderr_output(struct log_target *target, const char *log)
+{
+	fprintf(target->tgt_stdout.out, "%s", log);
+	fflush(target->tgt_stdout.out);
+}
+
+struct log_target *log_target_create(void)
+{
+	struct log_target *target;
+
+	target = talloc_zero(tall_log_ctx, struct log_target);
+	if (!target)
+		return NULL;
+
+	INIT_LLIST_HEAD(&target->entry);
+	memcpy(target->categories, log_info->cat,
+		sizeof(struct log_category)*log_info->num_cat);
+	target->use_color = 1;
+	target->print_timestamp = 0;
+	target->loglevel = 0;
+	return target;
+}
+
+struct log_target *log_target_create_stderr(void)
+{
+	struct log_target *target;
+
+	target = log_target_create();
+	if (!target)
+		return NULL;
+
+	target->tgt_stdout.out = stderr;
+	target->output = _stderr_output;
+	return target;
+}
+
+void log_init(const struct log_info *cat)
+{
+	tall_log_ctx = talloc_named_const(NULL, 1, "logging");
+	log_info = cat;
+}
diff --git a/libosmocore/src/write_queue.c b/libosmocore/src/write_queue.c
index 7d908b4..a0ac2d6 100644
--- a/libosmocore/src/write_queue.c
+++ b/libosmocore/src/write_queue.c
@@ -72,3 +72,14 @@
 
 	return 0;
 }
+
+void write_queue_clear(struct write_queue *queue)
+{
+	while (!llist_empty(&queue->msg_queue)) {
+		struct msgb *msg = msgb_dequeue(&queue->msg_queue);
+		msgb_free(msg);
+	}
+
+	queue->current_length = 0;
+	queue->bfd.when &= ~BSC_FD_WRITE;
+}
diff --git a/openbsc/configure.in b/openbsc/configure.in
index 27fb0ed..615e59d 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -18,7 +18,7 @@
 AC_SEARCH_LIBS(crypt, crypt,
     [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])])
 
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.1)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.3)
 
 dnl checks for header files
 AC_HEADER_STDC
diff --git a/openbsc/contrib/mgcp_server.py b/openbsc/contrib/mgcp_server.py
index cf3ef38..05c489d 100755
--- a/openbsc/contrib/mgcp_server.py
+++ b/openbsc/contrib/mgcp_server.py
@@ -10,7 +10,7 @@
 audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
 crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
 dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
-mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
+mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
 
 def hexdump(src, length=8):
     """Recipe is from http://code.activestate.com/recipes/142812/"""
@@ -25,15 +25,24 @@
 
 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
-server_socket.setblocking(0)
+server_socket.setblocking(1)
 
-
-def send_receive(packet):
+last_ci = 1
+def send_and_receive(packet):
+    global last_ci
     server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
     try:
         data, addr = server_socket.recvfrom(4096)
+
+        # attempt to store the CI of the response
+        list = data.split("\n")
+        for item in list:
+           if item.startswith("I: "):
+               last_ci = int(item[3:])
+
         print hexdump(data), addr
-    except socket.error:
+    except socket.error, e:
+        print e
         pass
 
 def generate_tid():
@@ -42,13 +51,10 @@
 
 
 
-i = 1
 while True:
-    send_receive(rsip_resp)
-    send_receive(audit_packet)
-    send_receive(crcx_packet % generate_tid() )
-    send_receive(mdcx_packet % (generate_tid(), i))
-    send_receive(dlcx_packet % (generate_tid(), i))
-    i = i + 1
+    send_and_receive(audit_packet % generate_tid())
+    send_and_receive(crcx_packet % generate_tid() )
+    send_and_receive(mdcx_packet % (generate_tid(), last_ci))
+    send_and_receive(dlcx_packet % (generate_tid(), last_ci))
 
     time.sleep(3)
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 483997a..259e6d6 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -5,7 +5,8 @@
 		 ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
 		 bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \
 		 silent_call.h mgcp.h meas_rep.h rest_octets.h \
-		 system_information.h handover.h mgcp_internal.h
+		 system_information.h handover.h mgcp_internal.h \
+		 vty.h
 
-openbsc_HEADERS = gsm_04_08.h meas_rep.h
+openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
 openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index b280184..e6973ee 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -59,7 +59,7 @@
 int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip,
 		   u_int16_t port, u_int8_t rtp_payload2);
 int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan);
-int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan);
+int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan, int act);
 
 int abis_rsl_rcvmsg(struct msgb *msg);
 
diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h
new file mode 100644
index 0000000..51e344f
--- /dev/null
+++ b/openbsc/include/openbsc/bsc_api.h
@@ -0,0 +1,6 @@
+/* GSM 08.08 like API for OpenBSC */
+
+#include "gsm_data.h"
+
+
+int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id);
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index df664db..d0a1278 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -55,8 +55,8 @@
 
 /* SMS store-and-forward */
 int db_sms_store(struct gsm_sms *sms);
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
-struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id);
+struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id);
+struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id);
 struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
 int db_sms_mark_sent(struct gsm_sms *sms);
 
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 4b67c61..f1c5a69 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -5,6 +5,7 @@
 #include <osmocore/linuxlist.h>
 
 #define DEBUG
+#include <osmocore/logging.h>
 
 /* Debug Areas of the code */
 enum {
@@ -31,31 +32,6 @@
 	Debug_LastEntry,
 };
 
-#ifdef DEBUG
-#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
-#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
-#else
-#define DEBUGP(xss, fmt, args...)
-#define DEBUGPC(ss, fmt, args...)
-#endif
-
-
-#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
-
-char *hexdump(const unsigned char *buf, int len);
-void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
-
-/* new logging interface */
-#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
-#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
-
-/* different levels */
-#define LOGL_DEBUG	1	/* debugging information */
-#define LOGL_INFO	3
-#define LOGL_NOTICE	5	/* abnormal/unexpected condition */
-#define LOGL_ERROR	7	/* error condition, requires user action */
-#define LOGL_FATAL	8	/* fatal, program aborted */
-
 /* context */
 #define BSC_CTX_LCHAN	0
 #define BSC_CTX_SUBSCR	1
@@ -65,67 +41,12 @@
 /* target */
 
 enum {
-	DEBUG_FILTER_IMSI = 1 << 0,
-	DEBUG_FILTER_ALL = 1 << 1,
+	//DEBUG_FILTER_ALL = 1 << 0,
+	LOG_FILTER_IMSI = 1 << 1,
 };
 
-struct debug_category {
-	int enabled;
-	int loglevel;
-};
+void log_set_imsi_filter(struct log_target *target, const char *imsi);
 
-struct debug_target {
-	int filter_map;
-	char *imsi_filter;
+extern const struct log_info log_info;
 
-
-	struct debug_category categories[Debug_LastEntry];
-	int use_color;
-	int print_timestamp;
-	int loglevel;
-
-	union {
-		struct {
-			FILE *out;
-		} tgt_stdout;
-
-		struct {
-			int priority;
-		} tgt_syslog;
-
-		struct {
-			void *vty;
-		} tgt_vty;
-	};
-
-        void (*output) (struct debug_target *target, const char *string);
-
-        struct llist_head entry;
-};
-
-/* use the above macros */
-void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7)));
-void debug_init(void);
-
-/* context management */
-void debug_reset_context(void);
-void debug_set_context(int ctx, void *value);
-
-/* filter on the targets */
-void debug_set_imsi_filter(struct debug_target *target, const char *imsi);
-void debug_set_all_filter(struct debug_target *target, int);
-void debug_set_use_color(struct debug_target *target, int);
-void debug_set_print_timestamp(struct debug_target *target, int);
-void debug_set_log_level(struct debug_target *target, int log_level);
-void debug_parse_category_mask(struct debug_target *target, const char* mask);
-int debug_parse_level(const char *lvl);
-int debug_parse_category(const char *category);
-void debug_set_category_filter(struct debug_target *target, int category, int enable, int level);
-
-
-/* management of the targets */
-struct debug_target *debug_target_create(void);
-struct debug_target *debug_target_create_stderr(void);
-void debug_add_target(struct debug_target *target);
-void debug_del_target(struct debug_target *target);
 #endif /* _DEBUG_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index c4018cd..daf3bd7 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -24,7 +24,6 @@
 int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan);
 struct msgb *gsm48_msgb_alloc(void);
 int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
-int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len);
 
 int gsm48_send_rr_release(struct gsm_lchan *lchan);
 int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 9badd36..8127af1 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -25,7 +25,7 @@
 
 int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id);
 
-int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms);
+int gsm411_send_sms_lchan(struct gsm_subscriber_connection *conn, struct gsm_sms *sms);
 
 struct gsm_sms *sms_alloc(void);
 void sms_free(struct gsm_sms *sms);
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 88e7f16..8dfa588 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -84,18 +84,18 @@
  * will be started.
  */
 #define LCHAN_RELEASE_TIMEOUT 20, 0
-#define use_lchan(lchan) \
-	do {	lchan->use_count++; \
+#define use_subscr_con(con) \
+	do {	(con)->use_count++; \
 		DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
-			lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
-			lchan->nr, lchan->use_count); \
-		bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
+			(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
+			(con)->lchan->nr, (con)->use_count); \
+		bsc_schedule_timer(&(con)->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
 
-#define put_lchan(lchan) \
-	do { lchan->use_count--; \
+#define put_subscr_con(con) \
+	do { (con)->use_count--; \
 		DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
-			lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
-			lchan->nr, lchan->use_count); \
+			(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
+			(con)->lchan->nr, (con)->use_count); \
 	} while(0);
 
 
@@ -182,6 +182,30 @@
 	LCHAN_S_INACTIVE,	/* channel is set inactive */
 };
 
+/* the per subscriber data for lchan */
+struct gsm_subscriber_connection {
+	/* To whom we are allocated at the moment */
+	struct gsm_subscriber *subscr;
+
+	/* Timer started to release the channel */
+	struct timer_list release_timer;
+
+	/*
+	 * Operations that have a state and might be pending
+	 */
+	struct gsm_loc_updating_operation *loc_operation;
+
+	/* use count. how many users use this channel */
+	unsigned int use_count;
+
+	/* Are we part of a special "silent" call */
+	int silent_call;
+
+	/* back pointers */
+	struct gsm_lchan *lchan;
+	struct gsm_bts *bts;
+};
+
 struct gsm_lchan {
 	/* The TS that we're part of */
 	struct gsm_bts_trx_ts *ts;
@@ -204,31 +228,15 @@
 		u_int8_t key_len;
 		u_int8_t key[MAX_A5_KEY_LEN];
 	} encr;
-	/* Are we part of a special "silent" call */
-	int silent_call;
+
+	struct timer_list T3101;
 
 	/* AMR bits */
 	struct gsm48_multi_rate_conf mr_conf;
 	
-	/* To whom we are allocated at the moment */
-	struct gsm_subscriber *subscr;
-
-	/* Timer started to release the channel */
-	struct timer_list release_timer;
-
-	struct timer_list T3101;
-
 	/* Established data link layer services */
 	u_int8_t sapis[8];
 
-	/*
-	 * Operations that have a state and might be pending
-	 */
-	struct gsm_loc_updating_operation *loc_operation;
-
-	/* use count. how many users use this channel */
-	unsigned int use_count;
-
 	/* cache of last measurement reports on this lchan */
 	struct gsm_meas_rep meas_rep[6];
 	int meas_rep_idx;
@@ -246,6 +254,8 @@
 		u_int8_t speech_mode;
 		struct rtp_socket *rtp_socket;
 	} abis_ip;
+
+	struct gsm_subscriber_connection conn;
 };
 
 struct gsm_e1_subslot {
@@ -257,7 +267,7 @@
 	u_int8_t	e1_ts_ss;
 };
 
-#define BTS_TRX_F_ACTIVATED	0x0001
+#define TS_F_PDCH_MODE	0x1000
 /* One Timeslot in a TRX */
 struct gsm_bts_trx_ts {
 	struct gsm_bts_trx *trx;
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
index f7e800b..71b7fc1 100644
--- a/openbsc/include/openbsc/mgcp.h
+++ b/openbsc/include/openbsc/mgcp.h
@@ -77,6 +77,7 @@
 
 typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state, int local_rtp);
 typedef int (*mgcp_policy)(struct mgcp_config *cfg, int endpoint, int state, const char *transactio_id);
+typedef int (*mgcp_reset)(struct mgcp_config *cfg);
 
 struct mgcp_config {
 	int source_port;
@@ -84,6 +85,7 @@
 	char *source_addr;
 	unsigned int number_endpoints;
 	char *bts_ip;
+	char *call_agent_addr;
 
 	struct in_addr bts_in;
 	char *audio_name;
@@ -95,8 +97,12 @@
 	char *forward_ip;
 	int forward_port;
 
+	/* spec handling */
+	int force_realloc;
+
 	mgcp_change change_cb;
 	mgcp_policy policy_cb;
+	mgcp_reset reset_cb;
 	void *data;
 
 	struct mgcp_endpoint *endpoints;
@@ -115,8 +121,15 @@
  * format helper functions
  */
 struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
-struct msgb *mgcp_create_rsip(void);
 struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, const char *data);
 
+/* adc helper */
+static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
+{
+	if (timeslot == 0)
+		timeslot = 1;
+	return timeslot + (31 * multiplex);
+}
+
 
 #endif
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 10d0ca6..3a28324 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -57,8 +57,21 @@
 
 	/* backpointer */
 	struct mgcp_config *cfg;
+
+	/* statistics */
+	unsigned int in_bts;
+	unsigned int in_remote;
 };
 
 #define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)
 
+struct mgcp_msg_ptr {
+	unsigned int start;
+	unsigned int length;
+};
+
+int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
+			struct mgcp_msg_ptr *ptr, int size,
+			const char **transaction_id, struct mgcp_endpoint **endp);
+
 #endif
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 0c22869..1b974e2 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -42,6 +42,7 @@
 	SS_SUBSCR,
 	SS_SCALL,
 	SS_GLOBAL,
+	SS_CHALLOC,
 };
 
 /* SS_PAGING signals */
@@ -93,6 +94,12 @@
 	S_LCHAN_MEAS_REP,		/* 08.58 Measurement Report */
 };
 
+/* SS_CHALLOC signals */
+enum signal_challoc {
+	S_CHALLOC_ALLOC_FAIL,	/* allocation of lchan has failed */
+	S_CHALLOC_FREED,	/* lchan has been successfully freed */
+};
+
 /* SS_SUBSCR signals */
 enum signal_subscr {
 	S_SUBSCR_ATTACHED,
@@ -130,4 +137,10 @@
 	u_int8_t msg_type;	
 };
 
+struct challoc_signal_data {
+	struct gsm_bts *bts;
+	struct gsm_lchan *lchan;
+	enum gsm_chan_t type;
+};
+
 #endif
diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h
index 20e794b..b8c36b6 100644
--- a/openbsc/include/openbsc/telnet_interface.h
+++ b/openbsc/include/openbsc/telnet_interface.h
@@ -22,7 +22,7 @@
 #define TELNET_INTERFACE_H
 
 #include "gsm_data.h"
-#include "debug.h"
+#include <openbsc/debug.h>
 #include <osmocore/select.h>
 
 #include <vty/vty.h>
@@ -32,7 +32,7 @@
 	struct gsm_network *network;
 	struct bsc_fd fd;
 	struct vty *vty;
-	struct debug_target *dbg;
+	struct log_target *dbg;
 };
 
 
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 50c3cc5..90a008b 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -20,8 +20,8 @@
 	/* To whom we belong, unique identifier of remote MM entity */
 	struct gsm_subscriber *subscr;
 
-	/* The LCHAN that we're currently using to transmit messages */
-	struct gsm_lchan *lchan;
+	/* The associated connection we are using to transmit messages */
+	struct gsm_subscriber_connection *conn;
 
 	/* reference from MNCC or other application */
 	u_int32_t callref;
@@ -71,6 +71,6 @@
 
 /* update all transactions to use a different LCHAN, e.g.
  * after handover has succeeded */
-int trans_lchan_change(struct gsm_lchan *lchan_old,
-		       struct gsm_lchan *lchan_new);
+int trans_lchan_change(struct gsm_subscriber_connection *conn_old,
+		       struct gsm_subscriber_connection *conn_new);
 #endif
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
new file mode 100644
index 0000000..40e2191
--- /dev/null
+++ b/openbsc/include/openbsc/vty.h
@@ -0,0 +1,6 @@
+#ifndef OPENBSC_VTY_H
+#define OPENBSC_VTY_H
+
+void openbsc_vty_add_cmds(void);
+
+#endif
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 1d18475..f4d1c01 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -17,7 +17,7 @@
 		input/misdn.c input/ipaccess.c \
 		talloc_ctx.c system_information.c rest_octets.c \
 		rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
-		bts_unknown.c bsc_version.c
+		bts_unknown.c bsc_version.c bsc_api.c vty_interface_cmds.c
 
 libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
 		mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index c9852bf..5e6e819 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -1,4 +1,4 @@
-/* GSM Network Management (OML) messages on the A-bis interface 
+/* GSM Network Management (OML) messages on the A-bis interface
  * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
 
 /* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
@@ -514,7 +514,7 @@
 static void debugp_foh(struct abis_om_fom_hdr *foh)
 {
 	DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
-		obj_class_name(foh->obj_class), foh->obj_class, 
+		obj_class_name(foh->obj_class), foh->obj_class,
 		foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
 		foh->obj_inst.ts_nr);
 }
@@ -938,7 +938,7 @@
 
 		abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
 		if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
-			DEBUGPC(DNM, "CAUSE=%s\n", 
+			DEBUGPC(DNM, "CAUSE=%s\n",
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			DEBUGPC(DNM, "\n");
@@ -1349,7 +1349,7 @@
 			return -1;
 		}
 		/* read first line and parse file ID and VERSION */
-		rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", 
+		rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n",
 			    file_id, file_version);
 		if (rc != 2) {
 			perror("parsing header line of software file");
@@ -2233,7 +2233,7 @@
 }
 
 /* like abis_nm_conn_terr_traf + set_tei */
-int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port, 
+int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
 			  u_int8_t e1_timeslot, u_int8_t e1_subslot,
 			  u_int8_t tei)
 {
@@ -2595,7 +2595,7 @@
 	NM_ATT_SW_DESCR, NM_ATT_GET_ARI };
 #endif
 	
-static u_int8_t req_attr[] = { 
+static u_int8_t req_attr[] = {
 	NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE,
 	0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO,
 	0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL };
@@ -2674,11 +2674,11 @@
 		DEBUGPC(DNM, "RSL CONNECT ACK ");
 		if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP))
 			DEBUGPC(DNM, "IP=%s ",
-				inet_ntoa(*((struct in_addr *) 
+				inet_ntoa(*((struct in_addr *)
 					TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP))));
 		if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT))
 			DEBUGPC(DNM, "PORT=%u ",
-				ntohs(*((u_int16_t *) 
+				ntohs(*((u_int16_t *)
 					TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT))));
 		if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID))
 			DEBUGPC(DNM, "STREAM=0x%02x ",
@@ -2688,7 +2688,7 @@
 	case NM_MT_IPACC_RSL_CONNECT_NACK:
 		LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK ");
 		if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
-			DEBUGPC(DNM, " CAUSE=%s\n", 
+			DEBUGPC(DNM, " CAUSE=%s\n",
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			DEBUGPC(DNM, "\n");
@@ -2700,7 +2700,7 @@
 	case NM_MT_IPACC_SET_NVATTR_NACK:
 		LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
 		if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
-			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", 
+			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			LOGPC(DNM, LOGL_ERROR, "\n");
@@ -2712,7 +2712,7 @@
 	case NM_MT_IPACC_GET_NVATTR_NACK:
 		LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
 		if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
-			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", 
+			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			LOGPC(DNM, LOGL_ERROR, "\n");
@@ -2723,7 +2723,7 @@
 	case NM_MT_IPACC_SET_ATTR_NACK:
 		LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
 		if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
-			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", 
+			LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
 				nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
 		else
 			LOGPC(DNM, LOGL_ERROR, "\n");
@@ -2800,7 +2800,7 @@
 				    attr_len);
 }
 
-int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, 
+int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
 				 u_int32_t ip, u_int16_t port, u_int8_t stream)
 {
 	struct in_addr ia;
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index e7844af..0e572cc 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1,4 +1,4 @@
-/* GSM Radio Signalling Link messages on the A-bis interface 
+/* GSM Radio Signalling Link messages on the A-bis interface
  * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
 
 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
@@ -118,8 +118,8 @@
 	}
 
 	lchan = &ts->lchan[lch_idx];
-	debug_set_context(BSC_CTX_LCHAN, lchan);
-	debug_set_context(BSC_CTX_SUBSCR, lchan->subscr);
+	log_set_context(BSC_CTX_LCHAN, lchan);
+	log_set_context(BSC_CTX_SUBSCR, lchan->conn.subscr);
 
 	return lchan;
 }
@@ -222,7 +222,7 @@
 
 	LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ",
 		cause_v[0], rsl_err_name(cause_v[0]));
-	for (i = 1; i < cause_len-1; i++) 
+	for (i = 1; i < cause_len-1; i++)
 		LOGPC(DRSL, lvl, "%02x ", cause_v[i]);
 }
 
@@ -245,7 +245,7 @@
 	return abis_rsl_sendmsg(msg);
 }
 
-int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, 
+int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
 		      const u_int8_t *data, int len)
 {
 	struct abis_rsl_common_hdr *ch;
@@ -416,7 +416,7 @@
 }
 #endif
 
-int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 
+int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
 			    u_int8_t ta, u_int8_t ho_ref)
 {
 	struct abis_rsl_dchan_hdr *dh;
@@ -778,7 +778,7 @@
 			msg->lchan->state = LCHAN_S_NONE;
 	} else
 		msg->lchan->state = LCHAN_S_NONE;
- 
+
 	LOGPC(DRSL, LOGL_ERROR, "\n");
 
 	dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan);
@@ -992,12 +992,14 @@
 		break;
 	case RSL_MT_IPAC_PDCH_ACT_ACK:
 		DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
+		msg->lchan->ts->flags |= TS_F_PDCH_MODE;
 		break;
 	case RSL_MT_IPAC_PDCH_ACT_NACK:
 		LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
 		break;
 	case RSL_MT_IPAC_PDCH_DEACT_ACK:
 		DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
+		msg->lchan->ts->flags &= ~TS_F_PDCH_MODE;
 		break;
 	case RSL_MT_IPAC_PDCH_DEACT_NACK:
 		LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);
@@ -1054,7 +1056,7 @@
 		//DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(msg->trx));
 		break;
 	case RSL_MT_OVERLOAD:
-		/* indicate CCCH / ACCH / processor overload */ 
+		/* indicate CCCH / ACCH / processor overload */
 		LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n",
 		     gsm_trx_name(msg->trx));
 		break;
@@ -1244,7 +1246,7 @@
 	return 0;
 }
 
-/*	ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST 
+/*	ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
 	0x02, 0x06,
 	0x01, 0x20,
 	0x02, 0x00,
@@ -1264,7 +1266,7 @@
 	switch (rllh->c.msg_type) {
 	case RSL_MT_DATA_IND:
 		DEBUGPC(DRLL, "DATA INDICATION\n");
-		if (msgb_l2len(msg) > 
+		if (msgb_l2len(msg) >
 		    sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
 		    rllh->data[0] == RSL_IE_L3_INFO) {
 			msg->l3h = &rllh->data[3];
@@ -1276,7 +1278,7 @@
 		/* lchan is established, stop T3101 */
 		msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS;
 		bsc_del_timer(&msg->lchan->T3101);
-		if (msgb_l2len(msg) > 
+		if (msgb_l2len(msg) >
 		    sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
 		    rllh->data[0] == RSL_IE_L3_INFO) {
 			msg->l3h = &rllh->data[3];
@@ -1491,17 +1493,24 @@
 	return rc;
 }
 
-int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
+int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan, int act)
 {
 	struct msgb *msg = rsl_msgb_alloc();
 	struct abis_rsl_dchan_hdr *dh;
+	u_int8_t msg_type;
+
+	if (act)
+		msg_type = RSL_MT_IPAC_PDCH_ACT;
+	else
+		msg_type = RSL_MT_IPAC_PDCH_DEACT;
 
 	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
-	init_dchan_hdr(dh, RSL_MT_IPAC_PDCH_ACT);
+	init_dchan_hdr(dh, msg_type);
 	dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
 	dh->chan_nr = lchan2chan_nr(lchan);
 
-	DEBUGP(DRSL, "%s IPAC_PDCH_ACT\n", gsm_lchan_name(lchan));
+	DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_lchan_name(lchan),
+		act ? "" : "DE");
 
 	msg->trx = lchan->ts->trx;
 
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 80f9ba9..a7493b4 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -3,7 +3,7 @@
 /* (C) 2009 by Harald Welte <laforge@gnumonks.org>
  * All Rights Reserved
  *
- * This software is based on ideas (but not code) of BS11Config 
+ * This software is based on ideas (but not code) of BS11Config
  * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -54,9 +54,9 @@
 static char *command, *value;
 struct timer_list status_timer;
 
-static const u_int8_t obj_li_attr[] = { 
+static const u_int8_t obj_li_attr[] = {
 	NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
-	NM_ATT_BS11_L1_PROT_TYPE, 0x00, 
+	NM_ATT_BS11_L1_PROT_TYPE, 0x00,
 	NM_ATT_BS11_LINE_CFG, 0x00,
 };
 static const u_int8_t obj_bbsig0_attr[] = {
@@ -71,7 +71,7 @@
 
 static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
 
-static struct debug_target *stderr_target;
+static struct log_target *stderr_target;
 
 /* dummy function to keep gsm_data.c happy */
 struct counter *counter_alloc(const char *name)
@@ -778,7 +778,7 @@
 			serial_port = optarg;
 			break;
 		case 'b':
-			debug_parse_category_mask(stderr_target, optarg);
+			log_parse_category_mask(stderr_target, optarg);
 			break;
 		case 's':
 			fname_software = optarg;
@@ -834,10 +834,10 @@
 	struct gsm_network *gsmnet;
 	int rc;
 
-	debug_init();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
-	debug_set_all_filter(stderr_target, 1);
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
 	handle_options(argc, argv);
 	bts_model_bs11_init();
 
diff --git a/openbsc/src/bsc_api.c b/openbsc/src/bsc_api.c
new file mode 100644
index 0000000..b504752
--- /dev/null
+++ b/openbsc/src/bsc_api.c
@@ -0,0 +1,33 @@
+/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ *
+ * 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 <openbsc/bsc_api.h>
+#include <openbsc/abis_rsl.h>
+
+
+int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
+			struct msgb *msg, int link_id)
+{
+	msg->lchan = conn->lchan;
+	msg->trx = msg->lchan->ts->trx;
+	return rsl_data_request(msg, link_id);
+}
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 4cde4dd..a50d4ab 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -38,7 +38,7 @@
 #include <openbsc/signal.h>
 
 /* MCC and MNC for the Location Area Identifier */
-static struct debug_target *stderr_target;
+static struct log_target *stderr_target;
 struct gsm_network *bsc_gsmnet = 0;
 static const char *database_name = "hlr.sqlite3";
 static const char *config_file = "openbsc.cfg";
@@ -127,10 +127,10 @@
 			print_help();
 			exit(0);
 		case 's':
-			debug_set_use_color(stderr_target, 0);
+			log_set_use_color(stderr_target, 0);
 			break;
 		case 'd':
-			debug_parse_category_mask(stderr_target, optarg);
+			log_parse_category_mask(stderr_target, optarg);
 			break;
 		case 'l':
 			database_name = strdup(optarg);
@@ -142,13 +142,13 @@
 			create_pcap_file(optarg);
 			break;
 		case 'T':
-			debug_set_print_timestamp(stderr_target, 1);
+			log_set_print_timestamp(stderr_target, 1);
 			break;
 		case 'P':
 			ipacc_rtp_direct = 0;
 			break;
 		case 'e':
-			debug_set_log_level(stderr_target, atoi(optarg));
+			log_set_log_level(stderr_target, atoi(optarg));
 			break;
 		case 'V':
 			print_version();
@@ -211,21 +211,21 @@
 {
 	int rc;
 
-	debug_init();
+	log_init(&log_info);
 	tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
 	talloc_ctx_init();
 	on_dso_load_token();
 	on_dso_load_rrlp();
 	on_dso_load_ho_dec();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
 
 	bts_model_unknown_init();
 	bts_model_bs11_init();
 	bts_model_nanobts_init();
 
 	/* enable filters */
-	debug_set_all_filter(stderr_target, 1);
+	log_set_all_filter(stderr_target, 1);
 
 	/* parse options */
 	handle_options(argc, argv);
@@ -262,7 +262,7 @@
 
 	while (1) {
 		bsc_upqueue(bsc_gsmnet);
-		debug_reset_context();
+		log_reset_context();
 		bsc_select_main(0);
 	}
 }
diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c
index 1551d94..9a4f5aa 100644
--- a/openbsc/src/bsc_rll.c
+++ b/openbsc/src/bsc_rll.c
@@ -51,8 +51,11 @@
 
 static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
 {
+	struct gsm_subscriber_connection *conn;
+
+	conn = &rllr->lchan->conn;
 	llist_del(&rllr->list);
-	put_lchan(rllr->lchan);
+	put_subscr_con(conn);
 	rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
 	talloc_free(rllr);
 }
@@ -70,6 +73,7 @@
 			     enum bsc_rllr_ind),
 		  void *data)
 {
+	struct gsm_subscriber_connection *conn;
 	struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
 	u_int8_t link_id;
 	if (!rllr)
@@ -83,7 +87,8 @@
 	     lchan->type == GSM_LCHAN_TCH_H) && sapi != 0)
 		link_id |= 0x40;
 
-	use_lchan(lchan);
+	conn = &lchan->conn;
+	use_subscr_con(conn);
 	rllr->lchan = lchan;
 	rllr->link_id = link_id;
 	rllr->cb = cb;
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 2e88524..cd48e43 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -152,6 +152,7 @@
 	[GSM_PCHAN_TCH_H] = 2,
 	[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
 	/* FIXME: what about dynamic TCH_F_TCH_H ? */
+	[GSM_PCHAN_TCH_F_PDCH] = 1,
 };
 
 static struct gsm_lchan *
@@ -167,7 +168,14 @@
 		ts = &trx->ts[j];
 		if (!ts_is_usable(ts))
 			continue;
-		if (ts->pchan != pchan)
+		/* ip.access dynamic TCH/F + PDCH combination */
+		if (ts->pchan == GSM_PCHAN_TCH_F_PDCH &&
+		    pchan == GSM_PCHAN_TCH_F) {
+			/* we can only consider such a dynamic channel
+			 * if the PDCH is currently inactive */
+			if (ts->flags & TS_F_PDCH_MODE)
+				continue;
+		} else if (ts->pchan != pchan)
 			continue;
 		/* check if all sub-slots are allocated yet */
 		for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
@@ -177,6 +185,7 @@
 				return lc;
 		}
 	}
+
 	return NULL;
 }
 
@@ -252,7 +261,6 @@
 
 	if (lchan) {
 		lchan->type = type;
-		lchan->use_count = 0;
 
 		/* clear sapis */
 		memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
@@ -260,10 +268,21 @@
 		/* clear multi rate config */
 		memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf));
 
+		/* clear per MSC/BSC data */
+		memset(&lchan->conn, 0, sizeof(lchan->conn));
+
 		/* Configure the time and start it so it will be closed */
-		lchan->release_timer.cb = auto_release_channel;
-		lchan->release_timer.data = lchan;
-		bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
+		lchan->conn.lchan = lchan;
+		lchan->conn.bts = lchan->ts->trx->bts;
+		lchan->conn.release_timer.cb = auto_release_channel;
+		lchan->conn.release_timer.data = lchan;
+		bsc_schedule_timer(&lchan->conn.release_timer, LCHAN_RELEASE_TIMEOUT);
+
+	} else {
+		struct challoc_signal_data sig;
+		sig.bts = bts;
+		sig.type = type;
+		dispatch_signal(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig);
 	}
 
 	return lchan;
@@ -272,22 +291,24 @@
 /* Free a logical channel */
 void lchan_free(struct gsm_lchan *lchan)
 {
+	struct challoc_signal_data sig;
 	int i;
 
+	sig.type = lchan->type;
 	lchan->type = GSM_LCHAN_NONE;
-	if (lchan->subscr) {
-		subscr_put(lchan->subscr);
-		lchan->subscr = NULL;
+	if (lchan->conn.subscr) {
+		subscr_put(lchan->conn.subscr);
+		lchan->conn.subscr = NULL;
 	}
 
 	/* We might kill an active channel... */
-	if (lchan->use_count != 0) {
+	if (lchan->conn.use_count != 0) {
 		dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
-		lchan->use_count = 0;
+		lchan->conn.use_count = 0;
 	}
 
 	/* stop the timer */
-	bsc_del_timer(&lchan->release_timer);
+	bsc_del_timer(&lchan->conn.release_timer);
 	bsc_del_timer(&lchan->T3101);
 
 	/* clear cached measuement reports */
@@ -299,7 +320,11 @@
 	for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
 		lchan->neigh_meas[i].arfcn = 0;
 
-	lchan->silent_call = 0;
+	lchan->conn.silent_call = 0;
+
+	sig.lchan = lchan;
+	sig.bts = lchan->ts->trx->bts;
+	dispatch_signal(SS_CHALLOC, S_CHALLOC_FREED, &sig);
 
 	/* FIXME: ts_free() the timeslot, if we're the last logical
 	 * channel using it */
@@ -308,19 +333,19 @@
 /* Consider releasing the channel now */
 int lchan_auto_release(struct gsm_lchan *lchan)
 {
-	if (lchan->use_count > 0) {
+	if (lchan->conn.use_count > 0) {
 		return 0;
 	}
 
 	/* Assume we have GSM04.08 running and send a release */
-	if (lchan->subscr) {
+	if (lchan->conn.subscr) {
 		gsm48_send_rr_release(lchan);
 	}
 
 	/* spoofed? message */
-	if (lchan->use_count < 0)
+	if (lchan->conn.use_count < 0)
 		LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
-			lchan->use_count);
+			lchan->conn.use_count);
 
 	DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
 	rsl_release_request(lchan, 0);
@@ -333,19 +358,19 @@
 	struct gsm_lchan *lchan = _lchan;
 
 	if (!lchan_auto_release(lchan))
-		bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
+		bsc_schedule_timer(&lchan->conn.release_timer, LCHAN_RELEASE_TIMEOUT);
 }
 
 struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
 	struct gsm_bts_trx *trx;
-	int ts_no, lchan_no; 
+	int ts_no, lchan_no;
 
 	llist_for_each_entry(trx, &bts->trx_list, list) {
 		for (ts_no = 0; ts_no < 8; ++ts_no) {
 			for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) {
 				struct gsm_lchan *lchan =
 					&trx->ts[ts_no].lchan[lchan_no];
-				if (subscr == lchan->subscr)
+				if (subscr == lchan->conn.subscr)
 					return lchan;
 			}
 		}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 10c1d6d..8bf47ab 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -254,7 +254,7 @@
 	struct gsm_subscriber *subscr;
 
 	/* Is this subscriber known in the db? */
-	subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); 
+	subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
 	if (subscr) {
 		result = dbi_conn_queryf(conn,
                          "UPDATE Subscriber set updated = datetime('now') "
@@ -288,6 +288,8 @@
 	return subscr;
 }
 
+static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size);
+
 static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
 {
 	dbi_result result;
@@ -316,9 +318,10 @@
 		strncpy(equip->imei, string, sizeof(equip->imei));
 
 	string = dbi_result_get_string(result, "classmark1");
-	if (string)
-		 cm1 = atoi(string) & 0xff;
-	equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
+	if (string) {
+		cm1 = atoi(string) & 0xff;
+		memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1));
+	}
 
 	equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
 	cm2 = dbi_result_get_binary(result, "classmark2");
@@ -1014,7 +1017,7 @@
 }
 
 /* retrieve the next unsent SMS with ID >= min_id */
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
+struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id)
 {
 	dbi_result result;
 	struct gsm_sms *sms;
@@ -1041,7 +1044,7 @@
 	return sms;
 }
 
-struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id)
+struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id)
 {
 	dbi_result result;
 	struct gsm_sms *sms;
@@ -1049,7 +1052,7 @@
 	result = dbi_conn_queryf(conn,
 		"SELECT * FROM SMS,Subscriber "
 		"WHERE sms.receiver_id >= %llu AND sms.sent is NULL "
-			"AND sms.receiver_id = subscriber.id " 
+			"AND sms.receiver_id = subscriber.id "
 			"AND subscriber.lac > 0 "
 		"ORDER BY sms.receiver_id, id LIMIT 1",
 		min_subscr_id);
@@ -1133,7 +1136,7 @@
 	return 0;
 }
 
-int db_apdu_blob_store(struct gsm_subscriber *subscr, 
+int db_apdu_blob_store(struct gsm_subscriber *subscr,
 			u_int8_t apdu_id_flags, u_int8_t len,
 			u_int8_t *apdu)
 {
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index 8f7a1c3..a55d790 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -1,5 +1,6 @@
-/* Debugging/Logging support code */
-/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+/* OpenBSC Debugging/Logging support code */
+
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
  * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
  * All Rights Reserved
  *
@@ -27,401 +28,161 @@
 #include <time.h>
 #include <errno.h>
 
-#include <openbsc/debug.h>
 #include <osmocore/talloc.h>
 #include <osmocore/utils.h>
+#include <osmocore/logging.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/gsm_subscriber.h>
+#include <openbsc/debug.h>
 
 /* default categories */
-static struct debug_category default_categories[Debug_LastEntry] = {
-    [DRLL]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DCC]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DNM]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DRR]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DRSL]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DMM]	= { .enabled = 1, .loglevel = LOGL_INFO },
-    [DMNCC]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DSMS]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DPAG]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DMEAS]	= { .enabled = 0, .loglevel = LOGL_NOTICE },
-    [DMI]	= { .enabled = 0, .loglevel = LOGL_NOTICE },
-    [DMIB]	= { .enabled = 0, .loglevel = LOGL_NOTICE },
-    [DMUX]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DINP]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DSCCP]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DMSC]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DMGCP]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DHO]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DDB]	= { .enabled = 1, .loglevel = LOGL_NOTICE },
-    [DREF]	= { .enabled = 0, .loglevel = LOGL_NOTICE },
+static const struct log_info_cat default_categories[] = {
+	[DRLL] = {
+		.name = "DRLL",
+		.description = "Radio Link Layer",
+		.color = "\033[1;31m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DCC] = {
+		.name = "DCC",
+		.description = "Call Control",
+		.color = "\033[1;32m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DMM] = {
+		.name = "DMM",
+		.description = "Mobility Management",
+		.color = "\033[1;33m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DRR] = {
+		.name = "DRR",
+		.description = "Radio Resource",
+		.color = "\033[1;34m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DRSL] = {
+		.name = "DRSL",
+		.description = "Radio Siganlling Link",
+		.color = "\033[1;35m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DNM] =	{
+		.name = "DNM",
+		.description = "Network Management (OML)",
+		.color = "\033[1;36m",
+		.enabled = 1, .loglevel = LOGL_INFO,
+	},
+	[DMNCC] = {
+		.name = "DMNCC",
+		.description = "BSC<->MSC interface",
+		.color = "\033[1;39m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DSMS] = {
+		.name = "DSMS",
+		.description = "Short Message Service",
+		.color = "\033[1;37m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DPAG]	= {
+		.name = "DPAG",
+		.description = "Paging",
+		.color = "\033[1;38m",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DMEAS] = {
+		.name = "DMEAS",
+		.description = "Measurement Processing",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
+	[DMI] = {
+		.name = "DMI",
+		.description = "mISDN Input Driver",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
+	[DMIB] = {
+		.name = "DMIB",
+		.description = "mISDN B-Channels",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
+	[DMUX] = {
+		.name = "DMUX",
+		.description = "TRAU Frame Multiplex",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DINP] = {
+		.name = "DINP",
+		.description = "Input Driver",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DSCCP] = {
+		.name = "DSCCP",
+		.description = "SCCP Protocol",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DMSC] = {
+		.name = "DMSC",
+		.description = "Mobile Switching Center",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DMGCP] = {
+		.name = "DMGCP",
+		.description = "Media Gateway Control Protocol",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DHO] = {
+		.name = "DHO",
+		.description = "Hand-Over",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DDB] = {
+		.name = "DDB",
+		.description = "Database",
+		.enabled = 1, .loglevel = LOGL_NOTICE,
+	},
+	[DREF] = {
+		.name = "DREF",
+		.description = "Reference Counting",
+		.enabled = 0, .loglevel = LOGL_NOTICE,
+	},
 };
 
-struct debug_info {
-	const char *name;
-	const char *color;
-	const char *description;
-	int number;
-	int position;
+enum log_ctxt {
+	CTX_SUBSCRIBER,
 };
 
-struct debug_context {
-	struct gsm_lchan *lchan;
-	struct gsm_subscriber *subscr;
-	struct gsm_bts *bts;
+enum log_filter {
+	_FLT_ALL = LOG_FILTER_ALL,	/* libosmocore */
+	FLT_IMSI = 1,
 };
 
-static struct debug_context debug_context;
-static void *tall_dbg_ctx = NULL;
-static LLIST_HEAD(target_list);
+static int filter_fn(const struct log_context *ctx,
+		     struct log_target *tar)
+{
+	struct gsm_subscriber *subscr = ctx->ctx[CTX_SUBSCRIBER];
 
-#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
-	{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
+	if ((tar->filter_map & (1 << FLT_IMSI)) != 0
+	    && subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0)
+		return 1;
 
-static const struct debug_info debug_info[] = {
-	DEBUG_CATEGORY(DRLL,  "DRLL", "\033[1;31m", "")
-	DEBUG_CATEGORY(DCC,   "DCC",  "\033[1;32m", "")
-	DEBUG_CATEGORY(DMM,   "DMM",  "\033[1;33m", "")
-	DEBUG_CATEGORY(DRR,   "DRR",  "\033[1;34m", "")
-	DEBUG_CATEGORY(DRSL,  "DRSL", "\033[1;35m", "")
-	DEBUG_CATEGORY(DNM,   "DNM",  "\033[1;36m", "")
-	DEBUG_CATEGORY(DSMS,  "DSMS", "\033[1;37m", "")
-	DEBUG_CATEGORY(DPAG,  "DPAG", "\033[1;38m", "")
-	DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
-	DEBUG_CATEGORY(DINP,  "DINP", "", "")
-	DEBUG_CATEGORY(DMI,  "DMI", "", "")
-	DEBUG_CATEGORY(DMIB,  "DMIB", "", "")
-	DEBUG_CATEGORY(DMUX,  "DMUX", "", "")
-	DEBUG_CATEGORY(DMEAS,  "DMEAS", "", "")
-	DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
-	DEBUG_CATEGORY(DMSC, "DMSC", "", "")
-	DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
-	DEBUG_CATEGORY(DHO, "DHO", "", "")
-	DEBUG_CATEGORY(DDB, "DDB", "", "")
-	DEBUG_CATEGORY(DREF, "DREF", "", "")
+	return 0;
+}
+
+const struct log_info log_info = {
+	.filter_fn = filter_fn,
+	.cat = default_categories,
+	.num_cat = ARRAY_SIZE(default_categories),
 };
 
-static const struct value_string loglevel_strs[] = {
-	{ 0,	"EVERYTHING" },
-	{ 1,	"DEBUG" },
-	{ 3,	"INFO" },
-	{ 5,	"NOTICE" },
-	{ 7,	"ERROR" },
-	{ 8,	"FATAL" },
-	{ 0, NULL },
-};
-
-int debug_parse_level(const char *lvl)
-{
-	return get_string_value(loglevel_strs, lvl);
-}
-
-int debug_parse_category(const char *category)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
-		if (!strcasecmp(debug_info[i].name+1, category))
-			return debug_info[i].number;
-	}
-
-	return -EINVAL;
-}
-
-/*
- * Parse the category mask.
- * The format can be this: category1:category2:category3
- * or category1,2:category2,3:...
- */
-void debug_parse_category_mask(struct debug_target* target, const char *_mask)
-{
-	int i = 0;
-	char *mask = strdup(_mask);
-	char *category_token = NULL;
-
-	/* Disable everything to enable it afterwards */
-	for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
-		target->categories[i].enabled = 0;
-
-	category_token = strtok(mask, ":");
-	do {
-		for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
-			char* colon = strstr(category_token, ",");
-			int length = strlen(category_token);
-
-			if (colon)
-			    length = colon - category_token;
-
-			if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
-				int number = debug_info[i].number;
-				int level = 0;
-
-				if (colon)
-					level = atoi(colon+1);
-
-				target->categories[number].enabled = 1;
-				target->categories[number].loglevel = level;
-			}
-		}
-	} while ((category_token = strtok(NULL, ":")));
-
-	free(mask);
-}
-
-static const char* color(int subsys)
-{
-	int i = 0;
-
-	for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
-		if (debug_info[i].number == subsys)
-			return debug_info[i].color;
-	}
-
-	return "";
-}
-
-static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
-		    int cont, const char *format, va_list ap)
-{
-	char col[30];
-	char sub[30];
-	char tim[30];
-	char buf[4096];
-	char final[4096];
-
-	/* prepare the data */
-	col[0] = '\0';
-	sub[0] = '\0';
-	tim[0] = '\0';
-	buf[0] = '\0';
-
-	/* are we using color */
-	if (target->use_color) {
-		snprintf(col, sizeof(col), "%s", color(subsys));
-		col[sizeof(col)-1] = '\0';
-	}
-	vsnprintf(buf, sizeof(buf), format, ap);
-	buf[sizeof(buf)-1] = '\0';
-
-	if (!cont) {
-		if (target->print_timestamp) {
-			char *timestr;
-			time_t tm;
-			tm = time(NULL);
-			timestr = ctime(&tm);
-			timestr[strlen(timestr)-1] = '\0';
-			snprintf(tim, sizeof(tim), "%s ", timestr);
-			tim[sizeof(tim)-1] = '\0';
-		}
-		snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
-		sub[sizeof(sub)-1] = '\0';
-	}
-
-	snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
-	final[sizeof(final)-1] = '\0';
-	target->output(target, final);
-}
-
-
-static void _debugp(unsigned int subsys, int level, char *file, int line,
-		    int cont, const char *format, va_list ap)
-{
-	struct debug_target *tar;
-
-	llist_for_each_entry(tar, &target_list, entry) {
-		struct debug_category *category;
-		int output = 0;
-
-		category = &tar->categories[subsys];
-		/* subsystem is not supposed to be debugged */
-		if (!category->enabled)
-			continue;
-
-		/* Check the global log level */
-		if (tar->loglevel != 0 && level < tar->loglevel)
-			continue;
-
-		/* Check the category log level */
-		if (tar->loglevel == 0 && category->loglevel != 0 && level < category->loglevel)
-			continue;
-
-		/*
-		 * Apply filters here... if that becomes messy we will need to put
-		 * filters in a list and each filter will say stop, continue, output
-		 */
-		if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
-			output = 1;
-		} else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
-			      && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
-			output = 1;
-		}
-
-		if (output) {
-			/* FIXME: copying the va_list is an ugly workaround against a bug
-			 * hidden somewhere in _output.  If we do not copy here, the first
-			 * call to _output() will corrupt the va_list contents, and any
-			 * further _output() calls with the same va_list will segfault */
-			va_list bp;
-			va_copy(bp, ap);
-			_output(tar, subsys, file, line, cont, format, bp);
-			va_end(bp);
-		}
-	}
-}
-
-void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
-{
-	va_list ap;
-
-	va_start(ap, format);
-	_debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
-	va_end(ap);
-}
-
-void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
-{
-	va_list ap;
-
-	va_start(ap, format);
-	_debugp(subsys, level, file, line, cont, format, ap);
-	va_end(ap);
-}
-
-static char hexd_buff[4096];
-
-char *hexdump(const unsigned char *buf, int len)
-{
-	int i;
-	char *cur = hexd_buff;
-
-	hexd_buff[0] = 0;
-	for (i = 0; i < len; i++) {
-		int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
-		int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
-		if (rc <= 0)
-			break;
-		cur += rc;
-	}
-	hexd_buff[sizeof(hexd_buff)-1] = 0;
-	return hexd_buff;
-}
-
-
-
-void debug_add_target(struct debug_target *target)
-{
-	llist_add_tail(&target->entry, &target_list);
-}
-
-void debug_del_target(struct debug_target *target)
-{
-	llist_del(&target->entry);
-}
-
-void debug_reset_context(void)
-{
-	memset(&debug_context, 0, sizeof(debug_context));
-}
-
-/* currently we are not reffing these */
-void debug_set_context(int ctx, void *value)
-{
-	switch (ctx) {
-	case BSC_CTX_LCHAN:
-		debug_context.lchan = (struct gsm_lchan *) value;
-		break;
-	case BSC_CTX_SUBSCR:
-		debug_context.subscr = (struct gsm_subscriber *) value;
-		break;
-	case BSC_CTX_BTS:
-		debug_context.bts = (struct gsm_bts *) value;
-		break;
-	case BSC_CTX_SCCP:
-		break;
-	default:
-		break;
-	}
-}
-
-void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
+void log_set_imsi_filter(struct log_target *target, const char *imsi)
 {
 	if (imsi) {
-		target->filter_map |= DEBUG_FILTER_IMSI;
-		target->imsi_filter = talloc_strdup(target, imsi);
-	} else if (target->imsi_filter) {
-		target->filter_map &= ~DEBUG_FILTER_IMSI;
-		talloc_free(target->imsi_filter);
-		target->imsi_filter = NULL;
+		target->filter_map |= (1 << FLT_IMSI);
+		target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi);
+	} else if (target->filter_data[FLT_IMSI]) {
+		target->filter_map &= ~(1 << FLT_IMSI);
+		talloc_free(target->filter_data[FLT_IMSI]);
+		target->filter_data[FLT_IMSI] = NULL;
 	}
 }
-
-void debug_set_all_filter(struct debug_target *target, int all)
-{
-	if (all)
-		target->filter_map |= DEBUG_FILTER_ALL;
-	else
-		target->filter_map &= ~DEBUG_FILTER_ALL;
-}
-
-void debug_set_use_color(struct debug_target *target, int use_color)
-{
-	target->use_color = use_color;
-}
-
-void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
-{
-	target->print_timestamp = print_timestamp;
-}
-
-void debug_set_log_level(struct debug_target *target, int log_level)
-{
-	target->loglevel = log_level;
-}
-
-void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
-{
-	if (category >= Debug_LastEntry)
-		return;
-	target->categories[category].enabled = !!enable;
-	target->categories[category].loglevel = level;
-}
-
-static void _stderr_output(struct debug_target *target, const char *log)
-{
-	fprintf(target->tgt_stdout.out, "%s", log);
-	fflush(target->tgt_stdout.out);
-}
-
-struct debug_target *debug_target_create(void)
-{
-	struct debug_target *target;
-
-	target = talloc_zero(tall_dbg_ctx, struct debug_target);
-	if (!target)
-		return NULL;
-
-	INIT_LLIST_HEAD(&target->entry);
-	memcpy(target->categories, default_categories, sizeof(default_categories));
-	target->use_color = 1;
-	target->print_timestamp = 0;
-	target->loglevel = 0;
-	return target;
-}
-
-struct debug_target *debug_target_create_stderr(void)
-{
-	struct debug_target *target;
-
-	target = debug_target_create();
-	if (!target)
-		return NULL;
-
-	target->tgt_stdout.out = stderr;
-	target->output = _stderr_output;
-	return target;
-}
-
-void debug_init(void)
-{
-	tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
-}
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index c20359c..fba59a7 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -442,7 +442,7 @@
 			return -EINVAL;
 		}
 
-		debug_set_context(BSC_CTX_BTS, link->trx->bts);
+		log_set_context(BSC_CTX_BTS, link->trx->bts);
 		switch (link->type) {
 		case E1INP_SIGN_OML:
 			msg->trx = link->trx;
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 2eda0bd..b0e5541 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1,4 +1,4 @@
-/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface 
+/* 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>
@@ -58,7 +58,7 @@
 int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
 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);
+static void schedule_reject(struct gsm_subscriber_connection *conn);
 
 struct gsm_lai {
 	u_int16_t mcc;
@@ -96,35 +96,35 @@
 	}
 }
 
-static void release_loc_updating_req(struct gsm_lchan *lchan)
+static void release_loc_updating_req(struct gsm_subscriber_connection *conn)
 {
-	if (!lchan->loc_operation)
+	if (!conn->loc_operation)
 		return;
 
-	bsc_del_timer(&lchan->loc_operation->updating_timer);
-	talloc_free(lchan->loc_operation);
-	lchan->loc_operation = 0;
-	put_lchan(lchan);
+	bsc_del_timer(&conn->loc_operation->updating_timer);
+	talloc_free(conn->loc_operation);
+	conn->loc_operation = 0;
+	put_subscr_con(conn);
 }
 
-static void allocate_loc_updating_req(struct gsm_lchan *lchan)
+static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn)
 {
-	use_lchan(lchan);
-	release_loc_updating_req(lchan);
+	use_subscr_con(conn)
+	release_loc_updating_req(conn);
 
-	lchan->loc_operation = talloc_zero(tall_locop_ctx,
+	conn->loc_operation = talloc_zero(tall_locop_ctx,
 					   struct gsm_loc_updating_operation);
 }
 
-static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
+static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg)
 {
-	if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) {
+	if (authorize_subscriber(conn->loc_operation, conn->subscr)) {
 		int rc;
 
-		db_subscriber_alloc_tmsi(lchan->subscr);
-		release_loc_updating_req(lchan);
-		rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
-		if (lchan->ts->trx->bts->network->send_mm_info) {
+		db_subscriber_alloc_tmsi(conn->subscr);
+		release_loc_updating_req(conn);
+		rc = gsm0408_loc_upd_acc(msg->lchan, conn->subscr->tmsi);
+		if (msg->lchan->ts->trx->bts->network->send_mm_info) {
 			/* send MM INFO with network name */
 			rc = gsm48_tx_mm_info(msg->lchan);
 		}
@@ -132,10 +132,11 @@
 		/* call subscr_update after putting the loc_upd_acc
 		 * in the transmit queue, since S_SUBSCR_ATTACHED might
 		 * trigger further action like SMS delivery */
-		subscr_update(lchan->subscr, msg->trx->bts,
+		subscr_update(conn->subscr, msg->trx->bts,
 			      GSM_SUBSCRIBER_UPDATE_ATTACHED);
+
 		/* try to close channel ASAP */
-		lchan_auto_release(lchan);
+		lchan_auto_release(conn->lchan);
 		return rc;
 	}
 
@@ -158,14 +159,14 @@
 	if (!lchan)
 		return 0;
 
-	release_loc_updating_req(lchan);
+	release_loc_updating_req(&lchan->conn);
 
 	/* Free all transactions that are associated with the released lchan */
 	/* FIXME: this is not neccessarily the right thing to do, we should
 	 * only set trans->lchan to NULL and wait for another lchan to be
 	 * established to the same MM entity (phone/subscriber) */
 	llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) {
-		if (trans->lchan == lchan)
+		if (trans->conn && trans->conn->lchan == lchan)
 			trans_free(trans);
 	}
 
@@ -175,11 +176,13 @@
 /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
 int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm_bts *bts = lchan->ts->trx->bts;
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
 	
 	msg->lchan = lchan;
+	conn = &lchan->conn;
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
 	gh->proto_discr = GSM48_PDISC_MM;
@@ -187,8 +190,8 @@
 	gh->data[0] = cause;
 
 	LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
-	     "LAC=%u BTS=%u\n", lchan->subscr ?
-	     			subscr_name(lchan->subscr) : "unknown",
+	     "LAC=%u BTS=%u\n", conn->subscr ?
+	     			subscr_name(conn->subscr) : "unknown",
 	     lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr);
 
 	counter_inc(bts->network->stats.loc_upd_resp.reject);
@@ -245,6 +248,7 @@
 /* Parse Chapter 9.2.11 Identity Response */
 static int mm_rx_id_resp(struct msgb *msg)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm_lchan *lchan = msg->lchan;
 	struct gsm_bts *bts = lchan->ts->trx->bts;
@@ -256,51 +260,54 @@
 	DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
 		mi_type, mi_string);
 
+	conn = &lchan->conn;
+
 	dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
 
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
 		/* look up subscriber based on IMSI, create if not found */
-		if (!lchan->subscr) {
-			lchan->subscr = subscr_get_by_imsi(net, mi_string);
-			if (!lchan->subscr)
-				lchan->subscr = db_create_subscriber(net, mi_string);
+		if (!conn->subscr) {
+			conn->subscr = subscr_get_by_imsi(net, mi_string);
+			if (!conn->subscr)
+				conn->subscr = db_create_subscriber(net, mi_string);
 		}
-		if (lchan->loc_operation)
-			lchan->loc_operation->waiting_for_imsi = 0;
+		if (conn->loc_operation)
+			conn->loc_operation->waiting_for_imsi = 0;
 		break;
 	case GSM_MI_TYPE_IMEI:
 	case GSM_MI_TYPE_IMEISV:
 		/* update subscribe <-> IMEI mapping */
-		if (lchan->subscr) {
-			db_subscriber_assoc_imei(lchan->subscr, mi_string);
-			db_sync_equipment(&lchan->subscr->equipment);
+		if (conn->subscr) {
+			db_subscriber_assoc_imei(conn->subscr, mi_string);
+			db_sync_equipment(&conn->subscr->equipment);
 		}
-		if (lchan->loc_operation)
-			lchan->loc_operation->waiting_for_imei = 0;
+		if (conn->loc_operation)
+			conn->loc_operation->waiting_for_imei = 0;
 		break;
 	}
 
 	/* Check if we can let the mobile station enter */
-	return gsm0408_authorize(lchan, msg);
+	return gsm0408_authorize(conn, msg);
 }
 
 
 static void loc_upd_rej_cb(void *data)
 {
-	struct gsm_lchan *lchan = data;
+	struct gsm_subscriber_connection *conn = data;
+	struct gsm_lchan *lchan = conn->lchan;
 	struct gsm_bts *bts = lchan->ts->trx->bts;
 
-	release_loc_updating_req(lchan);
+	release_loc_updating_req(conn);
 	gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
 	lchan_auto_release(lchan);
 }
 
-static void schedule_reject(struct gsm_lchan *lchan)
+static void schedule_reject(struct gsm_subscriber_connection *conn)
 {
-	lchan->loc_operation->updating_timer.cb = loc_upd_rej_cb;
-	lchan->loc_operation->updating_timer.data = lchan;
-	bsc_schedule_timer(&lchan->loc_operation->updating_timer, 5, 0);
+	conn->loc_operation->updating_timer.cb = loc_upd_rej_cb;
+	conn->loc_operation->updating_timer.data = conn;
+	bsc_schedule_timer(&conn->loc_operation->updating_timer, 5, 0);
 }
 
 static const char *lupd_name(u_int8_t type)
@@ -320,6 +327,7 @@
 /* Chapter 9.2.15: Receive Location Updating Request */
 static int mm_rx_loc_upd_req(struct msgb *msg)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm48_loc_upd_req *lu;
 	struct gsm_subscriber *subscr = NULL;
@@ -330,6 +338,7 @@
 	int rc;
 
  	lu = (struct gsm48_loc_upd_req *) gh->data;
+	conn = &lchan->conn;
 
 	mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
 
@@ -356,21 +365,21 @@
 	 * Pseudo Spoof detection: Just drop a second/concurrent
 	 * location updating request.
 	 */
-	if (lchan->loc_operation) {
+	if (conn->loc_operation) {
 		DEBUGPC(DMM, "ignoring request due an existing one: %p.\n",
-			lchan->loc_operation);
+			conn->loc_operation);
 		gsm0408_loc_upd_rej(lchan, GSM48_REJECT_PROTOCOL_ERROR);
 		return 0;
 	}
 
-	allocate_loc_updating_req(lchan);
+	allocate_loc_updating_req(&lchan->conn);
 
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
 		DEBUGPC(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;
+		conn->loc_operation->waiting_for_imei = 1;
 
 		/* look up subscriber based on IMSI, create if not found */
 		subscr = subscr_get_by_imsi(bts->network, mi_string);
@@ -382,7 +391,7 @@
 		DEBUGPC(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;
+		conn->loc_operation->waiting_for_imei = 1;
 
 		/* look up the subscriber based on TMSI, request IMSI if it fails */
 		subscr = subscr_get_by_tmsi(bts->network,
@@ -390,7 +399,7 @@
 		if (!subscr) {
 			/* send IDENTITY REQUEST message to get IMSI */
 			rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
-			lchan->loc_operation->waiting_for_imsi = 1;
+			conn->loc_operation->waiting_for_imsi = 1;
 		}
 		break;
 	case GSM_MI_TYPE_IMEI:
@@ -404,7 +413,7 @@
 	}
 
 	/* schedule the reject timer */
-	schedule_reject(lchan);
+	schedule_reject(conn);
 
 	if (!subscr) {
 		DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n");
@@ -412,12 +421,12 @@
 		return -EINVAL;
 	}
 
-	lchan->subscr = subscr;
-	lchan->subscr->equipment.classmark1 = lu->classmark1;
+	conn->subscr = subscr;
+	conn->subscr->equipment.classmark1 = lu->classmark1;
 
 	/* check if we can let the subscriber into our network immediately
 	 * or if we need to wait for identity responses. */
-	return gsm0408_authorize(lchan, msg);
+	return gsm0408_authorize(conn, msg);
 }
 
 #if 0
@@ -566,7 +575,7 @@
 }
 
 /* 9.2.6 CM service reject */
-static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan,
+static int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
 				enum gsm48_reject_value value)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
@@ -574,8 +583,8 @@
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
 
-	msg->lchan = lchan;
-	use_lchan(lchan);
+	msg->lchan = conn->lchan;
+	use_subscr_con(conn);
 
 	gh->proto_discr = GSM48_PDISC_MM;
 	gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
@@ -613,20 +622,20 @@
 	DEBUGP(DMM, "<- CM SERVICE REQUEST ");
 	if (msg->data_len < sizeof(struct gsm48_service_request*)) {
 		DEBUGPC(DMM, "wrong sized message\n");
-		return gsm48_tx_mm_serv_rej(msg->lchan,
+		return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
 					    GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
 	if (msg->data_len < req->mi_len + 6) {
 		DEBUGPC(DMM, "does not fit in packet\n");
-		return gsm48_tx_mm_serv_rej(msg->lchan,
+		return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
 					    GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
 	mi_type = mi[0] & GSM_MI_TYPE_MASK;
 	if (mi_type != GSM_MI_TYPE_TMSI) {
 		DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type);
-		return gsm48_tx_mm_serv_rej(msg->lchan,
+		return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
 					    GSM48_REJECT_INCORRECT_MESSAGE);
 	}
 
@@ -644,12 +653,12 @@
 
 	/* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
 	if (!subscr)
-		return gsm48_tx_mm_serv_rej(msg->lchan,
+		return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
 					    GSM48_REJECT_IMSI_UNKNOWN_IN_HLR);
 
-	if (!msg->lchan->subscr)
-		msg->lchan->subscr = subscr;
-	else if (msg->lchan->subscr == subscr)
+	if (!msg->lchan->conn.subscr)
+		msg->lchan->conn.subscr = subscr;
+	else if (msg->lchan->conn.subscr == subscr)
 		subscr_put(subscr); /* lchan already has a ref, don't need another one */
 	else {
 		DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
@@ -749,8 +758,8 @@
 		break;
 	case GSM48_MT_MM_TMSI_REALL_COMPL:
 		DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
-		       msg->lchan->subscr ?
-				subscr_name(msg->lchan->subscr) :
+		       msg->lchan->conn.subscr ?
+				subscr_name(msg->lchan->conn.subscr) :
 				"unknown subscriber");
 		break;
 	case GSM48_MT_MM_IMSI_DETACH_IND:
@@ -815,7 +824,7 @@
 static int gsm48_rx_rr_classmark(struct msgb *msg)
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
-	struct gsm_subscriber *subscr = msg->lchan->subscr;
+	struct gsm_subscriber *subscr = msg->lchan->conn.subscr;
 	unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
 	u_int8_t cm2_len, cm3_len = 0;
 	u_int8_t *cm2, *cm3 = NULL;
@@ -863,7 +872,7 @@
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
 
-	DEBUGP(DRR, "STATUS rr_cause = %s\n", 
+	DEBUGP(DRR, "STATUS rr_cause = %s\n",
 		rr_cause_name(gh->data[0]));
 
 	return 0;
@@ -896,7 +905,7 @@
 	DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s",
 		apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len));
 
-	return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
+	return db_apdu_blob_store(msg->lchan->conn.subscr, apdu_id_flags, apdu_len, apdu_data);
 }
 
 /* Chapter 9.1.16 Handover complete */
@@ -1055,19 +1064,19 @@
 		trans->cc.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)
+		if (trans->conn)
 			DEBUGP(DCC, "(bts %d trx %d ts %d ti %x 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->conn->lchan->ts->trx->bts->nr,
+				trans->conn->lchan->ts->trx->nr,
+				trans->conn->lchan->ts->nr, trans->transaction_id,
 				(trans->subscr)?(trans->subscr->extension):"-",
 				get_mncc_name(msg_type));
 		else
@@ -1116,12 +1125,12 @@
 	}
 	if (trans->cc.state != GSM_CSTATE_NULL)
 		new_cc_state(trans, GSM_CSTATE_NULL);
-	if (trans->lchan)
-		trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
+	if (trans->conn)
+		trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref);
 }
 
 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)
@@ -1133,7 +1142,7 @@
 
 	if (hooknum != GSM_HOOK_RR_PAGING)
 		return -EINVAL;
-  
+
 	if (!subscr)
 		return -EINVAL;
 	net = subscr->net;
@@ -1144,7 +1153,7 @@
 
 	/* check all tranactions (without lchan) for subscriber */
 	llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) {
-		if (transt->subscr != subscr || transt->lchan)
+		if (transt->subscr != subscr || transt->conn)
 			continue;
 		switch (event) {
 		case GSM_PAGING_SUCCEEDED:
@@ -1153,9 +1162,9 @@
 			DEBUGP(DCC, "Paging subscr %s succeeded!\n",
 				subscr->extension);
 			/* Assign lchan */
-			if (!transt->lchan) {
-				transt->lchan = lchan;
-				use_lchan(lchan);
+			if (!transt->conn) {
+				transt->conn = &lchan->conn;
+				use_subscr_con(transt->conn);
 			}
 			/* send SETUP request to called party */
 			gsm48_cc_tx_setup(transt, &transt->cc.msg);
@@ -1200,7 +1209,7 @@
 		 * a tch_recv_mncc request pending */
 		net = lchan->ts->trx->bts->network;
 		llist_for_each_entry(trans, &net->trans_list, entry) {
-			if (trans->lchan == lchan && trans->tch_recv) {
+			if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) {
 				DEBUGP(DCC, "pending tch_recv_mncc request\n");
 				tch_recv_mncc(net, trans->callref, 1);
 			}
@@ -1273,11 +1282,11 @@
 	if (!trans1 || !trans2)
 		return -EIO;
 
-	if (!trans1->lchan || !trans2->lchan)
+	if (!trans1->conn || !trans2->conn)
 		return -EIO;
 
 	/* through-connect channel */
-	return tch_map(trans1->lchan, trans2->lchan);
+	return tch_map(trans1->conn->lchan, trans2->conn->lchan);
 }
 
 /* enable receive of channels to MNCC upqueue */
@@ -1292,9 +1301,9 @@
 	trans = trans_find_by_callref(net, callref);
 	if (!trans)
 		return -EIO;
-	if (!trans->lchan)
+	if (!trans->conn)
 		return 0;
-	lchan = trans->lchan;
+	lchan = trans->conn->lchan;
 	bts = lchan->ts->trx->bts;
 
 	switch (bts->type) {
@@ -2490,7 +2499,7 @@
 {
 	struct gsm_mncc *mode = arg;
 
-	return gsm48_lchan_modify(trans->lchan, mode->lchan_mode);
+	return gsm48_lchan_modify(trans->conn->lchan, mode->lchan_mode);
 }
 
 static struct downstate {
@@ -2576,18 +2585,18 @@
 		trans = trans_find_by_callref(net, data->callref);
 		if (!trans)
 			return -EIO;
-		if (!trans->lchan)
+		if (!trans->conn)
 			return 0;
-		if (trans->lchan->type != GSM_LCHAN_TCH_F)
+		if (trans->conn->lchan->type != GSM_LCHAN_TCH_F)
 			return 0;
-		bts = trans->lchan->ts->trx->bts;
+		bts = trans->conn->lchan->ts->trx->bts;
 		switch (bts->type) {
 		case GSM_BTS_TYPE_NANOBTS:
-			if (!trans->lchan->abis_ip.rtp_socket)
+			if (!trans->conn->lchan->abis_ip.rtp_socket)
 				return 0;
-			return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
+			return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg);
 		case GSM_BTS_TYPE_BS11:
-			return trau_send_frame(trans->lchan, arg);
+			return trau_send_frame(trans->conn->lchan, arg);
 		default:
 			DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
 		}
@@ -2665,6 +2674,7 @@
 		}
 		/* Find lchan */
 		lchan = lchan_for_subscr(subscr);
+
 		/* If subscriber has no lchan */
 		if (!lchan) {
 			/* find transaction with this subscriber already paging */
@@ -2692,16 +2702,18 @@
 			return 0;
 		}
 		/* Assign lchan */
-		trans->lchan = lchan;
-		use_lchan(lchan);
+		trans->conn = &lchan->conn;
+		use_subscr_con(trans->conn);
 		subscr_put(subscr);
 	}
-	lchan = trans->lchan;
+
+	if (trans->conn)
+		lchan = trans->conn->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", 
+			"Received '%s' from MNCC in paging state\n",
 			(trans->subscr)?(trans->subscr->extension):"-",
 			get_mncc_name(msg_type));
 		mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
@@ -2719,7 +2731,7 @@
 		"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):"-",
+		(trans->conn->subscr)?(trans->conn->subscr->extension):"-",
 		get_mncc_name(msg_type), trans->cc.state,
 		gsm48_cc_state_name(trans->cc.state));
 
@@ -2756,7 +2768,7 @@
 	 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 */  
+	{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),
@@ -2795,6 +2807,7 @@
 
 static int gsm0408_rcv_cc(struct msgb *msg)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	u_int8_t msg_type = gh->msg_type & 0xbf;
 	u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
@@ -2806,14 +2819,16 @@
 		DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type);
 		return -EINVAL;
 	}
-	
+
+	conn = &lchan->conn;
+
 	/* Find transaction */
-	trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_CC, transaction_id);
+	trans = trans_find_by_id(conn->subscr, GSM48_PDISC_CC, transaction_id);
 
 	DEBUGP(DCC, "(bts %d trx %d ts %d ti %x 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):"-",
+		transaction_id, (conn->subscr)?(conn->subscr->extension):"-",
 		gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
 		gsm48_cc_state_name(trans?(trans->cc.state):0));
 
@@ -2822,7 +2837,7 @@
 		DEBUGP(DCC, "Unknown transaction ID %x, "
 			"creating new trans.\n", transaction_id);
 		/* Create transaction */
-		trans = trans_alloc(lchan->subscr, GSM48_PDISC_CC,
+		trans = trans_alloc(conn->subscr, GSM48_PDISC_CC,
 				    transaction_id, new_callref++);
 		if (!trans) {
 			DEBUGP(DCC, "No memory for trans.\n");
@@ -2832,8 +2847,8 @@
 			return -ENOMEM;
 		}
 		/* Assign transaction */
-		trans->lchan = lchan;
-		use_lchan(lchan);
+		trans->conn = &lchan->conn;
+		use_subscr_con(trans->conn);
 	}
 
 	/* find function for current state and message */
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index 2472f12..b770b52 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -57,7 +57,7 @@
 	 * work that the caller no longer has to do */
 	if (trans) {
 		gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
-		msg->lchan = trans->lchan;
+		msg->lchan = trans->conn->lchan;
 	}
 
 	if (msg->lchan) {
@@ -236,50 +236,6 @@
 	return rsl_deact_sacch(lchan);
 }
 
-/* Convert Mobile Identity (10.5.1.4) to string */
-int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len)
-{
-	int i;
-	u_int8_t mi_type;
-	char *str_cur = string;
-	u_int32_t tmsi;
-
-	mi_type = mi[0] & GSM_MI_TYPE_MASK;
-
-	switch (mi_type) {
-	case GSM_MI_TYPE_NONE:
-		break;
-	case GSM_MI_TYPE_TMSI:
-		/* Table 10.5.4.3, reverse generate_mid_from_tmsi */
-		if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
-			memcpy(&tmsi, &mi[1], 4);
-			tmsi = ntohl(tmsi);
-			return snprintf(string, str_len, "%u", tmsi);
-		}
-		break;
-	case GSM_MI_TYPE_IMSI:
-	case GSM_MI_TYPE_IMEI:
-	case GSM_MI_TYPE_IMEISV:
-		*str_cur++ = bcd2char(mi[0] >> 4);
-
-                for (i = 1; i < mi_len; i++) {
-			if (str_cur + 2 >= string + str_len)
-				return str_cur - string;
-			*str_cur++ = bcd2char(mi[i] & 0xf);
-			/* skip last nibble in last input byte when GSM_EVEN */
-			if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
-				*str_cur++ = bcd2char(mi[i] >> 4);
-		}
-		break;
-	default:
-		break;
-	}
-	*str_cur++ = '\0';
-
-	return str_cur - string;
-}
-
-
 int send_siemens_mrpci(struct gsm_lchan *lchan,
 		       u_int8_t *classmark2_lv)
 {
@@ -316,16 +272,16 @@
 	if (is_siemens_bts(bts))
 		send_siemens_mrpci(msg->lchan, classmark2_lv);
 
-	if (!msg->lchan->subscr) {
-		msg->lchan->subscr = subscr;
-	} else if (msg->lchan->subscr != subscr) {
+	if (!msg->lchan->conn.subscr) {
+		msg->lchan->conn.subscr = subscr;
+	} else if (msg->lchan->conn.subscr != subscr) {
 		LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
 		subscr_put(subscr);
 		return -EINVAL;
 	} else {
 		DEBUGP(DRR, "<- Channel already owned by us\n");
 		subscr_put(subscr);
-		subscr = msg->lchan->subscr;
+		subscr = msg->lchan->conn.subscr;
 	}
 
 	sig_data.subscr = subscr;
@@ -626,4 +582,3 @@
 
 	return 0;
 }
-
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 881c375..511ad47 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -24,6 +24,7 @@
  */
 
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -63,7 +64,7 @@
 	{ GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
 	{ GSM411_CP_CAUSE_INV_MAND_INF,	"Invalid Mandatory Information" },
 	{ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" },
-	{ GSM411_CP_CAUSE_MSG_INCOMP_STATE, 
+	{ GSM411_CP_CAUSE_MSG_INCOMP_STATE,
 				"Message incompatible with protocol state" },
 	{ GSM411_CP_CAUSE_IE_NOTEXIST,	"IE does not exist" },
 	{ GSM411_CP_CAUSE_PROTOCOL_ERR,	"Protocol Error" },
@@ -121,16 +122,11 @@
 				   "GSM 04.11");
 }
 
-static int gsm411_sendmsg(struct msgb *msg, u_int8_t link_id)
+static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, u_int8_t link_id)
 {
-	if (msg->lchan)
-		msg->trx = msg->lchan->ts->trx;
-
-	msg->l3h = msg->data;
-
 	DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
-
-	return rsl_data_request(msg, link_id);
+	msg->l3h = msg->data;
+	return gsm0808_submit_dtap(conn, msg, link_id);
 }
 
 /* SMC TC1* is expired */
@@ -154,9 +150,6 @@
 	gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
 	gh->msg_type = msg_type;
 
-	/* assign the outgoing lchan */
-	msg->lchan = trans->lchan;
-
 	/* mobile originating */
 	switch (gh->msg_type) {
 	case GSM411_MT_CP_DATA:
@@ -179,7 +172,7 @@
 
 	DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id);
 
-	return gsm411_sendmsg(msg, trans->sms.link_id);
+	return gsm411_sendmsg(trans->conn, msg, trans->sms.link_id);
 }
 
 /* Prefix msg with a RP-DATA header and send as CP-DATA */
@@ -215,7 +208,7 @@
 	u_int8_t ret;
 
 	if ((value & 0x0F) > 9 || (value >> 4) > 9)
-		LOGP(DSMS, LOGL_ERROR, 
+		LOGP(DSMS, LOGL_ERROR,
 		     "unbcdify got too big nibble: 0x%02X\n", value);
 
 	ret = (value&0x0F)*10;
@@ -509,11 +502,10 @@
 	return msg->len - old_msg_len;
 }
 
-/* process an incoming TPDU (called from RP-DATA) 
- * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ 
-static int gsm340_rx_tpdu(struct msgb *msg)
+/* process an incoming TPDU (called from RP-DATA)
+ * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
+static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg)
 {
-	struct gsm_bts *bts = msg->lchan->ts->trx->bts;
 	u_int8_t *smsp = msgb_sms(msg);
 	struct gsm_sms *gsms;
 	u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
@@ -522,7 +514,7 @@
 	u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
 	int rc = 0;
 
-	counter_inc(bts->network->stats.sms.submitted);
+	counter_inc(conn->bts->network->stats.sms.submitted);
 
 	gsms = sms_alloc();
 	if (!gsms)
@@ -575,7 +567,7 @@
 		sms_vp = 0;
 		break;
 	default:
-		LOGP(DSMS, LOGL_NOTICE, 
+		LOGP(DSMS, LOGL_NOTICE,
 		     "SMS Validity period not implemented: 0x%02x\n", sms_vpf);
 		return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
 	}
@@ -594,7 +586,7 @@
 		}
 	}
 
-	gsms->sender = subscr_get(msg->lchan->subscr);
+	gsms->sender = subscr_get(msg->lchan->conn.subscr);
 
 	LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
 	     "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
@@ -602,7 +594,7 @@
 	     subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref,
 	     gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr,
 	     gsms->user_data_len,
-			sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : 
+			sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
 				hexdump(gsms->user_data, gsms->user_data_len));
 
 	gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
@@ -610,10 +602,10 @@
 	dispatch_signal(SS_SMS, 0, gsms);
 
 	/* determine gsms->receiver based on dialled number */
-	gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
+	gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr);
 	if (!gsms->receiver) {
 		rc = 1; /* cause 1: unknown subscriber */
-		counter_inc(bts->network->stats.sms.no_receiver);
+		counter_inc(conn->bts->network->stats.sms.no_receiver);
 		goto out;
 	}
 
@@ -687,7 +679,7 @@
 
 	DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
 
-	rc = gsm340_rx_tpdu(msg);
+	rc = gsm340_rx_tpdu(trans->conn, msg);
 	if (rc == 0)
 		return gsm411_send_rp_ack(trans, rph->msg_ref);
 	else if (rc > 0)
@@ -753,14 +745,17 @@
 	trans->sms.sms = NULL;
 
 	/* check for more messages for this subscriber */
-	sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+	assert(msg->lchan->conn.subscr == trans->subscr);
+
+	sms = db_sms_get_unsent_for_subscr(trans->subscr);
 	if (sms)
-		gsm411_send_sms_lchan(msg->lchan, sms);
+		gsm411_send_sms_lchan(trans->conn, sms);
 
 	/* free the transaction here */
 	trans_free(trans);
 
 	/* release channel if done */
+#warning "BROKEN. The SAPI will be released automatically by the BSC"
 	if (!sms)
 		rsl_release_request(msg->lchan, trans->sms.link_id);
 
@@ -770,7 +765,7 @@
 static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
 			      struct gsm411_rp_hdr *rph)
 {
-	struct gsm_network *net = trans->lchan->ts->trx->bts->network;
+	struct gsm_network *net = trans->conn->bts->network;
 	struct gsm_sms *sms = trans->sms.sms;
 	u_int8_t cause_len = rph->data[0];
 	u_int8_t cause = rph->data[1];
@@ -780,7 +775,7 @@
 	 * the cause and take action depending on it */
 
 	LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n",
-	     subscr_name(msg->lchan->subscr), cause_len, cause,
+	     subscr_name(trans->conn->subscr), cause_len, cause,
 	     get_value_string(rp_cause_strs, cause));
 
 	if (!trans->sms.is_mt) {
@@ -833,11 +828,13 @@
 	dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
 
 	/* check for more messages for this subscriber */
-	sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+	assert(msg->lchan->conn.subscr == trans->subscr);
+	sms = db_sms_get_unsent_for_subscr(trans->subscr);
 	if (sms)
-		gsm411_send_sms_lchan(msg->lchan, sms);
+		gsm411_send_sms_lchan(trans->conn, sms);
 	else
 		rsl_release_request(msg->lchan, trans->sms.link_id);
+#warning "BROKEN: The SAPI=3 will be released automatically by the BSC"
 
 	return rc;
 }
@@ -920,16 +917,16 @@
 	struct gsm_trans *trans;
 	int rc = 0;
 
-	if (!lchan->subscr)
+	if (!lchan->conn.subscr)
 		return -EIO;
 		/* FIXME: send some error message */
 
 	DEBUGP(DSMS, "trans_id=%x ", transaction_id);
-	trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
+	trans = trans_find_by_id(lchan->conn.subscr, GSM48_PDISC_SMS,
 				 transaction_id);
 	if (!trans) {
 		DEBUGPC(DSMS, "(new) ");
-		trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
+		trans = trans_alloc(lchan->conn.subscr, GSM48_PDISC_SMS,
 				    transaction_id, new_callref++);
 		if (!trans) {
 			DEBUGPC(DSMS, "No memory for trans\n");
@@ -941,8 +938,8 @@
 		trans->sms.is_mt = 0;
 		trans->sms.link_id = link_id;
 
-		trans->lchan = lchan;
-		use_lchan(lchan);
+		trans->conn = &lchan->conn;
+		use_subscr_con(trans->conn);
 	}
 
 	switch(msg_type) {
@@ -961,7 +958,7 @@
 				if (i == transaction_id)
 					continue;
 
-				ptrans = trans_find_by_id(lchan->subscr,
+				ptrans = trans_find_by_id(lchan->conn.subscr,
 				                          GSM48_PDISC_SMS, i);
 				if (!ptrans)
 					continue;
@@ -1041,7 +1038,7 @@
 /* Take a SMS in gsm_sms structure and send it through an already
  * existing lchan. We also assume that the caller ensured this lchan already
  * has a SAPI3 RLL connection! */
-int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
+int gsm411_send_sms_lchan(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
 	struct gsm_trans *trans;
@@ -1050,18 +1047,16 @@
 	int transaction_id;
 	int rc;
 
-	transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0);
+	transaction_id = trans_assign_trans_id(conn->subscr, GSM48_PDISC_SMS, 0);
 	if (transaction_id == -1) {
 		LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n");
 		return -EBUSY;
 	}
 
-	msg->lchan = lchan;
-
 	DEBUGP(DSMS, "send_sms_lchan()\n");
 
 	/* FIXME: allocate transaction with message reference */
-	trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
+	trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS,
 			    transaction_id, new_callref++);
 	if (!trans) {
 		LOGP(DSMS, LOGL_ERROR, "No memory for trans\n");
@@ -1074,8 +1069,8 @@
 	trans->sms.sms = sms;
 	trans->sms.link_id = UM_SAPI_SMS;	/* FIXME: main or SACCH ? */
 
-	trans->lchan = lchan;
-	use_lchan(lchan);
+	trans->conn = conn;
+	use_subscr_con(trans->conn);
 
 	/* Hardcode SMSC Originating Address for now */
 	data = (u_int8_t *)msgb_put(msg, 8);
@@ -1112,7 +1107,7 @@
 
 	DEBUGP(DSMS, "TX: SMS DELIVER\n");
 
-	counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered);
+	counter_inc(conn->bts->network->stats.sms.delivered);
 
 	return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
 	/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
@@ -1130,11 +1125,13 @@
 
 	switch (type) {
 	case BSC_RLLR_IND_EST_CONF:
-		gsm411_send_sms_lchan(lchan, sms);
+#warning "BROKEN: The BSC will establish this transparently"
+		gsm411_send_sms_lchan(&lchan->conn, sms);
 		break;
 	case BSC_RLLR_IND_REL_IND:
 	case BSC_RLLR_IND_ERR_IND:
 	case BSC_RLLR_IND_TIMEOUT:
+#warning "BROKEN: We will need to handle SAPI n Reject"
 		sms_free(sms);
 		break;
 	}
diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c
index 8271274..2112e3f 100644
--- a/openbsc/src/gsm_04_80.c
+++ b/openbsc/src/gsm_04_80.c
@@ -236,7 +236,7 @@
 				gsm_7bit_decode(req->text,
 						&(uss_req_data[7]), num_chars);
 				/* append null-terminator */
-				req->text[num_chars+1] = 0;  
+				req->text[num_chars+1] = 0;
 				rc = 1;
 			}
 		}
diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c
index dee89c0..40c3bbd 100644
--- a/openbsc/src/gsm_subscriber_base.c
+++ b/openbsc/src/gsm_subscriber_base.c
@@ -187,6 +187,7 @@
 
 void subscr_put_channel(struct gsm_lchan *lchan)
 {
+	struct gsm_subscriber_connection *conn = &lchan->conn;
 	/*
 	 * FIXME: Continue with other requests now... by checking
 	 * the gsm_subscriber inside the gsm_lchan. Drop the ref count
@@ -205,9 +206,9 @@
 	 * will listen to the paging requests before we timeout
 	 */
 
-	put_lchan(lchan);
+	put_subscr_con(conn);
 
-	if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
-		subscr_send_paging_request(lchan->subscr);
+	if (lchan->conn.subscr && !llist_empty(&lchan->conn.subscr->requests))
+		subscr_send_paging_request(lchan->conn.subscr);
 }
 
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index bd4c563..7fb0b13 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -122,7 +122,7 @@
 	new_lchan->bs_power = old_lchan->bs_power;
 	new_lchan->rsl_cmode = old_lchan->rsl_cmode;
 	new_lchan->tch_mode = old_lchan->tch_mode;
-	new_lchan->subscr = subscr_get(old_lchan->subscr);
+	new_lchan->conn.subscr = subscr_get(old_lchan->conn.subscr);
 
 	/* FIXME: do we have a better idea of the timing advance? */
 	rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
@@ -218,7 +218,7 @@
 	}
 
 	LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
-	     "%u->%u\n", subscr_name(ho->old_lchan->subscr),
+	     "%u->%u\n", subscr_name(ho->old_lchan->conn.subscr),
 	     ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
 	     ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
 
@@ -227,7 +227,7 @@
 	bsc_del_timer(&ho->T3103);
 
 	/* update lchan pointer of transaction */
-	trans_lchan_change(ho->old_lchan, new_lchan);
+	trans_lchan_change(&ho->old_lchan->conn, &new_lchan->conn);
 
 	ho->old_lchan->state = LCHAN_S_INACTIVE;
 	lchan_auto_release(ho->old_lchan);
@@ -243,6 +243,7 @@
 /* GSM 04.08 HANDOVER FAIL has been received */
 static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm_network *net = old_lchan->ts->trx->bts->network;
 	struct bsc_handover *ho;
 
@@ -256,7 +257,8 @@
 
 	bsc_del_timer(&ho->T3103);
 	llist_del(&ho->list);
-	put_lchan(ho->new_lchan);
+	conn = &ho->new_lchan->conn;
+	put_subscr_con(conn);
 	talloc_free(ho);
 
 	return 0;
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 91d7563..323540f 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -62,7 +62,7 @@
 static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
 static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
 static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
-					0x01, IPAC_IDTAG_UNIT, 
+					0x01, IPAC_IDTAG_UNIT,
 					0x01, IPAC_IDTAG_MACADDR,
 					0x01, IPAC_IDTAG_LOCATION1,
 					0x01, IPAC_IDTAG_LOCATION2,
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
index 56930d4..83b01f2 100644
--- a/openbsc/src/input/misdn.c
+++ b/openbsc/src/input/misdn.c
@@ -262,7 +262,7 @@
 
 	ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
 	if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
-		DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
+		DEBUGP(DMIB, "send returns %d instead of %zu\n", ret,
 			sizeof(*hh) + BCHAN_TX_GRAN);
 
 	return ret;
diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
index 870950d..670e3f1 100644
--- a/openbsc/src/ipaccess/ipaccess-config.c
+++ b/openbsc/src/ipaccess/ipaccess-config.c
@@ -612,14 +612,14 @@
 	struct gsm_bts *bts;
 	struct sockaddr_in sin;
 	int rc, option_index = 0, stream_id = 0xff;
-	struct debug_target *stderr_target;
+	struct log_target *stderr_target;
 
-	debug_init();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
-	debug_set_all_filter(stderr_target, 1);
-	debug_set_log_level(stderr_target, 0);
-	debug_parse_category_mask(stderr_target, "DNM,0");
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
+	log_set_log_level(stderr_target, 0);
+	log_parse_category_mask(stderr_target, "DNM,0");
 	bts_model_nanobts_init();
 
 	printf("ipaccess-config (C) 2009 by Harald Welte\n");
diff --git a/openbsc/src/ipaccess/ipaccess-find.c b/openbsc/src/ipaccess/ipaccess-find.c
index 01f8a2d..ec4a0b7 100644
--- a/openbsc/src/ipaccess/ipaccess-find.c
+++ b/openbsc/src/ipaccess/ipaccess-find.c
@@ -71,7 +71,7 @@
 	rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
 	if (rc < 0)
 		goto err;
-#endif		
+#endif
 	return fd;
 
 err:
@@ -79,7 +79,7 @@
 	return rc;
 }
 
-const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, 
+const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
 				IPAC_MSGT_ID_GET,
 					0x01, IPAC_IDTAG_MACADDR,
 					0x01, IPAC_IDTAG_IPADDR,
diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c
index 217e0bd..3860813 100644
--- a/openbsc/src/ipaccess/ipaccess-proxy.c
+++ b/openbsc/src/ipaccess/ipaccess-proxy.c
@@ -42,7 +42,7 @@
 #include <openbsc/ipaccess.h>
 #include <osmocore/talloc.h>
 
-static struct debug_target *stderr_target;
+static struct log_target *stderr_target;
 
 /* one instance of an ip.access protocol proxy */
 struct ipa_proxy {
@@ -265,10 +265,10 @@
 			   struct ipa_bts_conn *ipbc, u_int8_t trx_id)
 {
 	if (ipbc)
-		debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
+		logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
 		     ipbc->unit_id.bts_id, trx_id);
 	else
-		debugp2(ss, lvl, file, line, 0, "unknown ");
+		logp2(ss, lvl, file, line, 0, "unknown ");
 }
 
 /* UDP socket handling */
@@ -550,6 +550,7 @@
 		}
 
 		/* lookup BTS, create sign_link, ... */
+		site_id = bts_id = trx_id = 0;
 		parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
 			     &site_id, &bts_id, &trx_id);
 		ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
@@ -946,7 +947,7 @@
 		perror("accept");
 		return ret;
 	}
-	DEBUGP(DINP, "accept()ed new %s link from %s\n", 
+	DEBUGP(DINP, "accept()ed new %s link from %s\n",
 		(listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
 		inet_ntoa(sa.sin_addr));
 
@@ -1108,11 +1109,11 @@
 
 	tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
 
-	debug_init();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
-	debug_set_all_filter(stderr_target, 1);
-	debug_parse_category_mask(stderr_target, "DINP:DMI");
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
+	log_parse_category_mask(stderr_target, "DINP:DMI");
 
 	rc = ipaccess_proxy_setup();
 	if (rc < 0)
diff --git a/openbsc/src/mgcp/mgcp_main.c b/openbsc/src/mgcp/mgcp_main.c
index e45a1e9..5e337e9 100644
--- a/openbsc/src/mgcp/mgcp_main.c
+++ b/openbsc/src/mgcp/mgcp_main.c
@@ -38,8 +38,11 @@
 #include <openbsc/gsm_data.h>
 #include <osmocore/select.h>
 #include <openbsc/mgcp.h>
+#include <openbsc/mgcp_internal.h>
 #include <openbsc/telnet_interface.h>
 
+#include <vty/command.h>
+
 #include "../../bscconfig.h"
 
 /* this is here for the vty... it will never be called */
@@ -51,10 +54,11 @@
 #warning "Make use of the rtp proxy code"
 
 static struct bsc_fd bfd;
-static int first_request = 1;
 static struct mgcp_config *cfg;
+static int reset_endpoints = 0;
+
 const char *openbsc_version = "OpenBSC MGCP " PACKAGE_VERSION;
-const char *openbsc_copyright = 
+const char *openbsc_copyright =
 	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\n"
 	"Contributions by Daniel Willmann, Jan Lübbe,Stefan Schmidt\n"
 	"Dieter Spaar, Andreas Eversberg, Harald Welte\n\n"
@@ -74,10 +78,10 @@
 	printf(" -c --config-file filename The config file to use.\n");
 }
 
-static void print_version()
+static void print_mgcp_version()
 {
 	printf("%s\n\n", openbsc_version);
-	printf(openbsc_copyright);
+	printf("%s", openbsc_copyright);
 }
 
 static void handle_options(int argc, char** argv)
@@ -105,7 +109,7 @@
 			config_file = talloc_strdup(tall_bsc_ctx, optarg);
 			break;
 		case 'V':
-			print_version();
+			print_mgcp_version();
 			exit(0);
 			break;
 		default:
@@ -115,12 +119,21 @@
 	}
 }
 
+/* simply remember this */
+static int mgcp_rsip_cb(struct mgcp_config *cfg)
+{
+	reset_endpoints = 1;
+
+	return 0;
+}
+
 static int read_call_agent(struct bsc_fd *fd, unsigned int what)
 {
 	struct sockaddr_in addr;
 	socklen_t slen = sizeof(addr);
 	struct msgb *msg;
 	struct msgb *resp;
+	int i;
 
 	msg = (struct msgb *) fd->data;
 
@@ -136,18 +149,6 @@
 		return -1;
 	}
 
-	if (first_request) {
-		first_request = 0;
-		resp = mgcp_create_rsip();
-
-		if (resp) {
-			sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0,
-				(struct sockaddr *) &addr, sizeof(addr));
-			msgb_free(resp);
-		}
-		return 0;
-        }
-
 	/* handle message now */
 	msg->l2h = msgb_put(msg, rc);
 	resp = mgcp_handle_message(cfg, msg);
@@ -157,6 +158,16 @@
 		sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
 		msgb_free(resp);
 	}
+
+	if (reset_endpoints) {
+		LOGP(DMGCP, LOGL_NOTICE, "Asked to reset endpoints.\n");
+		reset_endpoints = 0;
+
+		/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
+		for (i = 1; i < cfg->number_endpoints; ++i)
+			mgcp_free_endp(&cfg->endpoints[i]);
+	}
+
 	return 0;
 }
 
@@ -166,14 +177,14 @@
 	struct gsm_network dummy_network;
 	struct sockaddr_in addr;
 	int on = 1, rc;
-	struct debug_target *stderr_target;
+	struct log_target *stderr_target;
 
 	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 
-	debug_init();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
-	debug_set_all_filter(stderr_target, 1);
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
 
 	cfg = mgcp_config_alloc();
 	if (!cfg)
@@ -186,6 +197,8 @@
 	if (rc < 0)
 		return rc;
 
+	/* set some callbacks */
+	cfg->reset_cb = mgcp_rsip_cb;
 
         /* we need to bind a socket */
         if (rc == 0) {
@@ -217,11 +230,11 @@
 
 
 		if (bsc_register_fd(&bfd) != 0) {
-			DEBUGP(DMGCP, "Failed to register the fd\n");
+			LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n");
 			return -1;
 		}
 
-		DEBUGP(DMGCP, "Configured for MGCP.\n");
+		LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
 	}
 
 	/* initialisation */
@@ -235,3 +248,14 @@
 
 	return 0;
 }
+
+struct gsm_network;
+int bsc_vty_init(struct gsm_network *dummy)
+{
+	cmd_init(1);
+	vty_init();
+
+        mgcp_vty_init();
+	return 0;
+}
+
diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c
index b76ca47..cd10d2a 100644
--- a/openbsc/src/mgcp/mgcp_network.c
+++ b/openbsc/src/mgcp/mgcp_network.c
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <endian.h>
+#include <errno.h>
 
 #include <sys/socket.h>
 #include <arpa/inet.h>
@@ -90,6 +91,9 @@
 	if (len < sizeof(*rtp_hdr))
 		return;
 
+	if (payload < 0)
+		return;
+
 	rtp_hdr = (struct rtp_hdr *) data;
 	rtp_hdr->payload_type = payload;
 }
@@ -119,16 +123,14 @@
 	rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
 			    (struct sockaddr *) &addr, &slen);
 	if (rc < 0) {
-		LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x\n",
-			ENDPOINT_NUMBER(endp));
+		LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
+			ENDPOINT_NUMBER(endp), errno, strerror(errno));
 		return -1;
 	}
 
 	/* do not forward aynthing... maybe there is a packet from the bts */
-	if (endp->ci == CI_UNUSED) {
-		LOGP(DMGCP, LOGL_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
+	if (endp->ci == CI_UNUSED)
 		return -1;
-	}
 
 	/*
 	 * Figure out where to forward it to. This code assumes that we
@@ -146,7 +148,9 @@
 	/* We have no idea who called us, maybe it is the BTS. */
 	if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) {
 		/* it was the BTS... */
-		if (!cfg->bts_ip || memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0) {
+		if (!cfg->bts_ip
+		    || memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0
+		    || memcmp(&addr.sin_addr, &endp->bts, sizeof(endp->bts)) == 0) {
 			if (fd == &endp->local_rtp) {
 				endp->bts_rtp = addr.sin_port;
 			} else {
@@ -159,6 +163,12 @@
 		}
 	}
 
+	/* do this before the loop handling */
+	if (dest == DEST_NETWORK)
+		++endp->in_bts;
+	else
+		++endp->in_remote;
+
 	/* dispatch */
 	if (cfg->audio_loop)
 		dest = !dest;
diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c
index f7ef547..76eb7c2 100644
--- a/openbsc/src/mgcp/mgcp_protocol.c
+++ b/openbsc/src/mgcp/mgcp_protocol.c
@@ -80,11 +80,6 @@
 	}
 
 
-struct mgcp_msg_ptr {
-	unsigned int start;
-	unsigned int length;
-};
-
 struct mgcp_request {
 	char *name;
 	struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg);
@@ -98,6 +93,7 @@
 static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg);
 static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg);
 static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg);
+static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg);
 
 static int generate_call_id(struct mgcp_config *cfg)
 {
@@ -119,12 +115,6 @@
 	return cfg->last_call_id;
 }
 
-/* FIXIME/TODO: need to have a list of pending transactions and check that */
-static unsigned int generate_transaction_id()
-{
-	return abs(rand());
-}
-
 /*
  * array of function pointers for handling various
  * messages. In the future this might be binary sorted
@@ -135,6 +125,9 @@
 	MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
 	MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
 	MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
+
+	/* SPEC extension */
+	MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
 };
 
 static struct msgb *mgcp_msgb_alloc(void)
@@ -194,23 +187,6 @@
 	return mgcp_create_response_with_data(200, msg, trans_id, sdp_record);
 }
 
-/* send a static record */
-struct msgb *mgcp_create_rsip(void)
-{
-	struct msgb *msg;
-	int len;
-
-	msg = mgcp_msgb_alloc();
-	if (!msg)
-		return NULL;
-
-	len = snprintf((char *) msg->data, 2048,
-			"RSIP %u *@mgw MGCP 1.0\n"
-			"RM: restart\n", generate_transaction_id());
-	msg->l2h = msgb_put(msg, len);
-	return msg;
-}
-
 /*
  * handle incoming messages:
  *   - this can be a command (four letters, space, transaction id)
@@ -221,25 +197,25 @@
         int code;
 	struct msgb *resp = NULL;
 
-	if (msg->len < 4) {
+	if (msgb_l2len(msg) < 4) {
 		LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len);
 		return NULL;
 	}
 
         /* attempt to treat it as a response */
-        if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
+        if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
 		LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
 	} else {
 		int i, handled = 0;
 		msg->l3h = &msg->l2h[4];
 		for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
-			if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) {
+			if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
 				handled = 1;
 				resp = mgcp_requests[i].handle_request(cfg, msg);
 				break;
 			}
 		if (!handled) {
-			LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
+			LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
 		}
 	}
 
@@ -296,9 +272,9 @@
 	return &cfg->endpoints[gw];
 }
 
-static int analyze_header(struct mgcp_config *cfg, struct msgb *msg,
-			  struct mgcp_msg_ptr *ptr, int size,
-			  const char **transaction_id, struct mgcp_endpoint **endp)
+int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
+			struct mgcp_msg_ptr *ptr, int size,
+			const char **transaction_id, struct mgcp_endpoint **endp)
 {
 	int found;
 
@@ -334,8 +310,11 @@
 	}
 
 	*transaction_id = (const char *)&msg->l3h[ptr[0].start];
-	*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
-	return *endp == NULL;
+	if (endp) {
+		*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
+		return *endp == NULL;
+	}
+	return 0;
 }
 
 static int verify_call_id(const struct mgcp_endpoint *endp,
@@ -369,7 +348,7 @@
 	const char *trans_id;
 	struct mgcp_endpoint *endp;
 
-	found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+	found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
 	if (found != 0)
 	    response = 500;
 	else
@@ -402,13 +381,19 @@
 	int error_code = 500;
 	int port;
 
-	found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+	found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
 	if (found != 0)
 		return create_response(500, "CRCX", trans_id);
 
 	if (endp->ci != CI_UNUSED) {
-		LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
-		return create_response(500, "CRCX", trans_id);
+		if (cfg->force_realloc) {
+			LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
+			    ENDPOINT_NUMBER(endp));
+		} else {
+			LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
+			     ENDPOINT_NUMBER(endp));
+			return create_response(500, "CRCX", trans_id);
+		}
 	}
 
 	/* parse CallID C: and LocalParameters L: */
@@ -501,7 +486,7 @@
 	struct mgcp_endpoint *endp;
 	int error_code = 500;
 
-	found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+	found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
 	if (found != 0)
 		return create_response(error_code, "MDCX", trans_id);
 
@@ -614,7 +599,7 @@
 	struct mgcp_endpoint *endp;
 	int error_code = 500;
 
-	found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
+	found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
 	if (found != 0)
 		return create_response(error_code, "DLCX", trans_id);
 
@@ -678,6 +663,13 @@
 	return create_response(error_code, "DLCX", trans_id);
 }
 
+static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg)
+{
+	if (cfg->reset_cb)
+		cfg->reset_cb(cfg);
+	return NULL;
+}
+
 struct mgcp_config *mgcp_config_alloc(void)
 {
 	struct mgcp_config *cfg;
@@ -722,7 +714,7 @@
 
 void mgcp_free_endp(struct mgcp_endpoint *endp)
 {
-	LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
+	LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
 	endp->ci= CI_UNUSED;
 
 	if (endp->callid) {
@@ -732,7 +724,7 @@
 
 	if (endp->local_options) {
 		talloc_free(endp->local_options);
-		endp->callid = NULL;
+		endp->local_options = NULL;
 	}
 
 	if (!endp->cfg->early_bind) {
@@ -742,4 +734,7 @@
 
 	endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
 	endp->net_payload_type = endp->bts_payload_type = -1;
+	endp->in_bts = endp->in_remote = 0;
+	memset(&endp->remote, 0, sizeof(endp->remote));
+	memset(&endp->bts, 0, sizeof(endp->bts));
 }
diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c
index f13b3cf..af762c5 100644
--- a/openbsc/src/mgcp/mgcp_vty.c
+++ b/openbsc/src/mgcp/mgcp_vty.c
@@ -63,6 +63,8 @@
 		vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE);
 	if (g_cfg->forward_port != 0)
 		vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE);
+	if (g_cfg->call_agent_addr)
+		vty_out(vty, " call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
 
 	return CMD_SUCCESS;
 }
@@ -75,10 +77,12 @@
 	vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
 	for (i = 1; i < g_cfg->number_endpoints; ++i) {
 		struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
-		vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
+		vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s traffic in :%u/%u%s",
 			i, endp->ci,
 			ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
-			ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
+			ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
+			inet_ntoa(endp->bts), endp->in_bts, endp->in_remote,
+			VTY_NEWLINE);
 	}
 
 	return CMD_SUCCESS;
@@ -237,6 +241,17 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_mgcp_agent_addr,
+      cfg_mgcp_agent_addr_cmd,
+      "call agent ip IP",
+      "Set the address of the call agent.")
+{
+	if (g_cfg->call_agent_addr)
+		talloc_free(g_cfg->call_agent_addr);
+	g_cfg->call_agent_addr = talloc_strdup(g_cfg, argv[0]);
+	return CMD_SUCCESS;
+}
+
 int mgcp_vty_init(void)
 {
 	install_element(VIEW_NODE, &show_mgcp_cmd);
@@ -256,6 +271,7 @@
 	install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
 	return 0;
 }
 
@@ -274,6 +290,11 @@
 	if (!g_cfg->bts_ip)
 		fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
 
+	if (!g_cfg->source_addr) {
+		fprintf(stderr, "You need to specify a bind address.\n");
+		return -1;
+	}
+
 	if (mgcp_endpoints_allocate(g_cfg) != 0) {
 		fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", g_cfg->number_endpoints);
 		return -1;
@@ -327,13 +348,3 @@
 	return !!g_cfg->forward_ip;
 }
 
-struct gsm_network;
-int bsc_vty_init(struct gsm_network *dummy)
-{
-	cmd_init(1);
-	vty_init();
-
-        mgcp_vty_init();
-	return 0;
-}
-
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
index 01d59aa..afd5364 100644
--- a/openbsc/src/mncc.c
+++ b/openbsc/src/mncc.c
@@ -332,16 +332,16 @@
 	remote_trans = trans_find_by_callref(call->net, call->remote_ref);
 
 	/* this shouldn't really happen */
-	if (!remote_trans || !remote_trans->lchan) {
+	if (!remote_trans || !remote_trans->conn) {
 		LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
 		return -EIO;
 	}
 
 	/* RTP socket of remote end has meanwhile died */
-	if (!remote_trans->lchan->abis_ip.rtp_socket)
+	if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
 		return -EIO;
 
-	return rtp_send_frame(remote_trans->lchan->abis_ip.rtp_socket, dfr);
+	return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
 }
 
 
diff --git a/openbsc/src/rs232.c b/openbsc/src/rs232.c
index 36af59c..22adf56 100644
--- a/openbsc/src/rs232.c
+++ b/openbsc/src/rs232.c
@@ -156,7 +156,7 @@
 				fprintf(stderr, "Invalid length in hdr: %u\n",
 					sh->rxmsg_bytes_missing);
 		}
-	} else { 
+	} else {
 		/* try to read as many of the missing bytes as are available */
 		rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
 		if (rc < 0) {
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 9f2e2fd..375204e 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -504,7 +504,7 @@
 	return 0;
 }
 
-static void init_rss(struct rtp_sub_socket *rss, 
+static void init_rss(struct rtp_sub_socket *rss,
 		     struct rtp_socket *rs, int fd, int priv_nr)
 {
 	/* initialize bfd */
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
index cada24e..8bd5341 100644
--- a/openbsc/src/silent_call.c
+++ b/openbsc/src/silent_call.c
@@ -38,6 +38,7 @@
 static int paging_cb_silent(unsigned int hooknum, unsigned int event,
 			    struct msgb *msg, void *_lchan, void *_data)
 {
+	struct gsm_subscriber_connection *conn;
 	struct gsm_lchan *lchan = _lchan;
 	struct scall_signal_data sigdata;
 	int rc;
@@ -47,6 +48,8 @@
 
 	DEBUGP(DSMS, "paging_cb_silent: ");
 
+	conn = &lchan->conn;
+
 	sigdata.lchan = lchan;
 	sigdata.data = _data;
 
@@ -54,10 +57,10 @@
 	case GSM_PAGING_SUCCEEDED:
 		DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
 			lchan->ts->nr, lchan->ts->trx->arfcn);
-		lchan->silent_call = 1;
+		conn->silent_call = 1;
 		/* increment lchan reference count */
 		dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
-		use_lchan(lchan);
+		use_subscr_con(conn);
 		break;
 	case GSM_PAGING_EXPIRED:
 		DEBUGP(DSMS, "expired\n");
@@ -97,7 +100,7 @@
 	int i;
 
 	/* if we're not part of a silent call, never reroute */
-	if (!msg->lchan->silent_call)
+	if (!msg->lchan->conn.silent_call)
 		return 0;
 
 	/* check if we are a special message that is handled in openbsc */
@@ -126,16 +129,18 @@
 int gsm_silent_call_stop(struct gsm_subscriber *subscr)
 {
 	struct gsm_lchan *lchan;
+	struct gsm_subscriber_connection *conn;
 
 	lchan = lchan_for_subscr(subscr);
 	if (!lchan)
 		return -EINVAL;
 
 	/* did we actually establish a silent call for this guy? */
-	if (!lchan->silent_call)
+	conn = &lchan->conn;
+	if (!conn->silent_call)
 		return -EINVAL;
 
-	put_lchan(lchan);
+	put_subscr_con(conn);
 
 	return 0;
 }
diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c
index 6379e13..4b373b4 100644
--- a/openbsc/src/talloc_ctx.c
+++ b/openbsc/src/talloc_ctx.c
@@ -19,7 +19,7 @@
 void talloc_ctx_init(void)
 {
 	tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
-	tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, 
+	tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0,
 					  "bs11_file_list_entry");
 	tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper");
 	tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms");
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index aa119b4..c7de026 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -117,7 +117,7 @@
 	bsc_unregister_fd(fd);
 
 	if (conn->dbg) {
-		debug_del_target(conn->dbg);
+		log_del_target(conn->dbg);
 		talloc_free(conn->dbg);
 	}
 
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index 75a279d..5e0d507 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -95,10 +95,10 @@
 		break;
 	}
 
-	if (trans->lchan)
-		put_lchan(trans->lchan);
+	if (trans->conn)
+		put_subscr_con(trans->conn);
 
-	if (!trans->lchan && trans->subscr && trans->subscr->net) {
+	if (!trans->conn && trans->subscr && trans->subscr->net) {
 		/* Stop paging on all bts' */
 		paging_request_stop(NULL, trans->subscr, NULL);
 	}
@@ -148,21 +148,22 @@
 
 /* update all transactions to use a different LCHAN, e.g.
  * after handover has succeeded */
-int trans_lchan_change(struct gsm_lchan *lchan_old,
-		       struct gsm_lchan *lchan_new)
+int trans_lchan_change(struct gsm_subscriber_connection *conn_old,
+		       struct gsm_subscriber_connection *conn_new)
 {
-	struct gsm_network *net = lchan_old->ts->trx->bts->network;
+	struct gsm_network *net = conn_old->lchan->ts->trx->bts->network;
 	struct gsm_trans *trans;
 	int num = 0;
 
 	llist_for_each_entry(trans, &net->trans_list, entry) {
-		if (trans->lchan == lchan_old) {
-			/* drop old channel use cound */
-			put_lchan(trans->lchan);
+		if (trans->conn == conn_old) {
+
+			/* drop old channel use count */
+			put_subscr_con(conn_old);
 			/* assign new channel */
-			trans->lchan = lchan_new;
+			trans->conn = conn_new;
 			/* bump new channel use count */
-			use_lchan(trans->lchan);
+			use_subscr_con(conn_new);
 			num++;
 		}
 	}
diff --git a/openbsc/src/ussd.c b/openbsc/src/ussd.c
index a3d11f0..5476919 100644
--- a/openbsc/src/ussd.c
+++ b/openbsc/src/ussd.c
@@ -62,7 +62,7 @@
 /* A network-specific handler function */
 static int send_own_number(const struct msgb *msg, const struct ussd_request *req)
 {
-	char *own_number = msg->lchan->subscr->extension;
+	char *own_number = msg->lchan->conn.subscr->extension;
 	char response_string[GSM_EXTENSION_LENGTH + 20];
 
 	/* Need trailing CR as EOT character */
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
index 1260f38..0ac9530 100644
--- a/openbsc/src/vty/vty.c
+++ b/openbsc/src/vty/vty.c
@@ -526,7 +526,7 @@
       vty->iac_sb_in_progress = 1;
       return 0;
       break;
-    case SE: 
+    case SE:
       {
 	if (!vty->iac_sb_in_progress)
 	  return 0;
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index e894869..b5bdbc8 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -38,6 +38,7 @@
 #include <openbsc/db.h>
 #include <osmocore/talloc.h>
 #include <openbsc/telnet_interface.h>
+#include <openbsc/vty.h>
 
 static struct gsm_network *gsmnet;
 
@@ -162,7 +163,7 @@
 		"BSIC %u, TSC %u and %u TRX%s",
 		bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
 		bts->cell_identity,
-		bts->location_area_code, bts->bsic, bts->tsc, 
+		bts->location_area_code, bts->bsic, bts->tsc,
 		bts->num_trx, VTY_NEWLINE);
 	vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
 	vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
@@ -598,24 +599,24 @@
 	int idx;
 
 	vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s",
-		lchan->nr, lchan->ts->nr, lchan->ts->trx->nr, 
+		lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
 		lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
 		VTY_NEWLINE);
-	vty_out(vty, "  Use Count: %u, State: %s%s", lchan->use_count,
+	vty_out(vty, "  Use Count: %u, State: %s%s", lchan->conn.use_count,
 		gsm_lchans_name(lchan->state), VTY_NEWLINE);
 	vty_out(vty, "  BS Power: %u dBm, MS Power: %u dBm%s",
 		lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
 		- lchan->bs_power*2,
 		ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
 		VTY_NEWLINE);
-	if (lchan->subscr) {
+	if (lchan->conn.subscr) {
 		vty_out(vty, "  Subscriber:%s", VTY_NEWLINE);
-		subscr_dump_vty(vty, lchan->subscr);
+		subscr_dump_vty(vty, lchan->conn.subscr);
 	} else
 		vty_out(vty, "  No Subscriber%s", VTY_NEWLINE);
 	if (is_ipaccess_bts(lchan->ts->trx->bts)) {
 		struct in_addr ia;
-		ia.s_addr = lchan->abis_ip.bound_ip;
+		ia.s_addr = htonl(lchan->abis_ip.bound_ip);
 		vty_out(vty, "  Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
 			inet_ntoa(ia), lchan->abis_ip.bound_port,
 			lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
@@ -878,204 +879,6 @@
 	return CMD_SUCCESS;
 }
 
-static void _vty_output(struct debug_target *tgt, const char *line)
-{
-	struct vty *vty = tgt->tgt_vty.vty;
-	vty_out(vty, "%s", line);
-	/* This is an ugly hack, but there is no easy way... */
-	if (strchr(line, '\n'))
-		vty_out(vty, "\r");
-}
-
-struct debug_target *debug_target_create_vty(struct vty *vty)
-{
-	struct debug_target *target;
-
-	target = debug_target_create();
-	if (!target)
-		return NULL;
-
-	target->tgt_vty.vty = vty;
-	target->output = _vty_output;
-	return target;
-}
-
-DEFUN(enable_logging,
-      enable_logging_cmd,
-      "logging enable",
-      "Enables logging to this vty\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (conn->dbg) {
-		vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	conn->dbg = debug_target_create_vty(vty);
-	if (!conn->dbg)
-		return CMD_WARNING;
-
-	debug_add_target(conn->dbg);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_fltr_imsi,
-      logging_fltr_imsi_cmd,
-      "logging filter imsi IMSI",
-      "Print all messages related to a IMSI\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_set_imsi_filter(conn->dbg, argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_fltr_all,
-      logging_fltr_all_cmd,
-      "logging filter all <0-1>",
-      "Print all messages to the console\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_set_all_filter(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_use_clr,
-      logging_use_clr_cmd,
-      "logging color <0-1>",
-      "Use color for printing messages\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_set_use_color(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_prnt_timestamp,
-      logging_prnt_timestamp_cmd,
-      "logging timestamp <0-1>",
-      "Print the timestamp of each message\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_set_print_timestamp(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-/* FIXME: those have to be kept in sync with the log levels and categories */
-#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
-#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
-DEFUN(logging_level,
-      logging_level_cmd,
-      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
-      "Set the log level for a specified category\n")
-{
-	struct telnet_connection *conn;
-	int category = debug_parse_category(argv[0]);
-	int level = debug_parse_level(argv[1]);
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	if (category < 0) {
-		vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	if (level < 0) {
-		vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	conn->dbg->categories[category].enabled = 1;
-	conn->dbg->categories[category].loglevel = level;
-
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_set_category_mask,
-      logging_set_category_mask_cmd,
-      "logging set debug mask MASK",
-      "Decide which categories to output.\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_parse_category_mask(conn->dbg, argv[0]);
-	return CMD_SUCCESS;
-}
-
-DEFUN(logging_set_log_level,
-      logging_set_log_level_cmd,
-      "logging set log level <0-8>",
-      "Set the global log level. The value 0 implies no filtering.\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_set_log_level(conn->dbg, atoi(argv[0]));
-	return CMD_SUCCESS;
-}
-
-DEFUN(diable_logging,
-      disable_logging_cmd,
-      "logging disable",
-      "Disables logging to this vty\n")
-{
-	struct telnet_connection *conn;
-
-	conn = (struct telnet_connection *) vty->priv;
-	if (!conn->dbg) {
-		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
-		return CMD_WARNING;
-	}
-
-	debug_del_target(conn->dbg);
-	talloc_free(conn->dbg);
-	conn->dbg = NULL;
-	return CMD_SUCCESS;
-}
-
 DEFUN(show_stats,
       show_stats_cmd,
       "show statistics",
@@ -1346,7 +1149,7 @@
 		/* allocate a new one */
 		bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_UNKNOWN,
 				    HARDCODED_TSC, HARDCODED_BSIC);
-	} else 
+	} else
 		bts = gsm_bts_num(gsmnet, bts_nr);
 
 	if (!bts) {
@@ -1777,9 +1580,9 @@
 	} else if (trx_nr == bts->num_trx) {
 		/* we need to allocate a new one */
 		trx = gsm_bts_trx_alloc(bts);
-	} else 
+	} else
 		trx = gsm_bts_trx_num(bts, trx_nr);
-	
+
 	if (!trx)
 		return CMD_WARNING;
 
@@ -1957,15 +1760,7 @@
 	install_element(VIEW_NODE, &show_paging_cmd);
 	install_element(VIEW_NODE, &show_stats_cmd);
 
-	install_element(VIEW_NODE, &enable_logging_cmd);
-	install_element(VIEW_NODE, &disable_logging_cmd);
-	install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
-	install_element(VIEW_NODE, &logging_fltr_all_cmd);
-	install_element(VIEW_NODE, &logging_use_clr_cmd);
-	install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
-	install_element(VIEW_NODE, &logging_set_category_mask_cmd);
-	install_element(VIEW_NODE, &logging_level_cmd);
-	install_element(VIEW_NODE, &logging_set_log_level_cmd);
+	openbsc_vty_add_cmds();
 
 	install_element(CONFIG_NODE, &cfg_net_cmd);
 	install_node(&net_node, config_write_net);
diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c
new file mode 100644
index 0000000..d494584
--- /dev/null
+++ b/openbsc/src/vty_interface_cmds.c
@@ -0,0 +1,243 @@
+/* OpenBSC logging helper for the VTY */
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2010 by Holger Hans Peter Freyther
+ * 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 <openbsc/vty.h>
+#include <openbsc/telnet_interface.h>
+
+#include <osmocore/talloc.h>
+
+#include <vty/command.h>
+#include <vty/buffer.h>
+#include <vty/vty.h>
+
+#include <stdlib.h>
+
+static void _vty_output(struct log_target *tgt, const char *line)
+{
+	struct vty *vty = tgt->tgt_vty.vty;
+	vty_out(vty, "%s", line);
+	/* This is an ugly hack, but there is no easy way... */
+	if (strchr(line, '\n'))
+		vty_out(vty, "\r");
+}
+
+struct log_target *log_target_create_vty(struct vty *vty)
+{
+	struct log_target *target;
+
+	target = log_target_create();
+	if (!target)
+		return NULL;
+
+	target->tgt_vty.vty = vty;
+	target->output = _vty_output;
+	return target;
+}
+
+DEFUN(enable_logging,
+      enable_logging_cmd,
+      "logging enable",
+      "Enables logging to this vty\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (conn->dbg) {
+		vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	conn->dbg = log_target_create_vty(vty);
+	if (!conn->dbg)
+		return CMD_WARNING;
+
+	log_add_target(conn->dbg);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_imsi,
+      logging_fltr_imsi_cmd,
+      "logging filter imsi IMSI",
+      "Print all messages related to a IMSI\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_imsi_filter(conn->dbg, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_all,
+      logging_fltr_all_cmd,
+      "logging filter all <0-1>",
+      "Print all messages to the console\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_all_filter(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+      logging_use_clr_cmd,
+      "logging color <0-1>",
+      "Use color for printing messages\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_use_color(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+      logging_prnt_timestamp_cmd,
+      "logging timestamp <0-1>",
+      "Print the timestamp of each message\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_print_timestamp(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+/* FIXME: those have to be kept in sync with the log levels and categories */
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
+#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+DEFUN(logging_level,
+      logging_level_cmd,
+      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
+      "Set the log level for a specified category\n")
+{
+	struct telnet_connection *conn;
+	int category = log_parse_category(argv[0]);
+	int level = log_parse_level(argv[1]);
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (category < 0) {
+		vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (level < 0) {
+		vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	conn->dbg->categories[category].enabled = 1;
+	conn->dbg->categories[category].loglevel = level;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+      logging_set_category_mask_cmd,
+      "logging set log mask MASK",
+      "Decide which categories to output.\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_parse_category_mask(conn->dbg, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_log_level,
+      logging_set_log_level_cmd,
+      "logging set log level <0-8>",
+      "Set the global log level. The value 0 implies no filtering.\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_set_log_level(conn->dbg, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+      disable_logging_cmd,
+      "logging disable",
+      "Disables logging to this vty\n")
+{
+	struct telnet_connection *conn;
+
+	conn = (struct telnet_connection *) vty->priv;
+	if (!conn->dbg) {
+		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	log_del_target(conn->dbg);
+	talloc_free(conn->dbg);
+	conn->dbg = NULL;
+	return CMD_SUCCESS;
+}
+
+void openbsc_vty_add_cmds()
+{
+	install_element(VIEW_NODE, &enable_logging_cmd);
+	install_element(VIEW_NODE, &disable_logging_cmd);
+	install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
+	install_element(VIEW_NODE, &logging_fltr_all_cmd);
+	install_element(VIEW_NODE, &logging_use_clr_cmd);
+	install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
+	install_element(VIEW_NODE, &logging_set_category_mask_cmd);
+	install_element(VIEW_NODE, &logging_level_cmd);
+	install_element(VIEW_NODE, &logging_set_log_level_cmd);
+
+}
diff --git a/openbsc/tests/debug/debug_test.c b/openbsc/tests/debug/debug_test.c
index 0f0c284..695d65c 100644
--- a/openbsc/tests/debug/debug_test.c
+++ b/openbsc/tests/debug/debug_test.c
@@ -24,17 +24,17 @@
 
 int main(int argc, char** argv)
 {
-	struct debug_target *stderr_target;
+	struct log_target *stderr_target;
 
-	debug_init();
-	stderr_target = debug_target_create_stderr();
-	debug_add_target(stderr_target);
-	debug_set_all_filter(stderr_target, 1);
+	log_init(&log_info);
+	stderr_target = log_target_create_stderr();
+	log_add_target(stderr_target);
+	log_set_all_filter(stderr_target, 1);
 
-	debug_parse_category_mask(stderr_target, "DRLL");
+	log_parse_category_mask(stderr_target, "DRLL");
 	DEBUGP(DCC, "You should not see this\n");
 
-	debug_parse_category_mask(stderr_target, "DRLL:DCC");
+	log_parse_category_mask(stderr_target, "DRLL:DCC");
 	DEBUGP(DRLL, "You should see this\n");
 	DEBUGP(DCC, "You should see this\n");
 	DEBUGP(DMM, "You should not see this\n");