Merge remote branch 'origin/master' into on-waves/mgcp
diff --git a/libosmocore/.gitignore b/libosmocore/.gitignore
new file mode 100644
index 0000000..d61cdc5
--- /dev/null
+++ b/libosmocore/.gitignore
@@ -0,0 +1,25 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.o
+*.lo
+*.la
+*.pc
+aclocal.m4
+autom4te.cache
+config.h*
+config.sub
+config.log
+config.status
+config.guess
+configure
+depcomp
+missing
+ltmain.sh
+install-sh
+stamp-h1
+libtool
+
+.tarball-version
+.version
diff --git a/libosmocore/COPYING b/libosmocore/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/libosmocore/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/libosmocore/Makefile.am b/libosmocore/Makefile.am
new file mode 100644
index 0000000..2adf502
--- /dev/null
+++ b/libosmocore/Makefile.am
@@ -0,0 +1,14 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+ACLOCAL_AMFLAGS = -I m4
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libosmocore.pc
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+	echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+	echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/libosmocore/configure.in b/libosmocore/configure.in
new file mode 100644
index 0000000..9879ebc
--- /dev/null
+++ b/libosmocore/configure.in
@@ -0,0 +1,56 @@
+AC_INIT([libosmocore],
+	m4_esyscmd([./git-version-gen .tarball-version]),
+	[openbsc-devel@lists.openbsc.org])
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+LT_INIT
+AC_PROG_LIBTOOL
+
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS(execinfo.h sys/select.h)
+
+# The following test is taken from WebKit's webkit.m4
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fvisibility=hidden "
+AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
+AC_COMPILE_IFELSE([char foo;],
+      [ AC_MSG_RESULT([yes])
+        SYMBOL_VISIBILITY="-fvisibility=hidden"],
+        AC_MSG_RESULT([no]))
+CFLAGS="$saved_CFLAGS"
+AC_SUBST(SYMBOL_VISIBILITY)
+
+dnl Generate the output
+AM_CONFIG_HEADER(config.h)
+
+AC_ARG_ENABLE(talloc,
+	[  --disable-talloc Disable building talloc memory allocator ],
+	[enable_talloc=0], [enable_talloc=1])
+AM_CONDITIONAL(ENABLE_TALLOC, test "x$enable_talloc" = "x1")
+
+AC_ARG_ENABLE(tests,
+	[  --disable-tests Disable building test programs ],
+	[enable_tests=0], [enable_tests=1])
+AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1")
+
+AC_OUTPUT(
+	libosmocore.pc
+	include/osmocore/Makefile
+	include/osmocore/protocol/Makefile
+	include/Makefile
+	src/Makefile
+	tests/Makefile
+	tests/timer/Makefile
+	tests/sms/Makefile
+	Makefile)
diff --git a/libosmocore/git-version-gen b/libosmocore/git-version-gen
new file mode 100755
index 0000000..42cf3d2
--- /dev/null
+++ b/libosmocore/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#	echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#	echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+    1) ;;
+    *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+    v=`cat $tarball_version_file` || exit 1
+    case $v in
+	*$nl*) v= ;; # reject multi-line output
+	[0-9]*) ;;
+	*) v= ;;
+    esac
+    test -z "$v" \
+	&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+    : # use $v
+elif
+       v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+	  || git describe --abbrev=4 HEAD 2>/dev/null` \
+    && case $v in
+	 [0-9]*) ;;
+	 v[0-9]*) ;;
+	 *) (exit 1) ;;
+       esac
+then
+    # Is this a new git that lists number of commits since the last
+    # tag or the previous older version that did not?
+    #   Newer: v6.10-77-g0f8faeb
+    #   Older: v6.10-g0f8faeb
+    case $v in
+	*-*-*) : git describe is okay three part flavor ;;
+	*-*)
+	    : git describe is older two part flavor
+	    # Recreate the number of commits and rewrite such that the
+	    # result is the same as if we were using the newer version
+	    # of git describe.
+	    vtag=`echo "$v" | sed 's/-.*//'`
+	    numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+	    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+	    ;;
+    esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+    v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+    '') ;;
+    *) # Append the suffix only if there isn't one already.
+	case $v in
+	  *-dirty) ;;
+	  *) v="$v-dirty" ;;
+	esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/libosmocore/include/Makefile.am b/libosmocore/include/Makefile.am
new file mode 100644
index 0000000..f0015d5
--- /dev/null
+++ b/libosmocore/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = osmocore
diff --git a/libosmocore/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am
new file mode 100644
index 0000000..1c3a33f
--- /dev/null
+++ b/libosmocore/include/osmocore/Makefile.am
@@ -0,0 +1,12 @@
+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 logging.h
+
+if ENABLE_TALLOC
+osmocore_HEADERS += talloc.h
+endif
+
+osmocoredir = $(includedir)/osmocore
+
+SUBDIRS = protocol
diff --git a/libosmocore/include/osmocore/bitvec.h b/libosmocore/include/osmocore/bitvec.h
new file mode 100644
index 0000000..7a26bce
--- /dev/null
+++ b/libosmocore/include/osmocore/bitvec.h
@@ -0,0 +1,65 @@
+#ifndef _BITVEC_H
+#define _BITVEC_H
+
+/* bit vector utility routines */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.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.
+ *
+ */
+
+
+/* In GSM mac blocks, every bit can be 0 or 1, or L or H.  L/H are
+ * defined relative to the 0x2b padding pattern */
+enum bit_value {
+	ZERO	= 0,
+	ONE	= 1,
+	L	= 2,
+	H	= 3,
+};
+
+struct bitvec {
+	unsigned int cur_bit;	/* curser to the next unused bit */
+	unsigned int data_len;	/* length of data array in bytes */
+	uint8_t *data;		/* pointer to data array */
+};
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr);
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n);
+
+/* Set a bit at given position */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
+			enum bit_value bit);
+
+/* Set the next bit in the vector */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+
+/* Set multiple bits at the current position */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
+
+/* Add an unsigned integer (of length count bits) to current position */
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
+
+
+/* Pad the bit vector up to a certain bit position */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+
+#endif /* _BITVEC_H */
diff --git a/libosmocore/include/osmocore/comp128.h b/libosmocore/include/osmocore/comp128.h
new file mode 100644
index 0000000..c37808f
--- /dev/null
+++ b/libosmocore/include/osmocore/comp128.h
@@ -0,0 +1,22 @@
+/*
+ * COMP128 header
+ *
+ * See comp128.c for details
+ */
+
+#ifndef __COMP128_H__
+#define __COMP128_H__
+
+#include <stdint.h>
+
+/*
+ * Performs the COMP128 algorithm (used as A3/A8)
+ * ki    : uint8_t [16]
+ * srand : uint8_t [16]
+ * sres  : uint8_t [4]
+ * kc    : uint8_t [8]
+ */
+void comp128(uint8_t *ki, uint8_t *srand, uint8_t *sres, uint8_t *kc);
+
+#endif /* __COMP128_H__ */
+
diff --git a/libosmocore/include/osmocore/gsm48.h b/libosmocore/include/osmocore/gsm48.h
new file mode 100644
index 0000000..1e96357
--- /dev/null
+++ b/libosmocore/include/osmocore/gsm48.h
@@ -0,0 +1,17 @@
+#ifndef _OSMOCORE_GSM48_H
+
+#include <osmocore/tlv.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/gsm48_ie.h>
+
+extern const struct tlv_definition gsm48_att_tlvdef;
+const char *gsm48_cc_state_name(uint8_t state);
+const char *gsm48_cc_msg_name(uint8_t msgtype);
+const char *rr_cause_name(uint8_t cause);
+
+void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+			uint16_t mnc, uint16_t lac);
+int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
+int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
+
+#endif
diff --git a/libosmocore/include/osmocore/gsm48_ie.h b/libosmocore/include/osmocore/gsm48_ie.h
new file mode 100644
index 0000000..200619a
--- /dev/null
+++ b/libosmocore/include/osmocore/gsm48_ie.h
@@ -0,0 +1,107 @@
+#ifndef _OSMOCORE_GSM48_IE_H
+#define _OSMOCORE_GSM48_IE_H
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/mncc.h>
+#include <osmocore/protocol/gsm_04_08.h>
+
+/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
+int gsm48_decode_bcd_number(char *output, int output_len,
+			    const uint8_t *bcd_lv, int h_len);
+
+/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
+int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
+			    int h_len, const char *input);
+/* decode 'bearer capability' */
+int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+			     const uint8_t *lv);
+/* encode 'bearer capability' */
+int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
+			     const struct gsm_mncc_bearer_cap *bcap);
+/* decode 'call control cap' */
+int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv);
+/* encode 'call control cap' */
+int gsm48_encode_cccap(struct msgb *msg,
+			const struct gsm_mncc_cccap *ccap);
+/* decode 'called party BCD number' */
+int gsm48_decode_called(struct gsm_mncc_number *called,
+			 const uint8_t *lv);
+/* encode 'called party BCD number' */
+int gsm48_encode_called(struct msgb *msg,
+			 const struct gsm_mncc_number *called);
+/* decode callerid of various IEs */
+int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
+			 const uint8_t *lv);
+/* encode callerid of various IEs */
+int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+			   const struct gsm_mncc_number *callerid);
+/* decode 'cause' */
+int gsm48_decode_cause(struct gsm_mncc_cause *cause,
+			const uint8_t *lv);
+/* encode 'cause' */
+int gsm48_encode_cause(struct msgb *msg, int lv_only,
+			const struct gsm_mncc_cause *cause);
+/* decode 'calling number' */
+int gsm48_decode_calling(struct gsm_mncc_number *calling,
+			 const uint8_t *lv);
+/* encode 'calling number' */
+int gsm48_encode_calling(struct msgb *msg, 
+			  const struct gsm_mncc_number *calling);
+/* decode 'connected number' */
+int gsm48_decode_connected(struct gsm_mncc_number *connected,
+			 const uint8_t *lv);
+/* encode 'connected number' */
+int gsm48_encode_connected(struct msgb *msg,
+			    const struct gsm_mncc_number *connected);
+/* decode 'redirecting number' */
+int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+			 const uint8_t *lv);
+/* encode 'redirecting number' */
+int gsm48_encode_redirecting(struct msgb *msg,
+			      const struct gsm_mncc_number *redirecting);
+/* decode 'facility' */
+int gsm48_decode_facility(struct gsm_mncc_facility *facility,
+			   const uint8_t *lv);
+/* encode 'facility' */
+int gsm48_encode_facility(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_facility *facility);
+/* decode 'notify' */
+int gsm48_decode_notify(int *notify, const uint8_t *v);
+/* encode 'notify' */
+int gsm48_encode_notify(struct msgb *msg, int notify);
+/* decode 'signal' */
+int gsm48_decode_signal(int *signal, const uint8_t *v);
+/* encode 'signal' */
+int gsm48_encode_signal(struct msgb *msg, int signal);
+/* decode 'keypad' */
+int gsm48_decode_keypad(int *keypad, const uint8_t *lv);
+/* encode 'keypad' */
+int gsm48_encode_keypad(struct msgb *msg, int keypad);
+/* decode 'progress' */
+int gsm48_decode_progress(struct gsm_mncc_progress *progress,
+			   const uint8_t *lv);
+/* encode 'progress' */
+int gsm48_encode_progress(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_progress *p);
+/* decode 'user-user' */
+int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
+			   const uint8_t *lv);
+/* encode 'useruser' */
+int gsm48_encode_useruser(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_useruser *uu);
+/* decode 'ss version' */
+int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
+			    const uint8_t *lv);
+/* encode 'ss version' */
+int gsm48_encode_ssversion(struct msgb *msg,
+			   const struct gsm_mncc_ssversion *ssv);
+/* decode 'more data' does not require a function, because it has no value */
+/* encode 'more data' */
+int gsm48_encode_more(struct msgb *msg);
+
+#endif
diff --git a/libosmocore/include/osmocore/gsm_utils.h b/libosmocore/include/osmocore/gsm_utils.h
new file mode 100644
index 0000000..c87e967
--- /dev/null
+++ b/libosmocore/include/osmocore/gsm_utils.h
@@ -0,0 +1,84 @@
+/* GSM utility functions, e.g. coding and decoding */
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef GSM_UTILS_H
+#define GSM_UTILS_H
+
+#include <stdint.h>
+
+struct gsm_time {
+	uint32_t	fn;	/* FN count */
+	uint16_t	t1;	/* FN div (26*51) */
+	uint8_t		t2;	/* FN modulo 26 */
+	uint8_t		t3;	/* FN modulo 51 */
+	uint8_t		tc;
+};
+
+enum gsm_band {
+	GSM_BAND_850	= 1,
+	GSM_BAND_900	= 2,
+	GSM_BAND_1800	= 4,
+	GSM_BAND_1900	= 8,
+	GSM_BAND_450	= 0x10,
+	GSM_BAND_480	= 0x20,
+	GSM_BAND_750	= 0x40,
+	GSM_BAND_810	= 0x80,
+};
+
+const char *gsm_band_name(enum gsm_band band);
+enum gsm_band gsm_band_parse(const char *mhz);
+
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_encode(uint8_t *result, const char *data);
+
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev);
+uint8_t dbm2rxlev(int dbm);
+
+/* According to GSM 04.08 Chapter 10.5.2.29 */
+static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
+static inline int rach_max_trans_raw2val(int raw) {
+	const int tbl[4] = { 1, 2, 4, 7 };
+	return tbl[raw & 3];
+}
+
+#define	ARFCN_PCS	0x8000
+#define	ARFCN_UPLINK	0x4000
+
+enum gsm_band gsm_arfcn2band(uint16_t arfcn);
+
+/* Convert an ARFCN to the frequency in MHz * 10 */
+uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink);
+
+/* Convert from frame number to GSM time */
+void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
+
+/* Convert from GSM time to frame number */
+uint32_t gsm_gsmtime2fn(struct gsm_time *time);
+
+void generate_backtrace();
+#endif
diff --git a/libosmocore/include/osmocore/gsmtap.h b/libosmocore/include/osmocore/gsmtap.h
new file mode 100644
index 0000000..dcd64bd
--- /dev/null
+++ b/libosmocore/include/osmocore/gsmtap.h
@@ -0,0 +1,72 @@
+#ifndef _GSMTAP_H
+#define _GSMTAP_H
+
+/* gsmtap header, pseudo-header in front of the actua GSM payload */
+
+/* GSMTAP is a generic header format for GSM protocol captures,
+ * it uses the IANA-assigned UDP port number 4729 and carries
+ * payload in various formats of GSM interfaces such as Um MAC
+ * blocks or Um bursts.
+ *
+ * Example programs generating GSMTAP data are airprobe
+ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/)
+ */
+
+#include <stdint.h>
+
+#define GSMTAP_VERSION		0x02
+
+#define GSMTAP_TYPE_UM		0x01
+#define GSMTAP_TYPE_ABIS	0x02
+#define GSMTAP_TYPE_UM_BURST	0x03	/* raw burst bits */
+
+#define GSMTAP_BURST_UNKNOWN		0x00
+#define GSMTAP_BURST_FCCH		0x01
+#define GSMTAP_BURST_PARTIAL_SCH	0x02
+#define GSMTAP_BURST_SCH		0x03
+#define GSMTAP_BURST_CTS_SCH		0x04
+#define GSMTAP_BURST_COMPACT_SCH	0x05
+#define GSMTAP_BURST_NORMAL		0x06
+#define GSMTAP_BURST_DUMMY		0x07
+#define GSMTAP_BURST_ACCESS		0x08
+#define GSMTAP_BURST_NONE		0x09
+
+#define GSMTAP_CHANNEL_UNKNOWN	0x00
+#define GSMTAP_CHANNEL_BCCH	0x01
+#define GSMTAP_CHANNEL_CCCH	0x02
+#define GSMTAP_CHANNEL_RACH	0x03
+#define GSMTAP_CHANNEL_AGCH	0x04
+#define GSMTAP_CHANNEL_PCH	0x05
+#define GSMTAP_CHANNEL_SDCCH	0x06
+#define GSMTAP_CHANNEL_SDCCH4	0x07
+#define GSMTAP_CHANNEL_SDCCH8	0x08
+#define GSMTAP_CHANNEL_TCH_F	0x09
+#define GSMTAP_CHANNEL_TCH_H	0x0a
+#define GSMTAP_CHANNEL_ACCH	0x80
+
+#define GSMTAP_ARFCN_F_PCS	0x8000
+#define GSMTAP_ARFCN_F_UPLINK	0x4000
+#define GSMTAP_ARFCN_MASK	0x3fff
+
+#define GSMTAP_UDP_PORT			4729
+
+struct gsmtap_hdr {
+	uint8_t version;	/* version, set to 0x01 currently */
+	uint8_t hdr_len;	/* length in number of 32bit words */
+	uint8_t type;		/* see GSMTAP_TYPE_* */
+	uint8_t timeslot;	/* timeslot (0..7 on Um) */
+
+	uint16_t arfcn;		/* ARFCN (frequency) */
+	int8_t signal_dbm;	/* signal level in dBm */
+	int8_t snr_db;		/* signal/noise ratio in dB */
+
+	uint32_t frame_number;	/* GSM Frame Number (FN) */
+
+	uint8_t sub_type;	/* Type of burst/channel, see above */
+	uint8_t antenna_nr;	/* Antenna Number */
+	uint8_t sub_slot;	/* sub-slot within timeslot */
+	uint8_t res;		/* reserved for future use (RFU) */
+
+} __attribute__((packed));
+
+#endif /* _GSMTAP_H */
diff --git a/libosmocore/include/osmocore/linuxlist.h b/libosmocore/include/osmocore/linuxlist.h
new file mode 100644
index 0000000..fb99c5e
--- /dev/null
+++ b/libosmocore/include/osmocore/linuxlist.h
@@ -0,0 +1,360 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+        const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr);	\
+        (type *)( (char *)__mptr - offsetof(type, member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1  ((void *) 0x00100100)
+#define LLIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+	struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+	struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *_new,
+			      struct llist_head *prev,
+			      struct llist_head *next)
+{
+	next->prev = _new;
+	_new->next = next;
+	_new->prev = prev;
+	prev->next = _new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *_new, struct llist_head *head)
+{
+	__llist_add(_new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
+{
+	__llist_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+	__llist_del(entry->prev, entry->next);
+	entry->next = (struct llist_head *)LLIST_POISON1;
+	entry->prev = (struct llist_head *)LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+	__llist_del(entry->prev, entry->next);
+	INIT_LLIST_HEAD(entry); 
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+        __llist_del(llist->prev, llist->next);
+        llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+				  struct llist_head *head)
+{
+        __llist_del(llist->prev, llist->next);
+        llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+	return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+				 struct llist_head *head)
+{
+	struct llist_head *first = llist->next;
+	struct llist_head *last = llist->prev;
+	struct llist_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+	if (!llist_empty(llist))
+		__llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+				    struct llist_head *head)
+{
+	if (!llist_empty(llist)) {
+		__llist_splice(llist, head);
+		INIT_LLIST_HEAD(llist);
+	}
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr:	the &struct llist_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * llist_for_each	-	iterate over a llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each	-	iterate over a llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev	-	iterate over a llist backwards
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+        	pos = pos->prev, prefetch(pos->prev))
+        	
+/**
+ * llist_for_each_safe	-	iterate over a llist safe against removal of llist entry
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @n:		another &struct llist_head to use as temporary storage
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry	-	iterate over llist of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member)				\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member)			\
+	for (pos = llist_entry((head)->prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue -	iterate over llist of given type
+ *			continuing after existing point
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) 		\
+	for (pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head);					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		n = llist_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu	-	iterate over an rcu-protected llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+        	
+#define __llist_for_each_rcu(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+        	
+/**
+ * llist_for_each_safe_rcu	-	iterate over an rcu-protected llist safe
+ *					against removal of llist entry
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @n:		another &struct llist_head to use as temporary storage
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu	-	iterate over rcu llist of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member)			\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     ({ smp_read_barrier_depends(); 0;}),		\
+		     prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu	-	iterate over an rcu-protected llist 
+ *			continuing after existing point.
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+	for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+        	(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
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/mncc.h b/libosmocore/include/osmocore/mncc.h
new file mode 100644
index 0000000..a094bb9
--- /dev/null
+++ b/libosmocore/include/osmocore/mncc.h
@@ -0,0 +1,71 @@
+#ifndef _OSMOCORE_MNCC_H
+#define _OSMOCORE_MNCC_H
+
+#define GSM_MAX_FACILITY       128
+#define GSM_MAX_SSVERSION      128
+#define GSM_MAX_USERUSER       128
+
+/* Expanded fields from GSM TS 04.08, Table 10.5.102 */
+struct gsm_mncc_bearer_cap {
+	int		transfer;	/* Information Transfer Capability */
+	int 		mode;		/* Transfer Mode */
+	int		coding;		/* Coding Standard */
+	int		radio;		/* Radio Channel Requirement */
+	int		speech_ctm;	/* CTM text telephony indication */
+	int		speech_ver[8];	/* Speech version indication */
+};
+
+struct gsm_mncc_number {
+	int 		type;
+	int 		plan;
+	int		present;
+	int		screen;
+	char		number[33];
+};
+
+struct gsm_mncc_cause {
+	int		location;
+	int		coding;
+	int		rec;
+	int		rec_val;
+	int		value;
+	int		diag_len;
+	char		diag[32];
+};
+
+struct gsm_mncc_useruser {
+	int		proto;
+	char		info[GSM_MAX_USERUSER + 1]; /* + termination char */
+};
+
+struct gsm_mncc_progress {
+	int		coding;
+	int		location;
+	int 		descr;
+};
+
+struct gsm_mncc_facility {
+	int		len;
+	char		info[GSM_MAX_FACILITY];
+};
+
+struct gsm_mncc_ssversion {
+	int		len;
+	char		info[GSM_MAX_SSVERSION];
+};
+
+struct gsm_mncc_cccap {
+	int		dtmf;
+	int		pcp;
+};
+
+enum {
+	GSM_MNCC_BCAP_SPEECH	= 0,
+	GSM_MNCC_BCAP_UNR_DIG	= 1,
+	GSM_MNCC_BCAP_AUDIO	= 2,
+	GSM_MNCC_BCAP_FAX_G3	= 3,
+	GSM_MNCC_BCAP_OTHER_ITC = 5,
+	GSM_MNCC_BCAP_RESERVED	= 7,
+};
+
+#endif
diff --git a/libosmocore/include/osmocore/msgb.h b/libosmocore/include/osmocore/msgb.h
new file mode 100644
index 0000000..31db719
--- /dev/null
+++ b/libosmocore/include/osmocore/msgb.h
@@ -0,0 +1,175 @@
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.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 <stdint.h>
+#include "linuxlist.h"
+
+struct bts_link;
+
+struct msgb {
+	struct llist_head list;
+
+	/* ptr to the physical E1 link to the BTS(s) */
+	struct gsm_bts_link *bts_link;
+
+	/* Part of which TRX logical channel we were received / transmitted */
+	struct gsm_bts_trx *trx;
+	struct gsm_lchan *lchan;
+
+	/* the Layer1 header (if any) */
+	unsigned char *l1h;
+	/* the A-bis layer 2 header: OML, RSL(RLL), NS */
+	unsigned char *l2h;
+	/* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
+	unsigned char *l3h;
+
+	/* the layer 4 header */
+	union {
+		unsigned char *smsh;
+		unsigned char *llch;
+		unsigned char *l4h;
+	};
+
+	/* the layer 5 header, GPRS: GMM header */
+	unsigned char *gmmh;
+	uint32_t tlli;
+
+	uint16_t data_len;
+	uint16_t len;
+
+	unsigned char *head;
+	unsigned char *tail;
+	unsigned char *data;
+	unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(uint16_t size, const char *name);
+extern void msgb_free(struct msgb *m);
+extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
+extern struct msgb *msgb_dequeue(struct llist_head *queue);
+extern void msgb_reset(struct msgb *m);
+
+#define msgb_l1(m)	((void *)(m->l1h))
+#define msgb_l2(m)	((void *)(m->l2h))
+#define msgb_l3(m)	((void *)(m->l3h))
+#define msgb_sms(m)	((void *)(m->smsh))
+
+static inline unsigned int msgb_l1len(const struct msgb *msgb)
+{
+	return msgb->tail - (uint8_t *)msgb_l1(msgb);
+}
+
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+	return msgb->tail - (uint8_t *)msgb_l2(msgb);
+}
+
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+	return msgb->tail - (uint8_t *)msgb_l3(msgb);
+}
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+	return msgb->len - msgb->data_len;
+}
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+	unsigned char *tmp = msgb->tail;
+	msgb->tail += len;
+	msgb->len += len;
+	return tmp;
+}
+static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
+{
+	uint8_t *space = msgb_put(msgb, 1);
+	space[0] = word & 0xFF;
+}
+static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
+{
+	uint8_t *space = msgb_put(msgb, 2);
+	space[0] = word >> 8 & 0xFF;
+	space[1] = word & 0xFF;
+}
+static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
+{
+	uint8_t *space = msgb_put(msgb, 4);
+	space[0] = word >> 24 & 0xFF;
+	space[1] = word >> 16 & 0xFF;
+	space[2] = word >> 8 & 0xFF;
+	space[3] = word & 0xFF;
+}
+static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
+{
+	unsigned char *tmp = msgb->data;
+	msgb->data += len;
+	msgb->len -= len;
+	return tmp;
+}
+static inline uint8_t msgb_get_u8(struct msgb *msgb)
+{
+	uint8_t *space = msgb_get(msgb, 1);
+	return space[0];
+}
+static inline uint16_t msgb_get_u16(struct msgb *msgb)
+{
+	uint8_t *space = msgb_get(msgb, 2);
+	return space[0] << 8 | space[1];
+}
+static inline uint32_t msgb_get_u32(struct msgb *msgb)
+{
+	uint8_t *space = msgb_get(msgb, 4);
+	return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+	msgb->data -= len;
+	msgb->len += len;
+	return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+	msgb->len -= len;
+	return msgb->data += len;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+	return (msgb->head + msgb->data_len) - msgb->tail;
+}
+
+/* increase the headroom of an empty msgb, reducing the tailroom */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+	msg->data += len;
+	msg->tail += len;
+}
+
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+						const char *name)
+{
+	struct msgb *msg = msgb_alloc(size, name);
+	if (msg)
+		msgb_reserve(msg, headroom);
+	return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/libosmocore/include/osmocore/protocol/Makefile.am b/libosmocore/include/osmocore/protocol/Makefile.am
new file mode 100644
index 0000000..6d8883e
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/Makefile.am
@@ -0,0 +1,3 @@
+osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h
+
+osmocore_protodir = $(includedir)/osmocore/protocol
diff --git a/libosmocore/include/osmocore/protocol/gsm_04_08.h b/libosmocore/include/osmocore/protocol/gsm_04_08.h
new file mode 100644
index 0000000..801b9b5
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -0,0 +1,743 @@
+#ifndef PROTO_GSM_04_08_H
+#define PROTO_GSM_04_08_H
+
+#include <stdint.h>
+
+/* GSM TS 04.08  definitions */
+struct gsm_lchan;
+
+struct gsm48_classmark1 {
+	uint8_t spare:1,
+		 rev_level:2,
+		 es_ind:1,
+		 a5_1:1,
+		 pwr_lev:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+	uint8_t chan_nr;
+	union {
+		struct {
+			uint8_t maio_high:4,
+				 h:1,
+				 tsc:3;
+			uint8_t hsn:6,
+				 maio_low:2;
+		} h1;
+		struct {
+			uint8_t arfcn_high:2,
+				 spare:2,
+				 h:1,
+				 tsc:3;
+			uint8_t arfcn_low;
+		} h0;
+	};
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+	uint8_t smod : 2,
+		 spare: 1,
+		 icmi : 1,
+		 nscb : 1,
+		 ver : 3;
+	uint8_t m4_75 : 1,
+		 m5_15 : 1,
+		 m5_90 : 1,
+		 m6_70 : 1,
+		 m7_40 : 1,
+		 m7_95 : 1,
+		 m10_2 : 1,
+		 m12_2 : 1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+	uint8_t ra;
+	uint8_t t3_high:3,
+		 t1_:5;
+	uint8_t t2:5,
+		 t3_low:3;
+} __attribute__ ((packed));
+
+/*
+ * Chapter 9.1.5/9.1.6
+ *
+ * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
+ */
+struct gsm48_chan_mode_modify {
+	struct gsm48_chan_desc chan_desc;
+	uint8_t mode;
+} __attribute__ ((packed));
+
+enum gsm48_chan_mode {
+	GSM48_CMODE_SIGN	= 0x00,
+	GSM48_CMODE_SPEECH_V1	= 0x01,
+	GSM48_CMODE_SPEECH_EFR	= 0x21,
+	GSM48_CMODE_SPEECH_AMR	= 0x41,
+	GSM48_CMODE_DATA_14k5	= 0x0f,
+	GSM48_CMODE_DATA_12k0	= 0x03,
+	GSM48_CMODE_DATA_6k0	= 0x0b,
+	GSM48_CMODE_DATA_3k6	= 0x23,
+};
+
+/* Chapter 9.1.2 */
+struct gsm48_ass_cmd {
+	/* Semantic is from 10.5.2.5a */
+	struct gsm48_chan_desc chan_desc;
+	uint8_t power_command;
+	uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.2.2 */
+struct gsm48_cell_desc {
+	uint8_t bcc:3,
+		 ncc:3,
+		 arfcn_hi:2;
+	uint8_t arfcn_lo;
+} __attribute__((packed));
+
+/* Chapter 9.1.15 */
+struct gsm48_ho_cmd {
+	struct gsm48_cell_desc cell_desc;
+	struct gsm48_chan_desc chan_desc;
+	uint8_t ho_ref;
+	uint8_t power_command;
+	uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+	uint8_t l2_plen;
+	uint8_t proto_discr;
+	uint8_t msg_type;
+	uint8_t page_mode;
+	struct gsm48_chan_desc chan_desc;
+	struct gsm48_req_ref req_ref;
+	uint8_t timing_advance;
+	uint8_t mob_alloc_len;
+	uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.3 */
+struct gsm48_loc_area_id {
+	uint8_t digits[3];	/* BCD! */
+	uint16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.2 */
+struct gsm48_auth_req {
+	uint8_t key_seq:4,
+	         spare:4;
+	uint8_t rand[16];
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+	uint8_t type:4,
+		 key_seq:4;
+	struct gsm48_loc_area_id lai;
+	struct gsm48_classmark1 classmark1;
+	uint8_t mi_len;
+	uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+	uint8_t proto_discr;
+	uint8_t msg_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.3x System information Type header */
+struct gsm48_system_information_type_header {
+	uint8_t l2_plen;
+	uint8_t rr_protocol_discriminator :4,
+		skip_indicator:4; 
+	uint8_t system_information;
+} __attribute__ ((packed));
+
+struct gsm48_rach_control {
+	uint8_t re :1,
+		 cell_bar :1,
+		 tx_integer :4,
+		 max_trans :2;
+	uint8_t t2;
+	uint8_t t3;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+	uint8_t ms_txpwr_max_ccch:5,	/* GSM 05.08 MS-TXPWR-MAX-CCCH */
+		 cell_resel_hyst:3;	/* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+	uint8_t rxlev_acc_min:6,	/* GSM 05.08 RXLEV-ACCESS-MIN */
+		 neci:1,
+		 acs:1;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
+struct gsm48_control_channel_descr {
+	uint8_t ccch_conf :3,
+		bs_ag_blks_res :3,
+		att :1,
+		spare1 :1;
+	uint8_t bs_pa_mfrms : 3,
+		spare2 :5;
+	uint8_t t3212;
+} __attribute__ ((packed));
+
+struct gsm48_cell_options {
+	uint8_t radio_link_timeout:4,
+		 dtx:2,
+		 pwrc:1,
+		 spare:1;
+} __attribute__ ((packed));
+
+/* Section 9.2.9 CM service request */
+struct gsm48_service_request {
+	uint8_t cm_service_type : 4,
+		 cipher_key_seq  : 4;
+	/* length + 3 bytes */
+	uint32_t classmark;
+	uint8_t mi_len;
+	uint8_t mi[0];
+	/* optional priority level */
+} __attribute__ ((packed));
+
+/* Section 9.1.31 System information Type 1 */
+struct gsm48_system_information_type_1 {
+	struct gsm48_system_information_type_header header;
+	uint8_t cell_channel_description[16];
+	struct gsm48_rach_control rach_control;
+	uint8_t rest_octets[0]; /* NCH position on the CCCH */
+} __attribute__ ((packed));
+
+/* Section 9.1.32 System information Type 2 */
+struct gsm48_system_information_type_2 {
+	struct gsm48_system_information_type_header header;
+	uint8_t bcch_frequency_list[16];
+	uint8_t ncc_permitted;
+	struct gsm48_rach_control rach_control;
+} __attribute__ ((packed));
+
+/* Section 9.1.35 System information Type 3 */
+struct gsm48_system_information_type_3 {
+	struct gsm48_system_information_type_header header;
+	uint16_t cell_identity;
+	struct gsm48_loc_area_id lai;
+	struct gsm48_control_channel_descr control_channel_desc;
+	struct gsm48_cell_options cell_options;
+	struct gsm48_cell_sel_par cell_sel_par;
+	struct gsm48_rach_control rach_control;
+	uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.36 System information Type 4 */
+struct gsm48_system_information_type_4 {
+	struct gsm48_system_information_type_header header;
+	struct gsm48_loc_area_id lai;
+	struct gsm48_cell_sel_par cell_sel_par;
+	struct gsm48_rach_control rach_control;
+	/*	optional CBCH conditional CBCH... followed by
+		mandantory SI 4 Reset Octets
+	 */
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.37 System information Type 5 */
+struct gsm48_system_information_type_5 {
+	uint8_t rr_protocol_discriminator :4,
+		skip_indicator:4; 
+	uint8_t system_information;
+	uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.40 System information Type 6 */
+struct gsm48_system_information_type_6 {
+	uint8_t rr_protocol_discriminator :4,
+		skip_indicator:4; 
+	uint8_t system_information;
+	uint16_t cell_identity;
+	struct gsm48_loc_area_id lai;
+	struct gsm48_cell_options cell_options;
+	uint8_t ncc_permitted;
+	uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.43a System Information type 13 */
+struct gsm48_system_information_type_13 {
+	struct gsm48_system_information_type_header header;
+	uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.2.12 IMSI Detach Indication */
+struct gsm48_imsi_detach_ind {
+	struct gsm48_classmark1 classmark1;
+	uint8_t mi_len;
+	uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.2 + GSM 04.07 12.2.3.1.1 */
+#define GSM48_PDISC_GROUP_CC	0x00
+#define GSM48_PDISC_BCAST_CC	0x01
+#define GSM48_PDISC_PDSS1	0x02
+#define GSM48_PDISC_CC		0x03
+#define GSM48_PDISC_PDSS2	0x04
+#define GSM48_PDISC_MM		0x05
+#define GSM48_PDISC_RR		0x06
+#define GSM48_PDISC_MM_GPRS	0x08
+#define GSM48_PDISC_SMS		0x09
+#define GSM48_PDISC_SM_GPRS	0x0a
+#define GSM48_PDISC_NC_SS	0x0b
+#define GSM48_PDISC_LOC		0x0c
+#define GSM48_PDISC_MASK	0x0f
+#define GSM48_PDISC_USSD	0x11
+
+/* Section 10.4 */
+#define GSM48_MT_RR_INIT_REQ		0x3c
+#define GSM48_MT_RR_ADD_ASS		0x3b
+#define GSM48_MT_RR_IMM_ASS		0x3f
+#define GSM48_MT_RR_IMM_ASS_EXT		0x39
+#define GSM48_MT_RR_IMM_ASS_REJ		0x3a
+
+#define GSM48_MT_RR_CIPH_M_CMD		0x35
+#define GSM48_MT_RR_CIPH_M_COMPL	0x32
+
+#define GSM48_MT_RR_CFG_CHG_CMD		0x30
+#define GSM48_MT_RR_CFG_CHG_ACK		0x31
+#define GSM48_MT_RR_CFG_CHG_REJ		0x33
+
+#define GSM48_MT_RR_ASS_CMD		0x2e
+#define GSM48_MT_RR_ASS_COMPL		0x29
+#define GSM48_MT_RR_ASS_FAIL		0x2f
+#define GSM48_MT_RR_HANDO_CMD		0x2b
+#define GSM48_MT_RR_HANDO_COMPL		0x2c
+#define GSM48_MT_RR_HANDO_FAIL		0x28
+#define GSM48_MT_RR_HANDO_INFO		0x2d
+
+#define GSM48_MT_RR_CELL_CHG_ORDER	0x08
+#define GSM48_MT_RR_PDCH_ASS_CMD	0x23
+
+#define GSM48_MT_RR_CHAN_REL		0x0d
+#define GSM48_MT_RR_PART_REL		0x0a
+#define GSM48_MT_RR_PART_REL_COMP	0x0f
+
+#define GSM48_MT_RR_PAG_REQ_1		0x21
+#define GSM48_MT_RR_PAG_REQ_2		0x22
+#define GSM48_MT_RR_PAG_REQ_3		0x24
+#define GSM48_MT_RR_PAG_RESP		0x27
+#define GSM48_MT_RR_NOTIF_NCH		0x20
+#define GSM48_MT_RR_NOTIF_FACCH		0x25
+#define GSM48_MT_RR_NOTIF_RESP		0x26
+
+#define GSM48_MT_RR_SYSINFO_8		0x18
+#define GSM48_MT_RR_SYSINFO_1		0x19
+#define GSM48_MT_RR_SYSINFO_2		0x1a
+#define GSM48_MT_RR_SYSINFO_3		0x1b
+#define GSM48_MT_RR_SYSINFO_4		0x1c
+#define GSM48_MT_RR_SYSINFO_5		0x1d
+#define GSM48_MT_RR_SYSINFO_6		0x1e
+#define GSM48_MT_RR_SYSINFO_7		0x1f
+
+#define GSM48_MT_RR_SYSINFO_2bis	0x02
+#define GSM48_MT_RR_SYSINFO_2ter	0x03
+#define GSM48_MT_RR_SYSINFO_5bis	0x05
+#define GSM48_MT_RR_SYSINFO_5ter	0x06
+#define GSM48_MT_RR_SYSINFO_9		0x04
+#define GSM48_MT_RR_SYSINFO_13		0x00
+
+#define GSM48_MT_RR_SYSINFO_16		0x3d
+#define GSM48_MT_RR_SYSINFO_17		0x3e
+
+#define GSM48_MT_RR_CHAN_MODE_MODIF	0x10
+#define GSM48_MT_RR_STATUS		0x12
+#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK	0x17
+#define GSM48_MT_RR_FREQ_REDEF		0x14
+#define GSM48_MT_RR_MEAS_REP		0x15
+#define GSM48_MT_RR_CLSM_CHG		0x16
+#define GSM48_MT_RR_CLSM_ENQ		0x13
+#define GSM48_MT_RR_EXT_MEAS_REP	0x36
+#define GSM48_MT_RR_EXT_MEAS_REP_ORD	0x37
+#define GSM48_MT_RR_GPRS_SUSP_REQ	0x34
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT	0x08
+#define GSM48_MT_RR_UPLINK_RELEASE	0x0e
+#define GSM48_MT_RR_UPLINK_FREE		0x0c
+#define GSM48_MT_RR_UPLINK_BUSY		0x2a
+#define GSM48_MT_RR_TALKER_IND		0x11
+
+#define GSM48_MT_RR_APP_INFO		0x38
+
+/* Table 10.2/3GPP TS 04.08 */
+#define GSM48_MT_MM_IMSI_DETACH_IND	0x01
+#define GSM48_MT_MM_LOC_UPD_ACCEPT	0x02
+#define GSM48_MT_MM_LOC_UPD_REJECT	0x04
+#define GSM48_MT_MM_LOC_UPD_REQUEST	0x08
+
+#define GSM48_MT_MM_AUTH_REJ		0x11
+#define GSM48_MT_MM_AUTH_REQ		0x12
+#define GSM48_MT_MM_AUTH_RESP		0x14
+#define GSM48_MT_MM_ID_REQ		0x18
+#define GSM48_MT_MM_ID_RESP		0x19
+#define GSM48_MT_MM_TMSI_REALL_CMD	0x1a
+#define GSM48_MT_MM_TMSI_REALL_COMPL	0x1b
+
+#define GSM48_MT_MM_CM_SERV_ACC		0x21
+#define GSM48_MT_MM_CM_SERV_REJ		0x22
+#define GSM48_MT_MM_CM_SERV_ABORT	0x23
+#define GSM48_MT_MM_CM_SERV_REQ		0x24
+#define GSM48_MT_MM_CM_SERV_PROMPT	0x25
+#define GSM48_MT_MM_CM_REEST_REQ	0x28
+#define GSM48_MT_MM_ABORT		0x29
+
+#define GSM48_MT_MM_NULL		0x30
+#define GSM48_MT_MM_STATUS		0x31
+#define GSM48_MT_MM_INFO		0x32
+
+/* Table 10.3/3GPP TS 04.08 */
+#define GSM48_MT_CC_ALERTING		0x01
+#define GSM48_MT_CC_CALL_CONF		0x08
+#define GSM48_MT_CC_CALL_PROC		0x02
+#define GSM48_MT_CC_CONNECT		0x07
+#define GSM48_MT_CC_CONNECT_ACK		0x0f
+#define GSM48_MT_CC_EMERG_SETUP		0x0e
+#define GSM48_MT_CC_PROGRESS		0x03
+#define GSM48_MT_CC_ESTAB		0x04
+#define GSM48_MT_CC_ESTAB_CONF		0x06
+#define GSM48_MT_CC_RECALL		0x0b
+#define GSM48_MT_CC_START_CC		0x09
+#define GSM48_MT_CC_SETUP		0x05
+
+#define GSM48_MT_CC_MODIFY		0x17
+#define GSM48_MT_CC_MODIFY_COMPL	0x1f
+#define GSM48_MT_CC_MODIFY_REJECT	0x13
+#define GSM48_MT_CC_USER_INFO		0x10
+#define GSM48_MT_CC_HOLD		0x18
+#define GSM48_MT_CC_HOLD_ACK		0x19
+#define GSM48_MT_CC_HOLD_REJ		0x1a
+#define GSM48_MT_CC_RETR		0x1c
+#define GSM48_MT_CC_RETR_ACK		0x1d
+#define GSM48_MT_CC_RETR_REJ		0x1e
+
+#define GSM48_MT_CC_DISCONNECT		0x25
+#define GSM48_MT_CC_RELEASE		0x2d
+#define GSM48_MT_CC_RELEASE_COMPL	0x2a
+
+#define GSM48_MT_CC_CONG_CTRL		0x39
+#define GSM48_MT_CC_NOTIFY		0x3e
+#define GSM48_MT_CC_STATUS		0x3d
+#define GSM48_MT_CC_STATUS_ENQ		0x34
+#define GSM48_MT_CC_START_DTMF		0x35
+#define GSM48_MT_CC_STOP_DTMF		0x31
+#define GSM48_MT_CC_STOP_DTMF_ACK	0x32
+#define GSM48_MT_CC_START_DTMF_ACK	0x36
+#define GSM48_MT_CC_START_DTMF_REJ	0x37
+#define GSM48_MT_CC_FACILITY		0x3a
+
+/* FIXME: Table 10.4 / 10.4a (GPRS) */
+
+/* Section 10.5.2.26, Table 10.5.64 */
+#define GSM48_PM_MASK		0x03
+#define GSM48_PM_NORMAL		0x00
+#define GSM48_PM_EXTENDED	0x01
+#define GSM48_PM_REORG		0x02
+#define GSM48_PM_SAME		0x03
+
+/* Chapter 10.5.3.5 / Table 10.5.93 */
+#define GSM48_LUPD_NORMAL	0x0
+#define GSM48_LUPD_PERIODIC	0x1
+#define GSM48_LUPD_IMSI_ATT	0x2
+#define GSM48_LUPD_RESERVED	0x3
+
+/* Table 10.5.4 */
+#define GSM_MI_TYPE_MASK	0x07
+#define GSM_MI_TYPE_NONE	0x00
+#define GSM_MI_TYPE_IMSI	0x01
+#define GSM_MI_TYPE_IMEI	0x02
+#define GSM_MI_TYPE_IMEISV	0x03
+#define GSM_MI_TYPE_TMSI	0x04
+#define GSM_MI_ODD		0x08
+
+#define GSM48_IE_MUL_RATE_CFG	0x03	/* 10.5.2.21aa */
+#define GSM48_IE_MOBILE_ID	0x17
+#define GSM48_IE_NAME_LONG	0x43	/* 10.5.3.5a */
+#define GSM48_IE_NAME_SHORT	0x45	/* 10.5.3.5a */
+#define GSM48_IE_UTC		0x46	/* 10.5.3.8 */
+#define GSM48_IE_NET_TIME_TZ	0x47	/* 10.5.3.9 */
+#define GSM48_IE_LSA_IDENT	0x48	/* 10.5.3.11 */
+
+#define GSM48_IE_BEARER_CAP	0x04	/* 10.5.4.5 */
+#define GSM48_IE_CAUSE		0x08	/* 10.5.4.11 */
+#define GSM48_IE_CC_CAP		0x15	/* 10.5.4.5a */
+#define GSM48_IE_ALERT		0x19	/* 10.5.4.26 */
+#define GSM48_IE_FACILITY	0x1c	/* 10.5.4.15 */
+#define GSM48_IE_PROGR_IND	0x1e	/* 10.5.4.21 */
+#define GSM48_IE_AUX_STATUS	0x24	/* 10.5.4.4 */
+#define GSM48_IE_NOTIFY		0x27	/* 10.5.4.20 */
+#define GSM48_IE_KPD_FACILITY	0x2c	/* 10.5.4.17 */
+#define GSM48_IE_SIGNAL		0x34	/* 10.5.4.23 */
+#define GSM48_IE_CONN_BCD	0x4c	/* 10.5.4.13 */
+#define GSM48_IE_CONN_SUB	0x4d	/* 10.5.4.14 */
+#define GSM48_IE_CALLING_BCD	0x5c	/* 10.5.4.9 */
+#define GSM48_IE_CALLING_SUB	0x5d	/* 10.5.4.10 */
+#define GSM48_IE_CALLED_BCD	0x5e	/* 10.5.4.7 */
+#define GSM48_IE_CALLED_SUB	0x6d	/* 10.5.4.8 */
+#define GSM48_IE_REDIR_BCD	0x74	/* 10.5.4.21a */
+#define GSM48_IE_REDIR_SUB	0x75	/* 10.5.4.21b */
+#define GSM48_IE_LOWL_COMPAT	0x7c	/* 10.5.4.18 */
+#define GSM48_IE_HIGHL_COMPAT	0x7d	/* 10.5.4.16 */
+#define GSM48_IE_USER_USER	0x7e	/* 10.5.4.25 */
+#define GSM48_IE_SS_VERS	0x7f	/* 10.5.4.24 */
+#define GSM48_IE_MORE_DATA	0xa0	/* 10.5.4.19 */
+#define GSM48_IE_CLIR_SUPP	0xa1	/* 10.5.4.11a */
+#define GSM48_IE_CLIR_INVOC	0xa2	/* 10.5.4.11b */
+#define GSM48_IE_REV_C_SETUP	0xa3	/* 10.5.4.22a */
+#define GSM48_IE_REPEAT_CIR	0xd1	/* 10.5.4.22 */
+#define GSM48_IE_REPEAT_SEQ	0xd3	/* 10.5.4.22 */
+
+/* Section 10.5.4.11 / Table 10.5.122 */
+#define GSM48_CAUSE_CS_GSM	0x60
+
+/* Section 9.1.2 / Table 9.3 */
+#define GSM48_IE_FRQLIST_AFTER	0x05
+#define GSM48_IE_CELL_CH_DESC	0x62
+#define GSM48_IE_MSLOT_DESC	0x10
+#define GSM48_IE_CHANMODE_1	0x63
+#define GSM48_IE_CHANMODE_2	0x11
+#define GSM48_IE_CHANMODE_3	0x13
+#define GSM48_IE_CHANMODE_4	0x14
+#define GSM48_IE_CHANMODE_5	0x15
+#define GSM48_IE_CHANMODE_6	0x16
+#define GSM48_IE_CHANMODE_7	0x17
+#define GSM48_IE_CHANMODE_8	0x18
+#define GSM48_IE_CHANDESC_2	0x64
+/* FIXME */
+
+/* Section 10.5.4.23 / Table 10.5.130 */
+enum gsm48_signal_val {
+	GSM48_SIGNAL_DIALTONE	= 0x00,
+	GSM48_SIGNAL_RINGBACK	= 0x01,
+	GSM48_SIGNAL_INTERCEPT	= 0x02,
+	GSM48_SIGNAL_NET_CONG	= 0x03,
+	GSM48_SIGNAL_BUSY	= 0x04,
+	GSM48_SIGNAL_CONFIRM	= 0x05,
+	GSM48_SIGNAL_ANSWER	= 0x06,
+	GSM48_SIGNAL_CALL_WAIT	= 0x07,
+	GSM48_SIGNAL_OFF_HOOK	= 0x08,
+	GSM48_SIGNAL_OFF	= 0x3f,
+	GSM48_SIGNAL_ALERT_OFF	= 0x4f,
+};
+
+enum gsm48_cause_loc {
+	GSM48_CAUSE_LOC_USER		= 0x00,
+	GSM48_CAUSE_LOC_PRN_S_LU	= 0x01,
+	GSM48_CAUSE_LOC_PUN_S_LU	= 0x02,
+	GSM48_CAUSE_LOC_TRANS_NET	= 0x03,
+	GSM48_CAUSE_LOC_PUN_S_RU	= 0x04,
+	GSM48_CAUSE_LOC_PRN_S_RU	= 0x05,
+	/* not defined */
+	GSM48_CAUSE_LOC_INN_NET		= 0x07,
+	GSM48_CAUSE_LOC_NET_BEYOND	= 0x0a,
+};
+
+/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
+enum gsm48_rr_cause {
+	GSM48_RR_CAUSE_NORMAL		= 0x00,
+	GSM48_RR_CAUSE_ABNORMAL_UNSPEC	= 0x01,
+	GSM48_RR_CAUSE_ABNORMAL_UNACCT	= 0x02,
+	GSM48_RR_CAUSE_ABNORMAL_TIMER	= 0x03,
+	GSM48_RR_CAUSE_ABNORMAL_NOACT	= 0x04,
+	GSM48_RR_CAUSE_PREMPTIVE_REL	= 0x05,
+	GSM48_RR_CAUSE_HNDOVER_IMP	= 0x06,
+	GSM48_RR_CAUSE_CHAN_MODE_UNACCT	= 0x07,
+	GSM48_RR_CAUSE_FREQ_NOT_IMPL	= 0x08,
+	GSM48_RR_CAUSE_CALL_CLEARED	= 0x41,
+	GSM48_RR_CAUSE_SEMANT_INCORR	= 0x5f,
+	GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
+	GSM48_RR_CAUSE_MSG_TYPE_N	= 0x61,
+	GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
+	GSM48_RR_CAUSE_COND_IE_ERROR	= 0x64,
+	GSM48_RR_CAUSE_NO_CELL_ALLOC_A	= 0x65,
+	GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
+};
+
+/* Section 10.5.4.11 CC Cause / Table 10.5.123 */
+enum gsm48_cc_cause {
+	GSM48_CC_CAUSE_UNASSIGNED_NR	= 1,
+	GSM48_CC_CAUSE_NO_ROUTE		= 3,
+	GSM48_CC_CAUSE_CHAN_UNACCEPT	= 6,
+	GSM48_CC_CAUSE_OP_DET_BARRING	= 8,
+	GSM48_CC_CAUSE_NORM_CALL_CLEAR	= 16,
+	GSM48_CC_CAUSE_USER_BUSY	= 17,
+	GSM48_CC_CAUSE_USER_NOTRESPOND	= 18,
+	GSM48_CC_CAUSE_USER_ALERTING_NA	= 19,
+	GSM48_CC_CAUSE_CALL_REJECTED	= 21,
+	GSM48_CC_CAUSE_NUMBER_CHANGED	= 22,
+	GSM48_CC_CAUSE_PRE_EMPTION	= 25,
+	GSM48_CC_CAUSE_NONSE_USER_CLR	= 26,
+	GSM48_CC_CAUSE_DEST_OOO		= 27,
+	GSM48_CC_CAUSE_INV_NR_FORMAT	= 28,
+	GSM48_CC_CAUSE_FACILITY_REJ	= 29,
+	GSM48_CC_CAUSE_RESP_STATUS_INQ	= 30,
+	GSM48_CC_CAUSE_NORMAL_UNSPEC	= 31,
+	GSM48_CC_CAUSE_NO_CIRCUIT_CHAN	= 34,
+	GSM48_CC_CAUSE_NETWORK_OOO	= 38,
+	GSM48_CC_CAUSE_TEMP_FAILURE	= 41,
+	GSM48_CC_CAUSE_SWITCH_CONG	= 42,
+	GSM48_CC_CAUSE_ACC_INF_DISCARD	= 43,
+	GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL	= 44,
+	GSM48_CC_CAUSE_RESOURCE_UNAVAIL	= 47,
+	GSM48_CC_CAUSE_QOS_UNAVAIL	= 49,
+	GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50,
+	GSM48_CC_CAUSE_INC_BARRED_CUG	= 55,
+	GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57,
+	GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58,
+	GSM48_CC_CAUSE_SERV_OPT_UNAVAIL	= 63,
+	GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65,
+	GSM48_CC_CAUSE_ACM_GE_ACM_MAX	= 68,
+	GSM48_CC_CAUSE_REQ_FAC_NOTIMPL	= 69,
+	GSM48_CC_CAUSE_RESTR_BCAP_AVAIL	= 70,
+	GSM48_CC_CAUSE_SERV_OPT_UNIMPL	= 79,
+	GSM48_CC_CAUSE_INVAL_TRANS_ID	= 81,
+	GSM48_CC_CAUSE_USER_NOT_IN_CUG	= 87,
+	GSM48_CC_CAUSE_INCOMPAT_DEST	= 88,
+	GSM48_CC_CAUSE_INVAL_TRANS_NET	= 91,
+	GSM48_CC_CAUSE_SEMANTIC_INCORR	= 95,
+	GSM48_CC_CAUSE_INVAL_MAND_INF	= 96,
+	GSM48_CC_CAUSE_MSGTYPE_NOTEXIST	= 97,
+	GSM48_CC_CAUSE_MSGTYPE_INCOMPAT	= 98,
+	GSM48_CC_CAUSE_IE_NOTEXIST	= 99,
+	GSM48_CC_CAUSE_COND_IE_ERR	= 100,
+	GSM48_CC_CAUSE_MSG_INCOMP_STATE	= 101,
+	GSM48_CC_CAUSE_RECOVERY_TIMER	= 102,
+	GSM48_CC_CAUSE_PROTO_ERR	= 111,
+	GSM48_CC_CAUSE_INTERWORKING	= 127,
+};
+
+/* Annex G, GSM specific cause values for mobility management */
+enum gsm48_reject_value {
+	GSM48_REJECT_IMSI_UNKNOWN_IN_HLR	= 2,
+	GSM48_REJECT_ILLEGAL_MS			= 3,
+	GSM48_REJECT_IMSI_UNKNOWN_IN_VLR	= 4,
+	GSM48_REJECT_IMEI_NOT_ACCEPTED		= 5,
+	GSM48_REJECT_ILLEGAL_ME			= 6,
+	GSM48_REJECT_PLMN_NOT_ALLOWED		= 11,
+	GSM48_REJECT_LOC_NOT_ALLOWED		= 12,
+	GSM48_REJECT_ROAMING_NOT_ALLOWED	= 13,
+	GSM48_REJECT_NETWORK_FAILURE		= 17,
+	GSM48_REJECT_CONGESTION			= 22,
+	GSM48_REJECT_SRV_OPT_NOT_SUPPORTED	= 32,
+	GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED	= 33,
+	GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER	= 34,
+	GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED	= 38,
+	GSM48_REJECT_INCORRECT_MESSAGE		= 95,
+	GSM48_REJECT_INVALID_MANDANTORY_INF	= 96,
+	GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED	= 97,
+	GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE	= 98,
+	GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED	= 99,
+	GSM48_REJECT_CONDTIONAL_IE_ERROR	= 100,
+	GSM48_REJECT_MSG_NOT_COMPATIBLE		= 101,
+	GSM48_REJECT_PROTOCOL_ERROR		= 111,
+
+	/* according to G.6 Additional cause codes for GMM */
+	GSM48_REJECT_GPRS_NOT_ALLOWED		= 7,
+	GSM48_REJECT_SERVICES_NOT_ALLOWED	= 8,
+	GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
+	GSM48_REJECT_IMPLICITLY_DETACHED	= 10,
+	GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN	= 14,
+	GSM48_REJECT_MSC_TMP_NOT_REACHABLE	= 16,
+};
+
+enum chreq_type {
+	CHREQ_T_EMERG_CALL,
+	CHREQ_T_CALL_REEST_TCH_F,
+	CHREQ_T_CALL_REEST_TCH_H,
+	CHREQ_T_CALL_REEST_TCH_H_DBL,
+	CHREQ_T_SDCCH,
+	CHREQ_T_TCH_F,
+	CHREQ_T_VOICE_CALL_TCH_H,
+	CHREQ_T_DATA_CALL_TCH_H,
+	CHREQ_T_LOCATION_UPD,
+	CHREQ_T_PAG_R_ANY_NECI0,
+	CHREQ_T_PAG_R_ANY_NECI1,
+	CHREQ_T_PAG_R_TCH_F,
+	CHREQ_T_PAG_R_TCH_FH,
+	CHREQ_T_LMU,
+	CHREQ_T_RESERVED_SDCCH,
+	CHREQ_T_RESERVED_IGNORE,
+};
+
+/* Chapter 11.3 */
+#define GSM48_T301	180, 0
+#define GSM48_T303	30, 0
+#define GSM48_T305	30, 0
+#define GSM48_T306	30, 0
+#define GSM48_T308	10, 0
+#define GSM48_T310	180, 0
+#define GSM48_T313	30, 0
+#define GSM48_T323	30, 0
+#define GSM48_T331	30, 0
+#define GSM48_T333	30, 0
+#define GSM48_T334	25, 0 /* min 15 */
+#define GSM48_T338	30, 0
+
+/* Chapter 5.1.2.2 */
+#define	GSM_CSTATE_NULL			0
+#define	GSM_CSTATE_INITIATED		1
+#define	GSM_CSTATE_MO_CALL_PROC		3
+#define	GSM_CSTATE_CALL_DELIVERED	4
+#define	GSM_CSTATE_CALL_PRESENT		6
+#define	GSM_CSTATE_CALL_RECEIVED	7
+#define	GSM_CSTATE_CONNECT_REQUEST	8
+#define	GSM_CSTATE_MO_TERM_CALL_CONF	9
+#define	GSM_CSTATE_ACTIVE		10
+#define	GSM_CSTATE_DISCONNECT_REQ	12
+#define	GSM_CSTATE_DISCONNECT_IND	12
+#define	GSM_CSTATE_RELEASE_REQ		19
+#define	GSM_CSTATE_MO_ORIG_MODIFY	26
+#define	GSM_CSTATE_MO_TERM_MODIFY	27
+#define	GSM_CSTATE_CONNECT_IND		28
+
+#define SBIT(a) (1 << a)
+#define ALL_STATES 0xffffffff
+
+/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */
+#define GSM_LAC_RESERVED_DETACHED       0x0
+#define GSM_LAC_RESERVED_ALL_BTS        0xfffe
+
+/* GSM 04.08 Bearer Capability: Information Transfer Capability */
+enum gsm48_bcap_itcap {
+	GSM48_BCAP_ITCAP_SPEECH		= 0,
+	GSM48_BCAP_ITCAP_UNR_DIG_INF	= 1,
+	GSM48_BCAP_ITCAP_3k1_AUDIO	= 2,
+	GSM48_BCAP_ITCAP_FAX_G3		= 3,
+	GSM48_BCAP_ITCAP_OTHER		= 5,
+	GSM48_BCAP_ITCAP_RESERVED	= 7,
+};
+
+/* GSM 04.08 Bearer Capability: Transfer Mode */
+enum gsm48_bcap_tmod {
+	GSM48_BCAP_TMOD_CIRCUIT		= 0,
+	GSM48_BCAP_TMOD_PACKET		= 1,
+};
+
+/* GSM 04.08 Bearer Capability: Coding Standard */
+enum gsm48_bcap_coding {
+	GSM48_BCAP_CODING_GSM_STD	= 0,
+};
+
+/* GSM 04.08 Bearer Capability: Radio Channel Requirements */
+enum gsm48_bcap_rrq {
+	GSM48_BCAP_RRQ_FR_ONLY	= 1,
+	GSM48_BCAP_RRQ_DUAL_HR	= 2,
+	GSM48_BCAP_RRQ_DUAL_FR	= 3,
+};
+
+
+#define GSM48_TMSI_LEN	5
+#define GSM48_MID_TMSI_LEN	(GSM48_TMSI_LEN + 2)
+#define GSM48_MI_SIZE 32
+
+
+#endif /* PROTO_GSM_04_08_H */
diff --git a/libosmocore/include/osmocore/protocol/gsm_04_11.h b/libosmocore/include/osmocore/protocol/gsm_04_11.h
new file mode 100644
index 0000000..c6a2b19
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_04_11.h
@@ -0,0 +1,188 @@
+#ifndef PROTO_GSM_04_11_H
+#define PROTO_GSM_04_11_H
+
+#include <stdint.h>
+
+/* GSM TS 04.11  definitions */
+
+/* Chapter 5.2.3: SMC-CS states at the network side */
+enum gsm411_cp_state {
+	GSM411_CPS_IDLE 		= 0,
+	GSM411_CPS_MM_CONN_PENDING	= 1,	/* only MT ! */
+	GSM411_CPS_WAIT_CP_ACK		= 2,
+	GSM411_CPS_MM_ESTABLISHED	= 3,
+};
+
+/* Chapter 6.2.2: SMR states at the network side */
+enum gsm411_rp_state {
+	GSM411_RPS_IDLE			= 0,
+	GSM411_RPS_WAIT_FOR_RP_ACK	= 1,
+	GSM411_RPS_WAIT_TO_TX_RP_ACK	= 3,
+};
+
+/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
+#define GSM411_PDISC_SMS	0x09
+
+/* Chapter 8.1.3 */
+#define GSM411_MT_CP_DATA	0x01
+#define GSM411_MT_CP_ACK	0x04
+#define GSM411_MT_CP_ERROR	0x10
+
+enum gsm411_cp_ie {
+	GSM411_CP_IE_USER_DATA		= 0x01,	/* 8.1.4.1 */
+	GSM411_CP_IE_CAUSE		= 0x02,	/* 8.1.4.2. */
+};
+
+/* Section 8.1.4.2 / Table 8.2 */
+enum gsm411_cp_cause {
+	GSM411_CP_CAUSE_NET_FAIL	= 17,
+	GSM411_CP_CAUSE_CONGESTION	= 22,
+	GSM411_CP_CAUSE_INV_TRANS_ID	= 81,
+	GSM411_CP_CAUSE_SEMANT_INC_MSG	= 95,
+	GSM411_CP_CAUSE_INV_MAND_INF	= 96,
+	GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97,
+	GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98,
+	GSM411_CP_CAUSE_IE_NOTEXIST	= 99,
+	GSM411_CP_CAUSE_PROTOCOL_ERR	= 111,
+};
+
+/* Chapter 8.2.2 */
+#define GSM411_MT_RP_DATA_MO	0x00
+#define GSM411_MT_RP_DATA_MT	0x01
+#define GSM411_MT_RP_ACK_MO	0x02
+#define GSM411_MT_RP_ACK_MT	0x03
+#define GSM411_MT_RP_ERROR_MO	0x04
+#define GSM411_MT_RP_ERROR_MT	0x05
+#define GSM411_MT_RP_SMMA_MO	0x06
+
+enum gsm411_rp_ie {
+	GSM411_IE_RP_USER_DATA		= 0x41,	/* 8.2.5.3 */
+	GSM411_IE_RP_CAUSE		= 0x42,	/* 8.2.5.4 */
+};
+
+/* Chapter 8.2.5.4 Table 8.4 */
+enum gsm411_rp_cause {
+	/* valid only for MO */
+	GSM411_RP_CAUSE_MO_NUM_UNASSIGNED	= 1,
+	GSM411_RP_CAUSE_MO_OP_DET_BARR		= 8,
+	GSM411_RP_CAUSE_MO_CALL_BARRED		= 10,
+	GSM411_RP_CAUSE_MO_SMS_REJECTED		= 21,
+	GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER	= 27,
+	GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR	= 28,
+	GSM411_RP_CAUSE_MO_FACILITY_REJ		= 29,
+	GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR	= 30,
+	GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER	= 38,
+	GSM411_RP_CAUSE_MO_TEMP_FAIL		= 41,
+	GSM411_RP_CAUSE_MO_CONGESTION		= 42,
+	GSM411_RP_CAUSE_MO_RES_UNAVAIL		= 47,
+	GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR	= 50,
+	GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL	= 69,
+	GSM411_RP_CAUSE_MO_INTERWORKING		= 127,
+	/* valid only for MT */
+	GSM411_RP_CAUSE_MT_MEM_EXCEEDED		= 22,
+	/* valid for both directions */
+	GSM411_RP_CAUSE_INV_TRANS_REF		= 81,
+	GSM411_RP_CAUSE_SEMANT_INC_MSG		= 95,
+	GSM411_RP_CAUSE_INV_MAND_INF		= 96,
+	GSM411_RP_CAUSE_MSGTYPE_NOTEXIST	= 97,
+	GSM411_RP_CAUSE_MSG_INCOMP_STATE	= 98,
+	GSM411_RP_CAUSE_IE_NOTEXIST		= 99,
+	GSM411_RP_CAUSE_PROTOCOL_ERR		= 111,
+};
+
+/* Chapter 10: Timers */
+#define GSM411_TMR_TR1M		40, 0	/* 35 < x < 45 seconds */
+#define GSM411_TMR_TRAM		30, 0	/* 25 < x < 35 seconds */
+#define GSM411_TMR_TR2M		15, 0	/* 12 < x < 20 seconds */
+
+#define GSM411_TMR_TC1A		30, 0
+
+/* Chapter 8.2.1 */
+struct gsm411_rp_hdr {
+	uint8_t len;
+	uint8_t msg_type;
+	uint8_t msg_ref;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+/* our own enum, not related to on-air protocol */
+enum sms_alphabet {
+	DCS_NONE,
+	DCS_7BIT_DEFAULT,
+	DCS_UCS2,
+	DCS_8BIT_DATA,
+};
+
+/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
+#define GSM340_SMS_DELIVER_SC2MS	0x00
+#define GSM340_SMS_DELIVER_REP_MS2SC	0x00
+#define GSM340_SMS_STATUS_REP_SC2MS	0x02
+#define GSM340_SMS_COMMAND_MS2SC	0x02
+#define GSM340_SMS_SUBMIT_MS2SC		0x01
+#define GSM340_SMS_SUBMIT_REP_SC2MS	0x01
+#define GSM340_SMS_RESSERVED		0x03
+
+/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */
+#define GSM340_TP_MMS_MORE		0
+#define GSM340_TP_MMS_NO_MORE		1
+
+/* GSM 03.40 / Chapter 9.2.3.3: TP-Validity-Period-Format */
+#define GSM340_TP_VPF_NONE		0
+#define GSM340_TP_VPF_RELATIVE		2
+#define GSM340_TP_VPF_ENHANCED		1
+#define GSM340_TP_VPF_ABSOLUTE		3
+
+/* GSM 03.40 / Chapter 9.2.3.4: TP-Status-Report-Indication */
+#define GSM340_TP_SRI_NONE		0
+#define GSM340_TP_SRI_PRESENT		1
+
+/* GSM 03.40 / Chapter 9.2.3.5: TP-Status-Report-Request */
+#define GSM340_TP_SRR_NONE		0
+#define GSM340_TP_SRR_REQUESTED		1
+
+/* GSM 03.40 / Chapter 9.2.3.9: TP-Protocol-Identifier */
+/* telematic interworking (001 or 111 in bits 7-5) */
+#define GSM340_TP_PID_IMPLICIT		0x00
+#define GSM340_TP_PID_TELEX		0x01
+#define GSM340_TP_PID_FAX_G3		0x02
+#define GSM340_TP_PID_FAX_G4		0x03
+#define GSM340_TP_PID_VOICE		0x04
+#define GSM430_TP_PID_ERMES		0x05
+#define GSM430_TP_PID_NATIONAL_PAGING	0x06
+#define GSM430_TP_PID_VIDEOTEX		0x07
+#define GSM430_TP_PID_TELETEX_UNSPEC	0x08
+#define GSM430_TP_PID_TELETEX_PSPDN	0x09
+#define GSM430_TP_PID_TELETEX_CSPDN	0x0a
+#define GSM430_TP_PID_TELETEX_PSTN	0x0b
+#define GSM430_TP_PID_TELETEX_ISDN	0x0c
+#define GSM430_TP_PID_TELETEX_UCI	0x0d
+#define GSM430_TP_PID_MSG_HANDLING	0x10
+#define GSM430_TP_PID_MSG_X400		0x11
+#define GSM430_TP_PID_EMAIL		0x12
+#define GSM430_TP_PID_GSM_MS		0x1f
+/* if bit 7 = 0 and bit 6 = 1 */
+#define GSM430_TP_PID_SMS_TYPE_0	0
+#define GSM430_TP_PID_SMS_TYPE_1	1
+#define GSM430_TP_PID_SMS_TYPE_2	2
+#define GSM430_TP_PID_SMS_TYPE_3	3
+#define GSM430_TP_PID_SMS_TYPE_4	4
+#define GSM430_TP_PID_SMS_TYPE_5	5
+#define GSM430_TP_PID_SMS_TYPE_6	6
+#define GSM430_TP_PID_SMS_TYPE_7	7
+#define GSM430_TP_PID_RETURN_CALL_MSG	0x1f
+#define GSM430_TP_PID_ME_DATA_DNLOAD	0x3d
+#define GSM430_TP_PID_ME_DE_PERSONAL	0x3e
+#define GSM430_TP_PID_ME_SIM_DNLOAD	0x3f
+
+/* GSM 03.38 Chapter 4: SMS Data Coding Scheme */
+#define GSM338_DCS_00_
+
+#define GSM338_DCS_1110_7BIT		(0 << 2)
+#define GSM338_DCS_1111_7BIT		(0 << 2)
+#define GSM338_DCS_1111_8BIT_DATA	(1 << 2)
+#define GSM338_DCS_1111_CLASS0		0
+#define GSM338_DCS_1111_CLASS1_ME	1
+#define GSM338_DCS_1111_CLASS2_SIM	2
+#define GSM338_DCS_1111_CLASS3_TE	3	/* See TS 07.05 */
+
+#endif /* PROTO_GSM_04_11_H */
diff --git a/libosmocore/include/osmocore/protocol/gsm_04_80.h b/libosmocore/include/osmocore/protocol/gsm_04_80.h
new file mode 100644
index 0000000..fa5c945
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_04_80.h
@@ -0,0 +1,126 @@
+#ifndef PROTO_GSM_04_80_H
+#define PROTO_GSM_04_80_H
+
+/* GSM TS 04.80  definitions (Supplementary Services Specification, Formats and Coding) */
+
+/* Section 3.4 */
+#define GSM0480_MTYPE_RELEASE_COMPLETE	0x2A
+#define GSM0480_MTYPE_FACILITY			0x3A
+#define GSM0480_MTYPE_REGISTER			0x3B
+
+/* Section 3.5 */
+#define GSM0480_IE_FACILITY			0x1C
+#define GSM0480_IE_SS_VERSION			0x7F
+
+/* Section 3.6.2 */
+#define GSM0480_CTYPE_INVOKE			0xA1
+#define GSM0480_CTYPE_RETURN_RESULT		0xA2
+#define GSM0480_CTYPE_RETURN_ERROR		0xA3
+#define GSM0480_CTYPE_REJECT			0xA4
+
+/* Section 3.6.3 */
+#define GSM0480_COMPIDTAG_INVOKE_ID		0x02
+#define GSM0480_COMPIDTAG_LINKED_ID		0x80
+
+/* Section 3.6.4 */
+#define GSM0480_OPERATION_CODE			0x02
+
+/* Section 3.6.5 */
+#define GSM_0480_SEQUENCE_TAG			0x30
+#define GSM_0480_SET_TAG			0x31
+
+/* Section 3.6.6 */
+#define GSM_0480_ERROR_CODE_TAG			0x02
+
+/* Section 3.6.7 */
+/* Table 3.13 */
+#define GSM_0480_PROBLEM_CODE_TAG_GENERAL	0x80
+#define GSM_0480_PROBLEM_CODE_TAG_INVOKE	0x81
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_RESULT	0x82
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_ERROR	0x83
+
+/* Table 3.14 */
+#define GSM_0480_GEN_PROB_CODE_UNRECOGNISED	0x00
+#define GSM_0480_GEN_PROB_CODE_MISTYPED		0x01
+#define GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE	0x02
+
+/* Table 3.15 */
+#define GSM_0480_INVOKE_PROB_CODE_DUPLICATE_INVOKE_ID		0x00
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_OPERATION	0x01
+#define GSM_0480_INVOKE_PROB_CODE_MISTYPED_PARAMETER		0x02
+#define GSM_0480_INVOKE_PROB_CODE_RESOURCE_LIMITATION		0x03
+#define GSM_0480_INVOKE_PROB_CODE_INITIATING_RELEASE		0x04
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_LINKED_ID	0x05
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_RESPONSE	0x06
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_OPERATION	0x07
+
+/* Table 3.16 */
+#define GSM_0480_RESULT_PROB_CODE_UNRECOGNISED_INVOKE_ID	0x00
+#define GSM_0480_RESULT_PROB_CODE_RETURN_RESULT_UNEXPECTED	0x01
+#define GSM_0480_RESULT_PROB_CODE_MISTYPED_PARAMETER		0x02
+
+/* Table 3.17 */
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_INVOKE_ID		0x00
+#define GSM_0480_ERROR_PROB_CODE_RETURN_ERROR_UNEXPECTED	0x01
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_ERROR		0x02
+#define GSM_0480_ERROR_PROB_CODE_UNEXPECTED_ERROR		0x03
+#define GSM_0480_ERROR_PROB_CODE_MISTYPED_PARAMETER		0x04
+
+/* Section 4.5 */
+#define GSM0480_OP_CODE_REGISTER_SS		0x0A
+#define GSM0480_OP_CODE_ERASE_SS		0x0B
+#define GSM0480_OP_CODE_ACTIVATE_SS		0x0C
+#define GSM0480_OP_CODE_DEACTIVATE_SS		0x0D
+#define GSM0480_OP_CODE_INTERROGATE_SS		0x0E
+#define GSM0480_OP_CODE_NOTIFY_SS		0x10
+#define GSM0480_OP_CODE_REGISTER_PASSWORD	0x11
+#define GSM0480_OP_CODE_GET_PASSWORD		0x12
+#define GSM0480_OP_CODE_PROCESS_USS_DATA	0x13
+#define GSM0480_OP_CODE_FORWARD_CHECK_SS_IND	0x26
+#define GSM0480_OP_CODE_PROCESS_USS_REQ		0x3B
+#define GSM0480_OP_CODE_USS_REQUEST		0x3C
+#define GSM0480_OP_CODE_USS_NOTIFY		0x3D
+#define GSM0480_OP_CODE_FORWARD_CUG_INFO	0x78
+#define GSM0480_OP_CODE_SPLIT_MPTY		0x79
+#define GSM0480_OP_CODE_RETRIEVE_MPTY		0x7A
+#define GSM0480_OP_CODE_HOLD_MPTY		0x7B
+#define GSM0480_OP_CODE_BUILD_MPTY		0x7C
+#define GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE	0x7D
+
+#define GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER			0x01
+#define GSM0480_ERR_CODE_ILLEGAL_SUBSCRIBER			0x09
+#define GSM0480_ERR_CODE_BEARER_SERVICE_NOT_PROVISIONED		0x0A
+#define GSM0480_ERR_CODE_TELESERVICE_NOT_PROVISIONED		0x0B
+#define GSM0480_ERR_CODE_ILLEGAL_EQUIPMENT			0x0C
+#define GSM0480_ERR_CODE_CALL_BARRED				0x0D
+#define GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION			0x10
+#define GSM0480_ERR_CODE_SS_ERROR_STATUS			0x11
+#define GSM0480_ERR_CODE_SS_NOT_AVAILABLE			0x12
+#define GSM0480_ERR_CODE_SS_SUBSCRIPTION_VIOLATION		0x13
+#define GSM0480_ERR_CODE_SS_INCOMPATIBILITY			0x14
+#define GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED			0x15
+#define GSM0480_ERR_CODE_ABSENT_SUBSCRIBER			0x1B
+#define GSM0480_ERR_CODE_SYSTEM_FAILURE				0x22
+#define GSM0480_ERR_CODE_DATA_MISSING				0x23
+#define GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE			0x24
+#define GSM0480_ERR_CODE_PW_REGISTRATION_FAILURE		0x25
+#define GSM0480_ERR_CODE_NEGATIVE_PW_CHECK			0x26
+#define GSM0480_ERR_CODE_NUM_PW_ATTEMPTS_VIOLATION		0x2B
+#define GSM0480_ERR_CODE_UNKNOWN_ALPHABET			0x47
+#define GSM0480_ERR_CODE_USSD_BUSY				0x48
+#define GSM0480_ERR_CODE_MAX_MPTY_PARTICIPANTS			0x7E
+#define GSM0480_ERR_CODE_RESOURCES_NOT_AVAILABLE		0x7F
+
+/* ASN.1 type-tags */
+#define ASN1_BOOLEAN_TAG		0x01
+#define ASN1_INTEGER_TAG		0x02
+#define ASN1_BIT_STRING_TAG		0x03
+#define ASN1_OCTET_STRING_TAG		0x04
+#define ASN1_NULL_TYPE_TAG		0x05
+#define ASN1_OBJECT_ID_TAG		0x06
+#define ASN1_UTF8_STRING_TAG		0x0C
+#define ASN1_PRINTABLE_STRING_TAG	0x13
+#define ASN1_IA5_STRING_TAG		0x16
+#define ASN1_UNICODE_STRING_TAG		0x1E
+
+#endif /* PROTO_GSM_04_80_H */
diff --git a/libosmocore/include/osmocore/protocol/gsm_08_58.h b/libosmocore/include/osmocore/protocol/gsm_08_58.h
new file mode 100644
index 0000000..ca9398f
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_08_58.h
@@ -0,0 +1,512 @@
+#ifndef PROTO_GSM_08_58_H
+#define PROTO_GSM_08_58_H
+
+/* 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 by Harald Welte <laforge@gnumonks.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 <stdint.h>
+
+struct abis_rsl_common_hdr {
+	uint8_t	msg_discr;
+	uint8_t	msg_type;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 */
+struct abis_rsl_rll_hdr {
+	struct abis_rsl_common_hdr c;
+	uint8_t	ie_chan;
+	uint8_t	chan_nr;
+	uint8_t	ie_link_id;
+	uint8_t	link_id;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 and 8.4 */
+struct abis_rsl_dchan_hdr {
+	struct abis_rsl_common_hdr c;
+	uint8_t	ie_chan;
+	uint8_t	chan_nr;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+
+/* Chapter 9.1 */
+#define ABIS_RSL_MDISC_RLL		0x02
+#define ABIS_RSL_MDISC_DED_CHAN		0x08
+#define ABIS_RSL_MDISC_COM_CHAN		0x0c
+#define ABIS_RSL_MDISC_TRX		0x10
+#define ABIS_RSL_MDISC_LOC		0x20
+#define ABIS_RSL_MDISC_IPACCESS		0x7e
+#define ABIS_RSL_MDISC_TRANSP		0x01
+
+#define ABIS_RSL_MDISC_IS_TRANSP(x)	(x & 0x01)
+
+/* Chapter 9.1 */
+enum abis_rsl_msgtype {
+	/* Radio Link Layer Management */
+	RSL_MT_DATA_REQ			= 0x01,
+	RSL_MT_DATA_IND,
+	RSL_MT_ERROR_IND,
+	RSL_MT_EST_REQ,
+	RSL_MT_EST_CONF,
+	RSL_MT_EST_IND,
+	RSL_MT_REL_REQ,
+	RSL_MT_REL_CONF,
+	RSL_MT_REL_IND,
+	RSL_MT_UNIT_DATA_REQ,
+	RSL_MT_UNIT_DATA_IND,		/* 0x0b */
+
+	/* Common Channel Management / TRX Management */
+	RSL_MT_BCCH_INFO			= 0x11,
+	RSL_MT_CCCH_LOAD_IND,
+	RSL_MT_CHAN_RQD,
+	RSL_MT_DELETE_IND,
+	RSL_MT_PAGING_CMD,
+	RSL_MT_IMMEDIATE_ASSIGN_CMD,
+	RSL_MT_SMS_BC_REQ,
+	/* empty */
+	RSL_MT_RF_RES_IND			= 0x19,
+	RSL_MT_SACCH_FILL,
+	RSL_MT_OVERLOAD,
+	RSL_MT_ERROR_REPORT,
+	RSL_MT_SMS_BC_CMD,
+	RSL_MT_CBCH_LOAD_IND,
+	RSL_MT_NOT_CMD,			/* 0x1f */
+
+	/* Dedicate Channel Management */
+	RSL_MT_CHAN_ACTIV			= 0x21,
+	RSL_MT_CHAN_ACTIV_ACK,
+	RSL_MT_CHAN_ACTIV_NACK,
+	RSL_MT_CONN_FAIL,
+	RSL_MT_DEACTIVATE_SACCH,
+	RSL_MT_ENCR_CMD,
+	RSL_MT_HANDO_DET,
+	RSL_MT_MEAS_RES,
+	RSL_MT_MODE_MODIFY_REQ,
+	RSL_MT_MODE_MODIFY_ACK,
+	RSL_MT_MODE_MODIFY_NACK,
+	RSL_MT_PHY_CONTEXT_REQ,
+	RSL_MT_PHY_CONTEXT_CONF,
+	RSL_MT_RF_CHAN_REL,
+	RSL_MT_MS_POWER_CONTROL,
+	RSL_MT_BS_POWER_CONTROL,		/* 0x30 */
+	RSL_MT_PREPROC_CONFIG,
+	RSL_MT_PREPROC_MEAS_RES,
+	RSL_MT_RF_CHAN_REL_ACK,
+	RSL_MT_SACCH_INFO_MODIFY,
+	RSL_MT_TALKER_DET,
+	RSL_MT_LISTENER_DET,
+	RSL_MT_REMOTE_CODEC_CONF_REP,
+	RSL_MT_RTD_REP,
+	RSL_MT_PRE_HANDO_NOTIF,
+	RSL_MT_MR_CODEC_MOD_REQ,
+	RSL_MT_MR_CODEC_MOD_ACK,
+	RSL_MT_MR_CODEC_MOD_NACK,
+	RSL_MT_MR_CODEC_MOD_PER,
+	RSL_MT_TFO_REP,
+	RSL_MT_TFO_MOD_REQ,		/* 0x3f */
+	RSL_MT_LOCATION_INFO		= 0x41,
+
+	/* ip.access specific RSL message types */
+	RSL_MT_IPAC_DIR_RETR_ENQ	= 0x40,
+	RSL_MT_IPAC_PDCH_ACT		= 0x48,
+	RSL_MT_IPAC_PDCH_ACT_ACK,
+	RSL_MT_IPAC_PDCH_ACT_NACK,
+	RSL_MT_IPAC_PDCH_DEACT		= 0x4b,
+	RSL_MT_IPAC_PDCH_DEACT_ACK,
+	RSL_MT_IPAC_PDCH_DEACT_NACK,
+	RSL_MT_IPAC_CONNECT_MUX		= 0x50,
+	RSL_MT_IPAC_CONNECT_MUX_ACK,
+	RSL_MT_IPAC_CONNECT_MUX_NACK,
+	RSL_MT_IPAC_BIND_MUX		= 0x53,
+	RSL_MT_IPAC_BIND_MUX_ACK,
+	RSL_MT_IPAC_BIND_MUX_NACK,
+	RSL_MT_IPAC_DISC_MUX		= 0x56,
+	RSL_MT_IPAC_DISC_MUX_ACK,
+	RSL_MT_IPAC_DISC_MUX_NACK,
+	RSL_MT_IPAC_CRCX		= 0x70,		/* Bind to local BTS RTP port */
+	RSL_MT_IPAC_CRCX_ACK,
+	RSL_MT_IPAC_CRCX_NACK,
+	RSL_MT_IPAC_MDCX		= 0x73,
+	RSL_MT_IPAC_MDCX_ACK,
+	RSL_MT_IPAC_MDCX_NACK,
+	RSL_MT_IPAC_DLCX_IND		= 0x76,
+	RSL_MT_IPAC_DLCX		= 0x77,
+	RSL_MT_IPAC_DLCX_ACK,
+	RSL_MT_IPAC_DLCX_NACK,
+};
+
+/* Siemens vendor-specific */
+enum abis_rsl_msgtype_siemens {
+	RSL_MT_SIEMENS_MRPCI		= 0x41,
+	RSL_MT_SIEMENS_INTRAC_HO_COND_IND = 0x42,
+	RSL_MT_SIEMENS_INTERC_HO_COND_IND = 0x43,
+	RSL_MT_SIEMENS_FORCED_HO_REQ	= 0x44,
+	RSL_MT_SIEMENS_PREF_AREA_REQ	= 0x45,
+	RSL_MT_SIEMENS_PREF_AREA	= 0x46,
+	RSL_MT_SIEMENS_START_TRACE	= 0x47,
+	RSL_MT_SIEMENS_START_TRACE_ACK	= 0x48,
+	RSL_MT_SIEMENS_STOP_TRACE	= 0x49,
+	RSL_MT_SIEMENS_TRMR		= 0x4a,
+	RSL_MT_SIEMENS_HO_FAIL_IND	= 0x4b,
+	RSL_MT_SIEMENS_STOP_TRACE_ACK	= 0x4c,
+	RSL_MT_SIEMENS_UPLF		= 0x4d,
+	RSL_MT_SIEMENS_UPLB		= 0x4e,
+	RSL_MT_SIEMENS_SET_SYS_INFO_10	= 0x4f,
+	RSL_MT_SIEMENS_MODIF_COND_IND	= 0x50,
+};
+
+/* Chapter 9.3 */
+enum abis_rsl_ie {
+	RSL_IE_CHAN_NR			= 0x01,
+	RSL_IE_LINK_IDENT,
+	RSL_IE_ACT_TYPE,
+	RSL_IE_BS_POWER,
+	RSL_IE_CHAN_IDENT,
+	RSL_IE_CHAN_MODE,
+	RSL_IE_ENCR_INFO,
+	RSL_IE_FRAME_NUMBER,
+	RSL_IE_HANDO_REF,
+	RSL_IE_L1_INFO,
+	RSL_IE_L3_INFO,
+	RSL_IE_MS_IDENTITY,
+	RSL_IE_MS_POWER,
+	RSL_IE_PAGING_GROUP,
+	RSL_IE_PAGING_LOAD,
+	RSL_IE_PYHS_CONTEXT		= 0x10,
+	RSL_IE_ACCESS_DELAY,
+	RSL_IE_RACH_LOAD,
+	RSL_IE_REQ_REFERENCE,
+	RSL_IE_RELEASE_MODE,
+	RSL_IE_RESOURCE_INFO,
+	RSL_IE_RLM_CAUSE,
+	RSL_IE_STARTNG_TIME,
+	RSL_IE_TIMING_ADVANCE,
+	RSL_IE_UPLINK_MEAS,
+	RSL_IE_CAUSE,
+	RSL_IE_MEAS_RES_NR,
+	RSL_IE_MSG_ID,
+	/* reserved */
+	RSL_IE_SYSINFO_TYPE		= 0x1e,
+	RSL_IE_MS_POWER_PARAM,
+	RSL_IE_BS_POWER_PARAM,
+	RSL_IE_PREPROC_PARAM,
+	RSL_IE_PREPROC_MEAS,
+	RSL_IE_IMM_ASS_INFO,		/* Phase 1 (3.6.0), later Full below */
+	RSL_IE_SMSCB_INFO		= 0x24,
+	RSL_IE_MS_TIMING_OFFSET,
+	RSL_IE_ERR_MSG,
+	RSL_IE_FULL_BCCH_INFO,
+	RSL_IE_CHAN_NEEDED,
+	RSL_IE_CB_CMD_TYPE,
+	RSL_IE_SMSCB_MSG,
+	RSL_IE_FULL_IMM_ASS_INFO,
+	RSL_IE_SACCH_INFO,
+	RSL_IE_CBCH_LOAD_INFO,
+	RSL_IE_SMSCB_CHAN_INDICATOR,
+	RSL_IE_GROUP_CALL_REF,
+	RSL_IE_CHAN_DESC		= 0x30,
+	RSL_IE_NCH_DRX_INFO,
+	RSL_IE_CMD_INDICATOR,
+	RSL_IE_EMLPP_PRIO,
+	RSL_IE_UIC,
+	RSL_IE_MAIN_CHAN_REF,
+	RSL_IE_MR_CONFIG,
+	RSL_IE_MR_CONTROL,
+	RSL_IE_SUP_CODEC_TYPES,
+	RSL_IE_CODEC_CONFIG,
+	RSL_IE_RTD,
+	RSL_IE_TFO_STATUS,
+	RSL_IE_LLP_APDU,
+	/* Siemens vendor-specific */
+	RSL_IE_SIEMENS_MRPCI		= 0x40,
+	RSL_IE_SIEMENS_PREF_AREA_TYPE	= 0x43,
+	RSL_IE_SIEMENS_ININ_CELL_HO_PAR	= 0x45,
+	RSL_IE_SIEMENS_TRACE_REF_NR	= 0x46,
+	RSL_IE_SIEMENS_INT_TRACE_IDX	= 0x47,
+	RSL_IE_SIEMENS_L2_HDR_INFO	= 0x48,
+	RSL_IE_SIEMENS_HIGHEST_RATE	= 0x4e,
+	RSL_IE_SIEMENS_SUGGESTED_RATE	= 0x4f,
+
+	/* ip.access */
+	RSL_IE_IPAC_SRTP_CONFIG	= 0xe0,
+	RSL_IE_IPAC_PROXY_UDP	= 0xe1,
+	RSL_IE_IPAC_BSCMPL_TOUT	= 0xe2,
+	RSL_IE_IPAC_REMOTE_IP	= 0xf0,
+	RSL_IE_IPAC_REMOTE_PORT	= 0xf1,
+	RSL_IE_IPAC_RTP_PAYLOAD	= 0xf2,
+	RSL_IE_IPAC_LOCAL_PORT	= 0xf3,
+	RSL_IE_IPAC_SPEECH_MODE	= 0xf4,
+	RSL_IE_IPAC_LOCAL_IP	= 0xf5,
+	RSL_IE_IPAC_CONN_STAT	= 0xf6,
+	RSL_IE_IPAC_HO_C_PARMS	= 0xf7,
+	RSL_IE_IPAC_CONN_ID	= 0xf8,
+	RSL_IE_IPAC_RTP_CSD_FMT	= 0xf9,
+	RSL_IE_IPAC_RTP_JIT_BUF	= 0xfa,
+	RSL_IE_IPAC_RTP_COMPR	= 0xfb,
+	RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc,
+	RSL_IE_IPAC_RTP_MPLEX	= 0xfd,
+	RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
+};
+
+/* Chapter 9.3.1 */
+#define RSL_CHAN_NR_MASK	0xf8
+#define RSL_CHAN_Bm_ACCHs	0x08
+#define RSL_CHAN_Lm_ACCHs	0x10	/* .. 0x18 */
+#define RSL_CHAN_SDCCH4_ACCH	0x20	/* .. 0x38 */
+#define RSL_CHAN_SDCCH8_ACCH	0x40	/* ...0x78 */
+#define RSL_CHAN_BCCH		0x80
+#define RSL_CHAN_RACH		0x88
+#define RSL_CHAN_PCH_AGCH	0x90
+
+/* Chapter 9.3.3 */
+#define RSL_ACT_TYPE_INITIAL	0x00
+#define RSL_ACT_TYPE_REACT	0x80
+#define RSL_ACT_INTRA_IMM_ASS	0x00
+#define RSL_ACT_INTRA_NORM_ASS	0x01
+#define RSL_ACT_INTER_ASYNC	0x02
+#define RSL_ACT_INTER_SYNC	0x03
+#define RSL_ACT_SECOND_ADD	0x04
+#define RSL_ACT_SECOND_MULTI	0x05
+
+/* Chapter 9.3.6 */
+struct rsl_ie_chan_mode {
+	uint8_t dtx_dtu;
+	uint8_t spd_ind;
+	uint8_t chan_rt;
+	uint8_t chan_rate;
+} __attribute__ ((packed));
+#define RSL_CMOD_DTXu		0x01	/* uplink */
+#define RSL_CMOD_DTXd		0x02	/* downlink */
+enum rsl_cmod_spd {
+	RSL_CMOD_SPD_SPEECH	= 0x01,
+	RSL_CMOD_SPD_DATA	= 0x02,
+	RSL_CMOD_SPD_SIGN	= 0x03,
+};
+#define RSL_CMOD_CRT_SDCCH	0x01
+#define RSL_CMOD_CRT_TCH_Bm	0x08	/* full-rate */
+#define RSL_CMOD_CRT_TCH_Lm	0x09	/* half-rate */
+/* FIXME: More CRT types */
+/* Speech */
+#define RSL_CMOD_SP_GSM1	0x01
+#define RSL_CMOD_SP_GSM2	0x11
+#define RSL_CMOD_SP_GSM3	0x21
+/* Data */
+#define RSL_CMOD_SP_NT_14k5	0x58
+#define RSL_CMOD_SP_NT_12k0	0x50
+#define RSL_CMOD_SP_NT_6k0	0x51
+
+/* Chapter 9.3.5 */
+struct rsl_ie_chan_ident {
+	/* GSM 04.08 10.5.2.5 */
+	struct {
+		uint8_t iei;
+		uint8_t chan_nr;	/* enc_chan_nr */
+		uint8_t oct3;
+		uint8_t oct4;
+	} chan_desc;
+#if 0	/* spec says we need this but Abissim doesn't use it */
+	struct {
+		uint8_t tag;
+		uint8_t len;
+	} mobile_alloc;
+#endif
+} __attribute__ ((packed));
+
+/* Chapter 9.3.22 */
+#define RLL_CAUSE_T200_EXPIRED		0x01
+#define RLL_CAUSE_REEST_REQ		0x02
+#define RLL_CAUSE_UNSOL_UA_RESP		0x03
+#define RLL_CAUSE_UNSOL_DM_RESP		0x04
+#define RLL_CAUSE_UNSOL_DM_RESP_MF	0x05
+#define RLL_CAUSE_UNSOL_SPRV_RESP	0x06
+#define RLL_CAUSE_SEQ_ERR		0x07
+#define RLL_CAUSE_UFRM_INC_PARAM	0x08
+#define RLL_CAUSE_SFRM_INC_PARAM	0x09
+#define RLL_CAUSE_IFRM_INC_MBITS	0x0a
+#define RLL_CAUSE_IFRM_INC_LEN		0x0b
+#define RLL_CAUSE_FRM_UNIMPL		0x0c
+#define RLL_CAUSE_SABM_MF		0x0d
+#define RLL_CAUSE_SABM_INFO_NOTALL	0x0e
+
+/* Chapter 9.3.26 */
+#define RSL_ERRCLS_NORMAL		0x00
+#define RSL_ERRCLS_RESOURCE_UNAVAIL	0x20
+#define RSL_ERRCLS_SERVICE_UNAVAIL	0x30
+#define RSL_ERRCLS_SERVICE_UNIMPL	0x40
+#define RSL_ERRCLS_INVAL_MSG		0x50
+#define RSL_ERRCLS_PROTO_ERROR		0x60
+#define RSL_ERRCLS_INTERWORKING		0x70
+
+/* normal event */
+#define RSL_ERR_RADIO_IF_FAIL		0x00
+#define RSL_ERR_RADIO_LINK_FAIL		0x01
+#define RSL_ERR_HANDOVER_ACC_FAIL	0x02
+#define RSL_ERR_TALKER_ACC_FAIL		0x03
+#define RSL_ERR_OM_INTERVENTION		0x07
+#define RSL_ERR_NORMAL_UNSPEC		0x0f
+#define RSL_ERR_T_MSRFPCI_EXP		0x18
+/* resource unavailable */
+#define RSL_ERR_EQUIPMENT_FAIL		0x20
+#define RSL_ERR_RR_UNAVAIL		0x21
+#define RSL_ERR_TERR_CH_FAIL		0x22
+#define RSL_ERR_CCCH_OVERLOAD		0x23
+#define RSL_ERR_ACCH_OVERLOAD		0x24
+#define RSL_ERR_PROCESSOR_OVERLOAD	0x25
+#define RSL_ERR_RES_UNAVAIL		0x2f
+/* service or option not available */
+#define RSL_ERR_TRANSC_UNAVAIL		0x30
+#define RSL_ERR_SERV_OPT_UNAVAIL	0x3f
+/* service or option not implemented */
+#define RSL_ERR_ENCR_UNIMPL		0x40
+#define RSL_ERR_SERV_OPT_UNIMPL		0x4f
+/* invalid message */
+#define RSL_ERR_RCH_ALR_ACTV_ALLOC	0x50
+#define RSL_ERR_INVALID_MESSAGE		0x5f
+/* protocol error */
+#define RSL_ERR_MSG_DISCR		0x60
+#define RSL_ERR_MSG_TYPE		0x61
+#define RSL_ERR_MSG_SEQ			0x62
+#define RSL_ERR_IE_ERROR		0x63
+#define RSL_ERR_MAND_IE_ERROR		0x64
+#define RSL_ERR_OPT_IE_ERROR		0x65
+#define RSL_ERR_IE_NONEXIST		0x66
+#define RSL_ERR_IE_LENGTH		0x67
+#define RSL_ERR_IE_CONTENT		0x68
+#define RSL_ERR_PROTO			0x6f
+/* interworking */
+#define RSL_ERR_INTERWORKING		0x7f
+
+/* Chapter 9.3.30 */
+#define RSL_SYSTEM_INFO_8	0x00
+#define RSL_SYSTEM_INFO_1	0x01
+#define RSL_SYSTEM_INFO_2	0x02
+#define RSL_SYSTEM_INFO_3	0x03
+#define RSL_SYSTEM_INFO_4	0x04
+#define RSL_SYSTEM_INFO_5	0x05
+#define RSL_SYSTEM_INFO_6	0x06
+#define RSL_SYSTEM_INFO_7	0x07
+#define RSL_SYSTEM_INFO_16	0x08
+#define RSL_SYSTEM_INFO_17	0x09
+#define RSL_SYSTEM_INFO_2bis	0x0a
+#define RSL_SYSTEM_INFO_2ter	0x0b
+#define RSL_SYSTEM_INFO_5bis	0x0d
+#define RSL_SYSTEM_INFO_5ter	0x0e
+#define RSL_SYSTEM_INFO_10	0x0f
+#define REL_EXT_MEAS_ORDER	0x47
+#define RSL_MEAS_INFO		0x48
+#define RSL_SYSTEM_INFO_13	0x28
+#define RSL_SYSTEM_INFO_2quater	0x29
+#define RSL_SYSTEM_INFO_9	0x2a
+#define RSL_SYSTEM_INFO_18	0x2b
+#define RSL_SYSTEM_INFO_19	0x2c
+#define RSL_SYSTEM_INFO_20	0x2d
+
+/* Chapter 9.3.40 */
+#define RSL_CHANNEED_ANY	0x00
+#define RSL_CHANNEED_SDCCH	0x01
+#define RSL_CHANNEED_TCH_F	0x02
+#define RSL_CHANNEED_TCH_ForH	0x03
+
+/* Chapter 3.3.2.3 Brocast control channel */
+/* CCCH-CONF, NC is not combined */
+#define RSL_BCCH_CCCH_CONF_1_NC	0x00
+#define RSL_BCCH_CCCH_CONF_1_C	0x01
+#define RSL_BCCH_CCCH_CONF_2_NC	0x02
+#define RSL_BCCH_CCCH_CONF_3_NC	0x04
+#define RSL_BCCH_CCCH_CONF_4_NC	0x06
+
+/* BS-PA-MFRMS */
+#define RSL_BS_PA_MFRMS_2	0x00
+#define RSL_BS_PA_MFRMS_3	0x01
+#define RSL_BS_PA_MFRMS_4	0x02
+#define RSL_BS_PA_MFRMS_5	0x03
+#define RSL_BS_PA_MFRMS_6	0x04
+#define RSL_BS_PA_MFRMS_7	0x05
+#define RSL_BS_PA_MFRMS_8	0x06
+#define RSL_BS_PA_MFRMS_9	0x07
+
+/* RSL_IE_IPAC_RTP_PAYLOAD[2] */
+enum rsl_ipac_rtp_payload {
+	RSL_IPAC_RTP_GSM	= 1,
+	RSL_IPAC_RTP_EFR,
+	RSL_IPAC_RTP_AMR,
+	RSL_IPAC_RTP_CSD,
+	RSL_IPAC_RTP_MUX,
+};
+
+/* RSL_IE_IPAC_SPEECH_MODE, lower four bits */
+enum rsl_ipac_speech_mode_s {
+	RSL_IPAC_SPEECH_GSM_FR = 0,	/* GSM FR (Type 1, FS) */
+	RSL_IPAC_SPEECH_GSM_EFR = 1,	/* GSM EFR (Type 2, FS) */
+	RSL_IPAC_SPEECH_GSM_AMR_FR = 2,	/* GSM AMR/FR (Type 3, FS) */
+	RSL_IPAC_SPEECH_GSM_HR = 3,	/* GSM HR (Type 1, HS) */
+	RSL_IPAC_SPEECH_GSM_AMR_HR = 5,	/* GSM AMR/hr (Type 3, HS) */
+	RSL_IPAC_SPEECH_AS_RTP = 0xf,	/* As specified by RTP Payload IE */
+};
+/* RSL_IE_IPAC_SPEECH_MODE, upper four bits */
+enum rsl_ipac_speech_mode_m {
+	RSL_IPAC_SPEECH_M_RXTX = 0,	/* Send and Receive */
+	RSL_IPAC_SPEECH_M_RX = 1,	/* Receive only */
+	RSL_IPAC_SPEECH_M_TX = 2,	/* Send only */
+};
+
+/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */
+enum rsl_ipac_rtp_csd_format_d {
+	RSL_IPAC_RTP_CSD_EXT_TRAU = 0,
+	RSL_IPAC_RTP_CSD_NON_TRAU = 1,
+	RSL_IPAC_RTP_CSD_TRAU_BTS = 2,
+	RSL_IPAC_RTP_CSD_IWF_FREE = 3,
+};
+/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */
+enum rsl_ipac_rtp_csd_format_ir {
+	RSL_IPAC_RTP_CSD_IR_8k = 0,
+	RSL_IPAC_RTP_CSD_IR_16k = 1,
+	RSL_IPAC_RTP_CSD_IR_32k = 2,
+	RSL_IPAC_RTP_CSD_IR_64k = 3,
+};
+
+/* Siemens vendor-specific RSL extensions */
+struct rsl_mrpci {
+	uint8_t power_class:3,
+		 vgcs_capable:1,
+		 vbs_capable:1,
+		 gsm_phase:2;
+} __attribute__ ((packed));
+
+enum rsl_mrpci_pwrclass {
+	RSL_MRPCI_PWRC_1	= 0,
+	RSL_MRPCI_PWRC_2	= 1,
+	RSL_MRPCI_PWRC_3	= 2,
+	RSL_MRPCI_PWRC_4	= 3,
+	RSL_MRPCI_PWRC_5	= 4,
+};
+enum rsl_mrpci_phase {
+	RSL_MRPCI_PHASE_1	= 0,
+	/* reserved */
+	RSL_MRPCI_PHASE_2	= 2,
+	RSL_MRPCI_PHASE_2PLUS	= 3,
+};
+
+
+#endif /* PROTO_GSM_08_58_H */
diff --git a/libosmocore/include/osmocore/protocol/gsm_12_21.h b/libosmocore/include/osmocore/protocol/gsm_12_21.h
new file mode 100644
index 0000000..9cae45d
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_12_21.h
@@ -0,0 +1,713 @@
+#ifndef PROTO_GSM_12_21_H
+#define PROTO_GSM_12_21_H
+
+/* GSM Network Management 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>
+ * 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 <stdint.h>
+#include <osmocore/tlv.h>
+
+/* generic header in front of every OML message according to TS 08.59 */
+struct abis_om_hdr {
+	uint8_t	mdisc;
+	uint8_t	placement;
+	uint8_t	sequence;
+	uint8_t	length;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_MDISC_FOM		0x80
+#define ABIS_OM_MDISC_MMI		0x40
+#define ABIS_OM_MDISC_TRAU		0x20
+#define ABIS_OM_MDISC_MANUF		0x10
+#define ABIS_OM_PLACEMENT_ONLY		0x80
+#define ABIS_OM_PLACEMENT_FIRST 	0x40
+#define ABIS_OM_PLACEMENT_MIDDLE	0x20
+#define ABIS_OM_PLACEMENT_LAST		0x10
+
+struct abis_om_obj_inst {
+	uint8_t	bts_nr;
+	uint8_t	trx_nr;
+	uint8_t	ts_nr;
+} __attribute__ ((packed));
+
+struct abis_om_fom_hdr {
+	uint8_t	msg_type;
+	uint8_t	obj_class;
+	struct abis_om_obj_inst	obj_inst;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_FOM_HDR_SIZE	(sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
+
+/* Section 9.1: Message Types */
+enum abis_nm_msgtype {
+	/* SW Download Management Messages */
+	NM_MT_LOAD_INIT			= 0x01,
+	NM_MT_LOAD_INIT_ACK,
+	NM_MT_LOAD_INIT_NACK,
+	NM_MT_LOAD_SEG,
+	NM_MT_LOAD_SEG_ACK,
+	NM_MT_LOAD_ABORT,
+	NM_MT_LOAD_END,
+	NM_MT_LOAD_END_ACK,
+	NM_MT_LOAD_END_NACK,
+	NM_MT_SW_ACT_REQ,		/* BTS->BSC */
+	NM_MT_SW_ACT_REQ_ACK,
+	NM_MT_SW_ACT_REQ_NACK,
+	NM_MT_ACTIVATE_SW,		/* BSC->BTS */
+	NM_MT_ACTIVATE_SW_ACK,
+	NM_MT_ACTIVATE_SW_NACK,
+	NM_MT_SW_ACTIVATED_REP,		/* 0x10 */
+	/* A-bis Interface Management Messages */
+	NM_MT_ESTABLISH_TEI		= 0x21,
+	NM_MT_ESTABLISH_TEI_ACK,
+	NM_MT_ESTABLISH_TEI_NACK,
+	NM_MT_CONN_TERR_SIGN,
+	NM_MT_CONN_TERR_SIGN_ACK,
+	NM_MT_CONN_TERR_SIGN_NACK,
+	NM_MT_DISC_TERR_SIGN,
+	NM_MT_DISC_TERR_SIGN_ACK,
+	NM_MT_DISC_TERR_SIGN_NACK,
+	NM_MT_CONN_TERR_TRAF,
+	NM_MT_CONN_TERR_TRAF_ACK,
+	NM_MT_CONN_TERR_TRAF_NACK,
+	NM_MT_DISC_TERR_TRAF,
+	NM_MT_DISC_TERR_TRAF_ACK,
+	NM_MT_DISC_TERR_TRAF_NACK,
+	/* Transmission Management Messages */
+	NM_MT_CONN_MDROP_LINK		= 0x31,
+	NM_MT_CONN_MDROP_LINK_ACK,
+	NM_MT_CONN_MDROP_LINK_NACK,
+	NM_MT_DISC_MDROP_LINK,
+	NM_MT_DISC_MDROP_LINK_ACK,
+	NM_MT_DISC_MDROP_LINK_NACK,
+	/* Air Interface Management Messages */
+	NM_MT_SET_BTS_ATTR		= 0x41,
+	NM_MT_SET_BTS_ATTR_ACK,
+	NM_MT_SET_BTS_ATTR_NACK,
+	NM_MT_SET_RADIO_ATTR,
+	NM_MT_SET_RADIO_ATTR_ACK,
+	NM_MT_SET_RADIO_ATTR_NACK,
+	NM_MT_SET_CHAN_ATTR,
+	NM_MT_SET_CHAN_ATTR_ACK,
+	NM_MT_SET_CHAN_ATTR_NACK,
+	/* Test Management Messages */
+	NM_MT_PERF_TEST			= 0x51,
+	NM_MT_PERF_TEST_ACK,
+	NM_MT_PERF_TEST_NACK,
+	NM_MT_TEST_REP,
+	NM_MT_SEND_TEST_REP,
+	NM_MT_SEND_TEST_REP_ACK,
+	NM_MT_SEND_TEST_REP_NACK,
+	NM_MT_STOP_TEST,
+	NM_MT_STOP_TEST_ACK,
+	NM_MT_STOP_TEST_NACK,
+	/* State Management and Event Report Messages */
+	NM_MT_STATECHG_EVENT_REP	= 0x61,
+	NM_MT_FAILURE_EVENT_REP,
+	NM_MT_STOP_EVENT_REP,
+	NM_MT_STOP_EVENT_REP_ACK,
+	NM_MT_STOP_EVENT_REP_NACK,
+	NM_MT_REST_EVENT_REP,
+	NM_MT_REST_EVENT_REP_ACK,
+	NM_MT_REST_EVENT_REP_NACK,
+	NM_MT_CHG_ADM_STATE,
+	NM_MT_CHG_ADM_STATE_ACK,
+	NM_MT_CHG_ADM_STATE_NACK,
+	NM_MT_CHG_ADM_STATE_REQ,
+	NM_MT_CHG_ADM_STATE_REQ_ACK,
+	NM_MT_CHG_ADM_STATE_REQ_NACK,
+	NM_MT_REP_OUTST_ALARMS		= 0x93,
+	NM_MT_REP_OUTST_ALARMS_ACK,
+	NM_MT_REP_OUTST_ALARMS_NACK,
+	/* Equipment Management Messages */
+	NM_MT_CHANGEOVER		= 0x71,
+	NM_MT_CHANGEOVER_ACK,
+	NM_MT_CHANGEOVER_NACK,
+	NM_MT_OPSTART,
+	NM_MT_OPSTART_ACK,
+	NM_MT_OPSTART_NACK,
+	NM_MT_REINIT,
+	NM_MT_REINIT_ACK,
+	NM_MT_REINIT_NACK,
+	NM_MT_SET_SITE_OUT,		/* BS11: get alarm ?!? */
+	NM_MT_SET_SITE_OUT_ACK,
+	NM_MT_SET_SITE_OUT_NACK,
+	NM_MT_CHG_HW_CONF		= 0x90,
+	NM_MT_CHG_HW_CONF_ACK,
+	NM_MT_CHG_HW_CONF_NACK,
+	/* Measurement Management Messages */
+	NM_MT_MEAS_RES_REQ		= 0x8a,
+	NM_MT_MEAS_RES_RESP,
+	NM_MT_STOP_MEAS,
+	NM_MT_START_MEAS,
+	/* Other Messages */
+	NM_MT_GET_ATTR			= 0x81,
+	NM_MT_GET_ATTR_RESP,
+	NM_MT_GET_ATTR_NACK,
+	NM_MT_SET_ALARM_THRES,
+	NM_MT_SET_ALARM_THRES_ACK,
+	NM_MT_SET_ALARM_THRES_NACK,
+};
+
+enum abis_nm_msgtype_bs11 {
+	NM_MT_BS11_RESET_RESOURCE	= 0x74,
+
+	NM_MT_BS11_BEGIN_DB_TX		= 0xa3,
+	NM_MT_BS11_BEGIN_DB_TX_ACK,
+	NM_MT_BS11_BEGIN_DB_TX_NACK,
+	NM_MT_BS11_END_DB_TX		= 0xa6,
+	NM_MT_BS11_END_DB_TX_ACK,
+	NM_MT_BS11_END_DB_TX_NACK,
+	NM_MT_BS11_CREATE_OBJ		= 0xa9,
+	NM_MT_BS11_CREATE_OBJ_ACK,
+	NM_MT_BS11_CREATE_OBJ_NACK,
+	NM_MT_BS11_DELETE_OBJ		= 0xac,
+	NM_MT_BS11_DELETE_OBJ_ACK,
+	NM_MT_BS11_DELETE_OBJ_NACK,
+
+	NM_MT_BS11_SET_ATTR		= 0xd0,
+	NM_MT_BS11_SET_ATTR_ACK,
+	NM_MT_BS11_SET_ATTR_NACK,
+	NM_MT_BS11_LMT_SESSION		= 0xdc,
+
+	NM_MT_BS11_GET_STATE		= 0xe3,
+	NM_MT_BS11_GET_STATE_ACK,
+	NM_MT_BS11_LMT_LOGON		= 0xe5,
+	NM_MT_BS11_LMT_LOGON_ACK,
+	NM_MT_BS11_RESTART		= 0xe7,
+	NM_MT_BS11_RESTART_ACK,
+	NM_MT_BS11_DISCONNECT		= 0xe9,
+	NM_MT_BS11_DISCONNECT_ACK,
+	NM_MT_BS11_LMT_LOGOFF		= 0xec,
+	NM_MT_BS11_LMT_LOGOFF_ACK,
+	NM_MT_BS11_RECONNECT		= 0xf1,
+	NM_MT_BS11_RECONNECT_ACK,
+};
+
+enum abis_nm_msgtype_ipacc {
+	NM_MT_IPACC_RESTART		= 0x87,
+	NM_MT_IPACC_RESTART_ACK,
+	NM_MT_IPACC_RESTART_NACK,
+	NM_MT_IPACC_RSL_CONNECT		= 0xe0,
+	NM_MT_IPACC_RSL_CONNECT_ACK,
+	NM_MT_IPACC_RSL_CONNECT_NACK,
+	NM_MT_IPACC_RSL_DISCONNECT	= 0xe3,
+	NM_MT_IPACC_RSL_DISCONNECT_ACK,
+	NM_MT_IPACC_RSL_DISCONNECT_NACK,
+	NM_MT_IPACC_CONN_TRAF		= 0xe6,
+	NM_MT_IPACC_CONN_TRAF_ACK,
+	NM_MT_IPACC_CONN_TRAF_NACK,
+	NM_MT_IPACC_DEF_BOOT_SW		= 0xec,
+	NM_MT_IPACC_DEF_BOOT_SW_ACK,
+	MN_MT_IPACC_DEF_BOOT_SW_NACK,
+	NM_MT_IPACC_SET_NVATTR		= 0xef,
+	NM_MT_IPACC_SET_NVATTR_ACK,
+	NM_MT_IPACC_SET_NVATTR_NACK,
+	NM_MT_IPACC_GET_NVATTR		= 0xf2,
+	NM_MT_IPACC_GET_NVATTR_ACK,
+	NM_MT_IPACC_GET_NVATTR_NACK,
+	NM_MT_IPACC_SET_ATTR		= 0xf5,
+	NM_MT_IPACC_SET_ATTR_ACK,
+	NM_MT_IPACC_SET_ATTR_NACK,
+};
+
+enum abis_nm_bs11_cell_alloc {
+	NM_BS11_CANR_GSM	= 0x00,
+	NM_BS11_CANR_DCS1800	= 0x01,
+};
+
+/* Section 9.2: Object Class */
+enum abis_nm_obj_class {
+	NM_OC_SITE_MANAGER		= 0x00,
+	NM_OC_BTS,
+	NM_OC_RADIO_CARRIER,
+	NM_OC_CHANNEL,
+	NM_OC_BASEB_TRANSC,
+	/* RFU: 05-FE */
+
+	NM_OC_IPAC_E1_TRUNK		= 0x0e,
+	NM_OC_IPAC_E1_PORT		= 0x0f,
+	NM_OC_IPAC_E1_CHAN		= 0x10,
+	NM_OC_IPAC_CLK_MODULE		= 0x22,
+
+	NM_OC_BS11_ADJC			= 0xa0,
+	NM_OC_BS11_HANDOVER		= 0xa1,
+	NM_OC_BS11_PWR_CTRL		= 0xa2,
+	NM_OC_BS11_BTSE			= 0xa3,		/* LMT? */
+	NM_OC_BS11_RACK			= 0xa4,
+	NM_OC_BS11			= 0xa5,		/* 01: ALCO */
+	NM_OC_BS11_TEST			= 0xa6,
+	NM_OC_BS11_ENVABTSE		= 0xa8,
+	NM_OC_BS11_BPORT		= 0xa9,
+
+	NM_OC_GPRS_NSE			= 0xf0,
+	NM_OC_GPRS_CELL			= 0xf1,
+	NM_OC_GPRS_NSVC			= 0xf2,
+
+	NM_OC_NULL			= 0xff,
+};
+
+/* Section 9.4: Attributes */
+enum abis_nm_attr {
+	NM_ATT_ABIS_CHANNEL	= 0x01,
+	NM_ATT_ADD_INFO,
+	NM_ATT_ADD_TEXT,
+	NM_ATT_ADM_STATE,
+	NM_ATT_ARFCN_LIST,
+	NM_ATT_AUTON_REPORT,
+	NM_ATT_AVAIL_STATUS,
+	NM_ATT_BCCH_ARFCN,
+	NM_ATT_BSIC,
+	NM_ATT_BTS_AIR_TIMER,
+	NM_ATT_CCCH_L_I_P,
+	NM_ATT_CCCH_L_T,
+	NM_ATT_CHAN_COMB,
+	NM_ATT_CONN_FAIL_CRIT,
+	NM_ATT_DEST,
+	/* res */
+	NM_ATT_EVENT_TYPE	= 0x11, /* BS11: file data ?!? */
+	NM_ATT_FILE_ID,
+	NM_ATT_FILE_VERSION,
+	NM_ATT_GSM_TIME,
+	NM_ATT_HSN,
+	NM_ATT_HW_CONFIG,
+	NM_ATT_HW_DESC,
+	NM_ATT_INTAVE_PARAM,
+	NM_ATT_INTERF_BOUND,
+	NM_ATT_LIST_REQ_ATTR,
+	NM_ATT_MAIO,
+	NM_ATT_MANUF_STATE,
+	NM_ATT_MANUF_THRESH,
+	NM_ATT_MANUF_ID,
+	NM_ATT_MAX_TA,
+	NM_ATT_MDROP_LINK,	/* 0x20 */
+	NM_ATT_MDROP_NEXT,
+	NM_ATT_NACK_CAUSES,
+	NM_ATT_NY1,
+	NM_ATT_OPER_STATE,
+	NM_ATT_OVERL_PERIOD,
+	NM_ATT_PHYS_CONF,
+	NM_ATT_POWER_CLASS,
+	NM_ATT_POWER_THRESH,
+	NM_ATT_PROB_CAUSE,
+	NM_ATT_RACH_B_THRESH,
+	NM_ATT_LDAVG_SLOTS,
+	NM_ATT_RAD_SUBC,
+	NM_ATT_RF_MAXPOWR_R,
+	NM_ATT_SITE_INPUTS,
+	NM_ATT_SITE_OUTPUTS,
+	NM_ATT_SOURCE,		/* 0x30 */
+	NM_ATT_SPEC_PROB,
+	NM_ATT_START_TIME,
+	NM_ATT_T200,
+	NM_ATT_TEI,
+	NM_ATT_TEST_DUR,
+	NM_ATT_TEST_NO,
+	NM_ATT_TEST_REPORT,
+	NM_ATT_VSWR_THRESH,
+	NM_ATT_WINDOW_SIZE,
+	/* Res  */
+	NM_ATT_BS11_RSSI_OFFS	= 0x3d,
+	NM_ATT_BS11_TXPWR	= 0x3e,
+	NM_ATT_BS11_DIVERSITY	= 0x3f,
+	/* Res  */
+	NM_ATT_TSC		= 0x40,
+	NM_ATT_SW_CONFIG,
+	NM_ATT_SW_DESCR,
+	NM_ATT_SEVERITY,
+	NM_ATT_GET_ARI,
+	NM_ATT_HW_CONF_CHG,
+	NM_ATT_OUTST_ALARM,
+	NM_ATT_FILE_DATA,
+	NM_ATT_MEAS_RES,
+	NM_ATT_MEAS_TYPE,
+
+	NM_ATT_BS11_ESN_FW_CODE_NO	= 0x4c,
+	NM_ATT_BS11_ESN_HW_CODE_NO	= 0x4f,
+
+	NM_ATT_BS11_ESN_PCB_SERIAL	= 0x55,
+	NM_ATT_BS11_EXCESSIVE_DISTANCE	= 0x58,
+
+	NM_ATT_BS11_ALL_TEST_CATG	= 0x60,
+	NM_ATT_BS11_BTSLS_HOPPING,
+	NM_ATT_BS11_CELL_ALLOC_NR,
+	NM_ATT_BS11_CELL_GLOBAL_ID,
+	NM_ATT_BS11_ENA_INTERF_CLASS	= 0x66,
+	NM_ATT_BS11_ENA_INT_INTEC_HANDO	= 0x67,
+	NM_ATT_BS11_ENA_INT_INTRC_HANDO	= 0x68,
+	NM_ATT_BS11_ENA_MS_PWR_CTRL	= 0x69,
+	NM_ATT_BS11_ENA_PWR_BDGT_HO	= 0x6a,
+	NM_ATT_BS11_ENA_PWR_CTRL_RLFW	= 0x6b,
+	NM_ATT_BS11_ENA_RXLEV_HO	= 0x6c,
+	NM_ATT_BS11_ENA_RXQUAL_HO	= 0x6d,
+	NM_ATT_BS11_FACCH_QUAL		= 0x6e,
+
+	NM_ATT_IPACC_DST_IP		= 0x80,
+	NM_ATT_IPACC_DST_IP_PORT	= 0x81,
+	NM_ATT_IPACC_SSRC		= 0x82,
+	NM_ATT_IPACC_RTP_PAYLD_TYPE	= 0x83,
+	NM_ATT_IPACC_BASEB_ID		= 0x84,
+	NM_ATT_IPACC_STREAM_ID		= 0x85,
+	NM_ATT_IPACC_NV_FLAGS		= 0x86,
+	NM_ATT_IPACC_FREQ_CTRL		= 0x87,
+	NM_ATT_IPACC_PRIM_OML_CFG	= 0x88,
+	NM_ATT_IPACC_SEC_OML_CFG	= 0x89,
+	NM_ATT_IPACC_IP_IF_CFG		= 0x8a,		/* IP interface */
+	NM_ATT_IPACC_IP_GW_CFG		= 0x8b,		/* IP gateway */
+	NM_ATT_IPACC_IN_SERV_TIME	= 0x8c,
+	NM_ATT_IPACC_TRX_BTS_ASS	= 0x8d,
+	NM_ATT_IPACC_LOCATION		= 0x8e,		/* string describing location */
+	NM_ATT_IPACC_PAGING_CFG		= 0x8f,
+	NM_ATT_IPACC_FILE_DATA		= 0x90,
+	NM_ATT_IPACC_UNIT_ID		= 0x91,		/* Site/BTS/TRX */
+	NM_ATT_IPACC_PARENT_UNIT_ID	= 0x92,
+	NM_ATT_IPACC_UNIT_NAME		= 0x93,		/* default: nbts-<mac-as-string> */
+	NM_ATT_IPACC_SNMP_CFG		= 0x94,
+	NM_ATT_IPACC_PRIM_OML_CFG_LIST	= 0x95,
+	NM_ATT_IPACC_PRIM_OML_FB_TOUT	= 0x96,
+	NM_ATT_IPACC_CUR_SW_CFG		= 0x97,
+	NM_ATT_IPACC_TIMING_BUS		= 0x98,
+	NM_ATT_IPACC_CGI		= 0x99,
+	NM_ATT_IPACC_RAC		= 0x9a,
+	NM_ATT_IPACC_OBJ_VERSION	= 0x9b,
+	NM_ATT_IPACC_GPRS_PAGING_CFG	= 0x9c,
+	NM_ATT_IPACC_NSEI		= 0x9d,
+	NM_ATT_IPACC_BVCI		= 0x9e,
+	NM_ATT_IPACC_NSVCI		= 0x9f,
+	NM_ATT_IPACC_NS_CFG		= 0xa0,
+	NM_ATT_IPACC_BSSGP_CFG		= 0xa1,
+	NM_ATT_IPACC_NS_LINK_CFG	= 0xa2,
+	NM_ATT_IPACC_RLC_CFG		= 0xa3,	
+	NM_ATT_IPACC_ALM_THRESH_LIST	= 0xa4,
+	NM_ATT_IPACC_MONIT_VAL_LIST	= 0xa5,
+	NM_ATT_IPACC_TIB_CONTROL	= 0xa6,
+	NM_ATT_IPACC_SUPP_FEATURES	= 0xa7,
+	NM_ATT_IPACC_CODING_SCHEMES	= 0xa8,
+	NM_ATT_IPACC_RLC_CFG_2		= 0xa9,
+	NM_ATT_IPACC_HEARTB_TOUT	= 0xaa,
+	NM_ATT_IPACC_UPTIME		= 0xab,
+	NM_ATT_IPACC_RLC_CFG_3		= 0xac,
+	NM_ATT_IPACC_SSL_CFG		= 0xad,
+	NM_ATT_IPACC_SEC_POSSIBLE	= 0xae,
+	NM_ATT_IPACC_IML_SSL_STATE	= 0xaf,
+	NM_ATT_IPACC_REVOC_DATE		= 0xb0,
+
+
+	NM_ATT_BS11_RF_RES_IND_PER	= 0x8f,
+	
+	NM_ATT_BS11_RX_LEV_MIN_CELL	= 0x90,
+	NM_ATT_BS11_ABIS_EXT_TIME	= 0x91,
+	NM_ATT_BS11_TIMER_HO_REQUEST	= 0x92,
+	NM_ATT_BS11_TIMER_NCELL		= 0x93,
+	NM_ATT_BS11_TSYNC		= 0x94,
+	NM_ATT_BS11_TTRAU		= 0x95,
+	NM_ATT_BS11_EMRG_CFG_MEMBER	= 0x9b,
+	NM_ATT_BS11_TRX_AREA		= 0x9f,
+
+	NM_ATT_BS11_BCCH_RECONF		= 0xd7,
+	NM_ATT_BS11_BIT_ERR_THESH	= 0xa0,
+	NM_ATT_BS11_BOOT_SW_VERS	= 0xa1,
+	NM_ATT_BS11_CCLK_ACCURACY	= 0xa3,
+	NM_ATT_BS11_CCLK_TYPE		= 0xa4,
+	NM_ATT_BS11_INP_IMPEDANCE	= 0xaa,
+	NM_ATT_BS11_L1_PROT_TYPE	= 0xab,
+	NM_ATT_BS11_LINE_CFG		= 0xac,
+	NM_ATT_BS11_LI_PORT_1		= 0xad,
+	NM_ATT_BS11_LI_PORT_2		= 0xae,
+
+	NM_ATT_BS11_L1_REM_ALM_TYPE	= 0xb0,
+	NM_ATT_BS11_SW_LOAD_INTENDED	= 0xbb,
+	NM_ATT_BS11_SW_LOAD_SAFETY	= 0xbc,
+	NM_ATT_BS11_SW_LOAD_STORED	= 0xbd,
+
+	NM_ATT_BS11_VENDOR_NAME		= 0xc1,
+	NM_ATT_BS11_HOPPING_MODE	= 0xc5,
+	NM_ATT_BS11_LMT_LOGON_SESSION	= 0xc6,
+	NM_ATT_BS11_LMT_LOGIN_TIME	= 0xc7,
+	NM_ATT_BS11_LMT_USER_ACC_LEV	= 0xc8,
+	NM_ATT_BS11_LMT_USER_NAME	= 0xc9,
+
+	NM_ATT_BS11_L1_CONTROL_TS	= 0xd8,
+	NM_ATT_BS11_RADIO_MEAS_GRAN	= 0xdc,	/* in SACCH multiframes */
+	NM_ATT_BS11_RADIO_MEAS_REP	= 0xdd,
+
+	NM_ATT_BS11_SH_LAPD_INT_TIMER	= 0xe8,
+
+	NM_ATT_BS11_BTS_STATE		= 0xf0,
+	NM_ATT_BS11_E1_STATE		= 0xf1,
+	NM_ATT_BS11_PLL			= 0xf2,
+	NM_ATT_BS11_RX_OFFSET		= 0xf3,
+	NM_ATT_BS11_ANT_TYPE		= 0xf4,
+	NM_ATT_BS11_PLL_MODE		= 0xfc,
+	NM_ATT_BS11_PASSWORD		= 0xfd,
+};
+#define NM_ATT_BS11_FILE_DATA	NM_ATT_EVENT_TYPE
+
+/* Section 9.4.4: Administrative State */
+enum abis_nm_adm_state {
+	NM_STATE_LOCKED		= 0x01,
+	NM_STATE_UNLOCKED	= 0x02,
+	NM_STATE_SHUTDOWN	= 0x03,
+	NM_STATE_NULL		= 0xff,
+};
+
+/* Section 9.4.7: Administrative State */
+enum abis_nm_avail_state {
+	NM_AVSTATE_IN_TEST	= 1,
+	NM_AVSTATE_POWER_OFF	= 2,
+	NM_AVSTATE_OFF_LINE	= 3,
+	NM_AVSTATE_DEPENDENCY	= 5,
+	NM_AVSTATE_DEGRADED	= 6,
+	NM_AVSTATE_NOT_INSTALLED= 7,
+	NM_AVSTATE_OK		= 0xff,
+};
+
+enum abis_nm_op_state {
+	NM_OPSTATE_DISABLED	= 1,
+	NM_OPSTATE_ENABLED	= 2,
+	NM_OPSTATE_NULL		= 0xff,
+};
+
+/* Section 9.4.13: Channel Combination */
+enum abis_nm_chan_comb {
+	NM_CHANC_TCHFull	= 0x00,	/* TCH/F + TCH/H + SACCH/TF */
+	NM_CHANC_TCHHalf	= 0x01, /* TCH/H(0,1) + FACCH/H(0,1) +
+					   SACCH/TH(0,1) */
+	NM_CHANC_TCHHalf2	= 0x02, /* TCH/H(0) + FACCH/H(0) + SACCH/TH(0) +
+					   TCH/H(1) */
+	NM_CHANC_SDCCH		= 0x03,	/* SDCCH/8 + SACCH/8 */
+	NM_CHANC_mainBCCH	= 0x04,	/* FCCH + SCH + BCCH + CCCH */
+	NM_CHANC_BCCHComb	= 0x05,	/* FCCH + SCH + BCCH + CCCH + SDCCH/4 +
+					   SACCH/C4 */
+	NM_CHANC_BCCH		= 0x06,	/* BCCH + CCCH */
+	NM_CHANC_BCCH_CBCH	= 0x07,	/* CHANC_BCCHComb + CBCH */
+	NM_CHANC_SDCCH_CBCH	= 0x08,	/* CHANC_SDCCH8 + CBCH */
+	/* ip.access */
+	NM_CHANC_IPAC_bPDCH	= 0x0b,	/* PBCCH + PCCCH + PDTCH/F + PACCH/F +
+					   PTCCH/F */
+	NM_CHANC_IPAC_cPDCH	= 0x0c, /* PBCCH + PDTCH/F + PACCH/F + PTCCH/F */
+	NM_CHANC_IPAC_PDCH	= 0x0d,	/* PDTCH/F + PACCH/F + PTCCH/F */
+	NM_CHANC_IPAC_TCHFull_PDCH = 0x80,
+	NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81,
+};
+
+/* Section 9.4.16: Event Type */
+enum abis_nm_event_type {
+	NM_EVT_COMM_FAIL	= 0x00,
+	NM_EVT_QOS_FAIL		= 0x01,
+	NM_EVT_PROC_FAIL	= 0x02,
+	NM_EVT_EQUIP_FAIL	= 0x03,
+	NM_EVT_ENV_FAIL		= 0x04,
+};
+
+/* Section: 9.4.63: Perceived Severity */
+enum abis_nm_severity {
+	NM_SEVER_CEASED		= 0x00,
+	NM_SEVER_CRITICAL	= 0x01,
+	NM_SEVER_MAJOR		= 0x02,
+	NM_SEVER_MINOR		= 0x03,
+	NM_SEVER_WARNING	= 0x04,
+	NM_SEVER_INDETERMINATE	= 0x05,
+};
+
+/* Section 9.4.43: Probable Cause Type */
+enum abis_nm_pcause_type {
+	NM_PCAUSE_T_X721	= 0x01,
+	NM_PCAUSE_T_GSM		= 0x02,
+	NM_PCAUSE_T_MANUF	= 0x03,
+};
+
+/* Section 9.4.36: NACK Causes */
+enum abis_nm_nack_cause {
+	/* General Nack Causes */
+	NM_NACK_INCORR_STRUCT		= 0x01,
+	NM_NACK_MSGTYPE_INVAL		= 0x02,
+	NM_NACK_OBJCLASS_INVAL		= 0x05,
+	NM_NACK_OBJCLASS_NOTSUPP	= 0x06,
+	NM_NACK_BTSNR_UNKN		= 0x07,
+	NM_NACK_TRXNR_UNKN		= 0x08,
+	NM_NACK_OBJINST_UNKN		= 0x09,
+	NM_NACK_ATTRID_INVAL		= 0x0c,
+	NM_NACK_ATTRID_NOTSUPP		= 0x0d,
+	NM_NACK_PARAM_RANGE		= 0x0e,
+	NM_NACK_ATTRLIST_INCONSISTENT	= 0x0f,
+	NM_NACK_SPEC_IMPL_NOTSUPP	= 0x10,
+	NM_NACK_CANT_PERFORM		= 0x11,
+	/* Specific Nack Causes */
+	NM_NACK_RES_NOTIMPL		= 0x19,
+	NM_NACK_RES_NOTAVAIL		= 0x1a,
+	NM_NACK_FREQ_NOTAVAIL		= 0x1b,
+	NM_NACK_TEST_NOTSUPP		= 0x1c,
+	NM_NACK_CAPACITY_RESTR		= 0x1d,
+	NM_NACK_PHYSCFG_NOTPERFORM	= 0x1e,
+	NM_NACK_TEST_NOTINIT		= 0x1f,
+	NM_NACK_PHYSCFG_NOTRESTORE	= 0x20,
+	NM_NACK_TEST_NOSUCH		= 0x21,
+	NM_NACK_TEST_NOSTOP		= 0x22,
+	NM_NACK_MSGINCONSIST_PHYSCFG	= 0x23,
+	NM_NACK_FILE_INCOMPLETE		= 0x25,
+	NM_NACK_FILE_NOTAVAIL		= 0x26,
+	NM_NACK_FILE_NOTACTIVATE	= 0x27,
+	NM_NACK_REQ_NOT_GRANT		= 0x28,
+	NM_NACK_WAIT			= 0x29,
+	NM_NACK_NOTH_REPORT_EXIST	= 0x2a,
+	NM_NACK_MEAS_NOTSUPP		= 0x2b,
+	NM_NACK_MEAS_NOTSTART		= 0x2c,
+};
+
+/* Section 9.4.1 */
+struct abis_nm_channel {
+	uint8_t	attrib;
+	uint8_t	bts_port;
+	uint8_t	timeslot;
+	uint8_t	subslot;
+} __attribute__ ((packed));
+
+/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */
+enum abis_bs11_objtype {
+	BS11_OBJ_ALCO		= 0x01,
+	BS11_OBJ_BBSIG		= 0x02,	/* obj_class: 0,1 */
+	BS11_OBJ_TRX1		= 0x03,	/* only DEACTIVATE TRX1 */
+	BS11_OBJ_CCLK		= 0x04,
+	BS11_OBJ_GPSU		= 0x06,
+	BS11_OBJ_LI		= 0x07,
+	BS11_OBJ_PA		= 0x09,	/* obj_class: 0, 1*/
+};
+
+enum abis_bs11_trx_power {
+	BS11_TRX_POWER_GSM_2W	= 0x06,
+	BS11_TRX_POWER_GSM_250mW= 0x07,
+	BS11_TRX_POWER_GSM_80mW	= 0x08,
+	BS11_TRX_POWER_GSM_30mW	= 0x09,
+	BS11_TRX_POWER_DCS_3W	= 0x0a,
+	BS11_TRX_POWER_DCS_1W6	= 0x0b,
+	BS11_TRX_POWER_DCS_500mW= 0x0c,
+	BS11_TRX_POWER_DCS_160mW= 0x0d,
+};
+
+enum abis_bs11_li_pll_mode {
+	BS11_LI_PLL_LOCKED	= 2,
+	BS11_LI_PLL_STANDALONE	= 3,
+};
+
+enum abis_bs11_line_cfg {
+	BS11_LINE_CFG_STAR	= 0x00,
+	BS11_LINE_CFG_MULTIDROP	= 0x01,
+	BS11_LINE_CFG_LOOP	= 0x02,
+};
+
+enum abis_bs11_phase {
+	BS11_STATE_SOFTWARE_RQD		= 0x01,
+	BS11_STATE_LOAD_SMU_INTENDED	= 0x11,
+	BS11_STATE_LOAD_SMU_SAFETY	= 0x21,
+	BS11_STATE_LOAD_FAILED		= 0x31,
+	BS11_STATE_LOAD_DIAGNOSTIC	= 0x41,
+	BS11_STATE_WARM_UP		= 0x51,
+	BS11_STATE_WARM_UP_2		= 0x52,
+	BS11_STATE_WAIT_MIN_CFG		= 0x62,
+	BS11_STATE_MAINTENANCE		= 0x72,
+	BS11_STATE_LOAD_MBCCU		= 0x92,
+	BS11_STATE_WAIT_MIN_CFG_2	= 0xA2,
+	BS11_STATE_NORMAL		= 0x03,
+	BS11_STATE_ABIS_LOAD		= 0x13,
+};
+
+enum abis_nm_ipacc_test_no {
+	NM_IPACC_TESTNO_RLOOP_ANT	= 0x01,
+	NM_IPACC_TESTNO_RLOOP_XCVR	= 0x02,
+	NM_IPACC_TESTNO_FUNC_OBJ	= 0x03,
+	NM_IPACC_TESTNO_CHAN_USAGE	= 0x40,
+	NM_IPACC_TESTNO_BCCH_CHAN_USAGE	= 0x41,
+	NM_IPACC_TESTNO_FREQ_SYNC	= 0x42,
+	NM_IPACC_TESTNO_BCCH_INFO	= 0x43,
+	NM_IPACC_TESTNO_TX_BEACON	= 0x44,
+	NM_IPACC_TESTNO_SYSINFO_MONITOR	= 0x45,
+	NM_IPACC_TESTNO_BCCCH_MONITOR	= 0x46,
+};
+
+/* first byte after length inside NM_ATT_TEST_REPORT */
+enum abis_nm_ipacc_test_res {
+	NM_IPACC_TESTRES_SUCCESS	= 0,
+	NM_IPACC_TESTRES_TIMEOUT	= 1,
+	NM_IPACC_TESTRES_NO_CHANS	= 2,
+	NM_IPACC_TESTRES_PARTIAL	= 3,
+	NM_IPACC_TESTRES_STOPPED	= 4,
+};
+
+/* internal IE inside NM_ATT_TEST_REPORT */
+enum abis_nm_ipacc_testres_ie {
+	NM_IPACC_TR_IE_FREQ_ERR_LIST	= 3,
+	NM_IPACC_TR_IE_CHAN_USAGE	= 4,
+	NM_IPACC_TR_IE_BCCH_INFO	= 6,
+	NM_IPACC_TR_IE_RESULT_DETAILS	= 8,
+	NM_IPACC_TR_IE_FREQ_ERR		= 18,
+};
+
+enum ipac_eie {
+	NM_IPAC_EIE_ARFCN_WHITE		= 0x01,
+	NM_IPAC_EIE_ARFCH_BLACK		= 0x02,
+	NM_IPAC_EIE_FREQ_ERR_LIST	= 0x03,
+	NM_IPAC_EIE_CHAN_USE_LIST	= 0x04,
+	NM_IPAC_EIE_BCCH_INFO_TYPE	= 0x05,
+	NM_IPAC_EIE_BCCH_INFO		= 0x06,
+	NM_IPAC_EIE_CONFIG		= 0x07,
+	NM_IPAC_EIE_RES_DETAILS		= 0x08,
+	NM_IPAC_EIE_RXLEV_THRESH	= 0x09,
+	NM_IPAC_EIE_FREQ_SYNC_OPTS	= 0x0a,
+	NM_IPAC_EIE_MAC_ADDR		= 0x0b,
+	NM_IPAC_EIE_HW_SW_COMPAT_NR	= 0x0c,
+	NM_IPAC_EIE_MANUF_SER_NR	= 0x0d,
+	NM_IPAC_EIE_OEM_ID		= 0x0e,
+	NM_IPAC_EIE_DATE_TIME_MANUF	= 0x0f,
+	NM_IPAC_EIE_DATE_TIME_CALIB	= 0x10,
+	NM_IPAC_EIE_BEACON_INFO		= 0x11,
+	NM_IPAC_EIE_FREQ_ERR		= 0x12,
+	/* FIXME */
+	NM_IPAC_EIE_FREQ_BANDS		= 0x1c,
+	NM_IPAC_EIE_MAX_TA		= 0x1d,
+	NM_IPAC_EIE_CIPH_ALGOS		= 0x1e,
+	NM_IPAC_EIE_CHAN_TYPES		= 0x1f,
+	NM_IPAC_EIE_CHAN_MODES		= 0x20,
+	NM_IPAC_EIE_GPRS_CODING		= 0x21,
+	NM_IPAC_EIE_RTP_FEATURES	= 0x22,
+	NM_IPAC_EIE_RSL_FEATURES	= 0x23,
+	NM_IPAC_EIE_BTS_HW_CLASS	= 0x24,
+	NM_IPAC_EIE_BTS_ID		= 0x25,
+};
+
+enum ipac_bcch_info_type {
+	IPAC_BINF_RXLEV			= (1 << 8),
+	IPAC_BINF_RXQUAL		= (1 << 9),
+	IPAC_BINF_FREQ_ERR_QUAL		= (1 << 10),
+	IPAC_BINF_FRAME_OFFSET		= (1 << 11),
+	IPAC_BINF_FRAME_NR_OFFSET	= (1 << 12),
+	IPAC_BINF_BSIC			= (1 << 13),
+	IPAC_BINF_CGI			= (1 << 14),
+	IPAC_BINF_NEIGH_BA_SI2		= (1 << 15),
+	IPAC_BINF_NEIGH_BA_SI2bis	= (1 << 0),
+	IPAC_BINF_NEIGH_BA_SI2ter	= (1 << 1),
+	IPAC_BINF_CELL_ALLOC		= (1 << 2),
+};
+
+#endif /* PROTO_GSM_12_21_H */
diff --git a/libosmocore/include/osmocore/rsl.h b/libosmocore/include/osmocore/rsl.h
new file mode 100644
index 0000000..99b90d6
--- /dev/null
+++ b/libosmocore/include/osmocore/rsl.h
@@ -0,0 +1,32 @@
+#ifndef _OSMOCORE_RSL_H
+#define _OSMOCORE_RSL_H
+
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/protocol/gsm_08_58.h>
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type);
+
+extern const struct tlv_definition rsl_att_tlvdef;
+#define rsl_tlv_parse(dec, buf, len)     \
+			tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
+
+/* encode channel number as per Section 9.3.1 */
+uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
+/* decode channel number as per Section 9.3.1 */
+int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot);
+
+const char *rsl_err_name(uint8_t err);
+const char *rsl_rlm_cause_name(uint8_t err);
+
+/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
+
+/* Push a RSL RLL header with L3_INFO IE */
+void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
+		     uint8_t link_id, int transparent);
+
+/* Allocate msgb and fill with simple RSL RLL header */
+struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
+			    uint8_t link_id, int transparent);
+#endif /* _OSMOCORE_RSL_H */
diff --git a/libosmocore/include/osmocore/rxlev_stat.h b/libosmocore/include/osmocore/rxlev_stat.h
new file mode 100644
index 0000000..415509d
--- /dev/null
+++ b/libosmocore/include/osmocore/rxlev_stat.h
@@ -0,0 +1,22 @@
+#ifndef _OSMOCORE_RXLEV_STATS_H
+#define _OSMOCORE_RXLEV_STATS_H
+
+#define NUM_RXLEVS 32
+#define NUM_ARFCNS 1024
+
+struct rxlev_stats {
+	/* the maximum number of ARFCN's is 1024, and there are 32 RxLevels,
+	 * so in we keep one 1024bit-bitvec for each RxLev */
+	uint8_t rxlev_buckets[NUM_RXLEVS][NUM_ARFCNS/8];
+};
+
+void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev);
+
+/* get the next ARFCN that has the specified Rxlev */
+int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn);
+
+void rxlev_stat_reset(struct rxlev_stats *st);
+
+void rxlev_stat_dump(const struct rxlev_stats *st);
+
+#endif /* _OSMOCORE_RXLEV_STATS_H */
diff --git a/libosmocore/include/osmocore/select.h b/libosmocore/include/osmocore/select.h
new file mode 100644
index 0000000..2d8b3ec
--- /dev/null
+++ b/libosmocore/include/osmocore/select.h
@@ -0,0 +1,22 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include "linuxlist.h"
+
+#define BSC_FD_READ	0x0001
+#define BSC_FD_WRITE	0x0002
+#define BSC_FD_EXCEPT	0x0004
+
+struct bsc_fd {
+	struct llist_head list;
+	int fd;
+	unsigned int when;
+	int (*cb)(struct bsc_fd *fd, unsigned int what);
+	void *data;
+	unsigned int priv_nr;
+};
+
+int bsc_register_fd(struct bsc_fd *fd);
+void bsc_unregister_fd(struct bsc_fd *fd);
+int bsc_select_main(int polling);
+#endif /* _BSC_SELECT_H */
diff --git a/libosmocore/include/osmocore/signal.h b/libosmocore/include/osmocore/signal.h
new file mode 100644
index 0000000..02d83d2
--- /dev/null
+++ b/libosmocore/include/osmocore/signal.h
@@ -0,0 +1,15 @@
+#ifndef OSMOCORE_SIGNAL_H
+#define OSMOCORE_SIGNAL_H
+
+typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
+			void *handler_data, void *signal_data);
+
+
+/* Management */
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
+
+#endif /* OSMOCORE_SIGNAL_H */
diff --git a/libosmocore/include/osmocore/statistics.h b/libosmocore/include/osmocore/statistics.h
new file mode 100644
index 0000000..1d56054
--- /dev/null
+++ b/libosmocore/include/osmocore/statistics.h
@@ -0,0 +1,31 @@
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
+struct counter {
+	struct llist_head list;
+	const char *name;
+	const char *description;
+	unsigned long value;
+};
+
+static inline void counter_inc(struct counter *ctr)
+{
+	ctr->value++;
+}
+
+static inline unsigned long counter_get(struct counter *ctr)
+{
+	return ctr->value;
+}
+
+static inline void counter_reset(struct counter *ctr)
+{
+	ctr->value = 0;
+}
+
+struct counter *counter_alloc(const char *name);
+void counter_free(struct counter *ctr);
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
+
+#endif /* _STATISTICS_H */
diff --git a/libosmocore/include/osmocore/talloc.h b/libosmocore/include/osmocore/talloc.h
new file mode 100644
index 0000000..f7f7643
--- /dev/null
+++ b/libosmocore/include/osmocore/talloc.h
@@ -0,0 +1,192 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+
+   Copyright (C) Andrew Tridgell 2004-2005
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define HAVE_VA_COPY
+
+/* this is only needed for compatibility with the old talloc */
+typedef void TALLOC_CTX;
+
+/*
+  this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s)    #s
+#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+/* try to make talloc_set_destructor() and talloc_steal() type safe,
+   if we have a recent gcc */
+#if (__GNUC__ >= 3)
+#define _TALLOC_TYPEOF(ptr) __typeof__(ptr)
+#define talloc_set_destructor(ptr, function)				      \
+	do {								      \
+		int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function);	      \
+		_talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \
+	} while(0)
+/* this extremely strange macro is to avoid some braindamaged warning
+   stupidity in gcc 4.1.x */
+#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; })
+#else
+#define talloc_set_destructor(ptr, function) \
+	_talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
+#endif
+
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
+#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+
+/* useful macros for creating type checked pointers */
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx))
+
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
+
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+
+#if TALLOC_DEPRECATED
+#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
+#define talloc_p(ctx, type) talloc(ctx, type)
+#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
+#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
+#define talloc_destroy(ctx) talloc_free(ctx)
+#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a))
+#endif
+
+#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
+
+/* The following definitions come from talloc.c  */
+void *_talloc(const void *context, size_t size);
+void *talloc_pool(const void *context, size_t size);
+void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
+int talloc_increase_ref_count(const void *ptr);
+size_t talloc_reference_count(const void *ptr);
+void *_talloc_reference(const void *context, const void *ptr);
+int talloc_unlink(const void *context, void *ptr);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+void talloc_set_name_const(const void *ptr, const char *name);
+void *talloc_named(const void *context, size_t size, 
+		   const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+void *talloc_named_const(const void *context, size_t size, const char *name);
+const char *talloc_get_name(const void *ptr);
+void *talloc_check_name(const void *ptr, const char *name);
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location);
+void *talloc_parent(const void *ptr);
+const char *talloc_parent_name(const void *ptr);
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+int talloc_free(void *ptr);
+void talloc_free_children(void *ptr);
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+void *_talloc_steal(const void *new_ctx, const void *ptr);
+void *_talloc_move(const void *new_ctx, const void *pptr);
+size_t talloc_total_size(const void *ptr);
+size_t talloc_total_blocks(const void *ptr);
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data);
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+void talloc_report_full(const void *ptr, FILE *f);
+void talloc_report(const void *ptr, FILE *f);
+void talloc_enable_null_tracking(void);
+void talloc_disable_null_tracking(void);
+void talloc_enable_leak_report(void);
+void talloc_enable_leak_report_full(void);
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+void *talloc_autofree_context(void);
+size_t talloc_get_size(const void *ctx);
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+void talloc_show_parents(const void *context, FILE *file);
+int talloc_is_parent(const void *context, const void *ptr);
+
+char *talloc_strdup(const void *t, const char *p);
+char *talloc_strdup_append(char *s, const char *a);
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+char *talloc_strndup(const void *t, const char *p, size_t n);
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason));
+
+#endif
diff --git a/libosmocore/include/osmocore/timer.h b/libosmocore/include/osmocore/timer.h
new file mode 100644
index 0000000..fee888b
--- /dev/null
+++ b/libosmocore/include/osmocore/timer.h
@@ -0,0 +1,72 @@
+/*
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include "linuxlist.h"
+
+/**
+ * Timer management:
+ *      - Create a struct timer_list
+ *      - Fill out timeout and use add_timer or
+ *        use schedule_timer to schedule a timer in
+ *        x seconds and microseconds from now...
+ *      - Use del_timer to remove the timer
+ *
+ *  Internally:
+ *      - We hook into select.c to give a timeval of the
+ *        nearest timer. On already passed timers we give
+ *        it a 0 to immediately fire after the select
+ *      - update_timers will call the callbacks and remove
+ *        the timers.
+ *
+ */
+struct timer_list {
+	struct llist_head entry;
+	struct timeval timeout;
+	unsigned int active  : 1;
+	unsigned int handled : 1;
+	unsigned int in_list : 1;
+
+	void (*cb)(void*);
+	void *data;
+};
+
+/**
+ * timer management
+ */
+void bsc_add_timer(struct timer_list *timer);
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
+void bsc_del_timer(struct timer_list *timer);
+int bsc_timer_pending(struct timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
+struct timeval *bsc_nearest_timer();
+void bsc_prepare_timers();
+int bsc_update_timers();
+int bsc_timer_check(void);
+
+#endif
diff --git a/libosmocore/include/osmocore/tlv.h b/libosmocore/include/osmocore/tlv.h
new file mode 100644
index 0000000..c733dbc
--- /dev/null
+++ b/libosmocore/include/osmocore/tlv.h
@@ -0,0 +1,244 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocore/msgb.h>
+
+/* Terminology / wording
+		tag	length		value	(in bits)
+
+	    V	-	-		8
+	   LV	-	8		N * 8
+	  TLV	8	8		N * 8
+	TL16V	8	16		N * 8
+	TLV16	8	8		N * 16
+	 TvLV	8	8/16		N * 8
+
+*/
+
+#define LV_GROSS_LEN(x)		(x+1)
+#define TLV_GROSS_LEN(x)	(x+2)
+#define TLV16_GROSS_LEN(x)	((2*x)+2)
+#define TL16V_GROSS_LEN(x)	(x+3)
+#define L16TV_GROSS_LEN(x)	(x+3)
+
+#define TVLV_MAX_ONEBYTE	0x7f
+
+static inline uint16_t TVLV_GROSS_LEN(uint16_t len)
+{
+	if (len <= TVLV_MAX_ONEBYTE)
+		return TLV_GROSS_LEN(len);
+	else
+		return TL16V_GROSS_LEN(len);
+}
+
+/* TLV generation */
+
+static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,
+				const uint8_t *val)
+{
+	*buf++ = len;
+	memcpy(buf, val, len);
+	return buf + len;
+}
+
+static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,
+				const uint8_t *val)
+{
+	*buf++ = tag;
+	*buf++ = len;
+	memcpy(buf, val, len);
+	return buf + len;
+}
+
+static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,
+				const uint16_t *val)
+{
+	*buf++ = tag;
+	*buf++ = len;
+	memcpy(buf, val, len*2);
+	return buf + len*2;
+}
+
+static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,
+				const uint8_t *val)
+{
+	*buf++ = tag;
+	*buf++ = len >> 8;
+	*buf++ = len & 0xff;
+	memcpy(buf, val, len);
+	return buf + len*2;
+}
+
+static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,
+				 const uint8_t *val)
+{
+	uint8_t *ret;
+
+	if (len <= TVLV_MAX_ONEBYTE) {
+		ret = tlv_put(buf, tag, len, val);
+		buf[1] |= 0x80;
+	} else
+		ret = tl16v_put(buf, tag, len, val);
+
+	return ret;
+}
+
+static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)
+{
+	uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
+	return tlv16_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,
+					const uint8_t *val)
+{
+	uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));
+	return tl16v_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,
+				      const uint8_t *val)
+{
+	uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));
+	return tvlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
+                                       const uint8_t *val)
+{
+	uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));
+
+	*buf++ = len >> 8;
+	*buf++ = len & 0xff;
+	*buf++ = tag;
+	memcpy(buf, val, len);
+	return buf + len;
+}
+
+static inline uint8_t *v_put(uint8_t *buf, uint8_t val)
+{
+	*buf++ = val;
+	return buf;
+}
+
+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, 
+				uint8_t val)
+{
+	*buf++ = tag;
+	*buf++ = val;
+	return buf;
+}
+
+/* 'val' is still in host byte order! */
+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, 
+				 uint16_t val)
+{
+	*buf++ = tag;
+	*buf++ = val >> 8;
+	*buf++ = val & 0xff;
+	return buf;
+}
+
+static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)
+{
+	uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));
+	return lv_put(buf, len, val);
+}
+
+static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)
+{
+	uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
+	return tlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)
+{
+	uint8_t *buf = msgb_put(msg, 2);
+	return tv_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)
+{
+	uint8_t *buf = msgb_put(msg, 1);
+	return v_put(buf, val);
+}
+
+static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+	uint8_t *buf = msgb_put(msg, 3);
+	return tv16_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)
+{
+	uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
+	return tlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)
+{
+	uint8_t *buf = msgb_push(msg, 2);
+	return tv_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+	uint8_t *buf = msgb_push(msg, 3);
+	return tv16_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,
+				      const uint8_t *val)
+{
+	uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));
+	return tvlv_put(buf, tag, len, val);
+}
+
+/* TLV parsing */
+
+struct tlv_p_entry {
+	uint16_t len;
+	const uint8_t *val;
+};
+
+enum tlv_type {
+	TLV_TYPE_NONE,
+	TLV_TYPE_FIXED,
+	TLV_TYPE_T,
+	TLV_TYPE_TV,
+	TLV_TYPE_TLV,
+	TLV_TYPE_TL16V,
+	TLV_TYPE_TvLV,
+};
+
+struct tlv_def {
+	enum tlv_type type;
+	uint8_t fixed_len;
+};
+
+struct tlv_definition {
+	struct tlv_def def[0xff];
+};
+
+struct tlv_parsed {
+	struct tlv_p_entry lv[0xff];
+};
+
+extern struct tlv_definition tvlv_att_def;
+
+int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
+                  const struct tlv_definition *def,
+                  const uint8_t *buf, int buf_len);
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
+	      const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);
+/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
+
+#define TLVP_PRESENT(x, y)	((x)->lv[y].val)
+#define TLVP_LEN(x, y)		(x)->lv[y].len
+#define TLVP_VAL(x, y)		(x)->lv[y].val
+
+#endif /* _TLV_H */
diff --git a/libosmocore/include/osmocore/utils.h b/libosmocore/include/osmocore/utils.h
new file mode 100644
index 0000000..51c6f03
--- /dev/null
+++ b/libosmocore/include/osmocore/utils.h
@@ -0,0 +1,20 @@
+#ifndef OSMOCORE_UTIL_H
+#define OSMOCORE_UTIL_H
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#include <stdint.h>
+
+struct value_string {
+	unsigned int value;
+	const char *str;
+};
+
+const char *get_value_string(const struct value_string *vs, uint32_t val);
+int get_string_value(const struct value_string *vs, const char *str);
+
+char bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
+uint8_t char2bcd(char c);
+
+#endif
diff --git a/libosmocore/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h
new file mode 100644
index 0000000..64d4159
--- /dev/null
+++ b/libosmocore/include/osmocore/write_queue.h
@@ -0,0 +1,45 @@
+/* Generic write queue implementation */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef write_queue_h
+#define write_queue_h
+
+#include "select.h"
+#include "msgb.h"
+
+struct write_queue {
+	struct bsc_fd bfd;
+	unsigned int max_length;
+	unsigned int current_length;
+
+	struct llist_head msg_queue;
+
+	int (*read_cb)(struct bsc_fd *fd);
+	int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
+};
+
+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);
+
+#endif
diff --git a/libosmocore/libosmocore.pc.in b/libosmocore/libosmocore.pc.in
new file mode 100644
index 0000000..7c29869
--- /dev/null
+++ b/libosmocore/libosmocore.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom Core Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmocore
+Cflags: -I${includedir}/
+
diff --git a/libosmocore/m4/DUMMY b/libosmocore/m4/DUMMY
new file mode 100644
index 0000000..fda557a
--- /dev/null
+++ b/libosmocore/m4/DUMMY
@@ -0,0 +1 @@
+Dummply placeholder.
diff --git a/libosmocore/src/Makefile.am b/libosmocore/src/Makefile.am
new file mode 100644
index 0000000..1697807
--- /dev/null
+++ b/libosmocore/src/Makefile.am
@@ -0,0 +1,17 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
+LIBVERSION=0:0:0
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -fPIC -Wall
+
+lib_LTLIBRARIES = libosmocore.la
+
+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 \
+			 logging.c
+
+if ENABLE_TALLOC
+libosmocore_la_SOURCES += talloc.c
+endif
diff --git a/libosmocore/src/bitvec.c b/libosmocore/src/bitvec.c
new file mode 100644
index 0000000..eb83ac6
--- /dev/null
+++ b/libosmocore/src/bitvec.c
@@ -0,0 +1,170 @@
+/* bit vector utility routines */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.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 <errno.h>
+#include <stdint.h>
+
+#include <osmocore/bitvec.h>
+
+#define BITNUM_FROM_COMP(byte, bit)	((byte*8)+bit)
+
+static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
+{
+	unsigned int bytenum = bitnum / 8;
+
+	return bytenum;
+}
+
+/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
+static uint8_t bitval2mask(enum bit_value bit, uint8_t bitnum)
+{
+	int bitval;
+
+	switch (bit) {
+	case ZERO:
+		bitval = (0 << bitnum);
+		break;
+	case ONE:
+		bitval = (1 << bitnum);
+		break;
+	case L:
+		bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
+		break;
+	case H:
+		bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
+		break;
+	default:
+		return 0;
+	}
+	return bitval;
+}
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr)
+{
+	unsigned int bytenum = bytenum_from_bitnum(bitnr);
+	unsigned int bitnum = 7 - (bitnr % 8);
+	uint8_t bitval;
+
+	if (bytenum >= bv->data_len)
+		return -EINVAL;
+
+	bitval = bitval2mask(ONE, bitnum);
+
+	if (bv->data[bytenum] & bitval)
+		return ONE;
+
+	return ZERO;
+}
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n)
+{
+	unsigned int i, k = 0;
+
+	for (i = 0; i < bv->data_len*8; i++) {
+		if (bitvec_get_bit_pos(bv, i) == ONE) {
+			k++;
+			if (k == n)
+				return i;
+		}
+	}
+
+	return 0;
+}
+
+/* set the bit at a given position inside a bitvec */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
+			enum bit_value bit)
+{
+	unsigned int bytenum = bytenum_from_bitnum(bitnr);
+	unsigned int bitnum = 7 - (bitnr % 8);
+	uint8_t bitval;
+
+	if (bytenum >= bv->data_len)
+		return -EINVAL;
+
+	/* first clear the bit */
+	bitval = bitval2mask(ONE, bitnum);
+	bv->data[bytenum] &= ~bitval;
+
+	/* then set it to desired value */
+	bitval = bitval2mask(bit, bitnum);
+	bv->data[bytenum] |= bitval;
+
+	return 0;
+}
+
+/* set the next bit inside a bitvec */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
+{
+	int rc;
+
+	rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
+	if (!rc)
+		bv->cur_bit++;
+
+	return rc;
+}
+
+/* set multiple bits (based on array of bitvals) at current pos */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
+{
+	int i, rc;
+
+	for (i = 0; i < count; i++) {
+		rc = bitvec_set_bit(bv, bits[i]);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+/* set multiple bits (based on numeric value) at current pos */
+int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
+{
+	int i, rc;
+
+	for (i = 0; i < num_bits; i++) {
+		int bit = 0;
+		if (ui & (1 << (num_bits - i - 1)))
+			bit = 1;
+		rc = bitvec_set_bit(bv, bit);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+/* pad all remaining bits up to num_bits */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+{
+	unsigned int i;
+
+	for (i = bv->cur_bit; i <= up_to_bit; i++)
+		bitvec_set_bit(bv, L);
+
+	return 0;
+}
diff --git a/libosmocore/src/comp128.c b/libosmocore/src/comp128.c
new file mode 100644
index 0000000..5d5680c
--- /dev/null
+++ b/libosmocore/src/comp128.c
@@ -0,0 +1,230 @@
+/*
+ * COMP128 implementation
+ *
+ *
+ * This code is inspired by original code from :
+ *  Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>,
+ *  and David Wagner <daw@cs.berkeley.edu>
+ *
+ * But it has been fully rewritten from various PDFs found online describing
+ * the algorithm because the licence of the code referenced above was unclear.
+ * A comment snippet from the original code is included below, it describes
+ * where the doc came from and how the algorithm was reverse engineered.
+ *
+ *
+ * (C) 2009 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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.
+ *
+ */
+
+/*
+ * --- SNIP ---
+ *
+ * This code derived from a leaked document from the GSM standards.
+ * Some missing pieces were filled in by reverse-engineering a working SIM.
+ * We have verified that this is the correct COMP128 algorithm.
+ *
+ * The first page of the document identifies it as
+ * 	_Technical Information: GSM System Security Study_.
+ * 	10-1617-01, 10th June 1988.
+ * The bottom of the title page is marked
+ * 	Racal Research Ltd.
+ * 	Worton Drive, Worton Grange Industrial Estate,
+ * 	Reading, Berks. RG2 0SB, England.
+ * 	Telephone: Reading (0734) 868601   Telex: 847152
+ * The relevant bits are in Part I, Section 20 (pages 66--67).  Enjoy!
+ *
+ * Note: There are three typos in the spec (discovered by
+ * reverse-engineering).
+ * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read
+ * "z = (2 * x[m] + x[n]) mod 2^(9-j)".
+ * Second, the "k" loop in the "Form bits from bytes" section is severely
+ * botched: the k index should run only from 0 to 3, and clearly the range
+ * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8,
+ * to be consistent with the subsequent section).
+ * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as
+ * claimed in the document.  (And the document doesn't specify how Kc is
+ * derived, but that was also easily discovered with reverse engineering.)
+ * All of these typos have been corrected in the following code.
+ *
+ * --- /SNIP ---
+ */
+
+#include <string.h>
+#include <stdint.h>
+
+/* The compression tables (just copied ...) */
+static const uint8_t table_0[512] = {
+ 102, 177, 186, 162,   2, 156, 112,  75,  55,  25,   8,  12, 251, 193, 246, 188,
+ 109, 213, 151,  53,  42,  79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161,
+ 252,  37, 244,  47,  64, 211,   6, 237, 185, 160, 139, 113,  76, 138,  59,  70,
+  67,  26,  13, 157,  63, 179, 221,  30, 214,  36, 166,  69, 152, 124, 207, 116,
+ 247, 194,  41,  84,  71,   1,  49,  14,  95,  35, 169,  21,  96,  78, 215, 225,
+ 182, 243,  28,  92, 201, 118,   4,  74, 248, 128,  17,  11, 146, 132, 245,  48,
+ 149,  90, 120,  39,  87, 230, 106, 232, 175,  19, 126, 190, 202, 141, 137, 176,
+ 250,  27, 101,  40, 219, 227,  58,  20,  51, 178,  98, 216, 140,  22,  32, 121,
+  61, 103, 203,  72,  29, 110,  85, 212, 180, 204, 150, 183,  15,  66, 172, 196,
+  56, 197, 158,   0, 100,  45, 153,   7, 144, 222, 163, 167,  60, 135, 210, 231,
+ 174, 165,  38, 249, 224,  34, 220, 229, 217, 208, 241,  68, 206, 189, 125, 255,
+ 239,  54, 168,  89, 123, 122,  73, 145, 117, 234, 143,  99, 129, 200, 192,  82,
+ 104, 170, 136, 235,  93,  81, 205, 173, 236,  94, 105,  52,  46, 228, 198,   5,
+  57, 254,  97, 155, 142, 133, 199, 171, 187,  50,  65, 181, 127, 107, 147, 226,
+ 184, 218, 131,  33,  77,  86,  31,  44,  88,  62, 238,  18,  24,  43, 154,  23,
+  80, 159, 134, 111,   9, 114,   3,  91,  16, 130,  83,  10, 195, 240, 253, 119,
+ 177, 102, 162, 186, 156,   2,  75, 112,  25,  55,  12,   8, 193, 251, 188, 246,
+ 213, 109,  53, 151,  79,  42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108,
+  37, 252,  47, 244, 211,  64, 237,   6, 160, 185, 113, 139, 138,  76,  70,  59,
+  26,  67, 157,  13, 179,  63,  30, 221,  36, 214,  69, 166, 124, 152, 116, 207,
+ 194, 247,  84,  41,   1,  71,  14,  49,  35,  95,  21, 169,  78,  96, 225, 215,
+ 243, 182,  92,  28, 118, 201,  74,   4, 128, 248,  11,  17, 132, 146,  48, 245,
+  90, 149,  39, 120, 230,  87, 232, 106,  19, 175, 190, 126, 141, 202, 176, 137,
+  27, 250,  40, 101, 227, 219,  20,  58, 178,  51, 216,  98,  22, 140, 121,  32,
+ 103,  61,  72, 203, 110,  29, 212,  85, 204, 180, 183, 150,  66,  15, 196, 172,
+ 197,  56,   0, 158,  45, 100,   7, 153, 222, 144, 167, 163, 135,  60, 231, 210,
+ 165, 174, 249,  38,  34, 224, 229, 220, 208, 217,  68, 241, 189, 206, 255, 125,
+  54, 239,  89, 168, 122, 123, 145,  73, 234, 117,  99, 143, 200, 129,  82, 192,
+ 170, 104, 235, 136,  81,  93, 173, 205,  94, 236,  52, 105, 228,  46,   5, 198,
+ 254,  57, 155,  97, 133, 142, 171, 199,  50, 187, 181,  65, 107, 127, 226, 147,
+ 218, 184,  33, 131,  86,  77,  44,  31,  62,  88,  18, 238,  43,  24,  23, 154,
+ 159,  80, 111, 134, 114,   9,  91,   3, 130,  16,  10,  83, 240, 195, 119, 253,
+}, table_1[256] = {
+  19,  11,  80, 114,  43,   1,  69,  94,  39,  18, 127, 117,  97,   3,  85,  43,
+  27, 124,  70,  83,  47,  71,  63,  10,  47,  89,  79,   4,  14,  59,  11,   5,
+  35, 107, 103,  68,  21,  86,  36,  91,  85, 126,  32,  50, 109,  94, 120,   6,
+  53,  79,  28,  45,  99,  95,  41,  34,  88,  68,  93,  55, 110, 125, 105,  20,
+  90,  80,  76,  96,  23,  60,  89,  64, 121,  56,  14,  74, 101,   8,  19,  78,
+  76,  66, 104,  46, 111,  50,  32,   3,  39,   0,  58,  25,  92,  22,  18,  51,
+  57,  65, 119, 116,  22, 109,   7,  86,  59,  93,  62, 110,  78,  99,  77,  67,
+  12, 113,  87,  98, 102,   5,  88,  33,  38,  56,  23,   8,  75,  45,  13,  75,
+  95,  63,  28,  49, 123, 120,  20, 112,  44,  30,  15,  98, 106,   2, 103,  29,
+  82, 107,  42, 124,  24,  30,  41,  16, 108, 100, 117,  40,  73,  40,   7, 114,
+  82, 115,  36, 112,  12, 102, 100,  84,  92,  48,  72,  97,   9,  54,  55,  74,
+ 113, 123,  17,  26,  53,  58,   4,   9,  69, 122,  21, 118,  42,  60,  27,  73,
+ 118, 125,  34,  15,  65, 115,  84,  64,  62,  81,  70,   1,  24, 111, 121,  83,
+ 104,  81,  49, 127,  48, 105,  31,  10,   6,  91,  87,  37,  16,  54, 116, 126,
+  31,  38,  13,   0,  72, 106,  77,  61,  26,  67,  46,  29,  96,  37,  61,  52,
+ 101,  17,  44, 108,  71,  52,  66,  57,  33,  51,  25,  90,   2, 119, 122,  35,
+}, table_2[128] = {
+ 52,  50,  44,   6,  21,  49,  41,  59,  39,  51,  25,  32,  51,  47,  52,  43,
+ 37,   4,  40,  34,  61,  12,  28,   4,  58,  23,   8,  15,  12,  22,   9,  18,
+ 55,  10,  33,  35,  50,   1,  43,   3,  57,  13,  62,  14,   7,  42,  44,  59,
+ 62,  57,  27,   6,   8,  31,  26,  54,  41,  22,  45,  20,  39,   3,  16,  56,
+ 48,   2,  21,  28,  36,  42,  60,  33,  34,  18,   0,  11,  24,  10,  17,  61,
+ 29,  14,  45,  26,  55,  46,  11,  17,  54,  46,   9,  24,  30,  60,  32,   0,
+ 20,  38,   2,  30,  58,  35,   1,  16,  56,  40,  23,  48,  13,  19,  19,  27,
+ 31,  53,  47,  38,  63,  15,  49,   5,  37,  53,  25,  36,  63,  29,   5,   7,
+}, table_3[64] = {
+  1,   5,  29,   6,  25,   1,  18,  23,  17,  19,   0,   9,  24,  25,   6,  31,
+ 28,  20,  24,  30,   4,  27,   3,  13,  15,  16,  14,  18,   4,   3,   8,   9,
+ 20,   0,  12,  26,  21,   8,  28,   2,  29,   2,  15,   7,  11,  22,  14,  10,
+ 17,  21,  12,  30,  26,  27,  16,  31,  11,   7,  13,  23,  10,   5,  22,  19,
+}, table_4[32] = {
+ 15,  12,  10,   4,   1,  14,  11,   7,   5,   0,  14,   7,   1,   2,  13,   8,
+ 10,   3,   4,   9,   6,   0,   3,   2,   5,   6,   8,   9,  11,  13,  15,  12,
+};
+
+static const uint8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 };
+
+
+static inline void
+_comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl)
+{
+	int i, j, m, a, b, y, z;
+	m = 4 - n;
+	for (i=0; i<(1<<n); i++)
+		for (j=0; j<(1<<m); j++) {
+			a = j + i * (2<<m);
+			b = a + (1<<m);
+			y = (x[a] + (x[b]<<1)) & ((32<<m)-1);
+			z = ((x[a]<<1) + x[b]) & ((32<<m)-1);
+			x[a] = tbl[y];
+			x[b] = tbl[z];
+		}
+}
+
+static inline void
+_comp128_compression(uint8_t *x)
+{
+	int n;
+	for (n=0; n<5; n++)
+		_comp128_compression_round(x, n, _comp128_table[n]);
+}
+
+static inline void
+_comp128_bitsfrombytes(uint8_t *x, uint8_t *bits)
+{
+	int i;
+	memset(bits, 0x00, 128);
+	for (i=0; i<128; i++)
+		if (x[i>>2] & (1<<(3-(i&3))))
+			bits[i] = 1;
+}
+
+static inline void
+_comp128_permutation(uint8_t *x, uint8_t *bits)
+{
+	int i;
+	memset(&x[16], 0x00, 16);
+	for (i=0; i<128; i++)
+		x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7));
+}
+
+void
+comp128(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc)
+{
+	int i;
+	uint8_t x[32], bits[128];
+
+	/* x[16-31] = RAND */
+	memcpy(&x[16], rand, 16);
+
+	/* Round 1-7 */
+	for (i=0; i<7; i++) {
+		/* x[0-15] = Ki */
+		memcpy(x, ki, 16);
+
+		/* Compression */
+		_comp128_compression(x);
+
+		/* FormBitFromBytes */
+		_comp128_bitsfrombytes(x, bits);
+
+		/* Permutation */
+		_comp128_permutation(x, bits);
+	}
+
+	/* Round 8 (final) */
+		/* x[0-15] = Ki */
+	memcpy(x, ki, 16);
+
+		/* Compression */
+	_comp128_compression(x);
+
+	/* Output stage */
+	for (i=0; i<8; i+=2)
+		sres[i>>1] = x[i]<<4 | x[i+1];
+
+	for (i=0; i<12; i+=2)
+		kc[i>>1] = (x[i + 18] << 6) |
+		           (x[i + 19] << 2) |
+		           (x[i + 20] >> 2);
+
+	kc[6] = (x[30]<<6) | (x[31]<<2);
+	kc[7] = 0;
+}
+
diff --git a/libosmocore/src/gsm48.c b/libosmocore/src/gsm48.c
new file mode 100644
index 0000000..5761c67
--- /dev/null
+++ b/libosmocore/src/gsm48.c
@@ -0,0 +1,263 @@
+/* GSM Mobile Radio Interface Layer 3 messages
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <osmocore/utils.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm48.h>
+
+#include <osmocore/protocol/gsm_04_08.h>
+
+const struct tlv_definition gsm48_att_tlvdef = {
+	.def = {
+		[GSM48_IE_MOBILE_ID]	= { TLV_TYPE_TLV },
+		[GSM48_IE_NAME_LONG]	= { TLV_TYPE_TLV },
+		[GSM48_IE_NAME_SHORT]	= { TLV_TYPE_TLV },
+		[GSM48_IE_UTC]		= { TLV_TYPE_TV },
+		[GSM48_IE_NET_TIME_TZ]	= { TLV_TYPE_FIXED, 7 },
+		[GSM48_IE_LSA_IDENT]	= { TLV_TYPE_TLV },
+
+		[GSM48_IE_BEARER_CAP]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CAUSE]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CC_CAP]	= { TLV_TYPE_TLV },
+		[GSM48_IE_ALERT]	= { TLV_TYPE_TLV },
+		[GSM48_IE_FACILITY]	= { TLV_TYPE_TLV },
+		[GSM48_IE_PROGR_IND]	= { TLV_TYPE_TLV },
+		[GSM48_IE_AUX_STATUS]	= { TLV_TYPE_TLV },
+		[GSM48_IE_NOTIFY]	= { TLV_TYPE_TV },
+		[GSM48_IE_KPD_FACILITY]	= { TLV_TYPE_TV },
+		[GSM48_IE_SIGNAL]	= { TLV_TYPE_TV },
+		[GSM48_IE_CONN_BCD]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CONN_SUB]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CALLING_BCD]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CALLING_SUB]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CALLED_BCD]	= { TLV_TYPE_TLV },
+		[GSM48_IE_CALLED_SUB]	= { TLV_TYPE_TLV },
+		[GSM48_IE_REDIR_BCD]	= { TLV_TYPE_TLV },
+		[GSM48_IE_REDIR_SUB]	= { TLV_TYPE_TLV },
+		[GSM48_IE_LOWL_COMPAT]	= { TLV_TYPE_TLV },
+		[GSM48_IE_HIGHL_COMPAT]	= { TLV_TYPE_TLV },
+		[GSM48_IE_USER_USER]	= { TLV_TYPE_TLV },
+		[GSM48_IE_SS_VERS]	= { TLV_TYPE_TLV },
+		[GSM48_IE_MORE_DATA]	= { TLV_TYPE_T },
+		[GSM48_IE_CLIR_SUPP]	= { TLV_TYPE_T },
+		[GSM48_IE_CLIR_INVOC]	= { TLV_TYPE_T },
+		[GSM48_IE_REV_C_SETUP]	= { TLV_TYPE_T },
+		[GSM48_IE_REPEAT_CIR]   = { TLV_TYPE_T },
+		[GSM48_IE_REPEAT_SEQ]   = { TLV_TYPE_T },
+		/* FIXME: more elements */
+	},
+};
+
+static const struct value_string rr_cause_names[] = {
+	{ GSM48_RR_CAUSE_NORMAL,		"Normal event" },
+	{ GSM48_RR_CAUSE_ABNORMAL_UNSPEC,	"Abnormal release, unspecified" },
+	{ GSM48_RR_CAUSE_ABNORMAL_UNACCT,	"Abnormal release, channel unacceptable" },
+	{ GSM48_RR_CAUSE_ABNORMAL_TIMER,	"Abnormal release, timer expired" },
+	{ GSM48_RR_CAUSE_ABNORMAL_NOACT,	"Abnormal release, no activity on radio path" },
+	{ GSM48_RR_CAUSE_PREMPTIVE_REL,		"Preemptive release" },
+	{ GSM48_RR_CAUSE_HNDOVER_IMP,		"Handover impossible, timing advance out of range" },
+	{ GSM48_RR_CAUSE_CHAN_MODE_UNACCT,	"Channel mode unacceptable" },
+	{ GSM48_RR_CAUSE_FREQ_NOT_IMPL,		"Frequency not implemented" },
+	{ GSM48_RR_CAUSE_CALL_CLEARED,		"Call already cleared" },
+	{ GSM48_RR_CAUSE_SEMANT_INCORR,		"Semantically incorrect message" },
+	{ GSM48_RR_CAUSE_INVALID_MAND_INF,	"Invalid mandatory information" },
+	{ GSM48_RR_CAUSE_MSG_TYPE_N,		"Message type non-existant or not implemented" },
+	{ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT,	"Message type not compatible with protocol state" },
+	{ GSM48_RR_CAUSE_COND_IE_ERROR,		"Conditional IE error" },
+	{ GSM48_RR_CAUSE_NO_CELL_ALLOC_A,	"No cell allocation available" },
+	{ GSM48_RR_CAUSE_PROT_ERROR_UNSPC,	"Protocol error unspecified" },
+	{ 0,					NULL },
+};
+
+/* FIXME: convert to value_string */
+static const char *cc_state_names[32] = {
+	"NULL",
+	"INITIATED",
+	"illegal state 2",
+	"MO_CALL_PROC",
+	"CALL_DELIVERED",
+	"illegal state 5",
+	"CALL_PRESENT",
+	"CALL_RECEIVED",
+	"CONNECT_REQUEST",
+	"MO_TERM_CALL_CONF",
+	"ACTIVE",
+	"DISCONNECT_REQ",
+	"DISCONNECT_IND",
+	"illegal state 13",
+	"illegal state 14",
+	"illegal state 15",
+	"illegal state 16",
+	"illegal state 17",
+	"illegal state 18",
+	"RELEASE_REQ",
+	"illegal state 20",
+	"illegal state 21",
+	"illegal state 22",
+	"illegal state 23",
+	"illegal state 24",
+	"illegal state 25",
+	"MO_ORIG_MODIFY",
+	"MO_TERM_MODIFY",
+	"CONNECT_IND",
+	"illegal state 29",
+	"illegal state 30",
+	"illegal state 31",
+};
+
+const char *gsm48_cc_state_name(uint8_t state)
+{
+	if (state < ARRAY_SIZE(cc_state_names))
+		return cc_state_names[state];
+
+	return "invalid";
+}
+
+static const struct value_string cc_msg_names[] = {
+	{ GSM48_MT_CC_ALERTING,		"ALERTING" },
+	{ GSM48_MT_CC_CALL_PROC,	"CALL_PROC" },
+	{ GSM48_MT_CC_PROGRESS,		"PROGRESS" },
+	{ GSM48_MT_CC_ESTAB,		"ESTAB" },
+	{ GSM48_MT_CC_SETUP,		"SETUP" },
+	{ GSM48_MT_CC_ESTAB_CONF,	"ESTAB_CONF" },
+	{ GSM48_MT_CC_CONNECT,		"CONNECT" },
+	{ GSM48_MT_CC_CALL_CONF,	"CALL_CONF" },
+	{ GSM48_MT_CC_START_CC,		"START_CC" },
+	{ GSM48_MT_CC_RECALL,		"RECALL" },
+	{ GSM48_MT_CC_EMERG_SETUP,	"EMERG_SETUP" },
+	{ GSM48_MT_CC_CONNECT_ACK,	"CONNECT_ACK" },
+	{ GSM48_MT_CC_USER_INFO,	"USER_INFO" },
+	{ GSM48_MT_CC_MODIFY_REJECT,	"MODIFY_REJECT" },
+	{ GSM48_MT_CC_MODIFY,		"MODIFY" },
+	{ GSM48_MT_CC_HOLD,		"HOLD" },
+	{ GSM48_MT_CC_HOLD_ACK,		"HOLD_ACK" },
+	{ GSM48_MT_CC_HOLD_REJ,		"HOLD_REJ" },
+	{ GSM48_MT_CC_RETR,		"RETR" },
+	{ GSM48_MT_CC_RETR_ACK,		"RETR_ACK" },
+	{ GSM48_MT_CC_RETR_REJ,		"RETR_REJ" },
+	{ GSM48_MT_CC_MODIFY_COMPL,	"MODIFY_COMPL" },
+	{ GSM48_MT_CC_DISCONNECT,	"DISCONNECT" },
+	{ GSM48_MT_CC_RELEASE_COMPL,	"RELEASE_COMPL" },
+	{ GSM48_MT_CC_RELEASE,		"RELEASE" },
+	{ GSM48_MT_CC_STOP_DTMF,	"STOP_DTMF" },
+	{ GSM48_MT_CC_STOP_DTMF_ACK,	"STOP_DTMF_ACK" },
+	{ GSM48_MT_CC_STATUS_ENQ,	"STATUS_ENQ" },
+	{ GSM48_MT_CC_START_DTMF,	"START_DTMF" },
+	{ GSM48_MT_CC_START_DTMF_ACK,	"START_DTMF_ACK" },
+	{ GSM48_MT_CC_START_DTMF_REJ,	"START_DTMF_REJ" },
+	{ GSM48_MT_CC_CONG_CTRL,	"CONG_CTRL" },
+	{ GSM48_MT_CC_FACILITY,		"FACILITY" },
+	{ GSM48_MT_CC_STATUS,		"STATUS" },
+	{ GSM48_MT_CC_NOTIFY,		"NOTFIY" },
+	{ 0,				NULL }
+};
+
+const char *gsm48_cc_msg_name(uint8_t msgtype)
+{
+	return get_value_string(cc_msg_names, msgtype);
+}
+
+const char *rr_cause_name(uint8_t cause)
+{
+	return get_value_string(rr_cause_names, cause);
+}
+
+static void to_bcd(uint8_t *bcd, uint16_t val)
+{
+	bcd[2] = val % 10;
+	val = val / 10;
+	bcd[1] = val % 10;
+	val = val / 10;
+	bcd[0] = val % 10;
+	val = val / 10;
+}
+
+void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+			uint16_t mnc, uint16_t lac)
+{
+	uint8_t bcd[3];
+
+	to_bcd(bcd, mcc);
+	lai48->digits[0] = bcd[0] | (bcd[1] << 4);
+	lai48->digits[1] = bcd[2];
+
+	to_bcd(bcd, mnc);
+	/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
+#if 0
+	lai48->digits[1] |= bcd[2] << 4;
+	lai48->digits[2] = bcd[0] | (bcd[1] << 4);
+#else
+	lai48->digits[1] |= 0xf << 4;
+	lai48->digits[2] = bcd[1] | (bcd[2] << 4);
+#endif
+
+	lai48->lac = htons(lac);
+}
+
+int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
+{
+	uint32_t *tptr = (uint32_t *) &buf[3];
+
+	buf[0] = GSM48_IE_MOBILE_ID;
+	buf[1] = GSM48_TMSI_LEN;
+	buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
+	*tptr = htonl(tmsi);
+
+	return 7;
+}
+
+int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
+{
+	unsigned int length = strlen(imsi), i, off = 0;
+	uint8_t odd = (length & 0x1) == 1;
+
+	buf[0] = GSM48_IE_MOBILE_ID;
+	buf[2] = char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3);
+
+	/* if the length is even we will fill half of the last octet */
+	if (odd)
+		buf[1] = (length + 1) >> 1;
+	else
+		buf[1] = (length + 2) >> 1;
+
+	for (i = 1; i < buf[1]; ++i) {
+		uint8_t lower, upper;
+
+		lower = char2bcd(imsi[++off]);
+		if (!odd && off + 1 == length)
+			upper = 0x0f;
+		else
+			upper = char2bcd(imsi[++off]) & 0x0f;
+
+		buf[2 + i] = (upper << 4) | lower;
+	}
+
+	return 2 + buf[1];
+}
diff --git a/libosmocore/src/gsm48_ie.c b/libosmocore/src/gsm48_ie.c
new file mode 100644
index 0000000..4ca5fb8
--- /dev/null
+++ b/libosmocore/src/gsm48_ie.c
@@ -0,0 +1,659 @@
+/* GSM Mobile Radio Interface Layer 3 messages
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008-2010 by Andreas Eversberg
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocore/utils.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/mncc.h>
+#include <osmocore/protocol/gsm_04_08.h>
+
+static const char bcd_num_digits[] = {
+	'0', '1', '2', '3', '4', '5', '6', '7',
+	'8', '9', '*', '#', 'a', 'b', 'c', '\0'
+};
+
+/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
+int gsm48_decode_bcd_number(char *output, int output_len,
+			    const uint8_t *bcd_lv, int h_len)
+{
+	uint8_t in_len = bcd_lv[0];
+	int i;
+
+	for (i = 1 + h_len; i <= in_len; i++) {
+		/* lower nibble */
+		output_len--;
+		if (output_len <= 1)
+			break;
+		*output++ = bcd_num_digits[bcd_lv[i] & 0xf];
+
+		/* higher nibble */
+		output_len--;
+		if (output_len <= 1)
+			break;
+		*output++ = bcd_num_digits[bcd_lv[i] >> 4];
+	}
+	if (output_len >= 1)
+		*output++ = '\0';
+
+	return 0;
+}
+
+/* convert a single ASCII character to call-control BCD */
+static int asc_to_bcd(const char asc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
+		if (bcd_num_digits[i] == asc)
+			return i;
+	}
+	return -EINVAL;
+}
+
+/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
+int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
+		      int h_len, const char *input)
+{
+	int in_len = strlen(input);
+	int i;
+	uint8_t *bcd_cur = bcd_lv + 1 + h_len;
+
+	/* two digits per byte, plus type byte */
+	bcd_lv[0] = in_len/2 + h_len;
+	if (in_len % 2)
+		bcd_lv[0]++;
+
+	if (bcd_lv[0] > max_len)
+		return -EIO;
+
+	for (i = 0; i < in_len; i++) {
+		int rc = asc_to_bcd(input[i]);
+		if (rc < 0)
+			return rc;
+		if (i % 2 == 0)
+			*bcd_cur = rc;
+		else
+			*bcd_cur++ |= (rc << 4);
+	}
+	/* append padding nibble in case of odd length */
+	if (i % 2)
+		*bcd_cur++ |= 0xf0;
+
+	/* return how many bytes we used */
+	return (bcd_cur - bcd_lv);
+}
+
+/* decode 'bearer capability' */
+int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+			     const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+	int i, s;
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
+
+	/* octet 3 */
+	bcap->transfer = lv[1] & 0x07;
+	bcap->mode = (lv[1] & 0x08) >> 3;
+	bcap->coding = (lv[1] & 0x10) >> 4;
+	bcap->radio = (lv[1] & 0x60) >> 5;
+
+	if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+		i = 1;
+		s = 0;
+		while(!(lv[i] & 0x80)) {
+			i++; /* octet 3a etc */
+			if (in_len < i)
+				return 0;
+			bcap->speech_ver[s++] = lv[i] & 0x0f;
+			bcap->speech_ver[s] = -1; /* end of list */
+			if (i == 2) /* octet 3a */
+				bcap->speech_ctm = (lv[i] & 0x20) >> 5;
+			if (s == 7) /* maximum speech versions + end of list */
+				return 0;
+		}
+	} else {
+		i = 1;
+		while (!(lv[i] & 0x80)) {
+			i++; /* octet 3a etc */
+			if (in_len < i)
+				return 0;
+			/* ignore them */
+		}
+		/* FIXME: implement OCTET 4+ parsing */
+	}
+
+	return 0;
+}
+
+/* encode 'bearer capability' */
+int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
+			     const struct gsm_mncc_bearer_cap *bcap)
+{
+	uint8_t lv[32 + 1];
+	int i = 1, s;
+
+	lv[1] = bcap->transfer;
+	lv[1] |= bcap->mode << 3;
+	lv[1] |= bcap->coding << 4;
+	lv[1] |= bcap->radio << 5;
+
+	if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+		for (s = 0; bcap->speech_ver[s] >= 0; s++) {
+			i++; /* octet 3a etc */
+			lv[i] = bcap->speech_ver[s];
+			if (i == 2) /* octet 3a */
+				lv[i] |= bcap->speech_ctm << 5;
+		}
+		lv[i] |= 0x80; /* last IE of octet 3 etc */
+	} else {
+		/* FIXME: implement OCTET 4+ encoding */
+	}
+
+	lv[0] = i;
+	if (lv_only)
+		msgb_lv_put(msg, lv[0], lv+1);
+	else
+		msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'call control cap' */
+int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	/* octet 3 */
+	ccap->dtmf = lv[1] & 0x01;
+	ccap->pcp = (lv[1] & 0x02) >> 1;
+
+	return 0;
+}
+
+/* encode 'call control cap' */
+int gsm48_encode_cccap(struct msgb *msg,
+			const struct gsm_mncc_cccap *ccap)
+{
+	uint8_t lv[2];
+
+	lv[0] = 1;
+	lv[1] = 0;
+	if (ccap->dtmf)
+		lv [1] |= 0x01;
+	if (ccap->pcp)
+		lv [1] |= 0x02;
+
+	msgb_tlv_put(msg, GSM48_IE_CC_CAP, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'called party BCD number' */
+int gsm48_decode_called(struct gsm_mncc_number *called,
+			 const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	/* octet 3 */
+	called->plan = lv[1] & 0x0f;
+	called->type = (lv[1] & 0x70) >> 4;
+
+	/* octet 4..N */
+	gsm48_decode_bcd_number(called->number, sizeof(called->number), lv, 1);
+
+	return 0;
+}
+
+/* encode 'called party BCD number' */
+int gsm48_encode_called(struct msgb *msg,
+			 const struct gsm_mncc_number *called)
+{
+	uint8_t lv[18];
+	int ret;
+
+	/* octet 3 */
+	lv[1] = called->plan;
+	lv[1] |= called->type << 4;
+
+	/* octet 4..N, octet 2 */
+	ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number);
+	if (ret < 0)
+		return ret;
+
+	msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode callerid of various IEs */
+int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
+			 const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+	int i = 1;
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	/* octet 3 */
+	callerid->plan = lv[1] & 0x0f;
+	callerid->type = (lv[1] & 0x70) >> 4;
+
+	/* octet 3a */
+	if (!(lv[1] & 0x80)) {
+		callerid->screen = lv[2] & 0x03;
+		callerid->present = (lv[2] & 0x60) >> 5;
+		i = 2;
+	}
+
+	/* octet 4..N */
+	gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i);
+
+	return 0;
+}
+
+/* encode callerid of various IEs */
+int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+			   const struct gsm_mncc_number *callerid)
+{
+	uint8_t lv[max_len - 1];
+	int h_len = 1;
+	int ret;
+
+	/* octet 3 */
+	lv[1] = callerid->plan;
+	lv[1] |= callerid->type << 4;
+
+	if (callerid->present || callerid->screen) {
+		/* octet 3a */
+		lv[2] = callerid->screen;
+		lv[2] |= callerid->present << 5;
+		lv[2] |= 0x80;
+		h_len++;
+	} else
+		lv[1] |= 0x80;
+
+	/* octet 4..N, octet 2 */
+	ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
+	if (ret < 0)
+		return ret;
+
+	msgb_tlv_put(msg, ie, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'cause' */
+int gsm48_decode_cause(struct gsm_mncc_cause *cause,
+			const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+	int i;
+
+	if (in_len < 2)
+		return -EINVAL;
+
+	cause->diag_len = 0;
+
+	/* octet 3 */
+	cause->location = lv[1] & 0x0f;
+	cause->coding = (lv[1] & 0x60) >> 5;
+
+	i = 1;
+	if (!(lv[i] & 0x80)) {
+		i++; /* octet 3a */
+		if (in_len < i+1)
+			return 0;
+		cause->rec = 1;
+		cause->rec_val = lv[i] & 0x7f;
+	}
+	i++;
+
+	/* octet 4 */
+	cause->value = lv[i] & 0x7f;
+	i++;
+
+	if (in_len < i) /* no diag */
+		return 0;
+
+	if (in_len - (i-1) > 32) /* maximum 32 octets */
+		return 0;
+
+	/* octet 5-N */
+	memcpy(cause->diag, lv + i, in_len - (i-1));
+	cause->diag_len = in_len - (i-1);
+
+	return 0;
+}
+
+/* encode 'cause' */
+int gsm48_encode_cause(struct msgb *msg, int lv_only,
+			const struct gsm_mncc_cause *cause)
+{
+	uint8_t lv[32+4];
+	int i;
+
+	if (cause->diag_len > 32)
+		return -EINVAL;
+
+	/* octet 3 */
+	lv[1] = cause->location;
+	lv[1] |= cause->coding << 5;
+
+	i = 1;
+	if (cause->rec) {
+		i++; /* octet 3a */
+		lv[i] = cause->rec_val;
+	}
+	lv[i] |= 0x80; /* end of octet 3 */
+
+	/* octet 4 */
+	i++;
+	lv[i] = 0x80 | cause->value;
+
+	/* octet 5-N */
+	if (cause->diag_len) {
+		memcpy(lv + i, cause->diag, cause->diag_len);
+		i += cause->diag_len;
+	}
+
+	lv[0] = i;
+	if (lv_only)
+		msgb_lv_put(msg, lv[0], lv+1);
+	else
+		msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'calling number' */
+int gsm48_decode_calling(struct gsm_mncc_number *calling,
+			 const uint8_t *lv)
+{
+	return gsm48_decode_callerid(calling, lv);
+}
+
+/* encode 'calling number' */
+int gsm48_encode_calling(struct msgb *msg, 
+			  const struct gsm_mncc_number *calling)
+{
+	return gsm48_encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling);
+}
+
+/* decode 'connected number' */
+int gsm48_decode_connected(struct gsm_mncc_number *connected,
+			 const uint8_t *lv)
+{
+	return gsm48_decode_callerid(connected, lv);
+}
+
+/* encode 'connected number' */
+int gsm48_encode_connected(struct msgb *msg,
+			    const struct gsm_mncc_number *connected)
+{
+	return gsm48_encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected);
+}
+
+/* decode 'redirecting number' */
+int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+			 const uint8_t *lv)
+{
+	return gsm48_decode_callerid(redirecting, lv);
+}
+
+/* encode 'redirecting number' */
+int gsm48_encode_redirecting(struct msgb *msg,
+			      const struct gsm_mncc_number *redirecting)
+{
+	return gsm48_encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting);
+}
+
+/* decode 'facility' */
+int gsm48_decode_facility(struct gsm_mncc_facility *facility,
+			   const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	if (in_len > sizeof(facility->info))
+		return -EINVAL;
+
+	memcpy(facility->info, lv+1, in_len);
+	facility->len = in_len;
+
+	return 0;
+}
+
+/* encode 'facility' */
+int gsm48_encode_facility(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_facility *facility)
+{
+	uint8_t lv[GSM_MAX_FACILITY + 1];
+
+	if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
+		return -EINVAL;
+
+	memcpy(lv+1, facility->info, facility->len);
+	lv[0] = facility->len;
+	if (lv_only)
+		msgb_lv_put(msg, lv[0], lv+1);
+	else
+		msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'notify' */
+int gsm48_decode_notify(int *notify, const uint8_t *v)
+{
+	*notify = v[0] & 0x7f;
+
+	return 0;
+}
+
+/* encode 'notify' */
+int gsm48_encode_notify(struct msgb *msg, int notify)
+{
+	msgb_v_put(msg, notify | 0x80);
+
+	return 0;
+}
+
+/* decode 'signal' */
+int gsm48_decode_signal(int *signal, const uint8_t *v)
+{
+	*signal = v[0];
+
+	return 0;
+}
+
+/* encode 'signal' */
+int gsm48_encode_signal(struct msgb *msg, int signal)
+{
+	msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
+
+	return 0;
+}
+
+/* decode 'keypad' */
+int gsm48_decode_keypad(int *keypad, const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	*keypad = lv[1] & 0x7f;
+
+	return 0;
+}
+
+/* encode 'keypad' */
+int gsm48_encode_keypad(struct msgb *msg, int keypad)
+{
+	msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
+
+	return 0;
+}
+
+/* decode 'progress' */
+int gsm48_decode_progress(struct gsm_mncc_progress *progress,
+			   const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 2)
+		return -EINVAL;
+
+	progress->coding = (lv[1] & 0x60) >> 5;
+	progress->location = lv[1] & 0x0f;
+	progress->descr = lv[2] & 0x7f;
+
+	return 0;
+}
+
+/* encode 'progress' */
+int gsm48_encode_progress(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_progress *p)
+{
+	uint8_t lv[3];
+
+	lv[0] = 2;
+	lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
+	lv[2] = 0x80 | (p->descr & 0x7f);
+	if (lv_only)
+		msgb_lv_put(msg, lv[0], lv+1);
+	else
+		msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'user-user' */
+int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
+			   const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+	char *info = uu->info;
+	int info_len = sizeof(uu->info);
+	int i;
+
+	if (in_len < 1)
+		return -EINVAL;
+
+	uu->proto = lv[1];
+
+	for (i = 2; i <= in_len; i++) {
+		info_len--;
+		if (info_len <= 1)
+			break;
+		*info++ = lv[i];
+	}
+	if (info_len >= 1)
+		*info++ = '\0';
+
+	return 0;
+}
+
+/* encode 'useruser' */
+int gsm48_encode_useruser(struct msgb *msg, int lv_only,
+			   const struct gsm_mncc_useruser *uu)
+{
+	uint8_t lv[GSM_MAX_USERUSER + 2];
+
+	if (strlen(uu->info) > GSM_MAX_USERUSER)
+		return -EINVAL;
+
+	lv[0] = 1 + strlen(uu->info);
+	lv[1] = uu->proto;
+	memcpy(lv + 2, uu->info, strlen(uu->info));
+	if (lv_only)
+		msgb_lv_put(msg, lv[0], lv+1);
+	else
+		msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'ss version' */
+int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
+			    const uint8_t *lv)
+{
+	uint8_t in_len = lv[0];
+
+	if (in_len < 1 || in_len < sizeof(ssv->info))
+		return -EINVAL;
+
+	memcpy(ssv->info, lv + 1, in_len);
+	ssv->len = in_len;
+
+	return 0;
+}
+
+/* encode 'ss version' */
+int gsm48_encode_ssversion(struct msgb *msg,
+			   const struct gsm_mncc_ssversion *ssv)
+{
+	uint8_t lv[GSM_MAX_SSVERSION + 1];
+
+	if (ssv->len > GSM_MAX_SSVERSION)
+		return -EINVAL;
+
+	lv[0] = ssv->len;
+	memcpy(lv + 1, ssv->info, ssv->len);
+	msgb_tlv_put(msg, GSM48_IE_SS_VERS, lv[0], lv+1);
+
+	return 0;
+}
+
+/* decode 'more data' does not require a function, because it has no value */
+
+/* encode 'more data' */
+int gsm48_encode_more(struct msgb *msg)
+{
+	uint8_t *ie;
+
+	ie = msgb_put(msg, 1);
+	ie[0] = GSM48_IE_MORE_DATA;
+
+	return 0;
+}
+
diff --git a/libosmocore/src/gsm_utils.c b/libosmocore/src/gsm_utils.c
new file mode 100644
index 0000000..593dd5c
--- /dev/null
+++ b/libosmocore/src/gsm_utils.c
@@ -0,0 +1,361 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.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 <openbsc/gsm_data.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm_utils.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../config.h"
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
+{
+	int i = 0;
+	int l = 0;
+
+        /* FIXME: We need to account for user data headers here */
+	i += l;
+	for (; i < length; i ++)
+		*(text ++) =
+			((user_data[(i * 7 + 7) >> 3] <<
+			  (7 - ((i * 7 + 7) & 7))) |
+			 (user_data[(i * 7) >> 3] >>
+			  ((i * 7) & 7))) & 0x7f;
+	*text = '\0';
+
+	return i - l;
+}
+
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+	int i,j = 0;
+	unsigned char ch1, ch2;
+	int shift = 0;
+
+	for ( i=0; i<strlen(data); i++ ) {
+
+		ch1 = data[i] & 0x7F;
+		ch1 = ch1 >> shift;
+		ch2 = data[(i+1)] & 0x7F;
+		ch2 = ch2 << (7-shift);
+
+		ch1 = ch1 | ch2;
+
+		result[j++] = ch1;
+
+		shift++;
+
+		if ((shift == 7) && (i+1<strlen(data))) {
+			shift = 0;
+			i++;
+		}
+	}
+
+	return i;
+}
+
+/* determine power control level for given dBm value, as indicated
+ * by the tables in chapter 4.1.1 of GSM TS 05.05 */
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
+{
+	switch (band) {
+	case GSM_BAND_450:
+	case GSM_BAND_480:
+	case GSM_BAND_750:
+	case GSM_BAND_900:
+	case GSM_BAND_810:
+	case GSM_BAND_850:
+		if (dbm >= 39)
+			return 0;
+		else if (dbm < 5)
+			return 19;
+		else {
+			/* we are guaranteed to have (5 <= dbm < 39) */
+			return 2 + ((39 - dbm) / 2);
+		}
+		break;
+	case GSM_BAND_1800:
+		if (dbm >= 36)
+			return 29;
+		else if (dbm >= 34)	
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else if (dbm == 31)
+			return 0;
+		else {
+			/* we are guaranteed to have (0 <= dbm < 31) */
+			return (30 - dbm) / 2;
+		}
+		break;
+	case GSM_BAND_1900:
+		if (dbm >= 33)
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else if (dbm == 31)
+			return 0;
+		else {
+			/* we are guaranteed to have (0 <= dbm < 31) */
+			return (30 - dbm) / 2;
+		}
+		break;
+	}
+	return -EINVAL;
+}
+
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl)
+{
+	lvl &= 0x1f;
+
+	switch (band) {
+	case GSM_BAND_450:
+	case GSM_BAND_480:
+	case GSM_BAND_750:
+	case GSM_BAND_900:
+	case GSM_BAND_810:
+	case GSM_BAND_850:
+		if (lvl < 2)
+			return 39;
+		else if (lvl < 20)
+			return 39 - ((lvl - 2) * 2) ;
+		else
+			return 5;
+		break;
+	case GSM_BAND_1800:
+		if (lvl < 16)
+			return 30 - (lvl * 2);
+		else if (lvl < 29)
+			return 0;
+		else
+			return 36 - ((lvl - 29) * 2);
+		break;
+	case GSM_BAND_1900:
+		if (lvl < 16)
+			return 30 - (lvl * 2);
+		else if (lvl < 30)
+			return -EINVAL;
+		else
+			return 33 - (lvl - 30);
+		break;
+	}
+	return -EINVAL;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev)
+{
+	if (rxlev > 63)
+		rxlev = 63;
+
+	return -110 + rxlev;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+uint8_t dbm2rxlev(int dbm)
+{
+	int rxlev = dbm + 110;
+
+	if (rxlev > 63)
+		rxlev = 63;
+	else if (rxlev < 0)
+		rxlev = 0;
+
+	return rxlev;
+}
+
+const char *gsm_band_name(enum gsm_band band)
+{
+	switch (band) {
+	case GSM_BAND_450:
+		return "GSM450";
+	case GSM_BAND_480:
+		return "GSM450";
+	case GSM_BAND_750:
+		return "GSM750";
+	case GSM_BAND_810:
+		return "GSM810";
+	case GSM_BAND_850:
+		return "GSM850";
+	case GSM_BAND_900:
+		return "GSM900";
+	case GSM_BAND_1800:
+		return "DCS1800";
+	case GSM_BAND_1900:
+		return "PCS1900";
+	}
+	return "invalid";
+}
+
+enum gsm_band gsm_band_parse(const char* mhz)
+{
+	while (*mhz && !isdigit(*mhz))
+		mhz++;
+
+	if (*mhz == '\0')
+		return -EINVAL;
+
+	switch (strtol(mhz, NULL, 10)) {
+	case 450:
+		return GSM_BAND_450;
+	case 480:
+		return GSM_BAND_480;
+	case 750:
+		return GSM_BAND_750;
+	case 810:
+		return GSM_BAND_810;
+	case 850:
+		return GSM_BAND_850;
+	case 900:
+		return GSM_BAND_900;
+	case 1800:
+		return GSM_BAND_1800;
+	case 1900:
+		return GSM_BAND_1900;
+	default:
+		return -EINVAL;
+	}
+}
+
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+void generate_backtrace()
+{
+	int i, nptrs;
+	void *buffer[100];
+	char **strings;
+
+	nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
+	printf("backtrace() returned %d addresses\n", nptrs);
+
+	strings = backtrace_symbols(buffer, nptrs);
+	if (!strings)
+		return;
+
+	for (i = 1; i < nptrs; i++)
+		printf("%s\n", strings[i]);
+
+	free(strings);
+}
+#endif
+
+enum gsm_band gsm_arfcn2band(uint16_t arfcn)
+{
+	if (arfcn & ARFCN_PCS)
+		return GSM_BAND_1900;
+	else if (arfcn <= 124)
+		return GSM_BAND_900;
+	else if (arfcn >= 955 && arfcn <= 1023)
+		return GSM_BAND_900;
+	else if (arfcn >= 128 && arfcn <= 251)
+		return GSM_BAND_850;
+	else if (arfcn >= 512 && arfcn <= 885)
+		return GSM_BAND_1800;
+	else if (arfcn >= 259 && arfcn <= 293)
+		return GSM_BAND_450;
+	else if (arfcn >= 306 && arfcn <= 340)
+		return GSM_BAND_480;
+	else if (arfcn >= 350 && arfcn <= 425)
+		return GSM_BAND_810;
+	else if (arfcn >= 438 && arfcn <= 511)
+		return GSM_BAND_750;
+	else
+		return GSM_BAND_1800;
+}
+
+/* Convert an ARFCN to the frequency in MHz * 10 */
+uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink)
+{
+	uint16_t freq10_ul;
+	uint16_t freq10_dl;
+
+	if (arfcn & ARFCN_PCS) {
+		/* DCS 1900 */
+		arfcn &= ~ARFCN_PCS;
+		freq10_ul = 18502 + 2 * (arfcn-512);
+		freq10_dl = freq10_ul + 800;
+	} else if (arfcn <= 124) {
+		/* Primary GSM + ARFCN 0 of E-GSM */
+		freq10_ul = 8900 + 2 * arfcn;
+		freq10_dl = freq10_ul + 450;
+	} else if (arfcn >= 955 && arfcn <= 1023) {
+		/* E-GSM and R-GSM */
+		freq10_ul = 8900 + 2 * (arfcn - 1024);
+		freq10_dl = freq10_ul + 450;
+	} else if (arfcn >= 128 && arfcn <= 251) {
+		/* GSM 850 */
+		freq10_ul = 8242 + 2 * (arfcn - 128);
+		freq10_dl = freq10_ul + 450;
+	} else if (arfcn >= 512 && arfcn <= 885) {
+		/* DCS 1800 */
+		freq10_ul = 17102 + 2 * (arfcn - 512);
+		freq10_dl = freq10_ul + 950;
+	} else if (arfcn >= 259 && arfcn <= 293) {
+		/* GSM 450 */
+		freq10_ul = 4506 + 2 * (arfcn - 259);
+		freq10_dl = freq10_ul + 100;
+	} else if (arfcn >= 306 && arfcn <= 340) {
+		/* GSM 480 */
+		freq10_ul = 4790 + 2 * (arfcn - 306);
+		freq10_dl = freq10_ul + 100;
+	} else if (arfcn >= 350 && arfcn <= 425) {
+		/* GSM 810 */
+		freq10_ul = 8060 + 2 * (arfcn - 350);
+		freq10_dl = freq10_ul + 450;
+	} else if (arfcn >= 438 && arfcn <= 511) {
+		/* GSM 750 */
+		freq10_ul = 7472 + 2 * (arfcn - 438);
+		freq10_dl = freq10_ul + 300;
+	} else
+		return 0xffff;
+
+	if (uplink)
+		return freq10_ul;
+	else
+		return freq10_dl;
+}
+
+void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn)
+{
+	time->fn = fn;
+	time->t1 = time->fn / (26*51);
+	time->t2 = time->fn % 26;
+	time->t3 = time->fn % 51;
+	time->tc = (time->fn / 51) % 8;
+}
+
+uint32_t gsm_gsmtime2fn(struct gsm_time *time)
+{
+	/* TS 05.02 Chapter 4.3.3 TDMA frame number */
+	return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
+}
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/msgb.c b/libosmocore/src/msgb.c
new file mode 100644
index 0000000..60af373
--- /dev/null
+++ b/libosmocore/src/msgb.c
@@ -0,0 +1,89 @@
+/* (C) 2008 by Harald Welte <laforge@gnumonks.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 <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <osmocore/msgb.h>
+//#include <openbsc/gsm_data.h>
+#include <osmocore/talloc.h>
+//#include <openbsc/debug.h>
+
+void *tall_msgb_ctx;
+
+struct msgb *msgb_alloc(uint16_t size, const char *name)
+{
+	struct msgb *msg;
+
+	msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
+
+	if (!msg) {
+		//LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n");
+		return NULL;
+	}
+
+	msg->data_len = size;
+	msg->len = 0;
+	msg->data = msg->_data;
+	msg->head = msg->_data;
+	msg->tail = msg->_data;
+
+	return msg;
+}
+
+void msgb_free(struct msgb *m)
+{
+	talloc_free(m);
+}
+
+void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
+{
+	llist_add_tail(&msg->list, queue);
+}
+
+struct msgb *msgb_dequeue(struct llist_head *queue)
+{
+	struct llist_head *lh;
+
+	if (llist_empty(queue))
+		return NULL;
+
+	lh = queue->next;
+	llist_del(lh);
+	
+	return llist_entry(lh, struct msgb, list);
+}
+
+void msgb_reset(struct msgb *msg)
+{
+	msg->len = 0;
+	msg->data = msg->_data;
+	msg->head = msg->_data;
+	msg->tail = msg->_data;
+
+	msg->bts_link = NULL;
+	msg->trx = NULL;
+	msg->lchan = NULL;
+	msg->l2h = NULL;
+	msg->l3h = NULL;
+	msg->smsh = NULL;
+}
diff --git a/libosmocore/src/rsl.c b/libosmocore/src/rsl.c
new file mode 100644
index 0000000..c002d33
--- /dev/null
+++ b/libosmocore/src/rsl.c
@@ -0,0 +1,329 @@
+/* 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>
+ *
+ * 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 <stdint.h>
+#include <errno.h>
+
+#include <osmocore/tlv.h>
+#include <osmocore/rsl.h>
+
+#define RSL_ALLOC_SIZE		200
+#define RSL_ALLOC_HEADROOM	56
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type)
+{
+	dh->c.msg_discr = ABIS_RSL_MDISC_RLL;
+	dh->c.msg_type = msg_type;
+	dh->ie_chan = RSL_IE_CHAN_NR;
+	dh->ie_link_id = RSL_IE_LINK_IDENT;
+}
+
+const struct tlv_definition rsl_att_tlvdef = {
+	.def = {
+		[RSL_IE_CHAN_NR]		= { TLV_TYPE_TV },
+		[RSL_IE_LINK_IDENT]		= { TLV_TYPE_TV },
+		[RSL_IE_ACT_TYPE]		= { TLV_TYPE_TV },
+		[RSL_IE_BS_POWER]		= { TLV_TYPE_TV },
+		[RSL_IE_CHAN_IDENT]		= { TLV_TYPE_TLV },
+		[RSL_IE_CHAN_MODE]		= { TLV_TYPE_TLV },
+		[RSL_IE_ENCR_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_FRAME_NUMBER]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_HANDO_REF]		= { TLV_TYPE_TV },
+		[RSL_IE_L1_INFO]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_L3_INFO]		= { TLV_TYPE_TL16V },
+		[RSL_IE_MS_IDENTITY]		= { TLV_TYPE_TLV },
+		[RSL_IE_MS_POWER]		= { TLV_TYPE_TV },
+		[RSL_IE_PAGING_GROUP]		= { TLV_TYPE_TV },
+		[RSL_IE_PAGING_LOAD]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_PYHS_CONTEXT]		= { TLV_TYPE_TLV },
+		[RSL_IE_ACCESS_DELAY]		= { TLV_TYPE_TV },
+		[RSL_IE_RACH_LOAD]		= { TLV_TYPE_TLV },
+		[RSL_IE_REQ_REFERENCE]		= { TLV_TYPE_FIXED, 3 },
+		[RSL_IE_RELEASE_MODE]		= { TLV_TYPE_TV },
+		[RSL_IE_RESOURCE_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_RLM_CAUSE]		= { TLV_TYPE_TLV },
+		[RSL_IE_STARTNG_TIME]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_TIMING_ADVANCE]		= { TLV_TYPE_TV },
+		[RSL_IE_UPLINK_MEAS]		= { TLV_TYPE_TLV },
+		[RSL_IE_CAUSE]			= { TLV_TYPE_TLV },
+		[RSL_IE_MEAS_RES_NR]		= { TLV_TYPE_TV },
+		[RSL_IE_MSG_ID]			= { TLV_TYPE_TV },
+		[RSL_IE_SYSINFO_TYPE]		= { TLV_TYPE_TV },
+		[RSL_IE_MS_POWER_PARAM]		= { TLV_TYPE_TLV },
+		[RSL_IE_BS_POWER_PARAM]		= { TLV_TYPE_TLV },
+		[RSL_IE_PREPROC_PARAM]		= { TLV_TYPE_TLV },
+		[RSL_IE_PREPROC_MEAS]		= { TLV_TYPE_TLV },
+		[RSL_IE_IMM_ASS_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_SMSCB_INFO]		= { TLV_TYPE_FIXED, 23 },
+		[RSL_IE_MS_TIMING_OFFSET]	= { TLV_TYPE_TV },
+		[RSL_IE_ERR_MSG]		= { TLV_TYPE_TLV },
+		[RSL_IE_FULL_BCCH_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_CHAN_NEEDED]		= { TLV_TYPE_TV },
+		[RSL_IE_CB_CMD_TYPE]		= { TLV_TYPE_TV },
+		[RSL_IE_SMSCB_MSG]		= { TLV_TYPE_TLV },
+		[RSL_IE_FULL_IMM_ASS_INFO]	= { TLV_TYPE_TLV },
+		[RSL_IE_SACCH_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_CBCH_LOAD_INFO]		= { TLV_TYPE_TV },
+		[RSL_IE_SMSCB_CHAN_INDICATOR]	= { TLV_TYPE_TV },
+		[RSL_IE_GROUP_CALL_REF]		= { TLV_TYPE_TLV },
+		[RSL_IE_CHAN_DESC]		= { TLV_TYPE_TLV },
+		[RSL_IE_NCH_DRX_INFO]		= { TLV_TYPE_TLV },
+		[RSL_IE_CMD_INDICATOR]		= { TLV_TYPE_TLV },
+		[RSL_IE_EMLPP_PRIO]		= { TLV_TYPE_TV },
+		[RSL_IE_UIC]			= { TLV_TYPE_TLV },
+		[RSL_IE_MAIN_CHAN_REF]		= { TLV_TYPE_TV },
+		[RSL_IE_MR_CONFIG]		= { TLV_TYPE_TLV },
+		[RSL_IE_MR_CONTROL]		= { TLV_TYPE_TV },
+		[RSL_IE_SUP_CODEC_TYPES]	= { TLV_TYPE_TLV },
+		[RSL_IE_CODEC_CONFIG]		= { TLV_TYPE_TLV },
+		[RSL_IE_RTD]			= { TLV_TYPE_TV },
+		[RSL_IE_TFO_STATUS]		= { TLV_TYPE_TV },
+		[RSL_IE_LLP_APDU]		= { TLV_TYPE_TLV },
+		[RSL_IE_SIEMENS_MRPCI]		= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_PROXY_UDP]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_IPAC_BSCMPL_TOUT]	= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_REMOTE_IP]		= { TLV_TYPE_FIXED, 4 },
+		[RSL_IE_IPAC_REMOTE_PORT]	= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_IPAC_RTP_PAYLOAD]	= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_LOCAL_PORT]	= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_IPAC_SPEECH_MODE]	= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_LOCAL_IP]		= { TLV_TYPE_FIXED, 4 },
+		[RSL_IE_IPAC_CONN_ID]		= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_IPAC_RTP_CSD_FMT]	= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_RTP_JIT_BUF]	= { TLV_TYPE_FIXED, 2 },
+		[RSL_IE_IPAC_RTP_COMPR]		= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_RTP_PAYLOAD2]	= { TLV_TYPE_TV },
+		[RSL_IE_IPAC_RTP_MPLEX]		= { TLV_TYPE_FIXED, 8 },
+		[RSL_IE_IPAC_RTP_MPLEX_ID]	= { TLV_TYPE_TV },
+	},
+};
+
+/* encode channel number as per Section 9.3.1 */
+uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot)
+{
+	uint8_t ret;
+
+	ret = (timeslot & 0x07) | type;
+
+	switch (type) {
+	case RSL_CHAN_Lm_ACCHs:
+		subch &= 0x01;
+		break;
+	case RSL_CHAN_SDCCH4_ACCH:
+		subch &= 0x03;
+		break;
+	case RSL_CHAN_SDCCH8_ACCH:
+		subch &= 0x07;
+		break;
+	default:
+		/* no subchannels allowed */
+		subch = 0x00;
+		break;
+	}
+	ret |= (subch << 3);
+
+	return ret;
+}
+
+int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot)
+{
+	*timeslot = chan_nr & 0x7;
+
+	if ((chan_nr & 0xf8) == RSL_CHAN_Bm_ACCHs) {
+		*type = RSL_CHAN_Bm_ACCHs;
+		*subch = 0;
+	} else if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
+		*type = RSL_CHAN_Lm_ACCHs;
+		*subch = (chan_nr >> 3) & 0x1;
+	} else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
+		*type = RSL_CHAN_SDCCH4_ACCH;
+		*subch = (chan_nr >> 3) & 0x3;
+	} else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
+		*type = RSL_CHAN_SDCCH8_ACCH;
+		*subch = (chan_nr >> 3) & 0x7;
+	} else if ((chan_nr & 0xf8) == RSL_CHAN_BCCH) {
+		*type = RSL_CHAN_BCCH;
+		*subch = 0;
+	} else if ((chan_nr & 0xf8) == RSL_CHAN_RACH) {
+		*type = RSL_CHAN_RACH;
+		*subch = 0;
+	} else if ((chan_nr & 0xf8) == RSL_CHAN_PCH_AGCH) {
+		*type = RSL_CHAN_PCH_AGCH;
+		*subch = 0;
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct value_string rsl_err_vals[] = {
+	{ RSL_ERR_RADIO_IF_FAIL,	"Radio Interface Failure" },
+	{ RSL_ERR_RADIO_LINK_FAIL,	"Radio Link Failure" },
+	{ RSL_ERR_HANDOVER_ACC_FAIL,	"Handover Access Failure" },
+	{ RSL_ERR_TALKER_ACC_FAIL,	"Talker Access Failure" },
+	{ RSL_ERR_OM_INTERVENTION,	"O&M Intervention" },
+	{ RSL_ERR_NORMAL_UNSPEC,	"Normal event, unspecified" },
+	{ RSL_ERR_T_MSRFPCI_EXP,	"Siemens: T_MSRFPCI Expired" },
+	{ RSL_ERR_EQUIPMENT_FAIL,	"Equipment Failure" },
+	{ RSL_ERR_RR_UNAVAIL,		"Radio Resource not available" },
+	{ RSL_ERR_TERR_CH_FAIL,		"Terrestrial Channel Failure" },
+	{ RSL_ERR_CCCH_OVERLOAD,	"CCCH Overload" },
+	{ RSL_ERR_ACCH_OVERLOAD,	"ACCH Overload" },
+	{ RSL_ERR_PROCESSOR_OVERLOAD,	"Processor Overload" },
+	{ RSL_ERR_RES_UNAVAIL,		"Resource not available, unspecified" },
+	{ RSL_ERR_TRANSC_UNAVAIL,	"Transcoding not available" },
+	{ RSL_ERR_SERV_OPT_UNAVAIL,	"Service or Option not available" },
+	{ RSL_ERR_ENCR_UNIMPL,		"Encryption algorithm not implemented" },
+	{ RSL_ERR_SERV_OPT_UNIMPL,	"Service or Option not implemented" },
+	{ RSL_ERR_RCH_ALR_ACTV_ALLOC,	"Radio channel already activated" },
+	{ RSL_ERR_INVALID_MESSAGE,	"Invalid Message, unspecified" },
+	{ RSL_ERR_MSG_DISCR,		"Message Discriminator Error" },
+	{ RSL_ERR_MSG_TYPE,		"Message Type Error" },
+	{ RSL_ERR_MSG_SEQ,		"Message Sequence Error" },
+	{ RSL_ERR_IE_ERROR,		"General IE error" },
+	{ RSL_ERR_MAND_IE_ERROR,	"Mandatory IE error" },
+	{ RSL_ERR_OPT_IE_ERROR,		"Optional IE error" },
+	{ RSL_ERR_IE_NONEXIST,		"IE non-existent" },
+	{ RSL_ERR_IE_LENGTH,		"IE length error" },
+	{ RSL_ERR_IE_CONTENT,		"IE content error" },
+	{ RSL_ERR_PROTO,		"Protocol error, unspecified" },
+	{ RSL_ERR_INTERWORKING,		"Interworking error, unspecified" },
+	{ 0,				NULL }
+};
+
+const char *rsl_err_name(uint8_t err)
+{
+	return get_value_string(rsl_err_vals, err);
+}
+
+static const struct value_string rsl_rlm_cause_strs[] = {
+	{ RLL_CAUSE_T200_EXPIRED,	"Timer T200 expired (N200+1) times" },
+	{ RLL_CAUSE_REEST_REQ,		"Re-establishment request" },
+	{ RLL_CAUSE_UNSOL_UA_RESP,	"Unsolicited UA response" },
+	{ RLL_CAUSE_UNSOL_DM_RESP,	"Unsolicited DM response" },
+	{ RLL_CAUSE_UNSOL_DM_RESP_MF,	"Unsolicited DM response, multiple frame" },
+	{ RLL_CAUSE_UNSOL_SPRV_RESP,	"Unsolicited supervisory response" },
+	{ RLL_CAUSE_SEQ_ERR,		"Sequence Error" },
+	{ RLL_CAUSE_UFRM_INC_PARAM,	"U-Frame with incorrect parameters" },
+	{ RLL_CAUSE_SFRM_INC_PARAM,	"S-Frame with incorrect parameters" },
+	{ RLL_CAUSE_IFRM_INC_MBITS,	"I-Frame with incorrect use of M bit" },
+	{ RLL_CAUSE_IFRM_INC_LEN,	"I-Frame with incorrect length" },
+	{ RLL_CAUSE_FRM_UNIMPL,		"Fraeme not implemented" },
+	{ RLL_CAUSE_SABM_MF,		"SABM command, multiple frame established state" },
+	{ RLL_CAUSE_SABM_INFO_NOTALL,	"SABM frame with information not allowed in this state" },
+	{ 0,				NULL },
+};
+
+const char *rsl_rlm_cause_name(uint8_t err)
+{
+	return get_value_string(rsl_rlm_cause_strs, err);
+}
+
+/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf)
+{
+	switch (ccch_conf) {
+	case RSL_BCCH_CCCH_CONF_1_NC:
+		return 1;
+	case RSL_BCCH_CCCH_CONF_1_C:
+		return 1;
+	case RSL_BCCH_CCCH_CONF_2_NC:
+		return 2;
+	case RSL_BCCH_CCCH_CONF_3_NC:
+		return 3;
+	case RSL_BCCH_CCCH_CONF_4_NC:
+		return 4;
+	default:
+		return -1;
+	}
+}
+
+/* Section 3.3.2.3 TS 05.02 */
+int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
+{
+	switch (ccch_conf) {
+	case RSL_BCCH_CCCH_CONF_1_NC:
+		return 0;
+	case RSL_BCCH_CCCH_CONF_1_C:
+		return 1;
+	case RSL_BCCH_CCCH_CONF_2_NC:
+		return 0;
+	case RSL_BCCH_CCCH_CONF_3_NC:
+		return 0;
+	case RSL_BCCH_CCCH_CONF_4_NC:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/* Push a RSL RLL header with L3_INFO IE */
+void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
+		     uint8_t link_id, int transparent)
+{
+	uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
+	struct abis_rsl_rll_hdr *rh;
+
+	/* construct a RSLms RLL message (DATA INDICATION, UNIT DATA
+	 * INDICATION) and send it off via RSLms */
+
+	/* Push the L3 IE tag and lengh */
+	msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
+
+	/* Then push the RSL header */
+	rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
+	rsl_init_rll_hdr(rh, msg_type);
+	if (transparent)
+		rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+	rh->chan_nr = chan_nr;
+	rh->link_id = link_id;
+
+	/* set the l2 header pointer */
+	msg->l2h = (uint8_t *)rh;
+}
+
+struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
+			    uint8_t link_id, int transparent)
+{
+	struct abis_rsl_rll_hdr *rh;
+	struct msgb *msg;
+
+	msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM,
+				  RSL_ALLOC_HEADROOM, "rsl_rll_simple");
+
+	if (!msg)
+		return NULL;
+
+	/* put the RSL header */
+	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+	rsl_init_rll_hdr(rh, msg_type);
+	if (transparent)
+		rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+	rh->chan_nr = chan_nr;
+	rh->link_id = link_id;
+
+	/* set the l2 header pointer */
+	msg->l2h = (uint8_t *)rh;
+
+	return msg;
+}
diff --git a/libosmocore/src/rxlev_stat.c b/libosmocore/src/rxlev_stat.c
new file mode 100644
index 0000000..1bfd679
--- /dev/null
+++ b/libosmocore/src/rxlev_stat.c
@@ -0,0 +1,94 @@
+/* Rx Level statistics */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <osmocore/bitvec.h>
+#include <osmocore/rxlev_stat.h>
+
+int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val)
+{
+	unsigned int i;
+
+	for (i = n; i < bv->data_len*8; i++) {
+		if (bitvec_get_bit_pos(bv, i) == val)
+			return i;
+	}
+
+	return -1;
+}
+
+void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev)
+{
+	struct bitvec bv;
+
+	if (rxlev >= NUM_RXLEVS)
+		rxlev = NUM_RXLEVS-1;
+
+	bv.data_len = NUM_ARFCNS/8;
+	bv.data = st->rxlev_buckets[rxlev];
+
+	bitvec_set_bit_pos(&bv, arfcn, ONE);
+}
+
+/* get the next ARFCN that has the specified Rxlev */
+int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn)
+{
+	struct bitvec bv;
+
+	if (rxlev >= NUM_RXLEVS)
+		rxlev = NUM_RXLEVS-1;
+
+	bv.data_len = NUM_ARFCNS/8;
+
+	if (arfcn < 0)
+		arfcn = -1;
+
+	bv.data = st->rxlev_buckets[rxlev];
+
+	return bitvec_find_bit_pos(&bv, arfcn+1, ONE);
+}
+
+void rxlev_stat_reset(struct rxlev_stats *st)
+{
+	memset(st, 0, sizeof(*st));
+}
+
+void rxlev_stat_dump(const struct rxlev_stats *st)
+{
+	int i;
+
+	for (i = NUM_RXLEVS-1; i >= 0; i--) {
+		int16_t arfcn = -1;
+
+		printf("ARFCN with RxLev %u: ", i);
+		while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) {
+			printf("%u ", arfcn);
+		}
+		printf("\n");
+	}
+}
diff --git a/libosmocore/src/select.c b/libosmocore/src/select.c
new file mode 100644
index 0000000..9517778
--- /dev/null
+++ b/libosmocore/src/select.c
@@ -0,0 +1,130 @@
+/* select filedescriptor handling, taken from:
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem.
+ *
+ * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 
+ *  as published by the Free Software Foundation
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <fcntl.h>
+#include <osmocore/select.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/timer.h>
+
+#include "../config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+
+static int maxfd = 0;
+static LLIST_HEAD(bsc_fds);
+static int unregistered_count;
+
+int bsc_register_fd(struct bsc_fd *fd)
+{
+	int flags;
+
+	/* make FD nonblocking */
+	flags = fcntl(fd->fd, F_GETFL);
+	if (flags < 0)
+		return flags;
+	flags |= O_NONBLOCK;
+	flags = fcntl(fd->fd, F_SETFL, flags);
+	if (flags < 0)
+		return flags;
+
+	/* Register FD */
+	if (fd->fd > maxfd)
+		maxfd = fd->fd;
+
+	llist_add_tail(&fd->list, &bsc_fds);
+
+	return 0;
+}
+
+void bsc_unregister_fd(struct bsc_fd *fd)
+{
+	unregistered_count++;
+	llist_del(&fd->list);
+}
+
+int bsc_select_main(int polling)
+{
+	struct bsc_fd *ufd, *tmp;
+	fd_set readset, writeset, exceptset;
+	int work = 0, rc;
+	struct timeval no_time = {0, 0};
+
+	FD_ZERO(&readset);
+	FD_ZERO(&writeset);
+	FD_ZERO(&exceptset);
+
+	/* prepare read and write fdsets */
+	llist_for_each_entry(ufd, &bsc_fds, list) {
+		if (ufd->when & BSC_FD_READ)
+			FD_SET(ufd->fd, &readset);
+
+		if (ufd->when & BSC_FD_WRITE)
+			FD_SET(ufd->fd, &writeset);
+
+		if (ufd->when & BSC_FD_EXCEPT)
+			FD_SET(ufd->fd, &exceptset);
+	}
+
+	bsc_timer_check();
+
+	if (!polling)
+		bsc_prepare_timers();
+	rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
+	if (rc < 0)
+		return 0;
+
+	/* fire timers */
+	bsc_update_timers();
+
+	/* call registered callback functions */
+restart:
+	unregistered_count = 0;
+	llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
+		int flags = 0;
+
+		if (FD_ISSET(ufd->fd, &readset)) {
+			flags |= BSC_FD_READ;
+			FD_CLR(ufd->fd, &readset);
+		}
+
+		if (FD_ISSET(ufd->fd, &writeset)) {
+			flags |= BSC_FD_WRITE;
+			FD_CLR(ufd->fd, &writeset);
+		}
+
+		if (FD_ISSET(ufd->fd, &exceptset)) {
+			flags |= BSC_FD_EXCEPT;
+			FD_CLR(ufd->fd, &exceptset);
+		}
+
+		if (flags) {
+			work = 1;
+			ufd->cb(ufd, flags);
+		}
+		/* ugly, ugly hack. If more than one filedescriptors were
+		 * unregistered, they might have been consecutive and
+		 * llist_for_each_entry_safe() is no longer safe */
+		if (unregistered_count > 1)
+			goto restart;
+	}
+	return work;
+}
+
+#endif /* _HAVE_SYS_SELECT_H */
diff --git a/libosmocore/src/signal.c b/libosmocore/src/signal.c
new file mode 100644
index 0000000..c7ca86c
--- /dev/null
+++ b/libosmocore/src/signal.c
@@ -0,0 +1,84 @@
+/* Generic signalling/notification infrastructure */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <osmocore/signal.h>
+#include <osmocore/talloc.h>
+#include <osmocore/linuxlist.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+void *tall_sigh_ctx;
+static LLIST_HEAD(signal_handler_list);
+
+struct signal_handler {
+	struct llist_head entry;
+	unsigned int subsys;
+	signal_cbfn *cbfn;
+	void *data;
+};
+
+
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
+{
+	struct signal_handler *sig_data;
+
+	sig_data = talloc(tall_sigh_ctx, struct signal_handler);
+	if (!sig_data)
+		return -ENOMEM;
+
+	memset(sig_data, 0, sizeof(*sig_data));
+
+	sig_data->subsys = subsys;
+	sig_data->data = data;
+	sig_data->cbfn = cbfn;
+
+	/* FIXME: check if we already have a handler for this subsys/cbfn/data */
+
+	llist_add_tail(&sig_data->entry, &signal_handler_list);
+
+	return 0;
+}
+
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
+{
+	struct signal_handler *handler;
+
+	llist_for_each_entry(handler, &signal_handler_list, entry) {
+		if (handler->cbfn == cbfn && handler->data == data 
+		    && subsys == handler->subsys) {
+			llist_del(&handler->entry);
+			talloc_free(handler);
+			break;
+		}
+	}
+}
+
+
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data)
+{
+	struct signal_handler *handler;
+
+	llist_for_each_entry(handler, &signal_handler_list, entry) {
+		if (handler->subsys != subsys)
+			continue;
+		(*handler->cbfn)(subsys, signal, handler->data, signal_data);
+	}
+}
diff --git a/libosmocore/src/statistics.c b/libosmocore/src/statistics.c
new file mode 100644
index 0000000..34e6a40
--- /dev/null
+++ b/libosmocore/src/statistics.c
@@ -0,0 +1,66 @@
+/* utility routines for keeping some statistics */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.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 <sys/types.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/statistics.h>
+
+static LLIST_HEAD(counters);
+
+void *tall_ctr_ctx;
+
+struct counter *counter_alloc(const char *name)
+{
+	struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
+
+	if (!ctr)
+		return NULL;
+
+	ctr->name = name;
+	llist_add_tail(&ctr->list, &counters);
+
+	return ctr;
+}
+
+void counter_free(struct counter *ctr)
+{
+	llist_del(&ctr->list);
+	talloc_free(ctr);
+}
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
+{
+	struct counter *ctr;
+	int rc = 0;
+
+	llist_for_each_entry(ctr, &counters, list) {
+		rc = handle_counter(ctr, data);
+		if (rc < 0)
+			return rc;
+	}
+
+	return rc;
+}
+
diff --git a/libosmocore/src/talloc.c b/libosmocore/src/talloc.c
new file mode 100644
index 0000000..98c2ee0
--- /dev/null
+++ b/libosmocore/src/talloc.c
@@ -0,0 +1,1805 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+
+   Samba trivial allocation library - new interface
+
+   NOTE: Please read talloc_guide.txt for full documentation
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Stefan Metzmacher 2006
+   
+     ** NOTE! The following LGPL license applies to the talloc
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  inspired by http://swapped.cc/halloc/
+*/
+
+#ifdef _SAMBA_BUILD_
+#include "version.h"
+#if (SAMBA_VERSION_MAJOR<4)
+#include "includes.h"
+/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file
+ * we trust ourselves... */
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef realloc
+#undef realloc
+#endif
+#define _TALLOC_SAMBA3
+#endif /* (SAMBA_VERSION_MAJOR<4) */
+#endif /* _SAMBA_BUILD_ */
+
+#ifndef _TALLOC_SAMBA3
+//#include "replace.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#define __USE_GNU
+#include <string.h>
+#undef __USE_GNU
+#include <osmocore/talloc.h>
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#endif /* not _TALLOC_SAMBA3 */
+
+/* use this to force every realloc to change the pointer, to stress test
+   code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04		/* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08	/* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called 
+   on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+   as its first argument */
+#ifndef likely
+#define likely(x)   __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+#ifdef __APPLE__
+/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
+size_t strnlen(const char *s, size_t n)
+{
+  const char *p = (const char *)memchr(s, 0, n);
+  return(p ? p-s : n);
+}
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+   talloc_enable_leak_report_full() is called, otherwise it remains
+   NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+struct talloc_reference_handle {
+	struct talloc_reference_handle *next, *prev;
+	void *ptr;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+	struct talloc_chunk *next, *prev;
+	struct talloc_chunk *parent, *child;
+	struct talloc_reference_handle *refs;
+	talloc_destructor_t destructor;
+	const char *name;
+	size_t size;
+	unsigned flags;
+
+	/*
+	 * "pool" has dual use:
+	 *
+	 * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+	 * marks the end of the currently allocated area.
+	 *
+	 * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+	 * is a pointer to the struct talloc_chunk of the pool that it was
+	 * allocated from. This way children can quickly find the pool to chew
+	 * from.
+	 */
+	void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+static void (*talloc_abort_fn)(const char *reason);
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
+{
+	talloc_abort_fn = abort_fn;
+}
+
+static void talloc_abort(const char *reason)
+{
+	if (!talloc_abort_fn) {
+		TALLOC_ABORT(reason);
+	}
+
+	talloc_abort_fn(reason);
+}
+
+static void talloc_abort_double_free(void)
+{
+	talloc_abort("Bad talloc magic value - double free");
+}
+
+static void talloc_abort_unknown_value(void)
+{
+	talloc_abort("Bad talloc magic value - unknown value");
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+	const char *pp = (const char *)ptr;
+	struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+	if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
+		if (tc->flags & TALLOC_FLAG_FREE) {
+			talloc_abort_double_free();
+		} else {
+			talloc_abort_unknown_value();
+		}
+	}
+	return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+	if ((p) == (list)) { \
+		(list) = (p)->next; \
+		if (list) (list)->prev = NULL; \
+	} else { \
+		if ((p)->prev) (p)->prev->next = (p)->next; \
+		if ((p)->next) (p)->next->prev = (p)->prev; \
+	} \
+	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+  return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+	while (tc->prev) tc=tc->prev;
+
+	return tc->parent;
+}
+
+void *talloc_parent(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+  find parents name
+*/
+const char *talloc_parent_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? tc->name : NULL;
+}
+
+/*
+  A pool carries an in-pool object count count in the first 16 bytes.
+  bytes. This is done to support talloc_steal() to a parent outside of the
+  pool. The count includes the pool itself, so a talloc_free() on a pool will
+  only destroy the pool if the count has dropped to zero. A talloc_free() of a
+  pool member will reduce the count, and eventually also call free(3) on the
+  pool memory.
+
+  The object count is not put into "struct talloc_chunk" because it is only
+  relevant for talloc pools and the alignment to 16 bytes would increase the
+  memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+	return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
+}
+
+/*
+  Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+					      size_t size)
+{
+	struct talloc_chunk *pool_ctx = NULL;
+	size_t space_left;
+	struct talloc_chunk *result;
+	size_t chunk_size;
+
+	if (parent == NULL) {
+		return NULL;
+	}
+
+	if (parent->flags & TALLOC_FLAG_POOL) {
+		pool_ctx = parent;
+	}
+	else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+		pool_ctx = (struct talloc_chunk *)parent->pool;
+	}
+
+	if (pool_ctx == NULL) {
+		return NULL;
+	}
+
+	space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
+		- ((char *)pool_ctx->pool);
+
+	/*
+	 * Align size to 16 bytes
+	 */
+	chunk_size = ((size + 15) & ~15);
+
+	if (space_left < chunk_size) {
+		return NULL;
+	}
+
+	result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+	VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+	pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+	result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+	result->pool = pool_ctx;
+
+	*talloc_pool_objectcount(pool_ctx) += 1;
+
+	return result;
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+	struct talloc_chunk *tc = NULL;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	if (context != NULL) {
+		tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+				       TC_HDR_SIZE+size);
+	}
+
+	if (tc == NULL) {
+		tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+		if (unlikely(tc == NULL)) return NULL;
+		tc->flags = TALLOC_MAGIC;
+		tc->pool  = NULL;
+	}
+
+	tc->size = size;
+	tc->destructor = NULL;
+	tc->child = NULL;
+	tc->name = NULL;
+	tc->refs = NULL;
+
+	if (likely(context)) {
+		struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+		if (parent->child) {
+			parent->child->parent = NULL;
+			tc->next = parent->child;
+			tc->next->prev = tc;
+		} else {
+			tc->next = NULL;
+		}
+		tc->parent = parent;
+		tc->prev = NULL;
+		parent->child = tc;
+	} else {
+		tc->next = tc->prev = tc->parent = NULL;
+	}
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+void *talloc_pool(const void *context, size_t size)
+{
+	void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+	struct talloc_chunk *tc;
+
+	if (unlikely(result == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(result);
+
+	tc->flags |= TALLOC_FLAG_POOL;
+	tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
+
+	*talloc_pool_objectcount(tc) = 1;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+	VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
+#endif
+
+	return result;
+}
+
+/*
+  setup a destructor to be called on free of a pointer
+  the destructor should return 0 on success, or -1 on failure.
+  if the destructor fails then the free is failed, and the memory can
+  be continued to be used
+*/
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->destructor = destructor;
+}
+
+/*
+  increase the reference count on a piece of memory. 
+*/
+int talloc_increase_ref_count(const void *ptr)
+{
+	if (unlikely(!talloc_reference(null_context, ptr))) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+  helper for talloc_reference()
+
+  this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+	struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+	_TLIST_REMOVE(ptr_tc->refs, handle);
+	return 0;
+}
+
+/*
+   more efficient way to add a name to a pointer - the name must point to a 
+   true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = name;
+}
+
+/*
+  internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+	void *ptr;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	_talloc_set_name_const(ptr, name);
+
+	return ptr;
+}
+
+/*
+  make a secondary reference to a pointer, hanging off the given context.
+  the pointer remains valid until both the original caller and this given
+  context are freed.
+  
+  the major use for this is when two different structures need to reference the 
+  same underlying data, and you want to be able to free the two instances separately,
+  and in either order
+*/
+void *_talloc_reference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+	struct talloc_reference_handle *handle;
+	if (unlikely(ptr == NULL)) return NULL;
+
+	tc = talloc_chunk_from_ptr(ptr);
+	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+						   sizeof(struct talloc_reference_handle),
+						   TALLOC_MAGIC_REFERENCE);
+	if (unlikely(handle == NULL)) return NULL;
+
+	/* note that we hang the destructor off the handle, not the
+	   main context as that allows the caller to still setup their
+	   own destructor on the context if they want to */
+	talloc_set_destructor(handle, talloc_reference_destructor);
+	handle->ptr = discard_const_p(void, ptr);
+	_TLIST_ADD(tc->refs, handle);
+	return handle->ptr;
+}
+
+
+/* 
+   internal talloc_free call
+*/
+static inline int _talloc_free(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return -1;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(tc->refs)) {
+		int is_child;
+		/* check this is a reference from a child or grantchild
+		 * back to it's parent or grantparent
+		 *
+		 * in that case we need to remove the reference and
+		 * call another instance of talloc_free() on the current
+		 * pointer.
+		 */
+		is_child = talloc_is_parent(tc->refs, ptr);
+		_talloc_free(tc->refs);
+		if (is_child) {
+			return _talloc_free(ptr);
+		}
+		return -1;
+	}
+
+	if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+		/* we have a free loop - stop looping */
+		return 0;
+	}
+
+	if (unlikely(tc->destructor)) {
+		talloc_destructor_t d = tc->destructor;
+		if (d == (talloc_destructor_t)-1) {
+			return -1;
+		}
+		tc->destructor = (talloc_destructor_t)-1;
+		if (d(ptr) == -1) {
+			tc->destructor = d;
+			return -1;
+		}
+		tc->destructor = NULL;
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	tc->flags |= TALLOC_FLAG_FREE;
+
+	if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
+		struct talloc_chunk *pool;
+		unsigned int *pool_object_count;
+
+		pool = (tc->flags & TALLOC_FLAG_POOL)
+			? tc : (struct talloc_chunk *)tc->pool;
+
+		pool_object_count = talloc_pool_objectcount(pool);
+
+		if (*pool_object_count == 0) {
+			talloc_abort("Pool object count zero!");
+		}
+
+		*pool_object_count -= 1;
+
+		if (*pool_object_count == 0) {
+			free(pool);
+		}
+	}
+	else {
+		free(tc);
+	}
+	return 0;
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+void *_talloc_steal(const void *new_ctx, const void *ptr)
+{
+	struct talloc_chunk *tc, *new_tc;
+
+	if (unlikely(!ptr)) {
+		return NULL;
+	}
+
+	if (unlikely(new_ctx == NULL)) {
+		new_ctx = null_context;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(new_ctx == NULL)) {
+		if (tc->parent) {
+			_TLIST_REMOVE(tc->parent->child, tc);
+			if (tc->parent->child) {
+				tc->parent->child->parent = tc->parent;
+			}
+		} else {
+			if (tc->prev) tc->prev->next = tc->next;
+			if (tc->next) tc->next->prev = tc->prev;
+		}
+		
+		tc->parent = tc->next = tc->prev = NULL;
+		return discard_const_p(void, ptr);
+	}
+
+	new_tc = talloc_chunk_from_ptr(new_ctx);
+
+	if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+		return discard_const_p(void, ptr);
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->parent = new_tc;
+	if (new_tc->child) new_tc->child->parent = NULL;
+	_TLIST_ADD(new_tc->child, tc);
+
+	return discard_const_p(void, ptr);
+}
+
+
+
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	for (h=tc->refs;h;h=h->next) {
+		struct talloc_chunk *p = talloc_parent_chunk(h);
+		if (p == NULL) {
+			if (context == NULL) break;
+		} else if (TC_PTR_FROM_CHUNK(p) == context) {
+			break;
+		}
+	}
+	if (h == NULL) {
+		return -1;
+	}
+
+	return _talloc_free(h);
+}
+
+/*
+  remove a specific parent context from a pointer. This is a more
+  controlled varient of talloc_free()
+*/
+int talloc_unlink(const void *context, void *ptr)
+{
+	struct talloc_chunk *tc_p, *new_p;
+	void *new_parent;
+
+	if (ptr == NULL) {
+		return -1;
+	}
+
+	if (context == NULL) {
+		context = null_context;
+	}
+
+	if (talloc_unreference(context, ptr) == 0) {
+		return 0;
+	}
+
+	if (context == NULL) {
+		if (talloc_parent_chunk(ptr) != NULL) {
+			return -1;
+		}
+	} else {
+		if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+			return -1;
+		}
+	}
+	
+	tc_p = talloc_chunk_from_ptr(ptr);
+
+	if (tc_p->refs == NULL) {
+		return _talloc_free(ptr);
+	}
+
+	new_p = talloc_parent_chunk(tc_p->refs);
+	if (new_p) {
+		new_parent = TC_PTR_FROM_CHUNK(new_p);
+	} else {
+		new_parent = NULL;
+	}
+
+	if (talloc_unreference(new_parent, ptr) != 0) {
+		return -1;
+	}
+
+	talloc_steal(new_parent, ptr);
+
+	return 0;
+}
+
+/*
+  add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = talloc_vasprintf(ptr, fmt, ap);
+	if (likely(tc->name)) {
+		_talloc_set_name_const(tc->name, ".name");
+	}
+	return tc->name;
+}
+
+/*
+  add a name to an existing pointer
+*/
+const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+	const char *name;
+	va_list ap;
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+	return name;
+}
+
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  return the name of a talloc ptr, or "UNNAMED"
+*/
+const char *talloc_get_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+		return ".reference";
+	}
+	if (likely(tc->name)) {
+		return tc->name;
+	}
+	return "UNNAMED";
+}
+
+
+/*
+  check if a pointer has the given name. If it does, return the pointer,
+  otherwise return NULL
+*/
+void *talloc_check_name(const void *ptr, const char *name)
+{
+	const char *pname;
+	if (unlikely(ptr == NULL)) return NULL;
+	pname = talloc_get_name(ptr);
+	if (likely(pname == name || strcmp(pname, name) == 0)) {
+		return discard_const_p(void, ptr);
+	}
+	return NULL;
+}
+
+static void talloc_abort_type_missmatch(const char *location,
+					const char *name,
+					const char *expected)
+{
+	const char *reason;
+
+	reason = talloc_asprintf(NULL,
+				 "%s: Type mismatch: name[%s] expected[%s]",
+				 location,
+				 name?name:"NULL",
+				 expected);
+	if (!reason) {
+		reason = "Type mismatch";
+	}
+
+	talloc_abort(reason);
+}
+
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
+{
+	const char *pname;
+
+	if (unlikely(ptr == NULL)) {
+		talloc_abort_type_missmatch(location, NULL, name);
+		return NULL;
+	}
+
+	pname = talloc_get_name(ptr);
+	if (likely(pname == name || strcmp(pname, name) == 0)) {
+		return discard_const_p(void, ptr);
+	}
+
+	talloc_abort_type_missmatch(location, pname, name);
+	return NULL;
+}
+
+/*
+  this is for compatibility with older versions of talloc
+*/
+void *talloc_init(const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	/*
+	 * samba3 expects talloc_report_depth_cb(NULL, ...)
+	 * reports all talloc'ed memory, so we need to enable
+	 * null_tracking
+	 */
+	talloc_enable_null_tracking();
+
+	ptr = __talloc(NULL, 0);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+void talloc_free_children(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	if ((tc->flags & TALLOC_FLAG_POOL)
+	    && (*talloc_pool_objectcount(tc) == 1)) {
+		tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+		VALGRIND_MAKE_MEM_NOACCESS(
+			tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
+#endif
+	}
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+void *_talloc(const void *context, size_t size)
+{
+	return __talloc(context, size);
+}
+
+/*
+  externally callable talloc_set_name_const()
+*/
+void talloc_set_name_const(const void *ptr, const char *name)
+{
+	_talloc_set_name_const(ptr, name);
+}
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+	return _talloc_named_const(context, size, name);
+}
+
+/* 
+   free a talloc pointer. This also frees all child pointers of this 
+   pointer recursively
+
+   return 0 if the memory is actually freed, otherwise -1. The memory
+   will not be freed if the ref_count is > 1 or the destructor (if
+   any) returns non-zero
+*/
+int talloc_free(void *ptr)
+{
+	return _talloc_free(ptr);
+}
+
+
+
+/*
+  A talloc version of realloc. The context argument is only used if
+  ptr is NULL
+*/
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+	struct talloc_chunk *tc;
+	void *new_ptr;
+	bool malloced = false;
+
+	/* size zero is equivalent to free() */
+	if (unlikely(size == 0)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	/* realloc(NULL) is equivalent to malloc() */
+	if (ptr == NULL) {
+		return _talloc_named_const(context, size, name);
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	/* don't allow realloc on referenced pointers */
+	if (unlikely(tc->refs)) {
+		return NULL;
+	}
+
+	/* don't let anybody try to realloc a talloc_pool */
+	if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
+		return NULL;
+	}
+
+	/* don't shrink if we have less than 1k to gain */
+	if ((size < tc->size) && ((tc->size - size) < 1024)) {
+		tc->size = size;
+		return ptr;
+	}
+
+	/* by resetting magic we catch users of the old memory */
+	tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+	new_ptr = malloc(size + TC_HDR_SIZE);
+	if (new_ptr) {
+		memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
+		free(tc);
+	}
+#else
+	if (tc->flags & TALLOC_FLAG_POOLMEM) {
+
+		new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+		*talloc_pool_objectcount((struct talloc_chunk *)
+					 (tc->pool)) -= 1;
+
+		if (new_ptr == NULL) {
+			new_ptr = malloc(TC_HDR_SIZE+size);
+			malloced = true;
+		}
+
+		if (new_ptr) {
+			memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+		}
+	}
+	else {
+		new_ptr = realloc(tc, size + TC_HDR_SIZE);
+	}
+#endif
+	if (unlikely(!new_ptr)) {	
+		tc->flags &= ~TALLOC_FLAG_FREE; 
+		return NULL; 
+	}
+
+	tc = (struct talloc_chunk *)new_ptr;
+	tc->flags &= ~TALLOC_FLAG_FREE;
+	if (malloced) {
+		tc->flags &= ~TALLOC_FLAG_POOLMEM;
+	}
+	if (tc->parent) {
+		tc->parent->child = tc;
+	}
+	if (tc->child) {
+		tc->child->parent = tc;
+	}
+
+	if (tc->prev) {
+		tc->prev->next = tc;
+	}
+	if (tc->next) {
+		tc->next->prev = tc;
+	}
+
+	tc->size = size;
+	_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+  a wrapper around talloc_steal() for situations where you are moving a pointer
+  between two structures, and want the old pointer to be set to NULL
+*/
+void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+	const void **pptr = discard_const_p(const void *,_pptr);
+	void *ret = _talloc_steal(new_ctx, *pptr);
+	(*pptr) = NULL;
+	return ret;
+}
+
+/*
+  return the total size of a talloc pool (subtree)
+*/
+size_t talloc_total_size(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total = tc->size;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the total number of blocks in a talloc pool (subtree)
+*/
+size_t talloc_total_blocks(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total++;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+size_t talloc_reference_count(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+	size_t ret = 0;
+
+	for (h=tc->refs;h;h=h->next) {
+		ret++;
+	}
+	return ret;
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data)
+{
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) return;
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return;
+	}
+
+	callback(ptr, depth, max_depth, 0, private_data);
+
+	if (max_depth >= 0 && depth >= max_depth) {
+		return;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+	for (c=tc->child;c;c=c->next) {
+		if (c->name == TALLOC_MAGIC_REFERENCE) {
+			struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+			callback(h->ptr, depth + 1, max_depth, 1, private_data);
+		} else {
+			talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+		}
+	}
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+	const char *name = talloc_get_name(ptr);
+	FILE *f = (FILE *)_f;
+
+	if (is_ref) {
+		fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+		return;
+	}
+
+	if (depth == 0) {
+		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
+			(max_depth < 0 ? "full " :""), name,
+			(unsigned long)talloc_total_size(ptr),
+			(unsigned long)talloc_total_blocks(ptr));
+		return;
+	}
+
+	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
+		depth*4, "",
+		name,
+		(unsigned long)talloc_total_size(ptr),
+		(unsigned long)talloc_total_blocks(ptr),
+		(int)talloc_reference_count(ptr), ptr);
+
+#if 0
+	fprintf(f, "content: ");
+	if (talloc_total_size(ptr)) {
+		int tot = talloc_total_size(ptr);
+		int i;
+
+		for (i = 0; i < tot; i++) {
+			if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+				fprintf(f, "%c", ((char *)ptr)[i]);
+			} else {
+				fprintf(f, "~%02x", ((char *)ptr)[i]);
+			}
+		}
+	}
+	fprintf(f, "\n");
+#endif
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+	talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+	fflush(f);
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_full(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+  report on memory usage by all children of a pointer
+*/
+void talloc_report(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report(null_context, stderr);
+	}
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report_full(null_context, stderr);
+	}
+}
+
+/*
+  enable tracking of the NULL context
+*/
+void talloc_enable_null_tracking(void)
+{
+	if (null_context == NULL) {
+		null_context = _talloc_named_const(NULL, 0, "null_context");
+	}
+}
+
+/*
+  disable tracking of the NULL context
+*/
+void talloc_disable_null_tracking(void)
+{
+	_talloc_free(null_context);
+	null_context = NULL;
+}
+
+/*
+  enable leak reporting on exit
+*/
+void talloc_enable_leak_report(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null);
+}
+
+/*
+  enable full leak reporting on exit
+*/
+void talloc_enable_leak_report_full(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null_full);
+}
+
+/* 
+   talloc and zero memory. 
+*/
+void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+	void *p = _talloc_named_const(ctx, size, name);
+
+	if (p) {
+		memset(p, '\0', size);
+	}
+
+	return p;
+}
+
+/*
+  memdup with a talloc. 
+*/
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+	void *newp = _talloc_named_const(t, size, name);
+
+	if (likely(newp)) {
+		memcpy(newp, p, size);
+	}
+
+	return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+	char *ret;
+
+	ret = (char *)__talloc(t, len + 1);
+	if (unlikely(!ret)) return NULL;
+
+	memcpy(ret, p, len);
+	ret[len] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+  strdup with a talloc
+*/
+char *talloc_strdup(const void *t, const char *p)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+  strndup with a talloc
+*/
+char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, strnlen(p, n));
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+					      const char *a, size_t alen)
+{
+	char *ret;
+
+	ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (unlikely(!ret)) return NULL;
+
+	/* append the string and the trailing \0 */
+	memcpy(&ret[slen], a, alen);
+	ret[slen+alen] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strdup_append(char *s, const char *a)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, strnlen(a, n));
+}
+
+#ifndef HAVE_VA_COPY
+#ifdef HAVE___VA_COPY
+#define va_copy(dest, src) __va_copy(dest, src)
+#else
+#define va_copy(dest, src) (dest) = (src)
+#endif
+#endif
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+	int len;
+	char *ret;
+	va_list ap2;
+	char c;
+
+	/* this call looks strange, but it makes it work on older solaris boxes */
+	va_copy(ap2, ap);
+	len = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+	if (unlikely(len < 0)) {
+		return NULL;
+	}
+
+	ret = (char *)__talloc(t, len+1);
+	if (unlikely(!ret)) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(ret, len+1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+
+/*
+  Perform string formatting, and return a pointer to newly allocated
+  memory holding the result, inside a memory pool.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+	va_list ap;
+	char *ret;
+
+	va_start(ap, fmt);
+	ret = talloc_vasprintf(t, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+						 PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+{
+	ssize_t alen;
+	va_list ap2;
+	char c;
+
+	va_copy(ap2, ap);
+	alen = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+
+	if (alen <= 0) {
+		/* Either the vsnprintf failed or the format resulted in
+		 * no characters being formatted. In the former case, we
+		 * ought to return NULL, in the latter we ought to return
+		 * the original string. Most current callers of this
+		 * function expect it to never return NULL.
+		 */
+		return s;
+	}
+
+	s = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (!s) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(s + slen, alen + 1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(s, s);
+	return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a string buffer.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a buffer.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append_buffer(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  alloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+  alloc an zero array, checking for integer overflow in the array size
+*/
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+  realloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+  a function version of talloc_realloc(), so it can be passed as a function pointer
+  to libraries that want a realloc function (a realloc function encapsulates
+  all the basic capabilities of an allocation library, which is why this is useful)
+*/
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+	return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+	autofree_context = NULL;
+	return 0;
+}
+
+static void talloc_autofree(void)
+{
+	_talloc_free(autofree_context);
+}
+
+/*
+  return a context which will be auto-freed on exit
+  this is useful for reducing the noise in leak reports
+*/
+void *talloc_autofree_context(void)
+{
+	if (autofree_context == NULL) {
+		autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+		talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+		atexit(talloc_autofree);
+	}
+	return autofree_context;
+}
+
+size_t talloc_get_size(const void *context)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL)
+		return 0;
+
+	tc = talloc_chunk_from_ptr(context);
+
+	return tc->size;
+}
+
+/*
+  find a parent of this context that has the given name, if any
+*/
+void *talloc_find_parent_byname(const void *context, const char *name)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (tc->name && strcmp(tc->name, name) == 0) {
+			return TC_PTR_FROM_CHUNK(tc);
+		}
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return NULL;
+}
+
+/*
+  show the parentage of a context
+*/
+void talloc_show_parents(const void *context, FILE *file)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		fprintf(file, "talloc no parents for NULL\n");
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+	while (tc) {
+		fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	fflush(file);
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+int talloc_is_parent(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return 0;
+}
diff --git a/libosmocore/src/timer.c b/libosmocore/src/timer.c
new file mode 100644
index 0000000..37d7d16
--- /dev/null
+++ b/libosmocore/src/timer.c
@@ -0,0 +1,185 @@
+/*
+ * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <osmocore/timer.h>
+
+static LLIST_HEAD(timer_list);
+static struct timeval s_nearest_time;
+static struct timeval s_select_time;
+
+#define MICRO_SECONDS  1000000LL
+
+#define TIME_SMALLER(left, right) \
+        (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec)
+
+void bsc_add_timer(struct timer_list *timer)
+{
+	struct timer_list *list_timer;
+
+	/* TODO: Optimize and remember the closest item... */
+	timer->active = 1;
+
+	/* this might be called from within update_timers */
+	llist_for_each_entry(list_timer, &timer_list, entry)
+		if (timer == list_timer)
+			return;
+
+	timer->in_list = 1;
+	llist_add(&timer->entry, &timer_list);
+}
+
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds)
+{
+	struct timeval current_time;
+
+	gettimeofday(&current_time, NULL);
+	unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+	currentTime += seconds * MICRO_SECONDS + microseconds;
+	timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
+	timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
+	bsc_add_timer(timer);
+}
+
+void bsc_del_timer(struct timer_list *timer)
+{
+	if (timer->in_list) {
+		timer->active = 0;
+		timer->in_list = 0;
+		llist_del(&timer->entry);
+	}
+}
+
+int bsc_timer_pending(struct timer_list *timer)
+{
+	return timer->active;
+}
+
+/*
+ * if we have a nearest time return the delta between the current
+ * time and the time of the nearest timer.
+ * If the nearest timer timed out return NULL and then we will
+ * dispatch everything after the select
+ */
+struct timeval *bsc_nearest_timer()
+{
+	struct timeval current_time;
+
+	if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
+		return NULL;
+
+	if (gettimeofday(&current_time, NULL) == -1)
+		return NULL;
+
+	unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
+	unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+
+	if (nearestTime < currentTime) {
+		s_select_time.tv_sec = 0;
+		s_select_time.tv_usec = 0;
+	} else {
+		s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
+		s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
+	}
+
+	return &s_select_time;
+}
+
+/*
+ * Find the nearest time and update s_nearest_time
+ */
+void bsc_prepare_timers()
+{
+	struct timer_list *timer, *nearest_timer = NULL;
+	llist_for_each_entry(timer, &timer_list, entry) {
+		if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) {
+			nearest_timer = timer;
+		}
+	}
+
+	if (nearest_timer) {
+		s_nearest_time = nearest_timer->timeout;
+	} else {
+		memset(&s_nearest_time, 0, sizeof(struct timeval));
+	}
+}
+
+/*
+ * fire all timers... and remove them
+ */
+int bsc_update_timers()
+{
+	struct timeval current_time;
+	struct timer_list *timer, *tmp;
+	int work = 0;
+
+	gettimeofday(&current_time, NULL);
+
+	/*
+	 * The callbacks might mess with our list and in this case
+	 * even llist_for_each_entry_safe is not safe to use. To allow
+	 * del_timer, add_timer, schedule_timer to be called from within
+	 * the callback we jump through some loops.
+	 *
+	 * First we set the handled flag of each active timer to zero,
+	 * then we iterate over the list and execute the callbacks. As the
+	 * list might have been changed (specially the next) from within
+	 * the callback we have to start over again. Once every callback
+	 * is dispatched we will remove the non-active from the list.
+	 *
+	 * TODO: If this is a performance issue we can poison a global
+	 * variable in add_timer and del_timer and only then restart.
+	 */
+	llist_for_each_entry(timer, &timer_list, entry) {
+		timer->handled = 0;
+	}
+
+restart:
+	llist_for_each_entry(timer, &timer_list, entry) {
+		if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) {
+			timer->handled = 1;
+			timer->active = 0;
+			(*timer->cb)(timer->data);
+			work = 1;
+			goto restart;
+		}
+	}
+
+	llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
+		timer->handled = 0;
+		if (!timer->active) {
+			bsc_del_timer(timer);
+		}
+	}
+
+	return work;
+}
+
+int bsc_timer_check(void)
+{
+	struct timer_list *timer;
+	int i = 0;
+
+	llist_for_each_entry(timer, &timer_list, entry) {
+		i++;
+	}
+	return i;
+}
diff --git a/libosmocore/src/tlv_parser.c b/libosmocore/src/tlv_parser.c
new file mode 100644
index 0000000..407e57a
--- /dev/null
+++ b/libosmocore/src/tlv_parser.c
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/tlv.h>
+
+struct tlv_definition tvlv_att_def;
+
+int tlv_dump(struct tlv_parsed *dec)
+{
+	int i;
+
+	for (i = 0; i <= 0xff; i++) {
+		if (!dec->lv[i].val)
+			continue;
+		printf("T=%02x L=%d\n", i, dec->lv[i].len);
+	}
+	return 0;
+}
+
+/* o_tag:  output: tag found
+ * o_len:  output: length of the data
+ * o_val:  output: pointer to the data
+ * def:     input: a structure defining the valid TLV tags / configurations
+ * buf:     input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ *
+ * Also, returns the number of bytes consumed by the TLV entry
+ */
+int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
+		  const struct tlv_definition *def,
+		  const uint8_t *buf, int buf_len)
+{
+	uint8_t tag;
+	int len;
+
+	tag = *buf;
+	*o_tag = tag;
+
+	/* FIXME: use tables for knwon IEI */
+	switch (def->def[tag].type) {
+	case TLV_TYPE_T:
+		/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
+		*o_val = buf;
+		*o_len = 0;
+		len = 1;
+		break;
+	case TLV_TYPE_TV:
+		*o_val = buf+1;
+		*o_len = 1;
+		len = 2;
+		break;
+	case TLV_TYPE_FIXED:
+		*o_val = buf+1;
+		*o_len = def->def[tag].fixed_len;
+		len = def->def[tag].fixed_len + 1;
+		break;
+	case TLV_TYPE_TLV:
+		/* GSM TS 04.07 11.2.4: Type 4 TLV */
+		if (buf + 1 > buf + buf_len)
+			return -1;
+		*o_val = buf+2;
+		*o_len = *(buf+1);
+		len = *o_len + 2;
+		if (len > buf_len)
+			return -2;
+		break;
+	case TLV_TYPE_TvLV:
+		if (*(buf+1) & 0x80) {
+			/* like TLV, but without highest bit of len */
+			if (buf + 1 > buf + buf_len)
+				return -1;
+			*o_val = buf+2;
+			*o_len = *(buf+1) & 0x7f;
+			len = *o_len + 2;
+			if (len > buf_len)
+				return -2;
+			break;
+		}
+		/* like TL16V, fallthrough */
+	case TLV_TYPE_TL16V:
+		if (2 > buf_len)
+			return -1;
+		*o_val = buf+3;
+		*o_len = *(buf+1) << 8 | *(buf+2);
+		len = *o_len + 3;
+		if (len > buf_len)
+			return -2;
+		break;
+	default:
+		return -3;
+	}
+
+	return len;
+}
+
+/* dec:    output: a caller-allocated pointer to a struct tlv_parsed,
+ * def:     input: a structure defining the valid TLV tags / configurations
+ * buf:     input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ * lv_tag:  input: an initial LV tag at the start of the buffer
+ * lv_tag2: input: a second initial LV tag following lv_tag 
+ */
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
+	      const uint8_t *buf, int buf_len, uint8_t lv_tag,
+	      uint8_t lv_tag2)
+{
+	int ofs = 0, num_parsed = 0;
+	uint16_t len;
+
+	memset(dec, 0, sizeof(*dec));
+
+	if (lv_tag) {
+		if (ofs > buf_len)
+			return -1;
+		dec->lv[lv_tag].val = &buf[ofs+1];
+		dec->lv[lv_tag].len = buf[ofs];
+		len = dec->lv[lv_tag].len + 1;
+		if (ofs + len > buf_len)
+			return -2;
+		num_parsed++;
+		ofs += len;
+	}
+	if (lv_tag2) {
+		if (ofs > buf_len)
+			return -1;
+		dec->lv[lv_tag2].val = &buf[ofs+1];
+		dec->lv[lv_tag2].len = buf[ofs];
+		len = dec->lv[lv_tag2].len + 1;
+		if (ofs + len > buf_len)
+			return -2;
+		num_parsed++;
+		ofs += len;
+	}
+
+	while (ofs < buf_len) {
+		int rv;
+		uint8_t tag;
+		const uint8_t *val;
+
+		rv = tlv_parse_one(&tag, &len, &val, def,
+		                   &buf[ofs], buf_len-ofs);
+		if (rv < 0)
+			return rv;
+		dec->lv[tag].val = val;
+		dec->lv[tag].len = len;
+		ofs += rv;
+		num_parsed++;
+	}
+	//tlv_dump(dec);
+	return num_parsed;
+}
+
+/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
+		if (src->def[i].type == TLV_TYPE_NONE)
+			continue;
+		if (dst->def[i].type == TLV_TYPE_NONE)
+			dst->def[i] = src->def[i];
+	}
+}
+
+static __attribute__((constructor)) void on_dso_load_tlv(void)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
+		tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
+}
diff --git a/libosmocore/src/utils.c b/libosmocore/src/utils.c
new file mode 100644
index 0000000..4dab064
--- /dev/null
+++ b/libosmocore/src/utils.c
@@ -0,0 +1,50 @@
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocore/utils.h>
+
+static char namebuf[255];
+const char *get_value_string(const struct value_string *vs, uint32_t val)
+{
+	int i;
+
+	for (i = 0;; i++) {
+		if (vs[i].value == 0 && vs[i].str == NULL)
+			break;
+		if (vs[i].value == val)
+			return vs[i].str;
+	}
+
+	snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
+	return namebuf;
+}
+
+int get_string_value(const struct value_string *vs, const char *str)
+{
+	int i;
+
+	for (i = 0;; i++) {
+		if (vs[i].value == 0 && vs[i].str == NULL)
+			break;
+		if (!strcasecmp(vs[i].str, str))
+			return vs[i].value;
+	}
+	return -EINVAL;
+}
+
+char bcd2char(uint8_t bcd)
+{
+	if (bcd < 0xa)
+		return '0' + bcd;
+	else
+		return 'A' + (bcd - 0xa);
+}
+
+/* only works for numbers in ascci */
+uint8_t char2bcd(char c)
+{
+	return c - 0x30;
+}
diff --git a/libosmocore/src/write_queue.c b/libosmocore/src/write_queue.c
new file mode 100644
index 0000000..a0ac2d6
--- /dev/null
+++ b/libosmocore/src/write_queue.c
@@ -0,0 +1,85 @@
+/* Generic write queue implementation */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * 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 <osmocore/write_queue.h>
+
+int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
+{
+	struct write_queue *queue;
+
+	queue = container_of(fd, struct write_queue, bfd);
+
+	if (what & BSC_FD_READ)
+		queue->read_cb(fd);
+
+	if (what & BSC_FD_WRITE) {
+		struct msgb *msg;
+
+		fd->when &= ~BSC_FD_WRITE;
+		msg = msgb_dequeue(&queue->msg_queue);
+		if (!msg)
+			return -1;
+
+		--queue->current_length;
+		queue->write_cb(fd, msg);
+		msgb_free(msg);
+
+		if (!llist_empty(&queue->msg_queue))
+			fd->when |= BSC_FD_WRITE;
+	}
+
+	return 0;
+}
+
+void write_queue_init(struct write_queue *queue, int max_length)
+{
+	queue->max_length = max_length;
+	queue->current_length = 0;
+	queue->read_cb = NULL;
+	queue->write_cb = NULL;
+	queue->bfd.cb = write_queue_bfd_cb;
+	INIT_LLIST_HEAD(&queue->msg_queue);
+}
+
+int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
+{
+//	if (queue->current_length + 1 >= queue->max_length)
+//		LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n");
+
+	++queue->current_length;
+	msgb_enqueue(&queue->msg_queue, data);
+	queue->bfd.when |= BSC_FD_WRITE;
+
+	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/libosmocore/tests/Makefile.am b/libosmocore/tests/Makefile.am
new file mode 100644
index 0000000..0119a02c
--- /dev/null
+++ b/libosmocore/tests/Makefile.am
@@ -0,0 +1,3 @@
+if ENABLE_TESTS
+SUBDIRS = timer sms
+endif
diff --git a/libosmocore/tests/sms/Makefile.am b/libosmocore/tests/sms/Makefile.am
new file mode 100644
index 0000000..a8f1ff6
--- /dev/null
+++ b/libosmocore/tests/sms/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = sms_test
+
+sms_test_SOURCES = sms_test.c
+sms_test_LDADD = $(top_builddir)/src/libosmocore.la
diff --git a/libosmocore/tests/sms/sms_test.c b/libosmocore/tests/sms/sms_test.c
new file mode 100644
index 0000000..f5183d5
--- /dev/null
+++ b/libosmocore/tests/sms/sms_test.c
@@ -0,0 +1,47 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <osmocore/msgb.h>
+#include <osmocore/gsm_utils.h>
+
+int main(int argc, char** argv)
+{
+	printf("SMS testing\n");
+	struct msgb *msg;
+	uint8_t *sms;
+	uint8_t i;
+
+        /* test 7-bit coding/decoding */
+	const char *input = "test text";
+	uint8_t length;
+	uint8_t coded[256];
+	char result[256];
+
+	length = gsm_7bit_encode(coded, input);
+	gsm_7bit_decode(result, coded, length);
+	if (strcmp(result, input) != 0) {
+		printf("7 Bit coding failed... life sucks\n");
+		printf("Wanted: '%s' got '%s'\n", input, result);
+	}
+}
diff --git a/libosmocore/tests/timer/Makefile.am b/libosmocore/tests/timer/Makefile.am
new file mode 100644
index 0000000..d3decf5
--- /dev/null
+++ b/libosmocore/tests/timer/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = timer_test
+
+timer_test_SOURCES = timer_test.c
+timer_test_LDADD = $(top_builddir)/src/libosmocore.la
+
diff --git a/libosmocore/tests/timer/timer_test.c b/libosmocore/tests/timer/timer_test.c
new file mode 100644
index 0000000..1b458d8
--- /dev/null
+++ b/libosmocore/tests/timer/timer_test.c
@@ -0,0 +1,77 @@
+/*
+ * (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 <stdio.h>
+
+#include <osmocore/timer.h>
+#include <osmocore/select.h>
+
+#include "../../config.h"
+
+static void timer_fired(void *data);
+
+static struct timer_list timer_one = {
+    .cb = timer_fired,
+    .data = (void*)1,
+};
+
+static struct timer_list timer_two = {
+    .cb = timer_fired,
+    .data = (void*)2,
+};
+
+static struct timer_list timer_three = {
+    .cb = timer_fired,
+    .data = (void*)3,
+};
+
+static void timer_fired(void *_data)
+{
+    unsigned long data = (unsigned long) _data;
+    printf("Fired timer: %lu\n", data);
+
+    if (data == 1) {
+        bsc_schedule_timer(&timer_one, 3, 0);
+        bsc_del_timer(&timer_two);
+    } else if (data == 2) {
+        printf("Should not be fired... bug in del_timer\n");
+    } else if (data == 3) {
+        printf("Timer fired not registering again\n");
+    } else  {
+        printf("wtf... wrong data\n");
+    }
+}
+
+int main(int argc, char** argv)
+{
+    printf("Starting... timer\n");
+
+    bsc_schedule_timer(&timer_one, 3, 0);
+    bsc_schedule_timer(&timer_two, 5, 0);
+    bsc_schedule_timer(&timer_three, 4, 0);
+
+#ifdef HAVE_SYS_SELECT_H
+    while (1) {
+        bsc_select_main(0);
+    }
+#else
+    printf("Select not supported on this platform!\n");
+#endif
+}
diff --git a/openbsc/.gitignore b/openbsc/.gitignore
index 267c906..f968114 100644
--- a/openbsc/.gitignore
+++ b/openbsc/.gitignore
@@ -23,6 +23,9 @@
 missing
 stamp-h1
 
+# git-version-gen magic
+.tarball-version
+.version
 
 
 # apps and app data
diff --git a/openbsc/Makefile.am b/openbsc/Makefile.am
index 7acae75..4ce11ce 100644
--- a/openbsc/Makefile.am
+++ b/openbsc/Makefile.am
@@ -6,5 +6,8 @@
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = openbsc.pc libsccp.pc
 
-#dist-hook:
-#	rm -rf `find $(distdir) -name .svn`
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+	echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+	echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/openbsc/README b/openbsc/README
index 88715f5..51807bb 100644
--- a/openbsc/README
+++ b/openbsc/README
@@ -1,18 +1,30 @@
 About OpenBSC
 =============
 
-OpenBSC is a minimalistic implementation of the GSM Network, with particular
-emphasis on the functionality typically provided by the BSC, MSC, HLR, VLR.
+OpenBSC is a minimalistic implementation of the GSM Network, with
+particular emphasis on the functionality typically provided by the BSC,
+MSC, HLR, VLR and SMSC.
 
-Its only current interface is a mISDN based E1 interface utilizing the A-bis
-protocol between BSC and BTS.  In other words, you can connect an existing
-GSM Base Transceiver Station (BTS) through E1 to OpenBSC.
+Its currently supported interfaces towards the BTS are:
 
-So far, it has only been tested with the Siemens microBTS BS-11.  Test reports
-with other BTS are appreciated!
+ * Classic A-bis over E1 using a mISDN based E1 interface. In other
+   words, you can connect existing GSM Base Transceiver Station (BTS)
+   through E1 to OpenBSC.  So far, we have only tested the Siemens BS-11
+   Test reports with other BTS are much appreciated!
+
+ * A-bis over IP as used by the ip.access nanoBTS product family
 
 This project is still in its early days, and there are lots of areas where it
 doesn't behave as per GSM spec.
 
-December 29, 2008
 	Harald Welte <laforge@gnumonks.org>
+
+
+libosmocore
+===========
+
+Please note that as of March 2010, OpenBSC has a dependency to a library
+called "libosmocore".  You can obtain that library from
+
+	git://git.osmocom.org/libosmocore.git
+
diff --git a/openbsc/configure.in b/openbsc/configure.in
index 71ed10b..615e59d 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -1,7 +1,9 @@
 dnl Process this file with autoconf to produce a configure script
-AC_INIT
+AC_INIT([openbsc],
+	m4_esyscmd([./git-version-gen .tarball-version]),
+	[openbsc-devel@lists.openbsc.org])
 
-AM_INIT_AUTOMAKE(openbsc, 0.0alpha1)
+AM_INIT_AUTOMAKE([dist-bzip2])
 
 dnl kernel style compile messages
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -16,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)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.3)
 
 dnl checks for header files
 AC_HEADER_STDC
diff --git a/openbsc/git-version-gen b/openbsc/git-version-gen
new file mode 100755
index 0000000..f2ad4a7
--- /dev/null
+++ b/openbsc/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#	echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#	echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+    1) ;;
+    *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+    v=`cat $tarball_version_file` || exit 1
+    case $v in
+	*$nl*) v= ;; # reject multi-line output
+	[0-9]*) ;;
+	*) v= ;;
+    esac
+    test -z "$v" \
+	&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+    : # use $v
+elif test -d ./../.git \
+    && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+	  || git describe --abbrev=4 HEAD 2>/dev/null` \
+    && case $v in
+	 [0-9]*) ;;
+	 v[0-9]*) ;;
+	 *) (exit 1) ;;
+       esac
+then
+    # Is this a new git that lists number of commits since the last
+    # tag or the previous older version that did not?
+    #   Newer: v6.10-77-g0f8faeb
+    #   Older: v6.10-g0f8faeb
+    case $v in
+	*-*-*) : git describe is okay three part flavor ;;
+	*-*)
+	    : git describe is older two part flavor
+	    # Recreate the number of commits and rewrite such that the
+	    # result is the same as if we were using the newer version
+	    # of git describe.
+	    vtag=`echo "$v" | sed 's/-.*//'`
+	    numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+	    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+	    ;;
+    esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+    v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+    '') ;;
+    *) # Append the suffix only if there isn't one already.
+	case $v in
+	  *-dirty) ;;
+	  *) v="$v-dirty" ;;
+	esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 483997a..6a87782 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -7,5 +7,5 @@
 		 silent_call.h mgcp.h meas_rep.h rest_octets.h \
 		 system_information.h handover.h mgcp_internal.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_nm.h b/openbsc/include/openbsc/abis_nm.h
index cff91e0..45307e3 100644
--- a/openbsc/include/openbsc/abis_nm.h
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -154,6 +154,7 @@
 				u_int8_t *attr, u_int8_t attr_len);
 int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, 
 				 u_int32_t ip, u_int16_t port, u_int8_t stream);
+void abis_nm_ipaccess_cgi(u_int8_t *buf, struct gsm_bts *bts);
 int ipac_parse_bcch_info(struct ipac_bcch_info *binf, u_int8_t *buf);
 const char *ipacc_testres_name(u_int8_t res);
 
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 ed688c2..daf3bd7 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -4,6 +4,7 @@
 #include <openbsc/meas_rep.h>
 
 #include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/gsm48.h>
 
 struct msgb;
 struct gsm_bts;
@@ -15,8 +16,6 @@
 void gsm0408_allow_everyone(int allow);
 
 int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
-void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, 
-		u_int16_t mnc, u_int16_t lac);
 enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
 enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
 
@@ -25,9 +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_generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
-int gsm48_generate_mid_from_imsi(u_int8_t *buf, const char* imsi);
-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);
@@ -47,8 +43,6 @@
 int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv,
 		      int h_len);
 
-extern const char *gsm0408_cc_msg_names[];
-
 int send_siemens_mrpci(struct gsm_lchan *lchan, u_int8_t *classmark2_lv);
 int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type);
 int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
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 eebe6ab..8dfa588 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -3,14 +3,6 @@
 
 #include <sys/types.h>
 
-struct value_string {
-	unsigned int value;
-	const char *str;
-};
-
-const char *get_value_string(const struct value_string *vs, u_int32_t val);
-int get_string_value(const struct value_string *vs, const char *str);
-
 enum gsm_phys_chan_config {
 	GSM_PCHAN_NONE,
 	GSM_PCHAN_CCCH,
@@ -92,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);
 
 
@@ -190,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;
@@ -212,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;
@@ -254,6 +254,8 @@
 		u_int8_t speech_mode;
 		struct rtp_socket *rtp_socket;
 	} abis_ip;
+
+	struct gsm_subscriber_connection conn;
 };
 
 struct gsm_e1_subslot {
@@ -265,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;
@@ -382,6 +384,10 @@
 struct gsm_bts_gprs_nsvc {
 	struct gsm_bts *bts;
 	int id;
+	u_int16_t nsvci;
+	u_int16_t local_port;
+	u_int16_t remote_port;
+	u_int32_t remote_ip;
 	struct gsm_nm_state nm_state;
 };
 
@@ -470,13 +476,17 @@
 
 	/* Not entirely sure how ip.access specific this is */
 	struct {
+		int enabled;
 		struct {
 			struct gsm_nm_state nm_state;
+			u_int16_t nsei;
 		} nse;
 		struct {
 			struct gsm_nm_state nm_state;
+			u_int16_t bvci;
 		} cell;
 		struct gsm_bts_gprs_nsvc nsvc[2];
+		u_int8_t rac;
 	} gprs;
 	
 	/* transceivers */
@@ -654,9 +664,6 @@
 struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
 				struct gsm_bts *start_bts);
 
-char *gsm_band_name(enum gsm_band band);
-enum gsm_band gsm_band_parse(const char *mhz);
-
 extern void *tall_bsc_ctx;
 extern int ipacc_rtp_direct;
 
@@ -692,6 +699,18 @@
 
 void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
 
+/* A parsed GPRS routing area */
+struct gprs_ra_id {
+	u_int16_t	mnc;
+	u_int16_t	mcc;
+	u_int16_t	lac;
+	u_int8_t	rac;
+};
+
+int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts);
+void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
 struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
 
+int gsm_bts_model_register(struct gsm_bts_model *model);
+
 #endif
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h
index 904a962..86248aa 100644
--- a/openbsc/include/openbsc/ipaccess.h
+++ b/openbsc/include/openbsc/ipaccess.h
@@ -69,8 +69,7 @@
 	char date[14];
 	char text2[10];
 	char version[20];
-	u_int8_t dummy[2];
-	u_int16_t part_length;
+	u_int16_t table_offset;
 	/* stuff i don't know */
 } __attribute__((packed));
 
@@ -90,6 +89,7 @@
 struct sdp_header_item {
 	struct sdp_header_entry header_entry;
 	struct llist_head entry;
+	off_t absolute_offset;
 };
 
 struct sdp_header {
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
index 06d5942..cb8339a 100644
--- a/openbsc/include/openbsc/mncc.h
+++ b/openbsc/include/openbsc/mncc.h
@@ -26,6 +26,7 @@
 #define _MNCC_H
 
 #include <osmocore/linuxlist.h>
+#include <osmocore/mncc.h>
 
 /* One end of a call */
 struct gsm_call {
@@ -109,69 +110,6 @@
 #define MNCC_F_KEYPAD		0x1000
 #define MNCC_F_SIGNAL		0x2000
 
-/* Expanded fields from GSM TS 04.08, Table 10.5.102 */
-struct gsm_mncc_bearer_cap {
-	int		transfer;	/* Information Transfer Capability */
-	int 		mode;		/* Transfer Mode */
-	int		coding;		/* Coding Standard */
-	int		radio;		/* Radio Channel Requirement */
-	int		speech_ctm;	/* CTM text telephony indication */
-	int		speech_ver[8];	/* Speech version indication */
-};
-
-struct gsm_mncc_number {
-	int 		type;
-	int 		plan;
-	int		present;
-	int		screen;
-	char		number[33];
-};
-
-struct gsm_mncc_cause {
-	int		location;
-	int		coding;
-	int		rec;
-	int		rec_val;
-	int		value;
-	int		diag_len;
-	char		diag[32];
-};
-
-struct gsm_mncc_useruser {
-	int		proto;
-	char		info[GSM_MAX_USERUSER + 1]; /* + termination char */
-};
-
-struct gsm_mncc_progress {
-	int		coding;
-	int		location;
-	int 		descr;
-};
-
-struct gsm_mncc_facility {
-	int		len;
-	char		info[GSM_MAX_FACILITY];
-};
-
-struct gsm_mncc_ssversion {
-	int		len;
-	char		info[GSM_MAX_SSVERSION];
-};
-
-struct gsm_mncc_cccap {
-	int		dtmf;
-	int		pcp;
-};
-
-enum {
-	GSM_MNCC_BCAP_SPEECH	= 0,
-	GSM_MNCC_BCAP_UNR_DIG	= 1,
-	GSM_MNCC_BCAP_AUDIO	= 2,
-	GSM_MNCC_BCAP_FAX_G3	= 3,
-	GSM_MNCC_BCAP_OTHER_ITC = 5,
-	GSM_MNCC_BCAP_RESERVED	= 7,
-};
-
 struct gsm_mncc {
 	/* context based information */
 	u_int32_t	msg_type;
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/src/Makefile.am b/openbsc/src/Makefile.am
index 5dac9de..cbc809d 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -1,4 +1,4 @@
-INCLUDES = $(all_includes) -I$(top_srcdir)/include
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
 AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
 AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
 
@@ -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
+		bts_unknown.c bsc_version.c bsc_api.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 99d8dd6..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>
@@ -114,125 +114,117 @@
 	NM_MT_BS11_DELETE_OBJ_NACK,
 };
 
-static const char *nack_names[0xff] = {
-	[NM_MT_LOAD_INIT_NACK]		= "SOFTWARE LOAD INIT",
-	[NM_MT_LOAD_END_NACK]		= "SOFTWARE LOAD END",
-	[NM_MT_SW_ACT_REQ_NACK]		= "SOFTWARE ACTIVATE REQUEST",
-	[NM_MT_ACTIVATE_SW_NACK]	= "ACTIVATE SOFTWARE",
-	[NM_MT_ESTABLISH_TEI_NACK]	= "ESTABLISH TEI",
-	[NM_MT_CONN_TERR_SIGN_NACK]	= "CONNECT TERRESTRIAL SIGNALLING",
-	[NM_MT_DISC_TERR_SIGN_NACK]	= "DISCONNECT TERRESTRIAL SIGNALLING",
-	[NM_MT_CONN_TERR_TRAF_NACK]	= "CONNECT TERRESTRIAL TRAFFIC",
-	[NM_MT_DISC_TERR_TRAF_NACK]	= "DISCONNECT TERRESTRIAL TRAFFIC",
-	[NM_MT_CONN_MDROP_LINK_NACK]	= "CONNECT MULTI-DROP LINK",
-	[NM_MT_DISC_MDROP_LINK_NACK]	= "DISCONNECT MULTI-DROP LINK",
-	[NM_MT_SET_BTS_ATTR_NACK]	= "SET BTS ATTRIBUTE",
-	[NM_MT_SET_RADIO_ATTR_NACK]	= "SET RADIO ATTRIBUTE",
-	[NM_MT_SET_CHAN_ATTR_NACK]	= "SET CHANNEL ATTRIBUTE",
-	[NM_MT_PERF_TEST_NACK]		= "PERFORM TEST",
-	[NM_MT_SEND_TEST_REP_NACK]	= "SEND TEST REPORT",
-	[NM_MT_STOP_TEST_NACK]		= "STOP TEST",
-	[NM_MT_STOP_EVENT_REP_NACK]	= "STOP EVENT REPORT",
-	[NM_MT_REST_EVENT_REP_NACK]	= "RESET EVENT REPORT",
-	[NM_MT_CHG_ADM_STATE_NACK]	= "CHANGE ADMINISTRATIVE STATE",
-	[NM_MT_CHG_ADM_STATE_REQ_NACK]	= "CHANGE ADMINISTRATIVE STATE REQUEST",
-	[NM_MT_REP_OUTST_ALARMS_NACK]	= "REPORT OUTSTANDING ALARMS",
-	[NM_MT_CHANGEOVER_NACK]		= "CHANGEOVER",
-	[NM_MT_OPSTART_NACK]		= "OPSTART",
-	[NM_MT_REINIT_NACK]		= "REINIT",
-	[NM_MT_SET_SITE_OUT_NACK]	= "SET SITE OUTPUT",
-	[NM_MT_CHG_HW_CONF_NACK]	= "CHANGE HARDWARE CONFIGURATION",
-	[NM_MT_GET_ATTR_NACK]		= "GET ATTRIBUTE",
-	[NM_MT_SET_ALARM_THRES_NACK]	= "SET ALARM THRESHOLD",
-	[NM_MT_BS11_BEGIN_DB_TX_NACK]	= "BS11 BEGIN DATABASE TRANSMISSION",
-	[NM_MT_BS11_END_DB_TX_NACK]	= "BS11 END DATABASE TRANSMISSION",
-	[NM_MT_BS11_CREATE_OBJ_NACK]	= "BS11 CREATE OBJECT",
-	[NM_MT_BS11_DELETE_OBJ_NACK]	= "BS11 DELETE OBJECT",
+static const struct value_string nack_names[] = {
+	{ NM_MT_LOAD_INIT_NACK,		"SOFTWARE LOAD INIT" },
+	{ NM_MT_LOAD_END_NACK,		"SOFTWARE LOAD END" },
+	{ NM_MT_SW_ACT_REQ_NACK,	"SOFTWARE ACTIVATE REQUEST" },
+	{ NM_MT_ACTIVATE_SW_NACK,	"ACTIVATE SOFTWARE" },
+	{ NM_MT_ESTABLISH_TEI_NACK,	"ESTABLISH TEI" },
+	{ NM_MT_CONN_TERR_SIGN_NACK,	"CONNECT TERRESTRIAL SIGNALLING" },
+	{ NM_MT_DISC_TERR_SIGN_NACK,	"DISCONNECT TERRESTRIAL SIGNALLING" },
+	{ NM_MT_CONN_TERR_TRAF_NACK,	"CONNECT TERRESTRIAL TRAFFIC" },
+	{ NM_MT_DISC_TERR_TRAF_NACK,	"DISCONNECT TERRESTRIAL TRAFFIC" },
+	{ NM_MT_CONN_MDROP_LINK_NACK,	"CONNECT MULTI-DROP LINK" },
+	{ NM_MT_DISC_MDROP_LINK_NACK,	"DISCONNECT MULTI-DROP LINK" },
+	{ NM_MT_SET_BTS_ATTR_NACK,	"SET BTS ATTRIBUTE" },
+	{ NM_MT_SET_RADIO_ATTR_NACK,	"SET RADIO ATTRIBUTE" },
+	{ NM_MT_SET_CHAN_ATTR_NACK,	"SET CHANNEL ATTRIBUTE" },
+	{ NM_MT_PERF_TEST_NACK,		"PERFORM TEST" },
+	{ NM_MT_SEND_TEST_REP_NACK,	"SEND TEST REPORT" },
+	{ NM_MT_STOP_TEST_NACK,		"STOP TEST" },
+	{ NM_MT_STOP_EVENT_REP_NACK,	"STOP EVENT REPORT" },
+	{ NM_MT_REST_EVENT_REP_NACK,	"RESET EVENT REPORT" },
+	{ NM_MT_CHG_ADM_STATE_NACK,	"CHANGE ADMINISTRATIVE STATE" },
+	{ NM_MT_CHG_ADM_STATE_REQ_NACK,
+				"CHANGE ADMINISTRATIVE STATE REQUEST" },
+	{ NM_MT_REP_OUTST_ALARMS_NACK,	"REPORT OUTSTANDING ALARMS" },
+	{ NM_MT_CHANGEOVER_NACK,	"CHANGEOVER" },
+	{ NM_MT_OPSTART_NACK,		"OPSTART" },
+	{ NM_MT_REINIT_NACK,		"REINIT" },
+	{ NM_MT_SET_SITE_OUT_NACK,	"SET SITE OUTPUT" },
+	{ NM_MT_CHG_HW_CONF_NACK,	"CHANGE HARDWARE CONFIGURATION" },
+	{ NM_MT_GET_ATTR_NACK,		"GET ATTRIBUTE" },
+	{ NM_MT_SET_ALARM_THRES_NACK,	"SET ALARM THRESHOLD" },
+	{ NM_MT_BS11_BEGIN_DB_TX_NACK,	"BS11 BEGIN DATABASE TRANSMISSION" },
+	{ NM_MT_BS11_END_DB_TX_NACK,	"BS11 END DATABASE TRANSMISSION" },
+	{ NM_MT_BS11_CREATE_OBJ_NACK,	"BS11 CREATE OBJECT" },
+	{ NM_MT_BS11_DELETE_OBJ_NACK,	"BS11 DELETE OBJECT" },
+	{ 0,				NULL }
 };
 
 /* Chapter 9.4.36 */
-static const char *nack_cause_names[] = {
+static const struct value_string nack_cause_names[] = {
 	/* General Nack Causes */
-	[NM_NACK_INCORR_STRUCT]		= "Incorrect message structure",
-	[NM_NACK_MSGTYPE_INVAL]		= "Invalid message type value",
-	[NM_NACK_OBJCLASS_INVAL]	= "Invalid Object class value",
-	[NM_NACK_OBJCLASS_NOTSUPP]	= "Object class not supported",
-	[NM_NACK_BTSNR_UNKN]		= "BTS no. unknown",
-	[NM_NACK_TRXNR_UNKN]		= "Baseband Transceiver no. unknown",
-	[NM_NACK_OBJINST_UNKN]		= "Object Instance unknown",
-	[NM_NACK_ATTRID_INVAL]		= "Invalid attribute identifier value",
-	[NM_NACK_ATTRID_NOTSUPP]	= "Attribute identifier not supported",
-	[NM_NACK_PARAM_RANGE]		= "Parameter value outside permitted range",
-	[NM_NACK_ATTRLIST_INCONSISTENT]	= "Inconsistency in attribute list",
-	[NM_NACK_SPEC_IMPL_NOTSUPP]	= "Specified implementation not supported",
-	[NM_NACK_CANT_PERFORM]		= "Message cannot be performed",
+	{ NM_NACK_INCORR_STRUCT,	"Incorrect message structure" },
+	{ NM_NACK_MSGTYPE_INVAL,	"Invalid message type value" },
+	{ NM_NACK_OBJCLASS_INVAL,	"Invalid Object class value" },
+	{ NM_NACK_OBJCLASS_NOTSUPP,	"Object class not supported" },
+	{ NM_NACK_BTSNR_UNKN,		"BTS no. unknown" },
+	{ NM_NACK_TRXNR_UNKN,		"Baseband Transceiver no. unknown" },
+	{ NM_NACK_OBJINST_UNKN,		"Object Instance unknown" },
+	{ NM_NACK_ATTRID_INVAL,		"Invalid attribute identifier value" },
+	{ NM_NACK_ATTRID_NOTSUPP,	"Attribute identifier not supported" },
+	{ NM_NACK_PARAM_RANGE,		"Parameter value outside permitted range" },
+	{ NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" },
+	{ NM_NACK_SPEC_IMPL_NOTSUPP,	"Specified implementation not supported" },
+	{ NM_NACK_CANT_PERFORM,		"Message cannot be performed" },
 	/* Specific Nack Causes */
-	[NM_NACK_RES_NOTIMPL]		= "Resource not implemented",
-	[NM_NACK_RES_NOTAVAIL]		= "Resource not available",
-	[NM_NACK_FREQ_NOTAVAIL]		= "Frequency not available",
-	[NM_NACK_TEST_NOTSUPP]		= "Test not supported",
-	[NM_NACK_CAPACITY_RESTR]	= "Capacity restrictions",
-	[NM_NACK_PHYSCFG_NOTPERFORM]	= "Physical configuration cannot be performed",
-	[NM_NACK_TEST_NOTINIT]		= "Test not initiated",
-	[NM_NACK_PHYSCFG_NOTRESTORE]	= "Physical configuration cannot be restored",
-	[NM_NACK_TEST_NOSUCH]		= "No such test",
-	[NM_NACK_TEST_NOSTOP]		= "Test cannot be stopped",
-	[NM_NACK_MSGINCONSIST_PHYSCFG]	= "Message inconsistent with physical configuration",
-	[NM_NACK_FILE_INCOMPLETE]	= "Complete file notreceived",
-	[NM_NACK_FILE_NOTAVAIL]		= "File not available at destination",
-	[NM_NACK_FILE_NOTACTIVATE]	= "File cannot be activate",
-	[NM_NACK_REQ_NOT_GRANT]		= "Request not granted",
-	[NM_NACK_WAIT]			= "Wait",
-	[NM_NACK_NOTH_REPORT_EXIST]	= "Nothing reportable existing",
-	[NM_NACK_MEAS_NOTSUPP]		= "Measurement not supported",
-	[NM_NACK_MEAS_NOTSTART]		= "Measurement not started",
+	{ NM_NACK_RES_NOTIMPL,		"Resource not implemented" },
+	{ NM_NACK_RES_NOTAVAIL,		"Resource not available" },
+	{ NM_NACK_FREQ_NOTAVAIL,	"Frequency not available" },
+	{ NM_NACK_TEST_NOTSUPP,		"Test not supported" },
+	{ NM_NACK_CAPACITY_RESTR,	"Capacity restrictions" },
+	{ NM_NACK_PHYSCFG_NOTPERFORM,	"Physical configuration cannot be performed" },
+	{ NM_NACK_TEST_NOTINIT,		"Test not initiated" },
+	{ NM_NACK_PHYSCFG_NOTRESTORE,	"Physical configuration cannot be restored" },
+	{ NM_NACK_TEST_NOSUCH,		"No such test" },
+	{ NM_NACK_TEST_NOSTOP,		"Test cannot be stopped" },
+	{ NM_NACK_MSGINCONSIST_PHYSCFG,	"Message inconsistent with physical configuration" },
+	{ NM_NACK_FILE_INCOMPLETE,	"Complete file notreceived" },
+	{ NM_NACK_FILE_NOTAVAIL,	"File not available at destination" },
+	{ NM_NACK_FILE_NOTACTIVATE,	"File cannot be activate" },
+	{ NM_NACK_REQ_NOT_GRANT,	"Request not granted" },
+	{ NM_NACK_WAIT,			"Wait" },
+	{ NM_NACK_NOTH_REPORT_EXIST,	"Nothing reportable existing" },
+	{ NM_NACK_MEAS_NOTSUPP,		"Measurement not supported" },
+	{ NM_NACK_MEAS_NOTSTART,	"Measurement not started" },
+	{ 0,				NULL }
 };
 
-static char namebuf[255];
 static const char *nack_cause_name(u_int8_t cause)
 {
-	if (cause < ARRAY_SIZE(nack_cause_names) && nack_cause_names[cause])
-		return nack_cause_names[cause];
-
-	snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
-	return namebuf;
+	return get_value_string(nack_cause_names, cause);
 }
 
 /* Chapter 9.4.16: Event Type */
-static const char *event_type_names[] = {
-	[NM_EVT_COMM_FAIL]		= "communication failure",
-	[NM_EVT_QOS_FAIL]		= "quality of service failure",
-	[NM_EVT_PROC_FAIL]		= "processing failure",
-	[NM_EVT_EQUIP_FAIL]		= "equipment failure",
-	[NM_EVT_ENV_FAIL]		= "environment failure",
+static const struct value_string event_type_names[] = {
+	{ NM_EVT_COMM_FAIL,		"communication failure" },
+	{ NM_EVT_QOS_FAIL,		"quality of service failure" },
+	{ NM_EVT_PROC_FAIL,		"processing failure" },
+	{ NM_EVT_EQUIP_FAIL,		"equipment failure" },
+	{ NM_EVT_ENV_FAIL,		"environment failure" },
+	{ 0,				NULL }
 };
 
 static const char *event_type_name(u_int8_t cause)
 {
-	if (cause < ARRAY_SIZE(event_type_names) && event_type_names[cause])
-		return event_type_names[cause];
-
-	snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
-	return namebuf;
+	return get_value_string(event_type_names, cause);
 }
 
 /* Chapter 9.4.63: Perceived Severity */
-static const char *severity_names[] = {
-	[NM_SEVER_CEASED]		= "failure ceased",
-	[NM_SEVER_CRITICAL]		= "critical failure",
-	[NM_SEVER_MAJOR]		= "major failure",
-	[NM_SEVER_MINOR]		= "minor failure",
-	[NM_SEVER_WARNING]		= "warning level failure",
-	[NM_SEVER_INDETERMINATE]	= "indeterminate failure",
+static const struct value_string severity_names[] = {
+	{ NM_SEVER_CEASED,		"failure ceased" },
+	{ NM_SEVER_CRITICAL,		"critical failure" },
+	{ NM_SEVER_MAJOR,		"major failure" },
+	{ NM_SEVER_MINOR,		"minor failure" },
+	{ NM_SEVER_WARNING,		"warning level failure" },
+	{ NM_SEVER_INDETERMINATE,	"indeterminate failure" },
+	{ 0,				NULL }
 };
 
 static const char *severity_name(u_int8_t cause)
 {
-	if (cause < ARRAY_SIZE(severity_names) && severity_names[cause])
-		return severity_names[cause];
-
-	snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
-	return namebuf;
+	return get_value_string(severity_names, cause);
 }
 
 /* Attributes that the BSC can set, not only get, according to Section 9.4 */
@@ -427,46 +419,30 @@
 
 static int abis_nm_rcvmsg_sw(struct msgb *mb);
 
+static struct value_string obj_class_names[] = {
+	{ NM_OC_SITE_MANAGER,	"SITE MANAGER" },
+	{ NM_OC_BTS,		"BTS" },
+	{ NM_OC_RADIO_CARRIER,	"RADIO CARRIER" },
+	{ NM_OC_BASEB_TRANSC,	"BASEBAND TRANSCEIVER" },
+	{ NM_OC_CHANNEL,	"CHANNEL" },
+	{ NM_OC_BS11_ADJC,	"ADJC" },
+	{ NM_OC_BS11_HANDOVER,	"HANDOVER" },
+	{ NM_OC_BS11_PWR_CTRL,	"POWER CONTROL" },
+	{ NM_OC_BS11_BTSE,	"BTSE" },
+	{ NM_OC_BS11_RACK,	"RACK" },
+	{ NM_OC_BS11_TEST,	"TEST" },
+	{ NM_OC_BS11_ENVABTSE,	"ENVABTSE" },
+	{ NM_OC_BS11_BPORT,	"BPORT" },
+	{ NM_OC_GPRS_NSE,	"GPRS NSE" },
+	{ NM_OC_GPRS_CELL,	"GPRS CELL" },
+	{ NM_OC_GPRS_NSVC,	"GPRS NSVC" },
+	{ NM_OC_BS11,		"SIEMENSHW" },
+	{ 0,			NULL }
+};
+
 static const char *obj_class_name(u_int8_t oc)
 {
-	switch (oc) {
-	case NM_OC_SITE_MANAGER:
-		return "SITE MANAGER";
-	case NM_OC_BTS:
-		return "BTS";
-	case NM_OC_RADIO_CARRIER:
-		return "RADIO CARRIER";
-	case NM_OC_BASEB_TRANSC:
-		return "BASEBAND TRANSCEIVER";
-	case NM_OC_CHANNEL:
-		return "CHANNEL";
-	case NM_OC_BS11_ADJC:
-		return "ADJC";
-	case NM_OC_BS11_HANDOVER:
-		return "HANDOVER";
-	case NM_OC_BS11_PWR_CTRL:
-		return "POWER CONTROL";
-	case NM_OC_BS11_BTSE:
-		return "BTSE";
-	case NM_OC_BS11_RACK:
-		return "RACK";
-	case NM_OC_BS11_TEST:
-		return "TEST";
-	case NM_OC_BS11_ENVABTSE:
-		return "ENVABTSE";
-	case NM_OC_BS11_BPORT:
-		return "BPORT";
-	case NM_OC_GPRS_NSE:
-		return "GPRS NSE";
-	case NM_OC_GPRS_CELL:
-		return "GPRS CELL";
-	case NM_OC_GPRS_NSVC:
-		return "GPRS NSVC";
-	case NM_OC_BS11:
-		return "SIEMENSHW";
-	}
-
-	return "UNKNOWN";
+	return get_value_string(obj_class_names, oc);
 }
 
 const char *nm_opstate_name(u_int8_t os)
@@ -484,24 +460,22 @@
 }
 
 /* Chapter 9.4.7 */
-static const char *avail_names[] = {
-	"In test",
-	"Failed",
-	"Power off",
-	"Off line",
-	"<not used>",
-	"Dependency",
-	"Degraded",
-	"Not installed",
+static const struct value_string avail_names[] = {
+	{ 0, 	"In test" },
+	{ 1,	"Failed" },
+	{ 2,	"Power off" },
+	{ 3,	"Off line" },
+	/* Not used */
+	{ 5,	"Dependency" },
+	{ 6,	"Degraded" },
+	{ 7,	"Not installed" },
+	{ 0xff, "OK" },
+	{ 0,	NULL }
 };
 
 const char *nm_avail_name(u_int8_t avail)
 {
-	if (avail == 0xff)
-		return "OK";
-	if (avail >= ARRAY_SIZE(avail_names))
-		return "UNKNOWN";
-	return avail_names[avail];
+	return get_value_string(avail_names, avail);
 }
 
 static struct value_string test_names[] = {
@@ -540,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);
 }
@@ -856,28 +830,20 @@
 	const u_int8_t *sw_config;
 	int sw_config_len;
 	int file_id_len;
-	int nack = 0;
 	int ret;
 
 	debugp_foh(foh);
 
 	DEBUGPC(DNM, "SW Activate Request: ");
 
-	if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) {
-		DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class);
-		nack = 1;
-	} else
-		DEBUGPC(DNM, "ACKing and Activating\n");
+	DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n");
 
 	ret = abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class,
 				      foh->obj_inst.bts_nr,
 				      foh->obj_inst.trx_nr,
-				      foh->obj_inst.ts_nr, nack,
+				      foh->obj_inst.ts_nr, 0,
 				      foh->data, oh->length-sizeof(*foh));
 
-	if (nack)
-		return ret;
-
 	abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
 	sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG);
 	sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG);
@@ -968,15 +934,11 @@
 
 		debugp_foh(foh);
 
-		if (nack_names[mt])
-			DEBUGPC(DNM, "%s NACK ", nack_names[mt]);
-			/* FIXME: NACK cause */
-		else
-			DEBUGPC(DNM, "NACK 0x%02x ", mt);
+		DEBUGPC(DNM, "%s NACK ", get_value_string(nack_names, mt));
 
 		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");
@@ -1387,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");
@@ -2271,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)
 {
@@ -2633,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 };
@@ -2712,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 ",
@@ -2726,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");
@@ -2738,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");
@@ -2750,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");
@@ -2761,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");
@@ -2838,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;
@@ -2881,6 +2843,14 @@
 				     attr, attr_len);
 }
 
+void abis_nm_ipaccess_cgi(u_int8_t *buf, struct gsm_bts *bts)
+{
+	/* we simply reuse the GSM48 function and overwrite the RAC
+	 * with the Cell ID */
+	gsm48_ra_id_by_bts(buf, bts);
+	*((u_int16_t *)(buf + 5)) = htons(bts->cell_identity);
+}
+
 void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
 {
 	int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
@@ -2894,21 +2864,18 @@
 			      new_state);
 }
 
-static const char *ipacc_testres_names[] = {
-	[NM_IPACC_TESTRES_SUCCESS]	= "SUCCESS",
-	[NM_IPACC_TESTRES_TIMEOUT]	= "TIMEOUT",
-	[NM_IPACC_TESTRES_NO_CHANS]	= "NO CHANNELS",
-	[NM_IPACC_TESTRES_PARTIAL]	= "PARTIAL",
-	[NM_IPACC_TESTRES_STOPPED]	= "STOPPED",
+static const struct value_string ipacc_testres_names[] = {
+	{ NM_IPACC_TESTRES_SUCCESS,	"SUCCESS" },
+	{ NM_IPACC_TESTRES_TIMEOUT,	"TIMEOUT" },
+	{ NM_IPACC_TESTRES_NO_CHANS,	"NO CHANNELS" },
+	{ NM_IPACC_TESTRES_PARTIAL,	"PARTIAL" },
+	{ NM_IPACC_TESTRES_STOPPED,	"STOPPED" },
+	{ 0,				NULL }
 };
 
 const char *ipacc_testres_name(u_int8_t res)
 {
-	if (res < ARRAY_SIZE(ipacc_testres_names) &&
-	    ipacc_testres_names[res])
-		return ipacc_testres_names[res];
-
-	return "unknown";
+	return get_value_string(ipacc_testres_names, res);
 }
 
 void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_t *buf)
@@ -3000,5 +2967,3 @@
 
 	return 0;
 }
-
-
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index aebd02c..0e572cc 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1,7 +1,7 @@
-/* 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-2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -40,94 +40,13 @@
 #include <openbsc/signal.h>
 #include <openbsc/meas_rep.h>
 #include <openbsc/rtp_proxy.h>
+#include <osmocore/rsl.h>
 
 #define RSL_ALLOC_SIZE		1024
 #define RSL_ALLOC_HEADROOM	128
 
 #define MAX(a, b) (a) >= (b) ? (a) : (b)
 
-static const struct tlv_definition rsl_att_tlvdef = {
-	.def = {
-		[RSL_IE_CHAN_NR]		= { TLV_TYPE_TV },
-		[RSL_IE_LINK_IDENT]		= { TLV_TYPE_TV },
-		[RSL_IE_ACT_TYPE]		= { TLV_TYPE_TV },
-		[RSL_IE_BS_POWER]		= { TLV_TYPE_TV },
-		[RSL_IE_CHAN_IDENT]		= { TLV_TYPE_TLV },
-		[RSL_IE_CHAN_MODE]		= { TLV_TYPE_TLV },
-		[RSL_IE_ENCR_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_FRAME_NUMBER]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_HANDO_REF]		= { TLV_TYPE_TV },
-		[RSL_IE_L1_INFO]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_L3_INFO]		= { TLV_TYPE_TL16V },
-		[RSL_IE_MS_IDENTITY]		= { TLV_TYPE_TLV },
-		[RSL_IE_MS_POWER]		= { TLV_TYPE_TV },
-		[RSL_IE_PAGING_GROUP]		= { TLV_TYPE_TV },
-		[RSL_IE_PAGING_LOAD]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_PYHS_CONTEXT]		= { TLV_TYPE_TLV },
-		[RSL_IE_ACCESS_DELAY]		= { TLV_TYPE_TV },
-		[RSL_IE_RACH_LOAD]		= { TLV_TYPE_TLV },
-		[RSL_IE_REQ_REFERENCE]		= { TLV_TYPE_FIXED, 3 },
-		[RSL_IE_RELEASE_MODE]		= { TLV_TYPE_TV },
-		[RSL_IE_RESOURCE_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_RLM_CAUSE]		= { TLV_TYPE_TLV },
-		[RSL_IE_STARTNG_TIME]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_TIMING_ADVANCE]		= { TLV_TYPE_TV },
-		[RSL_IE_UPLINK_MEAS]		= { TLV_TYPE_TLV },
-		[RSL_IE_CAUSE]			= { TLV_TYPE_TLV },
-		[RSL_IE_MEAS_RES_NR]		= { TLV_TYPE_TV },
-		[RSL_IE_MSG_ID]			= { TLV_TYPE_TV },
-		[RSL_IE_SYSINFO_TYPE]		= { TLV_TYPE_TV },
-		[RSL_IE_MS_POWER_PARAM]		= { TLV_TYPE_TLV },
-		[RSL_IE_BS_POWER_PARAM]		= { TLV_TYPE_TLV },
-		[RSL_IE_PREPROC_PARAM]		= { TLV_TYPE_TLV },
-		[RSL_IE_PREPROC_MEAS]		= { TLV_TYPE_TLV },
-		[RSL_IE_IMM_ASS_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_SMSCB_INFO]		= { TLV_TYPE_FIXED, 23 },
-		[RSL_IE_MS_TIMING_OFFSET]	= { TLV_TYPE_TV },
-		[RSL_IE_ERR_MSG]		= { TLV_TYPE_TLV },
-		[RSL_IE_FULL_BCCH_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_CHAN_NEEDED]		= { TLV_TYPE_TV },
-		[RSL_IE_CB_CMD_TYPE]		= { TLV_TYPE_TV },
-		[RSL_IE_SMSCB_MSG]		= { TLV_TYPE_TLV },
-		[RSL_IE_FULL_IMM_ASS_INFO]	= { TLV_TYPE_TLV },
-		[RSL_IE_SACCH_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_CBCH_LOAD_INFO]		= { TLV_TYPE_TV },
-		[RSL_IE_SMSCB_CHAN_INDICATOR]	= { TLV_TYPE_TV },
-		[RSL_IE_GROUP_CALL_REF]		= { TLV_TYPE_TLV },
-		[RSL_IE_CHAN_DESC]		= { TLV_TYPE_TLV },
-		[RSL_IE_NCH_DRX_INFO]		= { TLV_TYPE_TLV },
-		[RSL_IE_CMD_INDICATOR]		= { TLV_TYPE_TLV },
-		[RSL_IE_EMLPP_PRIO]		= { TLV_TYPE_TV },
-		[RSL_IE_UIC]			= { TLV_TYPE_TLV },
-		[RSL_IE_MAIN_CHAN_REF]		= { TLV_TYPE_TV },
-		[RSL_IE_MR_CONFIG]		= { TLV_TYPE_TLV },
-		[RSL_IE_MR_CONTROL]		= { TLV_TYPE_TV },
-		[RSL_IE_SUP_CODEC_TYPES]	= { TLV_TYPE_TLV },
-		[RSL_IE_CODEC_CONFIG]		= { TLV_TYPE_TLV },
-		[RSL_IE_RTD]			= { TLV_TYPE_TV },
-		[RSL_IE_TFO_STATUS]		= { TLV_TYPE_TV },
-		[RSL_IE_LLP_APDU]		= { TLV_TYPE_TLV },
-		[RSL_IE_SIEMENS_MRPCI]		= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_PROXY_UDP]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_IPAC_BSCMPL_TOUT]	= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_REMOTE_IP]		= { TLV_TYPE_FIXED, 4 },
-		[RSL_IE_IPAC_REMOTE_PORT]	= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_IPAC_RTP_PAYLOAD]	= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_LOCAL_PORT]	= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_IPAC_SPEECH_MODE]	= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_LOCAL_IP]		= { TLV_TYPE_FIXED, 4 },
-		[RSL_IE_IPAC_CONN_ID]		= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_IPAC_RTP_CSD_FMT]	= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_RTP_JIT_BUF]	= { TLV_TYPE_FIXED, 2 },
-		[RSL_IE_IPAC_RTP_COMPR]		= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_RTP_PAYLOAD2]	= { TLV_TYPE_TV },
-		[RSL_IE_IPAC_RTP_MPLEX]		= { TLV_TYPE_FIXED, 8 },
-		[RSL_IE_IPAC_RTP_MPLEX_ID]	= { TLV_TYPE_TV },
-	},
-};
-#define rsl_tlv_parse(dec, buf, len)     \
-			tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
-
 static u_int8_t mdisc_by_msgtype(u_int8_t msg_type)
 {
 	/* mask off the transparent bit ? */
@@ -155,44 +74,6 @@
 	dh->ie_chan = RSL_IE_CHAN_NR;
 }
 
-static inline void init_llm_hdr(struct abis_rsl_rll_hdr *dh,
-				  u_int8_t msg_type)
-{
-	/* dh->c.msg_discr = mdisc_by_msgtype(msg_type); */
-	dh->c.msg_discr = ABIS_RSL_MDISC_RLL;
-	dh->c.msg_type = msg_type;
-	dh->ie_chan = RSL_IE_CHAN_NR;
-	dh->ie_link_id = RSL_IE_LINK_IDENT;
-}
-
-
-/* encode channel number as per Section 9.3.1 */
-u_int8_t rsl_enc_chan_nr(u_int8_t type, u_int8_t subch, u_int8_t timeslot)
-{
-	u_int8_t ret;
-
-	ret = (timeslot & 0x07) | type;
-	
-	switch (type) {
-	case RSL_CHAN_Lm_ACCHs:
-		subch &= 0x01;
-		break;
-	case RSL_CHAN_SDCCH4_ACCH:
-		subch &= 0x07;
-		break;
-	case RSL_CHAN_SDCCH8_ACCH:
-		subch &= 0x07;
-		break;
-	default:
-		/* no subchannels allowed */
-		subch = 0x00;
-		break;
-	}
-	ret |= (subch << 3);
-
-	return ret;
-}
-
 /* determine logical channel based on TRX and channel number IE */
 struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
 {
@@ -237,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;
 }
@@ -335,74 +216,13 @@
 	return lchan->encr.key_len + 1;
 }
 
-
-static const char *rsl_err_vals[0xff] = {
-	[RSL_ERR_RADIO_IF_FAIL] =	"Radio Interface Failure",
-	[RSL_ERR_RADIO_LINK_FAIL] =	"Radio Link Failure",
-	[RSL_ERR_HANDOVER_ACC_FAIL] =	"Handover Access Failure",
-	[RSL_ERR_TALKER_ACC_FAIL] =	"Talker Access Failure",
-	[RSL_ERR_OM_INTERVENTION] =	"O&M Intervention",
-	[RSL_ERR_NORMAL_UNSPEC] =	"Normal event, unspecified",
-	[RSL_ERR_T_MSRFPCI_EXP] =	"Siemens: T_MSRFPCI Expired",
-	[RSL_ERR_EQUIPMENT_FAIL] =	"Equipment Failure",
-	[RSL_ERR_RR_UNAVAIL] =		"Radio Resource not available",
-	[RSL_ERR_TERR_CH_FAIL] =	"Terrestrial Channel Failure",
-	[RSL_ERR_CCCH_OVERLOAD] =	"CCCH Overload",
-	[RSL_ERR_ACCH_OVERLOAD] =	"ACCH Overload",
-	[RSL_ERR_PROCESSOR_OVERLOAD] =	"Processor Overload",
-	[RSL_ERR_RES_UNAVAIL] =		"Resource not available, unspecified",
-	[RSL_ERR_TRANSC_UNAVAIL] =	"Transcoding not available",
-	[RSL_ERR_SERV_OPT_UNAVAIL] =	"Service or Option not available",
-	[RSL_ERR_ENCR_UNIMPL] =		"Encryption algorithm not implemented",
-	[RSL_ERR_SERV_OPT_UNIMPL] =	"Service or Option not implemented",
-	[RSL_ERR_RCH_ALR_ACTV_ALLOC] =	"Radio channel already activated",
-	[RSL_ERR_INVALID_MESSAGE] =	"Invalid Message, unspecified",
-	[RSL_ERR_MSG_DISCR] =		"Message Discriminator Error",
-	[RSL_ERR_MSG_TYPE] =		"Message Type Error",
-	[RSL_ERR_MSG_SEQ] =		"Message Sequence Error",
-	[RSL_ERR_IE_ERROR] =		"General IE error",
-	[RSL_ERR_MAND_IE_ERROR] =	"Mandatory IE error",
-	[RSL_ERR_OPT_IE_ERROR] =	"Optional IE error",
-	[RSL_ERR_IE_NONEXIST] =		"IE non-existent",
-	[RSL_ERR_IE_LENGTH] =		"IE length error",
-	[RSL_ERR_IE_CONTENT] =		"IE content error",
-	[RSL_ERR_PROTO] =		"Protocol error, unspecified",
-	[RSL_ERR_INTERWORKING] =	"Interworking error, unspecified",
-};
-
-static const struct value_string rlm_cause_strs[] = {
-	{ RLL_CAUSE_T200_EXPIRED,	"Timer T200 expired (N200+1) times" },
-	{ RLL_CAUSE_REEST_REQ,		"Re-establishment request" },
-	{ RLL_CAUSE_UNSOL_UA_RESP,	"Unsolicited UA response" },
-	{ RLL_CAUSE_UNSOL_DM_RESP,	"Unsolicited DM response" },
-	{ RLL_CAUSE_UNSOL_DM_RESP_MF,	"Unsolicited DM response, multiple frame" },
-	{ RLL_CAUSE_UNSOL_SPRV_RESP,	"Unsolicited supervisory response" },
-	{ RLL_CAUSE_SEQ_ERR,		"Sequence Error" },
-	{ RLL_CAUSE_UFRM_INC_PARAM,	"U-Frame with incorrect parameters" },
-	{ RLL_CAUSE_SFRM_INC_PARAM,	"S-Frame with incorrect parameters" },
-	{ RLL_CAUSE_IFRM_INC_MBITS,	"I-Frame with incorrect use of M bit" },
-	{ RLL_CAUSE_IFRM_INC_LEN,	"I-Frame with incorrect length" },
-	{ RLL_CAUSE_FRM_UNIMPL,		"Fraeme not implemented" },
-	{ RLL_CAUSE_SABM_MF,		"SABM command, multiple frame established state" },
-	{ RLL_CAUSE_SABM_INFO_NOTALL,	"SABM frame with information not allowed in this state" },
-	{ 0,				NULL },
-};
-
-static const char *rsl_err_name(u_int8_t err)
-{
-	if (rsl_err_vals[err])
-		return rsl_err_vals[err];
-	else
-		return "unknown";
-}
-
 static void print_rsl_cause(int lvl, const u_int8_t *cause_v, u_int8_t cause_len)
 {
 	int i;
 
 	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]);
 }
 
@@ -425,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;
@@ -596,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;
@@ -867,23 +687,13 @@
 /* Chapter 8.3.1 */
 int rsl_data_request(struct msgb *msg, u_int8_t link_id)
 {
-	u_int8_t l3_len = msg->tail - (u_int8_t *)msgb_l3(msg);
-	struct abis_rsl_rll_hdr *rh;
-
 	if (msg->lchan == NULL) {
 		LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n");
 		return -EINVAL;
 	}
 
-	/* First push the L3 IE tag and length */
-	msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
-
-	/* Then push the RSL header */
-	rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
-	init_llm_hdr(rh, RSL_MT_DATA_REQ);
-	rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
-	rh->chan_nr = lchan2chan_nr(msg->lchan);
-	rh->link_id = link_id;
+	rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, lchan2chan_nr(msg->lchan),
+			link_id, 1);
 
 	msg->trx = msg->lchan->ts->trx;
 
@@ -894,15 +704,10 @@
 /* Chapter 8.3.1 */
 int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id)
 {
-	struct msgb *msg = rsl_msgb_alloc();
-	struct abis_rsl_rll_hdr *rh;
+	struct msgb *msg;
 
-	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
-	init_llm_hdr(rh, RSL_MT_EST_REQ);
-	//rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
-	rh->chan_nr = lchan2chan_nr(lchan);
-	rh->link_id = link_id;
-
+	msg = rsl_rll_simple(RSL_MT_EST_REQ, lchan2chan_nr(lchan),
+			     link_id, 0);
 	msg->trx = lchan->ts->trx;
 
 	return abis_rsl_sendmsg(msg);
@@ -915,14 +720,11 @@
    lchan_free() */
 int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
 {
-	struct msgb *msg = rsl_msgb_alloc();
-	struct abis_rsl_rll_hdr *rh;
 
-	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
-	init_llm_hdr(rh, RSL_MT_REL_REQ);
-	//rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
-	rh->chan_nr = lchan2chan_nr(lchan);
-	rh->link_id = link_id;
+	struct msgb *msg;
+
+	msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan),
+			     link_id, 0);
 	msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0);	/* normal release */
 
 	lchan->state = LCHAN_S_REL_REQ;
@@ -976,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);
@@ -1190,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);
@@ -1252,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;
@@ -1432,7 +1236,7 @@
 
 	LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n",
 		gsm_lchan_name(msg->lchan),
-		get_value_string(rlm_cause_strs, rlm_cause[1]));
+		rsl_rlm_cause_name(rlm_cause[1]));
 
 	rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
 
@@ -1442,7 +1246,7 @@
 	return 0;
 }
 
-/*	ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST 
+/*	ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
 	0x02, 0x06,
 	0x01, 0x20,
 	0x02, 0x00,
@@ -1462,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];
@@ -1474,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];
@@ -1689,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;
 
@@ -1873,45 +1684,6 @@
 	return rc;
 }
 
-
-/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
-int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf)
-{
-	switch (ccch_conf) {
-	case RSL_BCCH_CCCH_CONF_1_NC:
-		return 1;
-	case RSL_BCCH_CCCH_CONF_1_C:
-		return 1;
-	case RSL_BCCH_CCCH_CONF_2_NC:
-		return 2;
-	case RSL_BCCH_CCCH_CONF_3_NC:
-		return 3;
-	case RSL_BCCH_CCCH_CONF_4_NC:
-		return 4;
-	default:
-		return -1;
-	}
-}
-
-/* Section 3.3.2.3 TS 05.02 */
-int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
-{
-	switch (ccch_conf) {
-	case RSL_BCCH_CCCH_CONF_1_NC:
-		return 0;
-	case RSL_BCCH_CCCH_CONF_1_C:
-		return 1;
-	case RSL_BCCH_CCCH_CONF_2_NC:
-		return 0;
-	case RSL_BCCH_CCCH_CONF_3_NC:
-		return 0;
-	case RSL_BCCH_CCCH_CONF_4_NC:
-		return 0;
-	default:
-		return -1;
-	}
-}
-
 /* From Table 10.5.33 of GSM 04.08 */
 int rsl_number_of_paging_subchannels(struct gsm_bts *bts)
 {
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 49c9d36..a50d4ab 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -38,11 +38,12 @@
 #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";
-
+extern const char *openbsc_version;
+extern const char *openbsc_copyright;
 
 /* timer to store statistics */
 #define DB_SYNC_INTERVAL	60, 0
@@ -75,12 +76,25 @@
 	printf("  Some useful help...\n");
 	printf("  -h --help this text\n");
 	printf("  -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
-	printf("  -s --disable-color\n");
 	printf("  -c --config-file filename The config file to use.\n");
+	printf("  -s --disable-color\n");
 	printf("  -l --database db-name The database to use\n");
+	printf("  -a --authorize-everyone. Authorize every new subscriber. Dangerous!.\n");
 	printf("  -p --pcap file  The filename of the pcap file\n");
 	printf("  -T --timestamp Prefix every log line with a timestamp\n");
+	printf("  -V --version. Print the version of OpenBSC.\n");
 	printf("  -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n");
+	printf("  -e --log-level number. Set a global loglevel.\n");
+}
+
+static void print_version()
+{
+	printf("%s\n", openbsc_version);
+}
+
+static void print_copyright()
+{
+	puts(openbsc_copyright);
 }
 
 static void handle_options(int argc, char** argv)
@@ -96,11 +110,13 @@
 			{"authorize-everyone", 0, 0, 'a'},
 			{"pcap", 1, 0, 'p'},
 			{"timestamp", 0, 0, 'T'},
+			{"version", 0, 0, 'V' },
 			{"rtp-proxy", 0, 0, 'P'},
+			{"log-level", 1, 0, 'e'},
 			{0, 0, 0, 0}
 		};
 
-		c = getopt_long(argc, argv, "hd:sl:ar:p:TPc:",
+		c = getopt_long(argc, argv, "hd:sl:ar:p:TPVc:e:",
 				long_options, &option_index);
 		if (c == -1)
 			break;
@@ -111,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);
@@ -126,11 +142,20 @@
 			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':
+			log_set_log_level(stderr_target, atoi(optarg));
+			break;
+		case 'V':
+			print_version();
+			printf("\n");
+			print_copyright();
+			exit(0);
+			break;
 		default:
 			/* ignore */
 			break;
@@ -186,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);
@@ -237,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_init.c b/openbsc/src/bsc_init.c
index 57fc4b3..f343662 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -330,6 +330,7 @@
 	NM_ATT_NY1, 10, /* 10 retransmissions of physical config */
 	NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
 	NM_ATT_BSIC, HARDCODED_BSIC,
+	NM_ATT_IPACC_CGI, 0, 7,  0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00,
 };
 
 static unsigned char nanobts_attr_radio[] = {
@@ -337,6 +338,66 @@
 	NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
 };
 
+static unsigned char nanobts_attr_nse[] = {
+	NM_ATT_IPACC_NSEI, 0, 2,  0x03, 0x9d, /* NSEI 925 */
+	NM_ATT_IPACC_NS_CFG, 0, 7,  3,  /* (un)blocking timer (Tns-block) */
+				    3,  /* (un)blocking retries */
+				    3,  /* reset timer (Tns-reset) */
+				    3,  /* reset retries */
+				    30,  /* test timer (Tns-test) */
+				    3,  /* alive timer (Tns-alive) */
+				    10, /* alive retrires */
+	NM_ATT_IPACC_BSSGP_CFG, 0, 11,
+				    3,  /* blockimg timer (T1) */
+				    3,  /* blocking retries */
+				    3,  /* unblocking retries */
+				    3,  /* reset timer */
+				    3,  /* reset retries */
+				    10, /* suspend timer (T3) in 100ms */
+				    3,  /* suspend retries */
+				    10, /* resume timer (T4) in 100ms */
+				    3,  /* resume retries */
+				    10, /* capability update timer (T5) */
+				    3,  /* capability update retries */
+};
+
+static unsigned char nanobts_attr_cell[] = {
+	NM_ATT_IPACC_RAC, 0, 1,  1, /* routing area code */
+	NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2,
+		5,	/* repeat time (50ms) */
+		3,	/* repeat count */
+	NM_ATT_IPACC_BVCI, 0, 2,  0x03, 0x9d, /* BVCI 925 */
+	NM_ATT_IPACC_RLC_CFG, 0, 9,
+		20, 	/* T3142 */
+		5, 	/* T3169 */
+		5,	/* T3191 */
+		200,	/* T3193 */
+		5,	/* T3195 */
+		10,	/* N3101 */
+		4,	/* N3103 */
+		8,	/* N3105 */
+		15,	/* RLC CV countdown */
+	NM_ATT_IPACC_CODING_SCHEMES, 0, 2,  0x0f, 0x00,
+	NM_ATT_IPACC_RLC_CFG_2, 0, 5,
+		0x00, 250,
+		0x00, 250,
+		2,	/* MCS2 */
+#if 0
+	/* EDGE model only, breaks older models.
+	 * Should inquire the BTS capabilities */
+	NM_ATT_IPACC_RLC_CFG_3, 0, 1,
+		2,	/* MCS2 */
+#endif
+};
+
+static unsigned char nanobts_attr_nsvc0[] = {
+	NM_ATT_IPACC_NSVCI, 0, 2,  0x03, 0x9d, /* 925 */
+	NM_ATT_IPACC_NS_LINK_CFG, 0, 8,
+		0x59, 0xd8, /* remote udp port (23000) */
+		192, 168, 100, 11, /* remote ip address */
+		0x59, 0xd8, /* local udp port (23000) */
+};
+
 /* Callback function to be called whenever we get a GSM 12.21 state change event */
 int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
 		   struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
@@ -344,6 +405,7 @@
 	struct gsm_bts *bts;
 	struct gsm_bts_trx *trx;
 	struct gsm_bts_trx_ts *ts;
+	struct gsm_bts_gprs_nsvc *nsvc;
 
 	/* This event-driven BTS setup is currently only required on nanoBTS */
 
@@ -397,6 +459,53 @@
 			abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
 					trx->nr, 0xff);
 		break;
+	case NM_OC_GPRS_NSE:
+		bts = container_of(obj, struct gsm_bts, gprs.nse);
+		if (!bts->gprs.enabled)
+			break;
+		if (new_state->availability == 5) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  0xff, 0xff, nanobts_attr_nse,
+						  sizeof(nanobts_attr_nse));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					0xff, 0xff);
+			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
+					      0xff, 0xff, NM_STATE_UNLOCKED);
+		}
+		break;
+	case NM_OC_GPRS_CELL:
+		bts = container_of(obj, struct gsm_bts, gprs.cell);
+		if (!bts->gprs.enabled)
+			break;
+		if (new_state->availability == 5) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  0, 0xff, nanobts_attr_cell,
+						  sizeof(nanobts_attr_cell));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					0, 0xff);
+			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
+					      0, 0xff, NM_STATE_UNLOCKED);
+		}
+		break;
+	case NM_OC_GPRS_NSVC:
+		nsvc = obj;
+		bts = nsvc->bts;
+		if (!bts->gprs.enabled)
+			break;
+	        /* We skip NSVC1 since we only use NSVC0 */
+		if (nsvc->id == 1)
+			break;
+		if (new_state->availability == NM_AVSTATE_OFF_LINE) {
+			abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
+						  nsvc->id, 0xff,
+						  nanobts_attr_nsvc0,
+						  sizeof(nanobts_attr_nsvc0));
+			abis_nm_opstart(bts, obj_class, bts->bts_nr,
+					nsvc->id, 0xff);
+			abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
+					      nsvc->id, 0xff,
+					      NM_STATE_UNLOCKED);
+		}
 	default:
 		break;
 	}
@@ -689,14 +798,14 @@
 			DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
 			rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
 		}
-#ifdef GPRS
-		i = 13;
-		rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
-		if (rc < 0)
-			goto err_out;
-		DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
-		rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
-#endif
+		if (bts->gprs.enabled) {
+			i = 13;
+			rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
+			if (rc < 0)
+				goto err_out;
+			DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+			rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
+		}
 	}
 
 	i = 5;
@@ -745,11 +854,37 @@
 
 	/* patch BSIC */
 	bs11_attr_bts[1] = bts->bsic;
-	nanobts_attr_bts[sizeof(nanobts_attr_bts)-1] = bts->bsic;
+	nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic;
+
+	/* patch CGI */
+	abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts);
 
 	/* patch the power reduction */
 	bs11_attr_radio[5] = bts->c0->max_power_red / 2;
 	nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
+
+	/* patch NSEI */
+	nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8;
+	nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff;
+
+	/* patch NSVCI */
+	nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8;
+	nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff;
+
+	/* patch IP address as SGSN IP */
+	*(u_int16_t *)(nanobts_attr_nsvc0+8) =
+				htons(bts->gprs.nsvc[0].remote_port);
+	*(u_int32_t *)(nanobts_attr_nsvc0+10) =
+				htonl(bts->gprs.nsvc[0].remote_ip);
+	*(u_int16_t *)(nanobts_attr_nsvc0+14) =
+				htons(bts->gprs.nsvc[0].local_port);
+
+	/* patch BVCI */
+	nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8;
+	nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff;
+	/* patch RAC */
+	nanobts_attr_cell[3] = bts->gprs.rac;
+
 }
 
 static void bootstrap_rsl(struct gsm_bts_trx *trx)
diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c
index e9d6f25..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)
@@ -80,11 +84,11 @@
 	/* If we are a TCH and not in signalling mode, we need to
 	 * indicate that the new RLL connection is to be made on the SACCH */
 	if ((lchan->type == GSM_LCHAN_TCH_F ||
-	     lchan->type == GSM_LCHAN_TCH_H) &&
-	    lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+	     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/bsc_version.c b/openbsc/src/bsc_version.c
new file mode 100644
index 0000000..0266194
--- /dev/null
+++ b/openbsc/src/bsc_version.c
@@ -0,0 +1,32 @@
+/* Hold the copyright and version string */
+/* (C) 2010 by Harald Welte <laforge@gnumonks.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 "bscconfig.h"
+
+const char *openbsc_version = "OpenBSC " PACKAGE_VERSION;
+const char *openbsc_copyright =
+	"Copyright (C) 2008-2010 Harald Welte, Holger Freyther\n"
+	"Contributions by Daniel Willmann, Jan Lübbe,Stefan Schmidt\n"
+	"Dieter Spaar, Andreas Eversberg\n\n"
+	"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+	"This is free software: you are free to change and redistribute it.\n"
+	"There is NO WARRANTY, to the extent permitted by law.\n";
+
+
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..312d0f2 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') "
@@ -1014,7 +1014,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 +1041,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 +1049,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 +1133,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 7488cd6..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,426 +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,
+	},
 };
 
-const char *get_value_string(const struct value_string *vs, u_int32_t val)
-{
-	int i;
-
-	for (i = 0;; i++) {
-		if (vs[i].value == 0 && vs[i].str == NULL)
-			break;
-		if (vs[i].value == val)
-			return vs[i].str;
-	}
-	return "unknown";
-}
-
-int get_string_value(const struct value_string *vs, const char *str)
-{
-	int i;
-
-	for (i = 0;; i++) {
-		if (vs[i].value == 0 && vs[i].str == NULL)
-			break;
-		if (!strcasecmp(vs[i].str, str))
-			return vs[i].value;
-	}
-	return -EINVAL;
-}
-
-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(DDB, "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 (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 3aebd7f..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>
@@ -48,128 +48,17 @@
 #include <openbsc/trau_mux.h>
 #include <openbsc/rtp_proxy.h>
 #include <osmocore/talloc.h>
+#include <osmocore/gsm48.h>
 #include <openbsc/transaction.h>
 #include <openbsc/ussd.h>
 #include <openbsc/silent_call.h>
 
-#define GSM_MAX_FACILITY       128
-#define GSM_MAX_SSVERSION      128
-#define GSM_MAX_USERUSER       128
-
 void *tall_locop_ctx;
 
-static const struct tlv_definition rsl_att_tlvdef = {
-	.def = {
-		[GSM48_IE_MOBILE_ID]	= { TLV_TYPE_TLV },
-		[GSM48_IE_NAME_LONG]	= { TLV_TYPE_TLV },
-		[GSM48_IE_NAME_SHORT]	= { TLV_TYPE_TLV },
-		[GSM48_IE_UTC]		= { TLV_TYPE_TV },
-		[GSM48_IE_NET_TIME_TZ]	= { TLV_TYPE_FIXED, 7 },
-		[GSM48_IE_LSA_IDENT]	= { TLV_TYPE_TLV },
-
-		[GSM48_IE_BEARER_CAP]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CAUSE]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CC_CAP]	= { TLV_TYPE_TLV },
-		[GSM48_IE_ALERT]	= { TLV_TYPE_TLV },
-		[GSM48_IE_FACILITY]	= { TLV_TYPE_TLV },
-		[GSM48_IE_PROGR_IND]	= { TLV_TYPE_TLV },
-		[GSM48_IE_AUX_STATUS]	= { TLV_TYPE_TLV },
-		[GSM48_IE_NOTIFY]	= { TLV_TYPE_TV },
-		[GSM48_IE_KPD_FACILITY]	= { TLV_TYPE_TV },
-		[GSM48_IE_SIGNAL]	= { TLV_TYPE_TV },
-		[GSM48_IE_CONN_BCD]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CONN_SUB]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CALLING_BCD]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CALLING_SUB]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CALLED_BCD]	= { TLV_TYPE_TLV },
-		[GSM48_IE_CALLED_SUB]	= { TLV_TYPE_TLV },
-		[GSM48_IE_REDIR_BCD]	= { TLV_TYPE_TLV },
-		[GSM48_IE_REDIR_SUB]	= { TLV_TYPE_TLV },
-		[GSM48_IE_LOWL_COMPAT]	= { TLV_TYPE_TLV },
-		[GSM48_IE_HIGHL_COMPAT]	= { TLV_TYPE_TLV },
-		[GSM48_IE_USER_USER]	= { TLV_TYPE_TLV },
-		[GSM48_IE_SS_VERS]	= { TLV_TYPE_TLV },
-		[GSM48_IE_MORE_DATA]	= { TLV_TYPE_T },
-		[GSM48_IE_CLIR_SUPP]	= { TLV_TYPE_T },
-		[GSM48_IE_CLIR_INVOC]	= { TLV_TYPE_T },
-		[GSM48_IE_REV_C_SETUP]	= { TLV_TYPE_T },
-		[GSM48_IE_REPEAT_CIR]   = { TLV_TYPE_T },
-		[GSM48_IE_REPEAT_SEQ]   = { TLV_TYPE_T },
-		/* FIXME: more elements */
-	},
-};
-
-static const char *rr_cause_names[] = {
-	[GSM48_RR_CAUSE_NORMAL]			= "Normal event",
-	[GSM48_RR_CAUSE_ABNORMAL_UNSPEC]	= "Abnormal release, unspecified",
-	[GSM48_RR_CAUSE_ABNORMAL_UNACCT]	= "Abnormal release, channel unacceptable",
-	[GSM48_RR_CAUSE_ABNORMAL_TIMER]		= "Abnormal release, timer expired",
-	[GSM48_RR_CAUSE_ABNORMAL_NOACT]		= "Abnormal release, no activity on radio path",
-	[GSM48_RR_CAUSE_PREMPTIVE_REL]		= "Preemptive release",
-	[GSM48_RR_CAUSE_HNDOVER_IMP]		= "Handover impossible, timing advance out of range",
-	[GSM48_RR_CAUSE_CHAN_MODE_UNACCT]	= "Channel mode unacceptable",
-	[GSM48_RR_CAUSE_FREQ_NOT_IMPL]		= "Frequency not implemented",
-	[GSM48_RR_CAUSE_CALL_CLEARED]		= "Call already cleared",
-	[GSM48_RR_CAUSE_SEMANT_INCORR]		= "Semantically incorrect message",
-	[GSM48_RR_CAUSE_INVALID_MAND_INF]	= "Invalid mandatory information",
-	[GSM48_RR_CAUSE_MSG_TYPE_N]		= "Message type non-existant or not implemented",
-	[GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT]	= "Message type not compatible with protocol state",
-	[GSM48_RR_CAUSE_COND_IE_ERROR]		= "Conditional IE error",
-	[GSM48_RR_CAUSE_NO_CELL_ALLOC_A]	= "No cell allocation available",
-	[GSM48_RR_CAUSE_PROT_ERROR_UNSPC]	= "Protocol error unspecified",
-};
-
-static const char *cc_state_names[] = {
-	"NULL",
-	"INITIATED",
-	"illegal state 2",
-	"MO_CALL_PROC",
-	"CALL_DELIVERED",
-	"illegal state 5",
-	"CALL_PRESENT",
-	"CALL_RECEIVED",
-	"CONNECT_REQUEST",
-	"MO_TERM_CALL_CONF",
-	"ACTIVE",
-	"DISCONNECT_REQ",
-	"DISCONNECT_IND",
-	"illegal state 13",
-	"illegal state 14",
-	"illegal state 15",
-	"illegal state 16",
-	"illegal state 17",
-	"illegal state 18",
-	"RELEASE_REQ",
-	"illegal state 20",
-	"illegal state 21",
-	"illegal state 22",
-	"illegal state 23",
-	"illegal state 24",
-	"illegal state 25",
-	"MO_ORIG_MODIFY",
-	"MO_TERM_MODIFY",
-	"CONNECT_IND",
-	"illegal state 29",
-	"illegal state 30",
-	"illegal state 31",
-};
-
-static char strbuf[64];
-
-static const char *rr_cause_name(u_int8_t cause)
-{
-	if (cause < ARRAY_SIZE(rr_cause_names) &&
-	    rr_cause_names[cause])
-		return rr_cause_names[cause];
-
-	snprintf(strbuf, sizeof(strbuf), "0x%02x", cause);
-	return strbuf;
-}
-
 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;
@@ -207,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);
 		}
@@ -243,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;
 	}
 
@@ -269,561 +159,30 @@
 	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);
 	}
 
 	return 0;
 }
 
-static const char bcd_num_digits[] = {
-	'0', '1', '2', '3', '4', '5', '6', '7', 
-	'8', '9', '*', '#', 'a', 'b', 'c', '\0'
-};
-
-/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
-int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv,
-		      int h_len)
-{
-	u_int8_t in_len = bcd_lv[0];
-	int i;
-
-	for (i = 1 + h_len; i <= in_len; i++) {
-		/* lower nibble */
-		output_len--;
-		if (output_len <= 1)
-			break;
-		*output++ = bcd_num_digits[bcd_lv[i] & 0xf];
-
-		/* higher nibble */
-		output_len--;
-		if (output_len <= 1)
-			break;
-		*output++ = bcd_num_digits[bcd_lv[i] >> 4];
-	}
-	if (output_len >= 1)
-		*output++ = '\0';
-
-	return 0;
-}
-
-/* convert a single ASCII character to call-control BCD */
-static int asc_to_bcd(const char asc)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
-		if (bcd_num_digits[i] == asc)
-			return i;
-	}
-	return -EINVAL;
-}
-
-/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
-int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
-		      int h_len, const char *input)
-{
-	int in_len = strlen(input);
-	int i;
-	u_int8_t *bcd_cur = bcd_lv + 1 + h_len;
-
-	/* two digits per byte, plus type byte */
-	bcd_lv[0] = in_len/2 + h_len;
-	if (in_len % 2)
-		bcd_lv[0]++;
-
-	if (bcd_lv[0] > max_len)
-		return -EIO;
-
-	for (i = 0; i < in_len; i++) {
-		int rc = asc_to_bcd(input[i]);
-		if (rc < 0)
-			return rc;
-		if (i % 2 == 0)
-			*bcd_cur = rc;	
-		else
-			*bcd_cur++ |= (rc << 4);
-	}
-	/* append padding nibble in case of odd length */
-	if (i % 2)
-		*bcd_cur++ |= 0xf0;
-
-	/* return how many bytes we used */
-	return (bcd_cur - bcd_lv);
-}
-
-/* decode 'bearer capability' */
-static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
-			     const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-	int i, s;
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
-
-	/* octet 3 */
-	bcap->transfer = lv[1] & 0x07;
-	bcap->mode = (lv[1] & 0x08) >> 3;
-	bcap->coding = (lv[1] & 0x10) >> 4;
-	bcap->radio = (lv[1] & 0x60) >> 5;
-
-	if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
-		i = 1;
-		s = 0;
-		while(!(lv[i] & 0x80)) {
-			i++; /* octet 3a etc */
-			if (in_len < i)
-				return 0;
-			bcap->speech_ver[s++] = lv[i] & 0x0f;
-			bcap->speech_ver[s] = -1; /* end of list */
-			if (i == 2) /* octet 3a */
-				bcap->speech_ctm = (lv[i] & 0x20) >> 5;
-			if (s == 7) /* maximum speech versions + end of list */
-				return 0;
-		}
-	} else {
-		i = 1;
-		while (!(lv[i] & 0x80)) {
-			i++; /* octet 3a etc */
-			if (in_len < i)
-				return 0;
-			/* ignore them */
-		}
-		/* FIXME: implement OCTET 4+ parsing */
-	}
-
-	return 0;
-}
-
-/* encode 'bearer capability' */
-static int encode_bearer_cap(struct msgb *msg, int lv_only,
-			     const struct gsm_mncc_bearer_cap *bcap)
-{
-	u_int8_t lv[32 + 1];
-	int i = 1, s;
-
-	lv[1] = bcap->transfer;
-	lv[1] |= bcap->mode << 3;
-	lv[1] |= bcap->coding << 4;
-	lv[1] |= bcap->radio << 5;
-
-	if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
-		for (s = 0; bcap->speech_ver[s] >= 0; s++) {
-			i++; /* octet 3a etc */
-			lv[i] = bcap->speech_ver[s];
-			if (i == 2) /* octet 3a */
-				lv[i] |= bcap->speech_ctm << 5;
-		}
-		lv[i] |= 0x80; /* last IE of octet 3 etc */
-	} else {
-		/* FIXME: implement OCTET 4+ encoding */
-	}
-
-	lv[0] = i;
-	if (lv_only)
-		msgb_lv_put(msg, lv[0], lv+1);
-	else
-		msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
-
-	return 0;
-}
-
-/* decode 'call control cap' */
-static int decode_cccap(struct gsm_mncc_cccap *ccap, const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	/* octet 3 */
-	ccap->dtmf = lv[1] & 0x01;
-	ccap->pcp = (lv[1] & 0x02) >> 1;
-	
-	return 0;
-}
-
-/* decode 'called party BCD number' */
-static int decode_called(struct gsm_mncc_number *called,
-			 const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	/* octet 3 */
-	called->plan = lv[1] & 0x0f;
-	called->type = (lv[1] & 0x70) >> 4;
-
-	/* octet 4..N */
-	decode_bcd_number(called->number, sizeof(called->number), lv, 1);
-	
-	return 0;
-}
-
-/* encode 'called party BCD number' */
-static int encode_called(struct msgb *msg,
-			 const struct gsm_mncc_number *called)
-{
-	u_int8_t lv[18];
-	int ret;
-
-	/* octet 3 */
-	lv[1] = called->plan;
-	lv[1] |= called->type << 4;
-
-	/* octet 4..N, octet 2 */
-	ret = encode_bcd_number(lv, sizeof(lv), 1, called->number);
-	if (ret < 0)
-		return ret;
-
-	msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
-
-	return 0;
-}
-
-/* encode callerid of various IEs */
-static int encode_callerid(struct msgb *msg, int ie, 
-			   const struct gsm_mncc_number *callerid)
-{
-	u_int8_t lv[13];
-	int h_len = 1;
-	int ret;
-
-	/* octet 3 */
-	lv[1] = callerid->plan;
-	lv[1] |= callerid->type << 4;
-
-	if (callerid->present || callerid->screen) {
-		/* octet 3a */
-		lv[2] = callerid->screen;
-		lv[2] |= callerid->present << 5;
-		lv[2] |= 0x80;
-		h_len++;
-	} else
-		lv[1] |= 0x80;
-
-	/* octet 4..N, octet 2 */
-	ret = encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
-	if (ret < 0)
-		return ret;
-
-	msgb_tlv_put(msg, ie, lv[0], lv+1);
-
-	return 0;
-}
-
-/* decode 'cause' */
-static int decode_cause(struct gsm_mncc_cause *cause,
-			const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-	int i;
-
-	if (in_len < 2)
-		return -EINVAL;
-
-	cause->diag_len = 0;
-
-	/* octet 3 */
-	cause->location = lv[1] & 0x0f;
-	cause->coding = (lv[1] & 0x60) >> 5;
-	
-	i = 1;
-	if (!(lv[i] & 0x80)) {
-		i++; /* octet 3a */
-		if (in_len < i+1)
-			return 0;
-		cause->rec = 1;
-		cause->rec_val = lv[i] & 0x7f;
-		
-	}
-	i++;
-
-	/* octet 4 */
-	cause->value = lv[i] & 0x7f;
-	i++;
-
-	if (in_len < i) /* no diag */
-		return 0;
-
-	if (in_len - (i-1) > 32) /* maximum 32 octets */
-		return 0;
-
-	/* octet 5-N */
-	memcpy(cause->diag, lv + i, in_len - (i-1));
-	cause->diag_len = in_len - (i-1);
-
-	return 0;
-}
-
-/* encode 'cause' */
-static int encode_cause(struct msgb *msg, int lv_only,
-			const struct gsm_mncc_cause *cause)
-{
-	u_int8_t lv[32+4];
-	int i;
-
-	if (cause->diag_len > 32)
-		return -EINVAL;
-
-	/* octet 3 */
-	lv[1] = cause->location;
-	lv[1] |= cause->coding << 5;
-
-	i = 1;
-	if (cause->rec) {
-		i++; /* octet 3a */
-		lv[i] = cause->rec_val;
-	}
-	lv[i] |= 0x80; /* end of octet 3 */
-
-	/* octet 4 */
-	i++;
-	lv[i] = 0x80 | cause->value;
-
-	/* octet 5-N */
-	if (cause->diag_len) {
-		memcpy(lv + i, cause->diag, cause->diag_len);
-		i += cause->diag_len;
-	}
-
-	lv[0] = i;
-	if (lv_only)
-		msgb_lv_put(msg, lv[0], lv+1);
-	else
-		msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
-
-	return 0;
-}
-
-/* encode 'calling number' */
-static int encode_calling(struct msgb *msg, 
-			  const struct gsm_mncc_number *calling)
-{
-	return encode_callerid(msg, GSM48_IE_CALLING_BCD, calling);
-}
-
-/* encode 'connected number' */
-static int encode_connected(struct msgb *msg, 
-			    const struct gsm_mncc_number *connected)
-{
-	return encode_callerid(msg, GSM48_IE_CONN_BCD, connected);
-}
-
-/* encode 'redirecting number' */
-static int encode_redirecting(struct msgb *msg,
-			      const struct gsm_mncc_number *redirecting)
-{
-	return encode_callerid(msg, GSM48_IE_REDIR_BCD, redirecting);
-}
-
-/* decode 'facility' */
-static int decode_facility(struct gsm_mncc_facility *facility,
-			   const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	if (in_len > sizeof(facility->info))
-		return -EINVAL;
-
-	memcpy(facility->info, lv+1, in_len);
-	facility->len = in_len;
-
-	return 0;
-}
-
-/* encode 'facility' */
-static int encode_facility(struct msgb *msg, int lv_only,
-			   const struct gsm_mncc_facility *facility)
-{
-	u_int8_t lv[GSM_MAX_FACILITY + 1];
-
-	if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
-		return -EINVAL;
-
-	memcpy(lv+1, facility->info, facility->len);
-	lv[0] = facility->len;
-	if (lv_only)
-		msgb_lv_put(msg, lv[0], lv+1);
-	else
-		msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
-
-	return 0;
-}
-
-/* decode 'notify' */
-static int decode_notify(int *notify, const u_int8_t *v)
-{
-	*notify = v[0] & 0x7f;
-	
-	return 0;
-}
-
-/* encode 'notify' */
-static int encode_notify(struct msgb *msg, int notify)
-{
-	msgb_v_put(msg, notify | 0x80);
-
-	return 0;
-}
-
-/* encode 'signal' */
-static int encode_signal(struct msgb *msg, int signal)
-{
-	msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
-
-	return 0;
-}
-
-/* decode 'keypad' */
-static int decode_keypad(int *keypad, const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	*keypad = lv[1] & 0x7f;
-	
-	return 0;
-}
-
-/* encode 'keypad' */
-static int encode_keypad(struct msgb *msg, int keypad)
-{
-	msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
-
-	return 0;
-}
-
-/* decode 'progress' */
-static int decode_progress(struct gsm_mncc_progress *progress,
-			   const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 2)
-		return -EINVAL;
-
-	progress->coding = (lv[1] & 0x60) >> 5;
-	progress->location = lv[1] & 0x0f;
-	progress->descr = lv[2] & 0x7f;
-	
-	return 0;
-}
-
-/* encode 'progress' */
-static int encode_progress(struct msgb *msg, int lv_only,
-			   const struct gsm_mncc_progress *p)
-{
-	u_int8_t lv[3];
-
-	lv[0] = 2;
-	lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
-	lv[2] = 0x80 | (p->descr & 0x7f);
-	if (lv_only)
-		msgb_lv_put(msg, lv[0], lv+1);
-	else
-		msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
-
-	return 0;
-}
-
-/* decode 'user-user' */
-static int decode_useruser(struct gsm_mncc_useruser *uu,
-			   const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-	char *info = uu->info;
-	int info_len = sizeof(uu->info);
-	int i;
-
-	if (in_len < 1)
-		return -EINVAL;
-
-	uu->proto = lv[1];
-
-	for (i = 2; i <= in_len; i++) {
-		info_len--;
-		if (info_len <= 1)
-			break;
-		*info++ = lv[i];
-	}
-	if (info_len >= 1)
-		*info++ = '\0';
-	
-	return 0;
-}
-
-/* encode 'useruser' */
-static int encode_useruser(struct msgb *msg, int lv_only,
-			   const struct gsm_mncc_useruser *uu)
-{
-	u_int8_t lv[GSM_MAX_USERUSER + 2];
-
-	if (strlen(uu->info) > GSM_MAX_USERUSER)
-		return -EINVAL;
-
-	lv[0] = 1 + strlen(uu->info);
-	lv[1] = uu->proto;
-	memcpy(lv + 2, uu->info, strlen(uu->info));
-	if (lv_only)
-		msgb_lv_put(msg, lv[0], lv+1);
-	else
-		msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
-
-	return 0;
-}
-
-/* decode 'ss version' */
-static int decode_ssversion(struct gsm_mncc_ssversion *ssv,
-			    const u_int8_t *lv)
-{
-	u_int8_t in_len = lv[0];
-
-	if (in_len < 1 || in_len < sizeof(ssv->info))
-		return -EINVAL;
-
-	memcpy(ssv->info, lv + 1, in_len);
-	ssv->len = in_len;
-
-	return 0;
-}
-
-/* encode 'more data' */
-static int encode_more(struct msgb *msg)
-{
-	u_int8_t *ie;
-
-	ie = msgb_put(msg, 1);
-	ie[0] = GSM48_IE_MORE_DATA;
-
-	return 0;
-}
-
 /* 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;
@@ -831,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);
@@ -856,7 +215,7 @@
 	gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
 
 	lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
-	gsm0408_generate_lai(lai, bts->network->country_code,
+	gsm48_generate_lai(lai, bts->network->country_code,
 		     bts->network->network_code, bts->location_area_code);
 
 	mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
@@ -889,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;
@@ -900,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)
@@ -964,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;
@@ -974,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;
 
@@ -1000,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);
@@ -1026,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,
@@ -1034,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:
@@ -1048,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");
@@ -1056,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
@@ -1210,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();
@@ -1218,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;
@@ -1257,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);
 	}
 
@@ -1288,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");
@@ -1393,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:
@@ -1459,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;
@@ -1507,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;
@@ -1540,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 */
@@ -1652,7 +1017,8 @@
 		return;
 
 	DEBUGP(DCC, "new state %s -> %s\n",
-		cc_state_names[trans->cc.state], cc_state_names[state]);
+		gsm48_cc_state_name(trans->cc.state),
+		gsm48_cc_state_name(state));
 
 	trans->cc.state = state;
 }
@@ -1698,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
@@ -1759,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)
@@ -1776,7 +1142,7 @@
 
 	if (hooknum != GSM_HOOK_RR_PAGING)
 		return -EINVAL;
-  
+
 	if (!subscr)
 		return -EINVAL;
 	net = subscr->net;
@@ -1787,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:
@@ -1796,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);
@@ -1843,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);
 			}
@@ -1916,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 */
@@ -1935,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) {
@@ -2092,7 +1458,7 @@
 
 	memset(&setup, 0, sizeof(struct gsm_mncc));
 	setup.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* emergency setup is identified by msg_type */
 	if (msg_type == GSM48_MT_CC_EMERG_SETUP)
 		setup.emergency = 1;
@@ -2108,31 +1474,31 @@
 	/* bearer capability */
 	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
 		setup.fields |= MNCC_F_BEARER_CAP;
-		decode_bearer_cap(&setup.bearer_cap,
+		gsm48_decode_bearer_cap(&setup.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		setup.fields |= MNCC_F_FACILITY;
-		decode_facility(&setup.facility,
+		gsm48_decode_facility(&setup.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* called party bcd number */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
 		setup.fields |= MNCC_F_CALLED;
-		decode_called(&setup.called,
+		gsm48_decode_called(&setup.called,
 			      TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
 	}
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		setup.fields |= MNCC_F_USERUSER;
-		decode_useruser(&setup.useruser,
+		gsm48_decode_useruser(&setup.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		setup.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&setup.ssversion,
+		gsm48_decode_ssversion(&setup.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 	/* CLIR suppression */
@@ -2144,7 +1510,7 @@
 	/* cc cap */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
 		setup.fields |= MNCC_F_CCCAP;
-		decode_cccap(&setup.cccap,
+		gsm48_decode_cccap(&setup.cccap,
 			     TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
 	}
 
@@ -2204,28 +1570,28 @@
 
 	/* bearer capability */
 	if (setup->fields & MNCC_F_BEARER_CAP)
-		encode_bearer_cap(msg, 0, &setup->bearer_cap);
+		gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap);
 	/* facility */
 	if (setup->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &setup->facility);
+		gsm48_encode_facility(msg, 0, &setup->facility);
 	/* progress */
 	if (setup->fields & MNCC_F_PROGRESS)
-		encode_progress(msg, 0, &setup->progress);
+		gsm48_encode_progress(msg, 0, &setup->progress);
 	/* calling party BCD number */
 	if (setup->fields & MNCC_F_CALLING)
-		encode_calling(msg, &setup->calling);
+		gsm48_encode_calling(msg, &setup->calling);
 	/* called party BCD number */
 	if (setup->fields & MNCC_F_CALLED)
-		encode_called(msg, &setup->called);
+		gsm48_encode_called(msg, &setup->called);
 	/* user-user */
 	if (setup->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &setup->useruser);
+		gsm48_encode_useruser(msg, 0, &setup->useruser);
 	/* redirecting party BCD number */
 	if (setup->fields & MNCC_F_REDIRECTING)
-		encode_redirecting(msg, &setup->redirecting);
+		gsm48_encode_redirecting(msg, &setup->redirecting);
 	/* signal */
 	if (setup->fields & MNCC_F_SIGNAL)
-		encode_signal(msg, setup->signal);
+		gsm48_encode_signal(msg, setup->signal);
 	
 	new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
 
@@ -2244,7 +1610,7 @@
 
 	memset(&call_conf, 0, sizeof(struct gsm_mncc));
 	call_conf.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 #if 0
 	/* repeat */
 	if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
@@ -2255,19 +1621,19 @@
 	/* bearer capability */
 	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
 		call_conf.fields |= MNCC_F_BEARER_CAP;
-		decode_bearer_cap(&call_conf.bearer_cap,
+		gsm48_decode_bearer_cap(&call_conf.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
 		call_conf.fields |= MNCC_F_CAUSE;
-		decode_cause(&call_conf.cause,
+		gsm48_decode_cause(&call_conf.cause,
 			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
 	}
 	/* cc cap */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
 		call_conf.fields |= MNCC_F_CCCAP;
-		decode_cccap(&call_conf.cccap,
+		gsm48_decode_cccap(&call_conf.cccap,
 			     TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
 	}
 
@@ -2289,13 +1655,13 @@
 
 	/* bearer capability */
 	if (proceeding->fields & MNCC_F_BEARER_CAP)
-		encode_bearer_cap(msg, 0, &proceeding->bearer_cap);
+		gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap);
 	/* facility */
 	if (proceeding->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &proceeding->facility);
+		gsm48_encode_facility(msg, 0, &proceeding->facility);
 	/* progress */
 	if (proceeding->fields & MNCC_F_PROGRESS)
-		encode_progress(msg, 0, &proceeding->progress);
+		gsm48_encode_progress(msg, 0, &proceeding->progress);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2312,24 +1678,24 @@
 
 	memset(&alerting, 0, sizeof(struct gsm_mncc));
 	alerting.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		alerting.fields |= MNCC_F_FACILITY;
-		decode_facility(&alerting.facility,
+		gsm48_decode_facility(&alerting.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 
 	/* progress */
 	if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
 		alerting.fields |= MNCC_F_PROGRESS;
-		decode_progress(&alerting.progress,
+		gsm48_decode_progress(&alerting.progress,
 				TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		alerting.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&alerting.ssversion,
+		gsm48_decode_ssversion(&alerting.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2349,13 +1715,13 @@
 
 	/* facility */
 	if (alerting->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &alerting->facility);
+		gsm48_encode_facility(msg, 0, &alerting->facility);
 	/* progress */
 	if (alerting->fields & MNCC_F_PROGRESS)
-		encode_progress(msg, 0, &alerting->progress);
+		gsm48_encode_progress(msg, 0, &alerting->progress);
 	/* user-user */
 	if (alerting->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &alerting->useruser);
+		gsm48_encode_useruser(msg, 0, &alerting->useruser);
 
 	new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
 	
@@ -2371,10 +1737,10 @@
 	gh->msg_type = GSM48_MT_CC_PROGRESS;
 
 	/* progress */
-	encode_progress(msg, 1, &progress->progress);
+	gsm48_encode_progress(msg, 1, &progress->progress);
 	/* user-user */
 	if (progress->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &progress->useruser);
+		gsm48_encode_useruser(msg, 0, &progress->useruser);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2392,16 +1758,16 @@
 
 	/* facility */
 	if (connect->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &connect->facility);
+		gsm48_encode_facility(msg, 0, &connect->facility);
 	/* progress */
 	if (connect->fields & MNCC_F_PROGRESS)
-		encode_progress(msg, 0, &connect->progress);
+		gsm48_encode_progress(msg, 0, &connect->progress);
 	/* connected number */
 	if (connect->fields & MNCC_F_CONNECTED)
-		encode_connected(msg, &connect->connected);
+		gsm48_encode_connected(msg, &connect->connected);
 	/* user-user */
 	if (connect->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &connect->useruser);
+		gsm48_encode_useruser(msg, 0, &connect->useruser);
 
 	new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
 
@@ -2419,7 +1785,7 @@
 
 	memset(&connect, 0, sizeof(struct gsm_mncc));
 	connect.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* use subscriber as connected party number */
 	if (trans->subscr) {
 		connect.fields |= MNCC_F_CONNECTED;
@@ -2431,19 +1797,19 @@
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		connect.fields |= MNCC_F_FACILITY;
-		decode_facility(&connect.facility,
+		gsm48_decode_facility(&connect.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		connect.fields |= MNCC_F_USERUSER;
-		decode_useruser(&connect.useruser,
+		gsm48_decode_useruser(&connect.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		connect.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&connect.ssversion,
+		gsm48_decode_ssversion(&connect.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2492,29 +1858,29 @@
 
 	memset(&disc, 0, sizeof(struct gsm_mncc));
 	disc.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0);
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
 		disc.fields |= MNCC_F_CAUSE;
-		decode_cause(&disc.cause,
+		gsm48_decode_cause(&disc.cause,
 			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		disc.fields |= MNCC_F_FACILITY;
-		decode_facility(&disc.facility,
+		gsm48_decode_facility(&disc.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		disc.fields |= MNCC_F_USERUSER;
-		decode_useruser(&disc.useruser,
+		gsm48_decode_useruser(&disc.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		disc.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&disc.ssversion,
+		gsm48_decode_ssversion(&disc.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2545,19 +1911,19 @@
 
 	/* cause */
 	if (disc->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 1, &disc->cause);
+		gsm48_encode_cause(msg, 1, &disc->cause);
 	else
-		encode_cause(msg, 1, &default_cause);
+		gsm48_encode_cause(msg, 1, &default_cause);
 
 	/* facility */
 	if (disc->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &disc->facility);
+		gsm48_encode_facility(msg, 0, &disc->facility);
 	/* progress */
 	if (disc->fields & MNCC_F_PROGRESS)
-		encode_progress(msg, 0, &disc->progress);
+		gsm48_encode_progress(msg, 0, &disc->progress);
 	/* user-user */
 	if (disc->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &disc->useruser);
+		gsm48_encode_useruser(msg, 0, &disc->useruser);
 
 	/* store disconnect cause for T306 expiry */
 	memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc));
@@ -2579,29 +1945,29 @@
 
 	memset(&rel, 0, sizeof(struct gsm_mncc));
 	rel.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
 		rel.fields |= MNCC_F_CAUSE;
-		decode_cause(&rel.cause,
+		gsm48_decode_cause(&rel.cause,
 			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		rel.fields |= MNCC_F_FACILITY;
-		decode_facility(&rel.facility,
+		gsm48_decode_facility(&rel.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		rel.fields |= MNCC_F_USERUSER;
-		decode_useruser(&rel.useruser,
+		gsm48_decode_useruser(&rel.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		rel.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&rel.ssversion,
+		gsm48_decode_ssversion(&rel.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2638,13 +2004,13 @@
 
 	/* cause */
 	if (rel->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 0, &rel->cause);
+		gsm48_encode_cause(msg, 0, &rel->cause);
 	/* facility */
 	if (rel->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &rel->facility);
+		gsm48_encode_facility(msg, 0, &rel->facility);
 	/* user-user */
 	if (rel->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &rel->useruser);
+		gsm48_encode_useruser(msg, 0, &rel->useruser);
 
 	trans->cc.T308_second = 0;
 	memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc));
@@ -2667,29 +2033,29 @@
 
 	memset(&rel, 0, sizeof(struct gsm_mncc));
 	rel.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
 		rel.fields |= MNCC_F_CAUSE;
-		decode_cause(&rel.cause,
+		gsm48_decode_cause(&rel.cause,
 			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		rel.fields |= MNCC_F_FACILITY;
-		decode_facility(&rel.facility,
+		gsm48_decode_facility(&rel.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		rel.fields |= MNCC_F_USERUSER;
-		decode_useruser(&rel.useruser,
+		gsm48_decode_useruser(&rel.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		rel.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&rel.ssversion,
+		gsm48_decode_ssversion(&rel.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2732,13 +2098,13 @@
 
 	/* cause */
 	if (rel->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 0, &rel->cause);
+		gsm48_encode_cause(msg, 0, &rel->cause);
 	/* facility */
 	if (rel->fields & MNCC_F_FACILITY)
-		encode_facility(msg, 0, &rel->facility);
+		gsm48_encode_facility(msg, 0, &rel->facility);
 	/* user-user */
 	if (rel->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 0, &rel->useruser);
+		gsm48_encode_useruser(msg, 0, &rel->useruser);
 
 	trans_free(trans);
 
@@ -2754,17 +2120,17 @@
 
 	memset(&fac, 0, sizeof(struct gsm_mncc));
 	fac.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0);
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
 		fac.fields |= MNCC_F_FACILITY;
-		decode_facility(&fac.facility,
+		gsm48_decode_facility(&fac.facility,
 				TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
 	}
 	/* ss-version */
 	if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
 		fac.fields |= MNCC_F_SSVERSION;
-		decode_ssversion(&fac.ssversion,
+		gsm48_decode_ssversion(&fac.ssversion,
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
@@ -2780,7 +2146,7 @@
 	gh->msg_type = GSM48_MT_CC_FACILITY;
 
 	/* facility */
-	encode_facility(msg, 1, &fac->facility);
+	gsm48_encode_facility(msg, 1, &fac->facility);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2814,9 +2180,9 @@
 
 	/* cause */
 	if (hold_rej->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 1, &hold_rej->cause);
+		gsm48_encode_cause(msg, 1, &hold_rej->cause);
 	else
-		encode_cause(msg, 1, &default_cause);
+		gsm48_encode_cause(msg, 1, &default_cause);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2851,9 +2217,9 @@
 
 	/* cause */
 	if (retrieve_rej->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 1, &retrieve_rej->cause);
+		gsm48_encode_cause(msg, 1, &retrieve_rej->cause);
 	else
-		encode_cause(msg, 1, &default_cause);
+		gsm48_encode_cause(msg, 1, &default_cause);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2867,11 +2233,11 @@
 
 	memset(&dtmf, 0, sizeof(struct gsm_mncc));
 	dtmf.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* keypad facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) {
 		dtmf.fields |= MNCC_F_KEYPAD;
-		decode_keypad(&dtmf.keypad,
+		gsm48_decode_keypad(&dtmf.keypad,
 			      TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
 	}
 
@@ -2888,7 +2254,7 @@
 
 	/* keypad */
 	if (dtmf->fields & MNCC_F_KEYPAD)
-		encode_keypad(msg, dtmf->keypad);
+		gsm48_encode_keypad(msg, dtmf->keypad);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2903,9 +2269,9 @@
 
 	/* cause */
 	if (dtmf->fields & MNCC_F_CAUSE)
-		encode_cause(msg, 1, &dtmf->cause);
+		gsm48_encode_cause(msg, 1, &dtmf->cause);
 	else
-		encode_cause(msg, 1, &default_cause);
+		gsm48_encode_cause(msg, 1, &default_cause);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -2939,11 +2305,11 @@
 
 	memset(&modify, 0, sizeof(struct gsm_mncc));
 	modify.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
 	/* bearer capability */
 	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
 		modify.fields |= MNCC_F_BEARER_CAP;
-		decode_bearer_cap(&modify.bearer_cap,
+		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
 	}
 
@@ -2963,7 +2329,7 @@
 	gsm48_start_cc_timer(trans, 0x323, GSM48_T323);
 
 	/* bearer capability */
-	encode_bearer_cap(msg, 1, &modify->bearer_cap);
+	gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap);
 
 	new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
 
@@ -2981,11 +2347,11 @@
 
 	memset(&modify, 0, sizeof(struct gsm_mncc));
 	modify.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
 	/* bearer capability */
 	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
 		modify.fields |= MNCC_F_BEARER_CAP;
-		decode_bearer_cap(&modify.bearer_cap,
+		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
 	}
 
@@ -3003,7 +2369,7 @@
 	gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
 
 	/* bearer capability */
-	encode_bearer_cap(msg, 1, &modify->bearer_cap);
+	gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap);
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
@@ -3021,17 +2387,17 @@
 
 	memset(&modify, 0, sizeof(struct gsm_mncc));
 	modify.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
 	/* bearer capability */
 	if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
 		modify.fields |= GSM48_IE_BEARER_CAP;
-		decode_bearer_cap(&modify.bearer_cap,
+		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
 		modify.fields |= MNCC_F_CAUSE;
-		decode_cause(&modify.cause,
+		gsm48_decode_cause(&modify.cause,
 			     TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
 	}
 
@@ -3049,9 +2415,9 @@
 	gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
 
 	/* bearer capability */
-	encode_bearer_cap(msg, 1, &modify->bearer_cap);
+	gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap);
 	/* cause */
-	encode_cause(msg, 1, &modify->cause);
+	gsm48_encode_cause(msg, 1, &modify->cause);
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
@@ -3067,7 +2433,7 @@
 	gh->msg_type = GSM48_MT_CC_NOTIFY;
 
 	/* notify */
-	encode_notify(msg, notify->notify);
+	gsm48_encode_notify(msg, notify->notify);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -3081,9 +2447,9 @@
 
 	memset(&notify, 0, sizeof(struct gsm_mncc));
 	notify.callref = trans->callref;
-//	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len);
+//	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len);
 	if (payload_len >= 1)
-		decode_notify(&notify.notify, gh->data);
+		gsm48_decode_notify(&notify.notify, gh->data);
 
 	return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, &notify);
 }
@@ -3098,10 +2464,10 @@
 
 	/* user-user */
 	if (user->fields & MNCC_F_USERUSER)
-		encode_useruser(msg, 1, &user->useruser);
+		gsm48_encode_useruser(msg, 1, &user->useruser);
 	/* more data */
 	if (user->more)
-		encode_more(msg);
+		gsm48_encode_more(msg);
 
 	return gsm48_sendmsg(msg, trans);
 }
@@ -3115,11 +2481,11 @@
 
 	memset(&user, 0, sizeof(struct gsm_mncc));
 	user.callref = trans->callref;
-	tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0);
+	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0);
 	/* user-user */
 	if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
 		user.fields |= MNCC_F_USERUSER;
-		decode_useruser(&user.useruser,
+		gsm48_decode_useruser(&user.useruser,
 				TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
 	}
 	/* more data */
@@ -3133,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 {
@@ -3219,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);
 		}
@@ -3308,6 +2674,7 @@
 		}
 		/* Find lchan */
 		lchan = lchan_for_subscr(subscr);
+
 		/* If subscriber has no lchan */
 		if (!lchan) {
 			/* find transaction with this subscriber already paging */
@@ -3335,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,
@@ -3362,9 +2731,9 @@
 		"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,
-		cc_state_names[trans->cc.state]);
+		gsm48_cc_state_name(trans->cc.state));
 
 	/* Find function for current state and message */
 	for (i = 0; i < DOWNSLLEN; i++)
@@ -3399,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),
@@ -3438,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 */
@@ -3449,23 +2819,25 @@
 		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):"-",
-		gsm0408_cc_msg_names[msg_type], trans?(trans->cc.state):0,
-		cc_state_names[trans?(trans->cc.state):0]);
+		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));
 
 	/* Create transaction */
 	if (!trans) {
 		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");
@@ -3475,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 53c57ca..b770b52 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -29,6 +29,7 @@
 #include <netinet/in.h>
 
 #include <osmocore/msgb.h>
+#include <osmocore/gsm48.h>
 #include <openbsc/debug.h>
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/transaction.h>
@@ -42,75 +43,6 @@
  * or should OpenBSC always act as RTP relay/proxy in between (0) ? */
 int ipacc_rtp_direct = 1;
 
-
-const char *gsm0408_cc_msg_names[] = {
-	"unknown 0x00",
-	"ALERTING",
-	"CALL_PROC",
-	"PROGRESS",
-	"ESTAB",
-	"SETUP",
-	"ESTAB_CONF",
-	"CONNECT",
-	"CALL_CONF",
-	"START_CC",
-	"unknown 0x0a",
-	"RECALL",
-	"unknown 0x0c",
-	"unknown 0x0d",
-	"EMERG_SETUP",
-	"CONNECT_ACK",
-	"USER_INFO",
-	"unknown 0x11",
-	"unknown 0x12",
-	"MODIFY_REJECT",
-	"unknown 0x14",
-	"unknown 0x15",
-	"unknown 0x16",
-	"MODIFY",
-	"HOLD",
-	"HOLD_ACK",
-	"HOLD_REJ",
-	"unknown 0x1b",
-	"RETR",
-	"RETR_ACK",
-	"RETR_REJ",
-	"MODIFY_COMPL",
-	"unknown 0x20",
-	"unknown 0x21",
-	"unknown 0x22",
-	"unknown 0x23",
-	"unknown 0x24",
-	"DISCONNECT",
-	"unknown 0x26",
-	"unknown 0x27",
-	"unknown 0x28",
-	"unknown 0x29",
-	"RELEASE_COMPL",
-	"unknown 0x2b",
-	"unknown 0x2c",
-	"RELEASE",
-	"unknown 0x2e",
-	"unknown 0x2f",
-	"unknown 0x30",
-	"STOP_DTMF",
-	"STOP_DTMF_ACK",
-	"unknown 0x33",
-	"STATUS_ENQ",
-	"START_DTMF",
-	"START_DTMF_ACK",
-	"START_DTMF_REJ",
-	"unknown 0x38",
-	"CONG_CTRL",
-	"FACILITY",
-	"unknown 0x3b",
-	"STATUS",
-	"unknown 0x3c",
-	"NOTIFY",
-	"unknown 0x3f",
-};
-
-
 struct msgb *gsm48_msgb_alloc(void)
 {
 	return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
@@ -125,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) {
@@ -136,7 +68,7 @@
 				"Sending '%s' to MS.\n", msg->trx->bts->nr,
 				msg->trx->nr, msg->lchan->ts->nr,
 				gh->proto_discr & 0xf0,
-				gsm0408_cc_msg_names[gh->msg_type & 0x3f]);
+				gsm48_cc_msg_name(gh->msg_type));
 		else
 			DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
 				"Sending 0x%02x to MS.\n", msg->trx->bts->nr,
@@ -149,94 +81,6 @@
 	return rsl_data_request(msg, 0);
 }
 
-static void to_bcd(u_int8_t *bcd, u_int16_t val)
-{
-	bcd[2] = val % 10;
-	val = val / 10;
-	bcd[1] = val % 10;
-	val = val / 10;
-	bcd[0] = val % 10;
-	val = val / 10;
-}
-
-static char bcd2char(u_int8_t bcd)
-{
-	if (bcd < 0xa)
-		return '0' + bcd;
-	else
-		return 'A' + (bcd - 0xa);
-}
-
-/* only works for numbers in ascci */
-static u_int8_t char2bcd(char c)
-{
-	return c - 0x30;
-}
-
-
-void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
-			 u_int16_t mnc, u_int16_t lac)
-{
-	u_int8_t bcd[3];
-
-	to_bcd(bcd, mcc);
-	lai48->digits[0] = bcd[0] | (bcd[1] << 4);
-	lai48->digits[1] = bcd[2];
-
-	to_bcd(bcd, mnc);
-	/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
-#if 0
-	lai48->digits[1] |= bcd[2] << 4;
-	lai48->digits[2] = bcd[0] | (bcd[1] << 4);
-#else
-	lai48->digits[1] |= 0xf << 4;
-	lai48->digits[2] = bcd[1] | (bcd[2] << 4);
-#endif
-
-	lai48->lac = htons(lac);
-}
-
-int gsm48_generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi)
-{
-	u_int32_t *tptr = (u_int32_t *) &buf[3];
-
-	buf[0] = GSM48_IE_MOBILE_ID;
-	buf[1] = GSM48_TMSI_LEN;
-	buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
-	*tptr = htonl(tmsi);
-
-	return 7;
-}
-
-int gsm48_generate_mid_from_imsi(u_int8_t *buf, const char *imsi)
-{
-	unsigned int length = strlen(imsi), i, off = 0;
-	u_int8_t odd = (length & 0x1) == 1;
-
-	buf[0] = GSM48_IE_MOBILE_ID;
-	buf[2] = char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3);
-
-	/* if the length is even we will fill half of the last octet */
-	if (odd)
-		buf[1] = (length + 1) >> 1;
-	else
-		buf[1] = (length + 2) >> 1;
-
-	for (i = 1; i < buf[1]; ++i) {
-		u_int8_t lower, upper;
-
-		lower = char2bcd(imsi[++off]);
-		if (!odd && off + 1 == length)
-			upper = 0x0f;
-		else
-			upper = char2bcd(imsi[++off]) & 0x0f;
-
-		buf[2 + i] = (upper << 4) | lower;
-	}
-
-	return 2 + buf[1];
-}
-
 /* Section 9.1.8 / Table 9.9 */
 struct chreq {
 	u_int8_t val;
@@ -392,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)
 {
@@ -472,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;
@@ -782,4 +582,3 @@
 
 	return 0;
 }
-
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index f03c666..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;
@@ -426,7 +419,7 @@
 
 	oa[1] = 0xb9; /* networks-specific number, private numbering plan */
 
-	len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
+	len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, subscr->extension);
 
 	/* GSM 03.40 tells us the length is in 'useful semi-octets' */
 	oa[0] = strlen(subscr->extension) & 0xff;
@@ -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)
@@ -551,7 +543,7 @@
 	/* mangle first byte to reflect length in bytes, not digits */
 	address_lv[0] = da_len_bytes - 1;
 	/* convert to real number */
-	decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
+	gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
 	smsp += da_len_bytes;
 
 	gsms->protocol_id = *smsp++;
@@ -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_data.c b/openbsc/src/gsm_data.c
index a6b060c..176367d 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -1,4 +1,4 @@
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -25,8 +25,11 @@
 #include <errno.h>
 #include <ctype.h>
 
+#include <netinet/in.h>
+
 #include <openbsc/gsm_data.h>
 #include <osmocore/talloc.h>
+#include <osmocore/gsm_utils.h>
 #include <openbsc/abis_nm.h>
 #include <osmocore/statistics.h>
 
@@ -42,52 +45,41 @@
 	ts->e1_link.e1_ts_ss = e1_ts_ss;
 }
 
-static const char *pchan_names[] = {
-	[GSM_PCHAN_NONE]	= "NONE",
-	[GSM_PCHAN_CCCH]	= "CCCH",
-	[GSM_PCHAN_CCCH_SDCCH4]	= "CCCH+SDCCH4",
-	[GSM_PCHAN_TCH_F]	= "TCH/F",
-	[GSM_PCHAN_TCH_H]	= "TCH/H",
-	[GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
-	[GSM_PCHAN_PDCH]	= "PDCH",
-	[GSM_PCHAN_TCH_F_PDCH]	= "TCH/F_PDCH",
-	[GSM_PCHAN_UNKNOWN]	= "UNKNOWN",
+static const struct value_string pchan_names[] = {
+	{ GSM_PCHAN_NONE,	"NONE" },
+	{ GSM_PCHAN_CCCH,	"CCCH" },
+	{ GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" },
+	{ GSM_PCHAN_TCH_F,	"TCH/F" },
+	{ GSM_PCHAN_TCH_H,	"TCH/H" },
+	{ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" },
+	{ GSM_PCHAN_PDCH,	"PDCH" },
+	{ GSM_PCHAN_TCH_F_PDCH,	"TCH/F_PDCH" },
+	{ GSM_PCHAN_UNKNOWN,	"UNKNOWN" },
+	{ 0,			NULL }
 };
 
 const char *gsm_pchan_name(enum gsm_phys_chan_config c)
 {
-	if (c >= ARRAY_SIZE(pchan_names))
-		return "INVALID";
-
-	return pchan_names[c];
+	return get_value_string(pchan_names, c);
 }
 
 enum gsm_phys_chan_config gsm_pchan_parse(const char *name)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(pchan_names); i++) {
-		if (!strcasecmp(name, pchan_names[i]))
-			return i;
-	}
-
-	return -1;
+	return get_string_value(pchan_names, name);
 }
 
-static const char *lchan_names[] = {
-	[GSM_LCHAN_NONE]	= "NONE",
-	[GSM_LCHAN_SDCCH]	= "SDCCH",
-	[GSM_LCHAN_TCH_F]	= "TCH/F",
-	[GSM_LCHAN_TCH_H]	= "TCH/H",
-	[GSM_LCHAN_UNKNOWN]	= "UNKNOWN",
+static const struct value_string lchant_names[] = {
+	{ GSM_LCHAN_NONE,	"NONE" },
+	{ GSM_LCHAN_SDCCH,	"SDCCH" },
+	{ GSM_LCHAN_TCH_F,	"TCH/F" },
+	{ GSM_LCHAN_TCH_H,	"TCH/H" },
+	{ GSM_LCHAN_UNKNOWN,	"UNKNOWN" },
+	{ 0,			NULL }
 };
 
 const char *gsm_lchant_name(enum gsm_chan_t c)
 {
-	if (c >= ARRAY_SIZE(lchan_names))
-		return "INVALID";
-
-	return lchan_names[c];
+	return get_value_string(lchant_names, c);
 }
 
 static const struct value_string lchan_s_names[] = {
@@ -96,7 +88,7 @@
 	{ LCHAN_S_ACTIVE,	"ACTIVE" },
 	{ LCHAN_S_INACTIVE,	"INACTIVE" },
 	{ LCHAN_S_REL_REQ,	"RELEASE REQUESTED" },
-	{ 0,			NULL },
+	{ 0,			NULL }
 };
 
 const char *gsm_lchans_name(enum gsm_lchan_state s)
@@ -104,20 +96,18 @@
 	return get_value_string(lchan_s_names, s);
 }
 
-static const char *chreq_names[] = {
-	[GSM_CHREQ_REASON_EMERG]	= "EMERGENCY",
-	[GSM_CHREQ_REASON_PAG]		= "PAGING",
-	[GSM_CHREQ_REASON_CALL]		= "CALL",
-	[GSM_CHREQ_REASON_LOCATION_UPD]	= "LOCATION_UPDATE",
-	[GSM_CHREQ_REASON_OTHER]	= "OTHER",
+static const struct value_string chreq_names[] = {
+	{ GSM_CHREQ_REASON_EMERG,	"EMERGENCY" },
+	{ GSM_CHREQ_REASON_PAG,		"PAGING" },
+	{ GSM_CHREQ_REASON_CALL,	"CALL" },
+	{ GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" },
+	{ GSM_CHREQ_REASON_OTHER,	"OTHER" },
+	{ 0,				NULL }
 };
 
 const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
 {
-	if (c >= ARRAY_SIZE(chreq_names))
-		return "INVALID";
-
-	return chreq_names[c];
+	return get_value_string(chreq_names, c);
 }
 
 static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
@@ -374,27 +364,21 @@
 	return ts2str;
 }
 
-static const char *bts_types[] = {
-	[GSM_BTS_TYPE_UNKNOWN] = "unknown",
-	[GSM_BTS_TYPE_BS11] = "bs11",
-	[GSM_BTS_TYPE_NANOBTS] = "nanobts",
+static const struct value_string bts_types[] = {
+	{ GSM_BTS_TYPE_UNKNOWN,	"unknown" },
+	{ GSM_BTS_TYPE_BS11,	"bs11" },
+	{ GSM_BTS_TYPE_NANOBTS,	"nanobts" },
+	{ 0,			NULL }
 };
 
 enum gsm_bts_type parse_btstype(const char *arg)
 {
-	int i;
-	for (i = 0; i < ARRAY_SIZE(bts_types); i++) {
-		if (!strcmp(arg, bts_types[i]))
-			return i;
-	}	
-	return GSM_BTS_TYPE_BS11; /* Default: BS11 */
+	return get_string_value(bts_types, arg);
 }
 
 const char *btstype2str(enum gsm_bts_type type)
 {
-	if (type > ARRAY_SIZE(bts_types))
-		return "undefined";
-	return bts_types[type];
+	return get_value_string(bts_types, type);
 }
 
 struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
@@ -435,104 +419,83 @@
 	return NULL;
 }
 
-char *gsm_band_name(enum gsm_band band)
-{
-	switch (band) {
-	case GSM_BAND_450:
-		return "GSM450";
-	case GSM_BAND_480:
-		return "GSM450";
-	case GSM_BAND_750:
-		return "GSM750";
-	case GSM_BAND_810:
-		return "GSM810";
-	case GSM_BAND_850:
-		return "GSM850";
-	case GSM_BAND_900:
-		return "GSM900";
-	case GSM_BAND_1800:
-		return "DCS1800";
-	case GSM_BAND_1900:
-		return "PCS1900";
-	}
-	return "invalid";
-}
-
-enum gsm_band gsm_band_parse(const char* mhz)
-{
-	while (*mhz && !isdigit(*mhz))
-		mhz++;
-
-	if (*mhz == '\0')
-		return -EINVAL;
-
-	switch (atoi(mhz)) {
-	case 450:
-		return GSM_BAND_450;
-	case 480:
-		return GSM_BAND_480;
-	case 750:
-		return GSM_BAND_750;
-	case 810:
-		return GSM_BAND_810;
-	case 850:
-		return GSM_BAND_850;
-	case 900:
-		return GSM_BAND_900;
-	case 1800:
-		return GSM_BAND_1800;
-	case 1900:
-		return GSM_BAND_1900;
-	default:
-		return -EINVAL;
-	}
-}
-
-static const char *gsm_auth_policy_names[] = {
-	[GSM_AUTH_POLICY_CLOSED] = "closed",
-	[GSM_AUTH_POLICY_ACCEPT_ALL] = "accept-all",
-	[GSM_AUTH_POLICY_TOKEN] = "token",
+static const struct value_string auth_policy_names[] = {
+	{ GSM_AUTH_POLICY_CLOSED,	"closed" },
+	{ GSM_AUTH_POLICY_ACCEPT_ALL,	"accept-all" },
+	{ GSM_AUTH_POLICY_TOKEN,	"token" },
+	{ 0,				NULL }
 };
 
 enum gsm_auth_policy gsm_auth_policy_parse(const char *arg)
 {
-	int i;
-	for (i = 0; i < ARRAY_SIZE(gsm_auth_policy_names); i++) {
-		if (!strcmp(arg, gsm_auth_policy_names[i]))
-			return i;
-	}
-	return GSM_AUTH_POLICY_CLOSED;
+	return get_string_value(auth_policy_names, arg);
 }
 
 const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
 {
-	if (policy > ARRAY_SIZE(gsm_auth_policy_names))
-		return "undefined";
-	return gsm_auth_policy_names[policy];
+	return get_value_string(auth_policy_names, policy);
 }
 
-static const char *rrlp_mode_names[] = {
-	[RRLP_MODE_NONE] = "none",
-	[RRLP_MODE_MS_BASED] = "ms-based",
-	[RRLP_MODE_MS_PREF] = "ms-preferred",
-	[RRLP_MODE_ASS_PREF] = "ass-preferred",
+/* this should not be here but in gsm_04_08... but that creates
+   in turn a dependency nightmare (abis_nm depending on 04_08, ...) */
+static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid)
+{
+	u_int16_t mcc = raid->mcc;
+	u_int16_t mnc = raid->mnc;
+
+	buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
+	buf[1] = (mcc % 10);
+
+	/* I wonder who came up with the stupidity of encoding the MNC
+	 * differently depending on how many digits its decimal number has! */
+	if (mnc < 100) {
+		buf[1] |= 0xf0;
+		buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
+	} else {
+		buf[1] |= (mnc % 10) << 4;
+		buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
+	}
+
+	*(u_int16_t *)(buf+3) = htons(raid->lac);
+
+	buf[5] = raid->rac;
+
+	return 6;
+}
+
+void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts)
+{
+	raid->mcc = bts->network->country_code;
+	raid->mnc = bts->network->network_code;
+	raid->lac = bts->location_area_code;
+	raid->rac = bts->gprs.rac;
+}
+
+int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts)
+{
+	struct gprs_ra_id raid;
+
+	gprs_ra_id_by_bts(&raid, bts);
+
+	return gsm48_construct_ra(buf, &raid);
+}
+
+static const struct value_string rrlp_mode_names[] = {
+	{ RRLP_MODE_NONE,	"none" },
+	{ RRLP_MODE_MS_BASED,	"ms-based" },
+	{ RRLP_MODE_MS_PREF,	"ms-preferred" },
+	{ RRLP_MODE_ASS_PREF,	"ass-preferred" },
+	{ 0,			NULL }
 };
 
 enum rrlp_mode rrlp_mode_parse(const char *arg)
 {
-	int i;
-	for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) {
-		if (!strcmp(arg, rrlp_mode_names[i]))
-			return i;
-	}
-	return RRLP_MODE_NONE;
+	return get_string_value(rrlp_mode_names, arg);
 }
 
 const char *rrlp_mode_name(enum rrlp_mode mode)
 {
-	if (mode > ARRAY_SIZE(rrlp_mode_names))
-		return "none";
-	return rrlp_mode_names[mode];
+	return get_value_string(rrlp_mode_names, mode);
 }
 
 struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
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 943a5e8..323540f 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -44,6 +44,9 @@
 #include <openbsc/ipaccess.h>
 #include <osmocore/talloc.h>
 
+#define PRIV_OML 1
+#define PRIV_RSL 2
+
 /* data structure for one E1 interface with A-bis */
 struct ia_e1_handle {
 	struct bsc_fd listen_fd;
@@ -59,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,
@@ -230,26 +233,34 @@
 			return -EIO;
 		}
 		DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
-		if (bfd->priv_nr == 1) {
-			bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
+		if (bfd->priv_nr == PRIV_OML) {
+			bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1],
 						  E1INP_SIGN_OML, bts->c0,
 						  bts->oml_tei, 0);
-		} else if (bfd->priv_nr == 2) {
+		} else if (bfd->priv_nr == PRIV_RSL) {
 			struct e1inp_ts *e1i_ts;
 			struct bsc_fd *newbfd;
 			struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
 			
 			bfd->data = line = bts->oml_link->ts->line;
-			e1i_ts = &line->ts[2+trx_id - 1];
+			e1i_ts = &line->ts[PRIV_RSL + trx_id - 1];
 			newbfd = &e1i_ts->driver.ipaccess.fd;
 			e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN);
 
 			trx->rsl_link = e1inp_sign_link_create(e1i_ts,
 							E1INP_SIGN_RSL, trx,
 							trx->rsl_tei, 0);
+
+			if (newbfd->fd >= 0) {
+				LOGP(DINP, LOGL_ERROR, "BTS is still registered. Closing old connection.\n");
+				bsc_unregister_fd(newbfd);
+				close(newbfd->fd);
+				newbfd->fd = -1;
+			}
+
 			/* get rid of our old temporary bfd */
 			memcpy(newbfd, bfd, sizeof(*newbfd));
-			newbfd->priv_nr = 2+trx_id;
+			newbfd->priv_nr = PRIV_RSL + trx_id;
 			bsc_unregister_fd(bfd);
 			bsc_register_fd(newbfd);
 			talloc_free(bfd);
@@ -279,14 +290,14 @@
 
 	/* first read our 3-byte header */
 	hh = (struct ipaccess_head *) msg->data;
-	ret = recv(bfd->fd, msg->data, 3, 0);
-	if (ret < 0) {
-		if (errno != EAGAIN)
-			LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
+	ret = recv(bfd->fd, msg->data, sizeof(*hh), 0);
+	if (ret == 0) {
 		msgb_free(msg);
 		*error = ret;
 		return NULL;
-	} else if (ret == 0) {
+	} else if (ret != sizeof(*hh)) {
+		if (errno != EAGAIN)
+			LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
 		msgb_free(msg);
 		*error = ret;
 		return NULL;
@@ -297,9 +308,17 @@
 	/* then read te length as specified in header */
 	msg->l2h = msg->data + sizeof(*hh);
 	len = ntohs(hh->len);
+
+	if (len < 0 || TS1_ALLOC_SIZE < len + sizeof(*hh)) {
+		LOGP(DINP, LOGL_ERROR, "Can not read this packet. %d avail\n", len);
+		msgb_free(msg);
+		*error = -EIO;
+		return NULL;
+	}
+
 	ret = recv(bfd->fd, msg->l2h, len, 0);
 	if (ret < len) {
-		LOGP(DINP, LOGL_ERROR, "short read!\n");
+		LOGP(DINP, LOGL_ERROR, "short read! Got %d from %d\n", ret, len);
 		msgb_free(msg);
 		*error = -EIO;
 		return NULL;
@@ -456,7 +475,7 @@
 	/* set tx delay timer for next event */
 	e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
 	e1i_ts->sign.tx_timer.data = e1i_ts;
-	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000);
+	bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100);
 
 	return ret;
 }
@@ -497,6 +516,7 @@
 {
 	int ret;
 	int idx = 0;
+	int i;
 	struct e1inp_line *line;
 	struct e1inp_ts *e1i_ts;
 	struct bsc_fd *bfd;
@@ -524,12 +544,16 @@
 	/* create virrtual E1 timeslots for signalling */
 	e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
 
+	/* initialize the fds */
+	for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
+		line->ts[i].driver.ipaccess.fd.fd = -1;
+
 	e1i_ts = &line->ts[idx];
 
 	bfd = &e1i_ts->driver.ipaccess.fd;
 	bfd->fd = ret;
 	bfd->data = line;
-	bfd->priv_nr = 1;
+	bfd->priv_nr = PRIV_OML;
 	bfd->cb = ipaccess_fd_cb;
 	bfd->when = BSC_FD_READ;
 	ret = bsc_register_fd(bfd);
@@ -571,7 +595,7 @@
 		return bfd->fd;
 	}
 	LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
-	bfd->priv_nr = 2;
+	bfd->priv_nr = PRIV_RSL;
 	bfd->cb = ipaccess_fd_cb;
 	bfd->when = BSC_FD_READ;
 	ret = bsc_register_fd(bfd);
@@ -645,7 +669,7 @@
 	bfd->cb = ipaccess_fd_cb;
 	bfd->when = BSC_FD_READ | BSC_FD_WRITE;
 	bfd->data = line;
-	bfd->priv_nr = 1;
+	bfd->priv_nr = PRIV_OML;
 
 	if (bfd->fd < 0) {
 		LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
index 037ed60..670e3f1 100644
--- a/openbsc/src/ipaccess/ipaccess-config.c
+++ b/openbsc/src/ipaccess/ipaccess-config.c
@@ -57,6 +57,8 @@
 static char *software = NULL;
 static int sw_load_state = 0;
 static int oml_state = 0;
+static int dump_files = 0;
+static char *firmware_analysis = NULL;
 
 struct sw_load {
 	u_int8_t file_id[255];
@@ -427,7 +429,7 @@
 	return load;
 }
 
-static void find_sw_load_params(const char *filename)
+static int find_sw_load_params(const char *filename)
 {
 	struct stat stat;
 	struct sdp_header *header;
@@ -441,19 +443,19 @@
 	fd = open(filename, O_RDONLY);
 	if (!fd) {
 		perror("nada");
-		return;
+		return -1;
 	}
 
 	/* verify the file */
 	if (fstat(fd, &stat) == -1) {
 		perror("Can not stat the file");
-		return;
+		return -1;
 	}
 
 	ipaccess_analyze_file(fd, stat.st_size, 0, entry);
 	if (close(fd) != 0) {
 		perror("Close failed.\n");
-		return;
+		return -1;
 	}
 
 	/* try to find what we are looking for */
@@ -465,7 +467,57 @@
 		}
 	}
 
+	if (!sw_load1 || !sw_load2) {
+		fprintf(stderr, "Did not find data.\n");
+		talloc_free(tall_firm_ctx);
+		return -1;
+        }
+
 	talloc_free(tall_firm_ctx);
+	return 0;
+}
+
+static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd)
+{
+	int out_fd;
+	int copied;
+	char filename[4096];
+	off_t target;
+
+	if (!dump_files)
+		return;
+
+	if (sub_entry->header_entry.something1 == 0)
+		return;
+
+	snprintf(filename, sizeof(filename), "part.%d", part++);
+	out_fd = open(filename, O_WRONLY | O_CREAT, 0660);
+	if (out_fd < 0) {
+		perror("Can not dump firmware");
+		return;
+	}
+
+	target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4;
+	if (lseek(fd, target, SEEK_SET) != target) {
+		perror("seek failed");
+		close(out_fd);
+		return;
+	}
+
+	for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) {
+		char c;
+		if (read(fd, &c, sizeof(c)) != sizeof(c)) {
+			perror("copy failed");
+			break;
+		}
+
+		if (write(out_fd, &c, sizeof(c)) != sizeof(c)) {
+			perror("write failed");
+			break;
+		}
+	}
+
+	close(out_fd);
 }
 
 static void analyze_firmware(const char *filename)
@@ -476,6 +528,7 @@
 	struct llist_head *entry;
 	int fd;
 	void *tall_firm_ctx = 0;
+	int part = 0;
 
 	entry = talloc_zero(tall_firm_ctx, struct llist_head);
 	INIT_LLIST_HEAD(entry);
@@ -494,10 +547,6 @@
 	}
 
 	ipaccess_analyze_file(fd, stat.st_size, 0, entry);
-	if (close(fd) != 0) {
-		perror("Close failed.\n");
-		return;
-	}
 
 	llist_for_each_entry(header, entry, entry) {
 		printf("Printing header information:\n");
@@ -523,11 +572,19 @@
 			printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1));
 			printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2));
 			printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start));
+			printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset);
 			printf("\n\n");
+
+			dump_entry(sub_entry, part++, fd);
 		}
 		printf("\n\n");
 	}
 
+	if (close(fd) != 0) {
+		perror("Close failed.\n");
+		return;
+	}
+
 	talloc_free(tall_firm_ctx);
 }
 
@@ -547,6 +604,7 @@
 	printf("  -s --stream-id ID\n");
 	printf("  -d --software firmware\n");
 	printf("  -f --firmware firmware Provide firmware information\n");
+	printf("  -w --write-firmware. This will dump the firmware parts to the filesystem. Use with -f.\n");
 }
 
 int main(int argc, char **argv)
@@ -554,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");
@@ -580,9 +638,10 @@
 			{ "stream-id", 1, 0, 's' },
 			{ "software", 1, 0, 'd' },
 			{ "firmware", 1, 0, 'f' },
+			{ "write-firmware", 0, 0, 'w' },
 		};
 
-		c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:", long_options,
+		c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options,
 				&option_index);
 
 		if (c == -1)
@@ -615,11 +674,15 @@
 			break;
 		case 'd':
 			software = strdup(optarg);
-			find_sw_load_params(optarg);
+			if (find_sw_load_params(optarg) != 0)
+				exit(0);
 			break;
 		case 'f':
-			analyze_firmware(optarg);
-			exit(0);
+			firmware_analysis = optarg;
+			break;
+		case 'w':
+			dump_files = 1;
+			break;
 		case 'h':
 			print_usage();
 			print_help();
@@ -627,8 +690,13 @@
 		}
 	};
 
+	if (firmware_analysis)
+		analyze_firmware(firmware_analysis);
+
 	if (optind >= argc) {
-		fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
+		/* only warn if we have not done anything else */
+		if (!firmware_analysis)
+			fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
 		exit(2);
 	}
 
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-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c
index 8aba509..bc40c1e 100644
--- a/openbsc/src/ipaccess/ipaccess-firmware.c
+++ b/openbsc/src/ipaccess/ipaccess-firmware.c
@@ -31,7 +31,7 @@
 #define PART_LENGTH 138
 
 static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
-static_assert(sizeof(struct sdp_firmware) == 160, _right_header_length);
+static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length);
 
 /* more magic, the second "int" in the header */
 static char more_magic[] = { 0x10, 0x02 };
@@ -42,6 +42,10 @@
 	struct sdp_header *header;
 	char buf[4096];
 	int rc, i;
+	u_int16_t table_size;
+	u_int16_t table_offset;
+	off_t table_start;
+
 
 	rc = read(fd, buf, sizeof(*firmware_header));
 	if (rc < 0) {
@@ -63,9 +67,6 @@
 	}
 
 
-	if (!firmware_header)
-		return -1;
-
 	if (ntohl(firmware_header->file_length) != st_size) {
 		fprintf(stderr, "The filesize and the header do not match.\n");
 		return -1;
@@ -77,20 +78,30 @@
 	INIT_LLIST_HEAD(&header->header_list);
 	llist_add(&header->entry, list);
 
-	/* this semantic appears to be only the case for 0x0000 */
-	if (firmware_header->more_more_magic != 0)
+	table_offset = ntohs(firmware_header->table_offset);
+	table_start = lseek(fd, table_offset, SEEK_CUR);
+	if (table_start == -1) {
+		fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset);
 		return -1;
+	}
 
-	if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) {
-		fprintf(stderr, "The part length seems to be wrong.\n");
+	if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) {
+		fprintf(stderr, "The table size could not be read.\n");
+		return -1;
+	}
+
+	table_size = ntohs(table_size);
+
+	if (table_size % PART_LENGTH != 0) {
+		fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size);
 		return -1;
 	}
 
 	/* look into each firmware now */
-	for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) {
+	for (i = 0; i < table_size / PART_LENGTH; ++i) {
 		struct sdp_header_entry entry;
 		struct sdp_header_item *header_entry;
-		unsigned int offset = base_offset + sizeof(struct sdp_firmware);
+		unsigned int offset = table_start + 2;
 		offset += i * 138;
 
 		if (lseek(fd, offset, SEEK_SET) != offset) {
@@ -104,6 +115,11 @@
 			return -1;
 		}
 
+		header_entry = talloc_zero(header,  struct sdp_header_item);
+		header_entry->header_entry = entry;
+		header_entry->absolute_offset = base_offset;
+		llist_add(&header_entry->entry, &header->header_list);
+
 		/* now we need to find the SDP file... */
 		offset = ntohl(entry.start) + 4 + base_offset;
 		if (lseek(fd, offset, SEEK_SET) != offset) {
@@ -111,9 +127,6 @@
 			return -1;
 		}
 
-		header_entry = talloc_zero(header,  struct sdp_header_item);
-		header_entry->header_entry = entry;
-		llist_add(&header_entry->entry, &header->header_list);
 
 		ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
 	}
diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c
index 217e0bd..73ce2df 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 */
@@ -946,7 +946,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 +1108,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 cea0ba4..147a765 100644
--- a/openbsc/src/mgcp/mgcp_main.c
+++ b/openbsc/src/mgcp/mgcp_main.c
@@ -40,6 +40,8 @@
 #include <openbsc/mgcp.h>
 #include <openbsc/telnet_interface.h>
 
+#include "../../bscconfig.h"
+
 /* this is here for the vty... it will never be called */
 void subscr_put() { abort(); }
 
@@ -51,6 +53,14 @@
 static struct bsc_fd bfd;
 static int first_request = 1;
 static struct mgcp_config *cfg;
+const char *openbsc_version = "OpenBSC MGCP " PACKAGE_VERSION;
+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"
+	"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+	"This is free software: you are free to change and redistribute it.\n"
+	"There is NO WARRANTY, to the extent permitted by law.\n";
 
 static char *config_file = "mgcp.cfg";
 
@@ -64,6 +74,12 @@
 	printf(" -c --config-file filename The config file to use.\n");
 }
 
+static void print_version()
+{
+	printf("%s\n\n", openbsc_version);
+	printf(openbsc_copyright);
+}
+
 static void handle_options(int argc, char** argv)
 {
 	while (1) {
@@ -71,10 +87,11 @@
 		static struct option long_options[] = {
 			{"help", 0, 0, 'h'},
 			{"config-file", 1, 0, 'c'},
+			{"version", 0, 0, 'V'},
 			{0, 0, 0, 0},
 		};
 
-		c = getopt_long(argc, argv, "hc:", long_options, &option_index);
+		c = getopt_long(argc, argv, "hc:V", long_options, &option_index);
 
 		if (c == -1)
 			break;
@@ -87,6 +104,10 @@
 		case 'c':
 			config_file = talloc_strdup(tall_bsc_ctx, optarg);
 			break;
+		case 'V':
+			print_version();
+			exit(0);
+			break;
 		default:
 			/* ignore */
 			break;
@@ -145,14 +166,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)
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/rest_octets.c b/openbsc/src/rest_octets.c
index a57e7df..16996ce 100644
--- a/openbsc/src/rest_octets.c
+++ b/openbsc/src/rest_octets.c
@@ -350,7 +350,7 @@
 		bitvec_set_bit(&bv, H);
 		bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
 		bitvec_set_uint(&bv, si13->si_change_field, 4);
-		if (0) {
+		if (1) {
 			bitvec_set_bit(&bv, 0);
 		} else {
 			bitvec_set_bit(&bv, 1);
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/sccp/sccp.c b/openbsc/src/sccp/sccp.c
index 9cd7c9c..b1da2c7 100644
--- a/openbsc/src/sccp/sccp.c
+++ b/openbsc/src/sccp/sccp.c
@@ -471,6 +471,25 @@
 	return 0;
 }
 
+static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static const u_int32_t header_size = sizeof(struct sccp_data_it);
+
+	struct sccp_data_it *it;
+
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	it = (struct sccp_data_it *) msgb->l2h;
+	result->data_len = 0;
+	result->source_local_reference = &it->source_local_reference;
+	result->destination_local_reference = &it->destination_local_reference;
+	return 0;
+}
+
 
 /*
  * Send UDT. Currently we have a fixed address...
@@ -1307,8 +1326,12 @@
 	case SCCP_MSG_TYPE_UDT:
 		return _sccp_parse_udt(msg, result);
 		break;
+	case SCCP_MSG_TYPE_IT:
+		return _sccp_parse_it(msg, result);
+		break;
 	};
 
+	LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type);
 	return -1;
 }
 
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/system_information.c b/openbsc/src/system_information.c
index 36dc6b9..3f9d609 100644
--- a/openbsc/src/system_information.c
+++ b/openbsc/src/system_information.c
@@ -249,7 +249,7 @@
 	return sizeof(*si2);
 }
 
-struct gsm48_si_ro_info si_info = {
+static struct gsm48_si_ro_info si_info = {
 	.selection_params = {
 		.present = 0,
 	},
@@ -264,7 +264,7 @@
 	.gprs_ind = {
 		.si13_position = 0,
 		.ra_colour = 0,
-		.present = 0,
+		.present = 1,
 	},
 	.lsa_params = {
 		.present = 0,
@@ -287,9 +287,9 @@
 	si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
 
 	si3->cell_identity = htons(bts->cell_identity);
-	gsm0408_generate_lai(&si3->lai, bts->network->country_code,
-			     bts->network->network_code,
-			     bts->location_area_code);
+	gsm48_generate_lai(&si3->lai, bts->network->country_code,
+			   bts->network->network_code,
+			   bts->location_area_code);
 	si3->control_channel_desc = bts->si_common.chan_desc;
 	si3->cell_options = bts->si_common.cell_options;
 	si3->cell_sel_par = bts->si_common.cell_sel_par;
@@ -319,9 +319,9 @@
 	si4->header.skip_indicator = 0;
 	si4->header.system_information = GSM48_MT_RR_SYSINFO_4;
 
-	gsm0408_generate_lai(&si4->lai, bts->network->country_code,
-			     bts->network->network_code,
-			     bts->location_area_code);
+	gsm48_generate_lai(&si4->lai, bts->network->country_code,
+			   bts->network->network_code,
+			   bts->location_area_code);
 	si4->cell_sel_par = bts->si_common.cell_sel_par;
 	si4->rach_control = bts->si_common.rach_control;
 
@@ -384,9 +384,9 @@
 	si6->skip_indicator = 0;
 	si6->system_information = GSM48_MT_RR_SYSINFO_6;
 	si6->cell_identity = htons(bts->cell_identity);
-	gsm0408_generate_lai(&si6->lai, bts->network->country_code,
-			     bts->network->network_code,
-			     bts->location_area_code);
+	gsm48_generate_lai(&si6->lai, bts->network->country_code,
+			   bts->network->network_code,
+			   bts->location_area_code);
 	si6->cell_options = bts->si_common.cell_options;
 	si6->ncc_permitted = bts->si_common.ncc_permitted;
 
@@ -398,9 +398,9 @@
 static struct gsm48_si13_info si13_default = {
 	.cell_opts = {
 		.nmo 		= GPRS_NMO_III,
-		.t3168		= 1000,
-		.t3192		= 1000,
-		.drx_timer_max	= 1,
+		.t3168		= 1500,
+		.t3192		= 500,
+		.drx_timer_max	= 3,
 		.bs_cv_max	= 15,
 	},
 	.pwr_ctrl_pars = {
@@ -410,15 +410,15 @@
 		.pc_meas_chan	= 0, 	/* downling measured on CCCH */
 		.n_avg_i	= 15,
 	},
-	.bcch_change_mark	= 0,
+	.bcch_change_mark	= 1,
 	.si_change_field	= 0,
 	.pbcch_present		= 0,
 	{
 		.no_pbcch = {
-			.rac		= 0,
+			.rac		= 0,	/* needs to be patched */
 			.spgc_ccch_sup 	= 0,
 			.net_ctrl_ord	= 0,
-			.prio_acc_thr	= 0,
+			.prio_acc_thr	= 6,
 		},
 	},
 };
@@ -435,6 +435,8 @@
 	si13->header.skip_indicator = 0;
 	si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
 
+	si13_default.no_pbcch.rac = bts->gprs.rac;
+
 	ret = rest_octets_si13(si13->rest_octets, &si13_default);
 	if (ret < 0)
 		return ret;
@@ -446,6 +448,8 @@
 
 int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type)
 {
+	si_info.gprs_ind.present = bts->gprs.enabled;
+
 	switch (type) {
 	case RSL_SYSTEM_INFO_1:
 		return generate_si1(output, bts);
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 805dd12..c7de026 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -98,21 +98,16 @@
 	bsc_register_fd(&server_socket);
 }
 
+extern const char *openbsc_copyright;
+extern const char *openbsc_version;
+
 static void print_welcome(int fd) {
 	int ret;
 	static char *msg =
-		"Welcome to the OpenBSC Control interface\n"
-		"Copyright (C) 2008-2010 Harald Welte\n"
-		"Contributions by Daniel Willmann, Jan Lübbe, "
-		"Stefan Schmidt, Holger Freyther, Andreas Eversberg\n\n"
-		"License GPLv2+: GNU GPL version 2 or later "
-		"<http://gnu.org/licenses/gpl.html>\n"
-		"This is free software: you are free to change "
-		"and redistribute it.\n"
-		"There is NO WARRANTY, to the extent permitted "
-		"by law.\nType \"help\" to get a short introduction.\n";
+		"Welcome to the OpenBSC Control interface\n";
 
 	ret = write(fd, msg, strlen(msg));
+	ret = write(fd, openbsc_copyright, strlen(openbsc_copyright));
 }
 
 int telnet_close_client(struct bsc_fd *fd) {
@@ -122,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/cardshell.h b/openbsc/src/vty/cardshell.h
index d963a38..85164d2 100644
--- a/openbsc/src/vty/cardshell.h
+++ b/openbsc/src/vty/cardshell.h
@@ -1,5 +1,6 @@
-#define QUAGGA_PROGNAME	"OpenBSC"
-#define QUAGGA_VERSION	"0.01"
+#include "../../bscconfig.h"
+#define QUAGGA_PROGNAME	PACKAGE_NAME
+#define QUAGGA_VERSION	PACKAGE_VERSION
 #define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>"
 #define CONFIGFILE_MASK 022
 #define SYSCONFDIR "/usr/local/etc"
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 dc70e98..f2ac12d 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -1,5 +1,5 @@
 /* OpenBSC interface to quagga VTY */
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
  * All Rights Reserved
  *
  * This program is free software; you can redistribute it and/or modify
@@ -162,7 +162,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",
@@ -278,6 +278,7 @@
 static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
 {
 	struct gsm_bts_trx *trx;
+	int i;
 
 	vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
 	vty_out(vty, "  type %s%s", btstype2str(bts->type), VTY_NEWLINE);
@@ -313,6 +314,30 @@
 		config_write_e1_link(vty, &bts->oml_e1_link, "  oml ");
 		vty_out(vty, "  oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
 	}
+	vty_out(vty, "  gprs enabled %u%s", bts->gprs.enabled, VTY_NEWLINE);
+	if (bts->gprs.enabled) {
+		vty_out(vty, "  gprs routing area %u%s", bts->gprs.rac,
+			VTY_NEWLINE);
+		vty_out(vty, "  gprs cell bvci %u%s", bts->gprs.cell.bvci,
+			VTY_NEWLINE);
+		vty_out(vty, "  gprs nsei %u%s", bts->gprs.nse.nsei,
+			VTY_NEWLINE);
+		for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
+			struct gsm_bts_gprs_nsvc *nsvc =
+						&bts->gprs.nsvc[i];
+			struct in_addr ia;
+
+			ia.s_addr = htonl(nsvc->remote_ip);
+			vty_out(vty, "  gprs nsvc %u nsvci %u%s", i,
+				nsvc->nsvci, VTY_NEWLINE);
+			vty_out(vty, "  gprs nsvc %u local udp port %u%s", i,
+				nsvc->local_port, VTY_NEWLINE);
+			vty_out(vty, "  gprs nsvc %u remote udp port %u%s", i,
+				nsvc->remote_port, VTY_NEWLINE);
+			vty_out(vty, "  gprs nsvc %u remote ip %s%s", i,
+				inet_ntoa(ia), VTY_NEWLINE);
+		}
+	}
 
 	llist_for_each_entry(trx, &bts->trx_list, list)
 		config_write_trx_single(vty, trx);
@@ -573,19 +598,19 @@
 	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)) {
@@ -853,7 +878,7 @@
 	return CMD_SUCCESS;
 }
 
-static void _vty_output(struct debug_target *tgt, const char *line)
+static void _vty_output(struct log_target *tgt, const char *line)
 {
 	struct vty *vty = tgt->tgt_vty.vty;
 	vty_out(vty, "%s", line);
@@ -862,11 +887,11 @@
 		vty_out(vty, "\r");
 }
 
-struct debug_target *debug_target_create_vty(struct vty *vty)
+struct log_target *log_target_create_vty(struct vty *vty)
 {
-	struct debug_target *target;
+	struct log_target *target;
 
-	target = debug_target_create();
+	target = log_target_create();
 	if (!target)
 		return NULL;
 
@@ -888,11 +913,11 @@
 		return CMD_WARNING;
 	}
 
-	conn->dbg = debug_target_create_vty(vty);
+	conn->dbg = log_target_create_vty(vty);
 	if (!conn->dbg)
 		return CMD_WARNING;
 
-	debug_add_target(conn->dbg);
+	log_add_target(conn->dbg);
 	return CMD_SUCCESS;
 }
 
@@ -909,7 +934,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_set_imsi_filter(conn->dbg, argv[0]);
+	log_set_imsi_filter(conn->dbg, argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -926,7 +951,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_set_all_filter(conn->dbg, atoi(argv[0]));
+	log_set_all_filter(conn->dbg, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
 
@@ -943,7 +968,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_set_use_color(conn->dbg, atoi(argv[0]));
+	log_set_use_color(conn->dbg, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
 
@@ -960,7 +985,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_set_print_timestamp(conn->dbg, atoi(argv[0]));
+	log_set_print_timestamp(conn->dbg, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
 
@@ -973,8 +998,8 @@
       "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]);
+	int category = log_parse_category(argv[0]);
+	int level = log_parse_level(argv[1]);
 
 	conn = (struct telnet_connection *) vty->priv;
 	if (!conn->dbg) {
@@ -1000,7 +1025,7 @@
 
 DEFUN(logging_set_category_mask,
       logging_set_category_mask_cmd,
-      "logging set debug mask MASK",
+      "logging set log mask MASK",
       "Decide which categories to output.\n")
 {
 	struct telnet_connection *conn;
@@ -1011,7 +1036,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_parse_category_mask(conn->dbg, argv[0]);
+	log_parse_category_mask(conn->dbg, argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -1028,7 +1053,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_set_log_level(conn->dbg, atoi(argv[0]));
+	log_set_log_level(conn->dbg, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
 
@@ -1045,7 +1070,7 @@
 		return CMD_WARNING;
 	}
 
-	debug_del_target(conn->dbg);
+	log_del_target(conn->dbg);
 	talloc_free(conn->dbg);
 	conn->dbg = NULL;
 	return CMD_SUCCESS;
@@ -1321,7 +1346,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) {
@@ -1604,6 +1629,136 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd,
+	"gprs cell bvci <0-65535>",
+	"GPRS BSSGP VC Identifier")
+{
+	struct gsm_bts *bts = vty->index;
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.cell.bvci = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd,
+	"gprs nsei <0-65535>",
+	"GPRS NS Entity Identifier")
+{
+	struct gsm_bts *bts = vty->index;
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.nse.nsei = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd,
+	"gprs nsvc <0-1> nsvci <0-65535>",
+	"GPRS NS VC Identifier")
+{
+	struct gsm_bts *bts = vty->index;
+	int idx = atoi(argv[0]);
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.nsvc[idx].nsvci = atoi(argv[1]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd,
+	"gprs nsvc <0-1> local udp port <0-65535>",
+	"GPRS NS Local UDP Port")
+{
+	struct gsm_bts *bts = vty->index;
+	int idx = atoi(argv[0]);
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.nsvc[idx].local_port = atoi(argv[1]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd,
+	"gprs nsvc <0-1> remote udp port <0-65535>",
+	"GPRS NS Remote UDP Port")
+{
+	struct gsm_bts *bts = vty->index;
+	int idx = atoi(argv[0]);
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.nsvc[idx].remote_port = atoi(argv[1]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
+	"gprs nsvc <0-1> remote ip A.B.C.D",
+	"GPRS NS Remote IP Address")
+{
+	struct gsm_bts *bts = vty->index;
+	int idx = atoi(argv[0]);
+	struct in_addr ia;
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	inet_aton(argv[1], &ia);
+	bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
+	"gprs routing area <0-255>",
+	"GPRS Routing Area Code")
+{
+	struct gsm_bts *bts = vty->index;
+
+	if (!bts->gprs.enabled) {
+		vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->gprs.rac = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_gprs_enabled, cfg_bts_gprs_enabled_cmd,
+	"gprs enabled <0-1>",
+	"GPRS Enabled on this BTS")
+{
+	struct gsm_bts *bts = vty->index;
+
+	bts->gprs.enabled = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
 
 /* per TRX configuration */
 DEFUN(cfg_trx,
@@ -1622,9 +1777,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;
 
@@ -1865,7 +2020,14 @@
 	install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
 	install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
 	install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
-
+	install_element(BTS_NODE, &cfg_bts_gprs_enabled_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd);
+	install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd);
 
 	install_element(BTS_NODE, &cfg_trx_cmd);
 	install_node(&trx_node, dummy_config_write);
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");
diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c
index bbf8129..287d4ee 100644
--- a/openbsc/tests/gsm0408/gsm0408_test.c
+++ b/openbsc/tests/gsm0408/gsm0408_test.c
@@ -23,6 +23,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <arpa/inet.h>
+
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/debug.h>
@@ -52,13 +54,13 @@
      * Test the default/test setup. Coming from
      * bsc_hack.c dumps
      */
-    gsm0408_generate_lai(&lai48, 1, 1, 1);
+    gsm48_generate_lai(&lai48, 1, 1, 1);
     COMPARE(lai48.digits[0], ==, 0x00);
     COMPARE(lai48.digits[1], ==, 0xF1);
     COMPARE(lai48.digits[2], ==, 0x10);
     COMPARE(lai48.lac, ==, htons(0x0001));
 
-    gsm0408_generate_lai(&lai48, 602, 1, 15);
+    gsm48_generate_lai(&lai48, 602, 1, 15);
     COMPARE(lai48.digits[0], ==, 0x06);
     COMPARE(lai48.digits[1], ==, 0xF2);
     COMPARE(lai48.digits[2], ==, 0x10);
@@ -97,6 +99,8 @@
 {
 	test_location_area_identifier();
 	test_mi_functionality();
+
+	exit(0);
 }
 
 
diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c
index 982c168..eb41d3e 100644
--- a/openbsc/tests/sccp/sccp_test.c
+++ b/openbsc/tests/sccp/sccp_test.c
@@ -244,6 +244,52 @@
 	},
 };
 
+struct sccp_parse_header_result {
+	/* results */
+	int msg_type;
+	int wanted_len;
+	int src_ssn;
+	int dst_ssn;
+
+	int has_src_ref, has_dst_ref;
+	struct sccp_source_reference src_ref;
+	struct sccp_source_reference dst_ref;
+
+	/* the input */
+	const u_int8_t *input;
+	int input_len;
+};
+
+static const u_int8_t it_test[] = {
+0x10, 0x01, 0x07, 
+0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 };
+
+static const struct sccp_parse_header_result parse_result[] = {
+	{
+		.msg_type	= SCCP_MSG_TYPE_IT,
+		.wanted_len	= 0,
+		.src_ssn	= -1,
+		.dst_ssn	= -1,
+		.has_src_ref	= 1,
+		.has_dst_ref	= 1,
+
+		.src_ref	= {
+			.octet1 = 0x01,
+			.octet2 = 0x04,
+			.octet3 = 0x00
+		},
+		.dst_ref	= {
+			.octet1 = 0x01,
+			.octet2 = 0x07,
+			.octet3 = 0x94,
+		},
+
+		.input		= it_test,
+		.input_len	= sizeof(it_test),
+	},
+};
+
+
 /* testing procedure:
  *	- we will use sccp_write and see what will be set in the
  *	  outgoing callback
@@ -714,6 +760,59 @@
 	printf("survived\n");
 }
 
+static void test_sccp_parsing(void)
+{
+	for (current_test = 0; current_test < ARRAY_SIZE(parse_result); ++current_test) {
+		struct msgb *msg;
+		struct sccp_parse_result result;
+
+		msg = msgb_alloc_headroom(1024, 128, "parse-test");
+		msgb_put(msg, 1);
+		msg->l2h = msgb_put(msg, parse_result[current_test].input_len);
+		memcpy(msg->l2h, parse_result[current_test].input, msgb_l2len(msg));
+
+		memset(&result, 0, sizeof(result));
+		if (sccp_parse_header(msg, &result) != 0) {
+			fprintf(stderr, "Failed to parse test: %d\n", current_test);
+		} else {
+			if (parse_result[current_test].wanted_len != result.data_len) {
+				fprintf(stderr, "Unexpected data length.\n");
+				abort();
+			}
+
+			if (parse_result[current_test].has_src_ref) {
+				if (memcmp(result.source_local_reference,
+					   &parse_result[current_test].src_ref,
+					   sizeof(struct sccp_source_reference)) != 0) {
+					fprintf(stderr, "SRC REF did not match\n");
+					abort();
+				}
+			}
+
+			if (parse_result[current_test].has_dst_ref) {
+				if (memcmp(result.destination_local_reference,
+					   &parse_result[current_test].dst_ref,
+					   sizeof(struct sccp_source_reference)) != 0) {
+					fprintf(stderr, "DST REF did not match\n");
+					abort();
+				}
+			}
+
+			if (parse_result[current_test].src_ssn != -1) {
+				fprintf(stderr, "Not implemented.\n");
+				abort();
+			}
+
+			if (parse_result[current_test].dst_ssn != -1) {
+				fprintf(stderr, "Not implemented.\n");
+				abort();
+			}
+		}
+
+		msgb_free(msg);
+	}
+}
+
 
 int main(int argc, char **argv)
 {
@@ -722,6 +821,7 @@
 	test_sccp_udt_communication();
 	test_sccp_connection();
 	test_sccp_system_crash();
+	test_sccp_parsing();
 	return 0;
 }